反射
如何在运行期间查找对象和类信息
主要用两种形式:
- 传统的“RTTI”,他假定我们已知编译和运行期拥有所有类型。
- Java反射机制,利用他可在运行期独立查找类信息。
为什么不用RTTI,因为它有一个限制:类型必须是在编译期间已知的。
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。对于一个对象,都能够调用它的任意方法和属性。
这种动态获取信息以及动态调用对象的方法的功能称为Java语言的反射机制。
用反射的条件?
要想使用反射机制,就必须要先获取该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法、属性、类名、父类、实现的所有接口等等)。
在运行期,一旦我们想生成那个类的一个对象,用于执行程序的JVM首先会检查那个类型的class对象是否已经载入。若尚未载入,JVM就会查找同名的.class文件,并将其载入。
每一个类对应着一个字节码文件也就对应着一个class类型的对象,也就是字节码文件对象。
反射常用API
在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的class对象。
1.获取反射中的class对象
首先我们介绍一下动态加载和静态加载。
动态加载:程序在运行时调用相应方法,即使其他方法是错误的,程序依旧会执行。通过动态加载可以让程序的可延长性大大提升,对以后的维护和扩展有重要意义。
静态加载:程序在编译时执行。在执行过程中加载所有可能执行到的程序。在这种加载方式下,只要加载中一个方法出错,程序就不能运行。我们一般写程序默认的是静态加载。
有三种方法:
- 当知道该类的全路径名时,可以使用
Class.forName(类名)
- 动态加载,运行时,开始装入类,并作类的静态初始化。
- 指定什么类名就获取什么类字节码文件对象。
- 该方法含义是加载参数指定的类,并且初始化他。
Class clz = String.class
只适合在编译前就知道要操作的Class。
- 静态加载(编译时已加载)
- 通过类对象的获取class对象
obj.getClass()。
- 静态加载(编译时已加载)
第一种有更好的扩展性,所以一般情况下,第一种用的较多。
2.通过反射创建类对象
有两种方式:
通过Class对象的
newInstance()
方法Class clz = Person.class;Person per = (Person)clz.newInstance();
通过Constructor对象的
newInstance()
- 选择无参构造方法构造对象:
Class clz = Person.class; Constructor constructor = clz.getConstructor(); Person per = (Person)constructor.newInstance();
- 选择带参的构造方法来构造对象
Class clz = Person.class; Constructor constructor = clz.getConstructor(String.class, int.class); Person per = (Person)constructor.newInstance("小明", 15);
3.通过反射获取类属性、方法、构造器
通过class对象的getFields()
可以获取到class的非私有属性。
通过class对象的getDeclaredFields()
方法可以获取包括私有属性在内的所有属性。
通过class对象得getMethods
获取该类中的所有方法,包括继承和实现的方法。
通过class对象得getDeclaredMethods
获取该类中的所有方法(包括私有方法),不包括包括继承方法。
PS:私有方法和私有属性不能直接访问,因为权限不够。不过还是有办法的。
method.setAccessible(true)
field.setAccessible(true)
4.通过反射调用私有方法
通过反射,我们可以调用其它类的私有方法。
import java.lang.reflect.Method;
class A
{
private void pow(int n)
{
System.out.println(n * n);
}
}
class Test
{
public static void main(String args[]) throws Exception
{
Class<A> c = A.class;
Object obj = c.newInstance();
Method m = c.getDeclaredMethod("pow", new Class[]{ int.class });
m.setAccessible(true);//修改访问权限
m.invoke(obj, 4);//调用方法
}
}