Java反射(Reflection)
A. Class类
1.概念
class
- class(包括interface)本质是数据类型(Type)
- 无继承关系的数据类型无法赋值
String s = new Double(899.98); //无法赋值,Java是强类型语言,编译器报错
Class
- class/interface的数据类型Class
- 每加载一个class,JVM都为其创建一个Class类型的实例,并关联起来
- JVM持有的每个Class实例都指向一个数据类型(class/interface)
- 一个Class实例包含了该class的完整信息
小结
- JVM为每个加载的class创建对应的Class实例,并在实例中保存该class的所有信息
- 如果获取了某个Class实例,则可以获取到该实例的对应的class的所有信息
- 通过Class实例获取class信息的方法称为反射(Reflection)
2.如何获取一个class的Class实例
-
Type.class
-
getClass()
-
Class.forName
3.Class实例在JVM中是唯一的
- 可以用==比较两个Class实例
4.反射的目的
- 当获得某个Object实例时,就可以获取该Object的class实例信息
5.从Class实例获取class信息
- getName() // 获取完整类名
- getSimpleName() // 获取类名
- getPackage() //获取包名
6.从Class实例判断class类型
- isInterface()
- isEnum()
- isArarry()
- isPrimitive() //判断是不是基本类型,虽然基本类型变量本身不是Class,但JVM内部会创建包装类型
7.创建class实例
- newInstance() //调用无参数的构造方法创建一个新的实例,有参数的无法调用
8.利用JVM动态加载class的特性,可以在运行期根据条件加载不同的实现类(或叫控制加载class)
// Commons Logging优先使用Log4j
LogFactory factroy;
if (isClassPresent("org.apache.logging.log4j.Logger")) {
factory = createLog4j();
} else {
factroy = createJdkLog();
}
boolean isClassPresent(String name) {
try{
Class.forName(name);
return true;
}catch (Exception e){
return false;
}
}
B.Field 访问字段
1.通过Class实例获取field信息
- getField(name) // 获取某个public的field(包括父类)
- getDeclaredField(name) // 获取当前类的某个field(不包括父类)
- getFields() // 获取所以public的field (包括父类)
- getDeclaredFields() // 获取当前类的所有field(不包括父类)
2.通过Field实例获取field信息
- getName() //获得field名称
- getType() //获得field字段定义的类型
- getModifiers() //获取field字段的修饰符
3.Field实例获取或设置某个对象的字段
- get(Object instance) // 获取一个实例的该字段的值
- set(Object instance , Object fieldValue) // 设置某个实例的字段值
Integer n = new Integer(123);
Class cls = n.getClass();
Field f = cls.getDeclaredField("value");
f.get(n); // 123, 相当于n.value
f.set(n, 456); //相当于n.value = 456
4.设置setAccessible(true)来访问非public字段
-
setAccessible(true)可能会失败:
-
定义了SecurityManager
-
SecurityManager 的规则阻止对Field设置accessible:
例如,规则应用于所有java和javax开头的package的类
-
C. Method 调用方法
1.通过Class实例获取method信息
- getMethod(name,Class…) //获取某个public的method(包括父类)
- getDeclaredMethod(name, Class…) //获取当前类的某个method(不包括父类)
- getMethods() // 获取所有public的method(包括父类)
- getDeclaredMethods() // 获取当前类的所有method(不包括父类)
- 注意:,Class…表示可变参数。Java定义中可变参数就是数组参数,但调用起来时不用写成数组形式
2.通过Method获取method信息
- getNmae() //获取方法名称
- getReturnType() // 获取方法返回 类型
- getParameterTypes() //获取方法参数类型
- getModifiers()// 获取方法修饰符
2.1调用无参数的Method
- Object invoke(Object obj)
Integer n = new Integer(123);
Class cls = n.getClass();
Method m = cls.getMethod("toString");
String s = (String) m.invoke(n);
// "123" ,相当于 String s = n.toString();
2.2调用带参数的Method
Object invoke(Object obj, Object… args)
Integer n = new Integer(123);
Class cls = n.getClass();
Method m = cls.getMethod("compareTo",Integer.class);
Integer s = (Integer) m.invoke(n,456);
// "-1" ,相当于 int r = n.compareTo(456);
3.设置setAccessible(true)来访问非public方法
-
setAccessible(true)可能会失败:
-
定义了SecurityManager
-
SecurityManager 的规则阻止对Method设置accessible:
例如,规则应用于所有java和javax开头的package的类
-
4.从父类获取的Method,作用于子类实例时
- 实际调用方法是子类覆写的方法
- 保证了多态的正确性
class Person{
public void hello(){
System.out.println("Person:hello");
}
}
class Student extend Person{
public void hello(){
System.out.println("Student:hello");
}
}
Method m = Person.class.getMethod("hello");
m.invoke(new Student());
// 相当于如下
// Person p = new Student();
// p.hello();
D. Constructor 调用构造函数
1.Constructor对象包含一个构造方法的所有信息,可以创建一个实例(通过Class实例可以获取Constructor信息)
注意,Class.newInstance()只能调用public无参数构造方法;Constructor总是当前类定义的构造方法,Constructor和继承是没有关系的,所以也不可能获得父类的构造方法
- getConstructor(Class… ) //获取某个public的Constructor
- getDeclaredConstructor(Class… ) //获取某个Constructor
- getConstructors(Class… ) //获取所有public的Constructor
- getDeclaredConstructors(Class… ) //获取所有Constructor
2.通过Constructor实例可以创建一个实例对象
- newInstance(Object… parameters)
Class cls = Student.class;
Constructor c = cls.getDeclaredConstructor(String.class, int.class);
//c.setAccessible(true);
Student s = (Student) c.newInstance("Yang",27);
s.hello();
3.设置setAccessible(true)来访问非public构造方法
-
setAccessible(true)可能会失败:
-
定义了SecurityManager
-
SecurityManager 的规则阻止对Constructor设置accessible:
例如,规则应用于所有java和javax开头的package的类
-
E.获取继承关系
1.getSuperclass() 获取父类对象
-
当拿到一个实例的时,可以获取父类的Class
-
getSuperclass() //通过Class实例可以获取父类对象
注意
- 当父类为Object时,返回的是null
- 当父类为interface时,返回是null
-
Class s = Integer.class.getSuperclass(); // Number.class
Object.class.getSuperclass(); //null
Runnable.class.getSuperclass(); //null
2.getInterfaces() 获取直接实现的接口
- 如果当前类实现interface,可以调用getInterfaces()获得直接实现的interface对象
- Class[] getInterfaces() // 获得直接实现的interfaces数组
- 不包括间接实现的interface
- 没有interface的class返回空数组
Class[] ifs = Integer.class.getInterfaces(); // [Comparable.class]
Class[] ifs = java.util.ArrayList.class.getInterfaces();
//[List.Class, RandomAccess.class, Cloneable.class, Serializaable.class]
Class[] ifs = Math.class.getinterfaces(); // [] ,没有实现任何接口
Class[] ifs = java.util.List.class.getInterfaces(); //[Collection.class]
- 如果要获取当前类所有的interface(直接实现和间接实现的interface),使用***递归法***来实现
Class s = Student.class;
printAllInterfaces(s);
void printAllInterfaces(Class cls){
System.out.println(cls.getSimpleName() +":");
_printAllInterfaces(cls);
System.out.println();
}
void _printAllInterfaces(Class cls){
Class[] ifs = cls.getInterfaces();
for(Class f : ifs){
System.out.println(f.getSimpleName());
System.out.print(",");
_printAllInterfaces(f);
}
Class sup = cls.getSuperclass();
if(sup != null){
_printAllInterfaces(sup);
}
}
3.isAssignableFrom() 判断向上转型是否成立
- boolean isAssignableFrom(Class)
Number.class.isAssignableFrom(Integer.class); // true
Integer.class.isAssignableFrom(Number.class); // false
the end.