一、概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
1. Class对象
所有类都是在对其第一次使用时,类加载器首先会检查这个类的Class对象是否已被加载过,如果尚未加载,默认的类加载器就会根据类名查找对应的.class文件。了使用类而做的准备工作一般有以下3个步骤:
- 加载:由类加载器完成,找到对应的字节码,创建一个Class对象
- 链接:验证类中的字节码,为静态域分配空间
- 初始化:如果该类有超类,则对其初始化,执行静态初始化器和静态初始化块
所以,运行着的每个类都会有一个对应的Class对象,这个对象包含着类的几乎所有有关信息。
想要在运行时获取一个类的信息,首先需要获取它的Class对象的引用,使用功能Class.forName(“Base”)可以实现该目的,或者使用base.class。
注意,使用功能”.class”来创建Class对象的引用时,不会自动初始化该Class对象,使用forName()会自动初始化该Class对象,所以建议使用forName和getClass方法:
//第一种方法:forName
try {
Class<?> class1 = Class.forName("com.app.Person");
System.out.println( class1 );
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//第二种方法:getClass
Person person = new Person();
Class<?> class3 = person.getClass();
2. 向下转型与instanceof
Person per = new Student(); //向上转型
Student stu = (Student) per; //向下转型
因为Java支持多态,一个父类类型的引用可以指向任一它的子类的实例,但向下转型就不一样,父类类型的引用可以指向任一子类的实例,所以per还可能指向的是Teacher子类的实例,如果贸然把per强制转换为Student类型的引用,有可能会出错:
Exception in thread "main" java.lang.ClassCastException: human.Person cannot be cast to human.Student at human.TestMain.main(TestMain.java:15)
所以,在进行向下转型的时候一定要先用instanceof判断引用指向的实例的类型:
if(stu instanceof Student){
Student stu = (Student) per;
}
3. 反射:获取运行时类信息
Class类与java.lang.reflect类库一起对反射进行了支持,该类库包含Field、Method和Constructor类,这些类的对象由JVM在启动时创建,用以表示未知类里对应的成员。这样的话就可以使用Contructor创建新的对象,用get()和set()方法获取和修改类中与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。另外,还可以调用getFields()、getMethods()和getConstructors()等许多便利的方法,以返回表示字段、方法、以及构造器对象的数组,这样,对象信息可以在运行时被完全确定下来,而在编译时不需要知道关于类的任何事情。
二、反射使用总结
(1)获取成员的方法:
Class<?> class1 = Class.forName("com.app.Person");
//获取所有的public方法
Method[] methods = class1.getMethods();
Method getMethod(String name, Class[] params) //根据方法名和参数,返回一个具体的具有public属性的方法
Method[] getMethods() //返回所有具有public属性的方法数组
Method getDeclaredMethod(String name, Class[] params) //根据方法名和参数,返回一个具体的方法(不分public和非public属性)
Method[] getDeclaredMethods() //返回该类中的所有的方法数组(不分public和非public属性)
(2)获取所有实现的接口:getInterfaces()
Class<?> class1 = Class.forName("com.app.Person");
//获取所有的接口
Class<?>[] interS = class1.getInterfaces() ;
(3)获取父类:getSuperclass()
Class<?> superclass = class1.getSuperclass() ;
(4)获取所有的构造函数:getConstructors()
Constructor<?>[] constructors = class1.getConstructors();
Constructor getConstructor(Class[] params) //根据构造函数的参数,返回一个具体的具有public属性的构造函数
Constructor getConstructors() //返回所有具有public属性的构造函数数组
Constructor getDeclaredConstructor(Class[] params) //根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)
Constructor getDeclaredConstructors() //返回该类中所有的构造函数数组(不分public和非public属性)
代码示例:
try {
//创建类
Class<?> class1 = Class.forName("com.app.Person");
//无参构造函数
Object object = class1.newInstance() ;
//有参构造函数:一个参数
Constructor<?> constructor = class1.getDeclaredConstructor( String.class ) ;
constructor.newInstance( "1000" ) ;
//有参构造函数:二个参数
Constructor<?> constructor2 = class1.getDeclaredConstructor( String.class , String.class ) ;
constructor2.newInstance( "1001" , "jack" ) ;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace() ;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
(5)获取所有的属性:getDeclaredFields()
Field[] field = class1.getDeclaredFields();
- getDeclaredFields()获得某个类的所有申明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
- getFields()获得某个类的所有的公共(public)的字段,包括父类。
Field getField(String name) //根据变量名,返回一个具体的具有public属性的成员变量
Field[] getFields() //返回具有public属性的成员变量的数组
Field getDeclaredField(String name) //根据变量名,返回一个成员变量(不分public和非public属性)
Field[] getDelcaredField() //返回所有成员变量组成的数组(不分public和非public属性)
代码示例:
public static void main(String[] args) throws Exception {
Class<?> clz = Class.forName("A");
Object o = clz.newInstance();
Method m = clz.getMethod("foo", String.class);
for (int i = 0; i < 16; i++) {
m.invoke(o, Integer.toString(i));
}
}
(6)创建实例:newInstance()
//创建实例化:相当于 new 了一个对象
Object object = class1.newInstance() ;
//向下转型
Person person = (Person) object ;
三、常见场景
1. 动态代理
代理模式是为了提供额外或不同的操作,而插入的用来替代”实际”对象的对象,这些操作涉及到与”实际”对象的通信,因此代理通常充当中间人角色。Java的动态代理比代理的思想更前进了一步,它可以动态地创建代理并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的策略。以下是一个动态代理示例:
接口和实现类:
public interface Interface {
void doSomething();
void somethingElse(String arg);
}
public class RealObject implements Interface {
public void doSomething() {
System.out.println("doSomething.");
}
public void somethingElse(String arg) {
System.out.println("somethingElse " + arg);
}
}
动态代理对象处理器:
public class DynamicProxyHandler implements InvocationHandler {
private Object proxyed;
public DynamicProxyHandler(Object proxyed) {
this.proxyed = proxyed;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
System.out.println("代理工作了.");
return method.invoke(proxyed, args);
}
}
测试类:
public class Main {
public static void main(String[] args) {
RealObject real = new RealObject();
Interface proxy = (Interface) Proxy.newProxyInstance(
Interface.class.getClassLoader(), new Class[] {Interface.class},
new DynamicProxyHandler(real));
proxy.doSomething();
proxy.somethingElse("luoxn28");
}
}
2. 通过反射获取泛型的类型
(1)如果是继承基类而来的泛型,就用 getGenericSuperclass() , 转型为 ParameterizedType 来获得实际类型:
ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericSuperclass();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for(Type actualTypeArgument: actualTypeArguments) {
System.out.println(actualTypeArgument);
}
(2)如果是实现接口而来的泛型,就用 getGenericInterfaces() , 针对其中的元素转型为 ParameterizedType 来获得实际类型:
ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericInterfaces()[0];
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}