Java反射
1. 什么是反射(Reflection)?
Java
的反射机制是在运行状态,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法称为Java
语言的反射机制(关键字:运行状态),换句话说,Java
程序可以加载一个运行是才得知名称的class
,获取其完整构造(但不包括methods
定义),并生成其对象实体、或对其fields
设值、或唤起其methods
。
2. 反射机制主要提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法
3. 反射机制的相关类
-
Class
类:代表一个类,位于java.lang
包下 -
Field
类:代表类的成员变量(成员变量也称为类的属性) -
Method
类:代表类的方法 -
Constructor
类:代表类的构造方法 -
Array
类:提供了动态创建数组,以及访问数组的元素的静态方法
4. Java中的Class介绍
Class
类十分特殊,它没有共有的构造方法,被JVM调用(简单的理解:new
对象或者被类加载器加载的时候),在Java中,每个class都有一个相应的Class
对象。也就是说,当我们编写一个类,编译完成后,在生成的.class
文件中,就会产生一个Class
对象,用于表示这个类的类型信息。
5. Java中Class对象的三种获取方式
- 利用对象调用
getClass()
方法获取该对象的Class实例; - 使用
Class
类的静态方法forName()
,用类的名字获取一个Class
实例; - 类运用
.class
的方式来获取Class
实例,对于基本数据类型的封装类,还可以采用.TYPE
来获取相应的基本数据类型的Class
实例;
说明:在运行期间,如果我们要产生某个类的对象,JVM会检查该类型的Class
对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class
文件并加载它。一旦某个类型的Class
对象已被加载到内存,就可以用它来产生该类型的所有对象。
//方式一:利用对象调用`getClass()`方法获取该对象的Class实例;
Person person = new Person();
Class extends Person> personClazz01 = person.getclass();
//方式二:使用`Class`类的静态方法`forName()`
try{
Class> personClazz02 = Class.forName("Person");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
//方式三:类运用`.class`的方式类获取Class实例
Class extends Person> personClazz03 = Person.class;
6. Java反射机制中的一些重要的方法
(1)Class类
Class
代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。
-
获得类相关的方法
方法 用途 asSubclass(Class clazz)
把传递的类的对象转换成代表其子类的对象 getClassLoader()
获得类的类加载器 getClasses()
返回一个数组,数组中包含该类中所有公共类和接口类的对象 getDeclaredClasses()
返回一个数组,数组中包含该类中所有类和接口类的对象 forName(String className)
根据类名返回类的对象 getName()
获得类的完整路径名字 newInstance()
创建类的实例 getPackage()
获得类的包 getSimpleName()
获得类的名字 getSuperClass()
获得当前类继承的父类的名字 getInterfaces()
获得当前类实现的类或是接口 -
获得类中属性相关的方法
方法 用途 getField(String name)
获得某个公有的属性对象 getFields()
获得所有公有的属性对象 getDeclaredField(String name)
获得某个属性对象 getDeclaredFields()
获得所有属性对象 -
获得类中注解相关的方法
方法 用途 getAnnotation(Class annotationClass)
返回该类中与参数类型匹配的公有注解对象 getAnnotations()
返回该类所有的公有注解对象 getDeclaredAnnotation(Class annotationClass)
返回该类中与参数类型匹配的所有注解对象 getDeclaredAnnotations()
返回该类所有的注解对象 -
获得类中构造器相关的方法
方法 用途 getConstructor(Class...> parameterTypes)
获得该类中与参数类型匹配的公有构造方法 getConstructors()
获得该类的所有公有构造方法 getDeclaredConstructor(Class...> parameterTypes)
获得该类中与参数类型匹配的构造方法 getDeclaredConstructors()
获得该类所有构造方法 -
获得类中方法相关的方法
方法 用途 getMethod(String name, Class...> parameterTypes)
获得该类某个公有的方法 getMethods()
获得该类所有公有的方法 getDeclaredMethod(String name, Class...> parameterTypes)
获得该类的某个方法 getDeclaredMethods()
获得该类所有方法 -
类中其他重要的方法
方法 用途 isAnnotation()
如果是注解类型则返回 true
isAnnotationPresent(Class extends Annotation> annotationClass)
如果是指令类型注解类型则返回 true
isAnonymousClass()
如果是匿名类则返回 true
isArray()
如果是一个数组类则返回 true
isEnum()
如果是枚举类则返回 true
isInstance(Object obj)
如果 obj
是该类的实例则返回true
isInterface()
如果是接口类则返回 true
isLocalClass()
如果是局部类则返回 true
isMemberClass()
如果是内部类则返回 true
(2)Field类
Field
代表类的成员变量(成员变量也成为类的属性)
方法 | 用途 |
---|---|
equals(Object obj) |
属性与obj 相同则返回true |
get(Object obj) |
获得obj 中对应的属性值 |
set(Object obj, Object value) |
设置obj 中对于属性值 |
(3)Method类
Method
代表类的方法。
方法 | 用途 |
---|---|
invoke(Object obj, Object... args) |
传递object 对象及参数调用该对象对应的方法 |
(4)Constructor类
Constructor
代表类的构造方法
方法 | 用途 |
---|---|
newInstance(Object...initargs) |
根据传递的参数创建类的对象 |
7.示例
-
如何通过反射获取私有变量和私有方法
Person
类:/** *Created by guirunxiang on 2020/3/22 */ import java.lang.reflect.Field; public class Person { private String name = "zhangsan"; private String age; public String getName(){ return name; } public void setName(String name){ this.name = name; } public static void main(String[] args) { Person person = new Person(); System.out.println("before: " + getPrivateValue(person, "name")); person.setName("lisi"); System.out.println("after: " + getPrivateValue(person, "name")); } private static Object getPrivateValue(Person person, String fieldName){ try{ Field field = person.getClass().getDeclaredField(fieldName); //参数为true, 打开禁用访问控制检查 //setAccessible(true)并不是将方法的访问权限改成了public,而是取消java的权限控制 //所以即使是public方法,其accessible属性默认也是false field.setAccessible(true); return field.get(person); }catch (Exception e){ e.printStackTrace(); } return null; } }
参数为
true
的运行结果:
-
案例演示反射
Person
类:package com.guirunxiang.reflection; public class Person { private int age; private String name; public Person() { } public Person(int age, String name) { this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Smoking
接口:package com.guirunxiang.reflection; public interface Smoking { public void smoke(int count); }
SuperPerson
类:
package com.guirunxiang.reflection;
public class SuperPerson extends Person implements Smoking{
private boolean isMan;
public void fly(){
System.out.println("走你~~~");
}
@Override
public void smoke(int count) {
}
}
Main
方法:
package com.guirunxiang.reflection;
import com.sun.org.apache.xpath.internal.SourceTree;
import java.lang.reflect.*;
import java.util.logging.FileHandler;
public class Main {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException, NoSuchMethodException {
test1();
System.out.println("====================================");
test2();
System.out.println("====================================");
test3();
System.out.println("====================================");
test4();
System.out.println("====================================");
test5();
System.out.println("====================================");
test6();
System.out.println("====================================");
test7();
System.out.println("====================================");
test8();
System.out.println("====================================");
}
//Demo1:通过Java反射机制得到类的包名和类名
public static void test1(){
Person person = new Person();
System.out.println("Test1: 包名: " + person.getClass().getPackage());
}
//Demo2:验证所有的类都是Class类的实例对象
public static void test2() throws ClassNotFoundException {
//定义两个类型都未知的Class,设置初值为null, 看看如何给它们复制成Person类
Class> class1 = null;
Class> class2 = null;
//写法1:可能抛出ClassNotFoundException[多用这个写法]
class1 = Class.forName("com.guirunxiang.reflection.Person");
System.out.println("Test2:(写法1) 包名" + class1.getPackage().getName() + "," + "完整类名:" + class1.getName());
//写法2:
class2 = Person.class;
System.out.println("Test2:(写法2) 包名" + class2.getPackage().getName() + "," + "完整类名:" + class2.getName());
}
//Demo3:通过Java反射机制,用Class创建类对象[这也是反射存在的意义所在]
public static void test3() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class> class1 = null;
class1 = Class.forName("com.guirunxiang.reflection.Person");
//由于这里不能带参数,所以实例化这个类的Person,一定要有无参构造函数
Person person = (Person) class1.newInstance();
person.setAge(22);
person.setName("guirunxiang");
System.out.println("Test3:" + person.getName() + ":" + person.getAge());
}
//Demo4: 通过Java反射机制得到一个类的构造函数,并实现创建带参实例对象
public static void test4() throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class> class1 = null;
Person person1 = null;
Person person2 = null;
class1 = Class.forName("com.guirunxiang.reflection.Person");
//得到一系列构造函数集合
Constructor>[] constructors = class1.getConstructors();
try{
person1 = (Person) constructors[0].newInstance();
}catch (InvocationTargetException e){
e.printStackTrace();
}
person1.setAge(20);
person1.setName("grx");
person2 = (Person) constructors[1].newInstance(29, "grx");
System.out.println("Test4:" + person1.getName() + ":" + person1.getAge() + " , " + person2.getName() + " : " + person2.getAge());
}
//Demo5:通过Java反射机制操作成员变量,set和get
public static void test5() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
Class> class1 = null;
class1 = Class.forName("com.guirunxiang.reflection.Person");
Object obj = class1.newInstance();
Field nameField = class1.getDeclaredField("name");
nameField.setAccessible(true);;
nameField.set(obj, "grx");
System.out.println("Test5:修改属性之后得到属性变量的值:" + nameField.get(obj));
}
//Demo6:通过Java反射机制得到类的一些属性:继承的接口,父类,函数信息,成员信息,类型等
public static void test6() throws ClassNotFoundException {
Class> class1 = null;
class1 = Class.forName("com.guirunxiang.reflection.Person");
//取得父类名称
Class> superClass = class1.getSuperclass();
System.out.println("Test6: SuperMan类的父类名:" + superClass.getName());
System.out.println("====================================");
//取得类中成员
Field[] fields = class1.getDeclaredFields();
for(int i = 0; i < fields.length; i++){
System.out.println("类中的成员:" + fields[i]);
}
System.out.println("====================================");
//取得类方法
Method[] methods = class1.getDeclaredMethods();
for(int i = 0; i < methods.length; i++){
System.out.println("Test6, 取得SuperMan类的方法:");
System.out.println("函数名:" + methods[i].getName());
System.out.println("函数返回类型:" + methods[i].getReturnType());
System.out.println("函数访问修饰符:" + Modifier.toString(methods[i].getModifiers()));
System.out.println("函数代码写法:" + methods[i]);
}
System.out.println("====================================");
//取得类实现的接口
Class> interfaces[] = class1.getInterfaces();
for(int i = 0; i < interfaces.length; i++){
System.out.println("实现的接口类目:" + interfaces[i].getName());
}
}
//Demo7:通过Java反射机制调用类方法
public static void test7() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class> class1 = null;
class1 = Class.forName("com.guirunxiang.reflection.SuperPerson");
System.out.println("Test7:调用无参方法fly():");
Method method = class1.getMethod("fly");
method.invoke(class1.newInstance());
System.out.println("调用有参方法smoke(int m):");
method = class1.getMethod("smoke", int.class);
method.invoke(class1.newInstance(), 100);
}
/**
* Demo8:通过Java反射机制得到类加载器信息
* 在Java中有三种类加载器
* 1.Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见
* 2.Extension ClassLoader 用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类
* 3.AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。
*
*/
public static void test8() throws ClassNotFoundException {
Class> class1 = null;
class1 = Class.forName("com.guirunxiang.reflection.Person");
String name = class1.getClassLoader().getClass().getName();
System.out.println("Test8:类加载器类名:" + name);
}
}
运行结果截图: