一、定义
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。
在面向对象的世界里万事万物皆对象,类也是对象,是java.lang.Class的实例对象。我们创建一个类的实例对象可以通过new关键字创建比如:User user = new User()。new对象是静态加载类,在编译时刻就得加载所有可能使用的类,当我们不想在编译时刻加载所有的类,而是在运行时候需要哪个就动态的加载哪个比如:Class.froName(“类的全称”)。
二、用法
(一) 场景
1.在运行时判断任意一个对象所属的类。
2.在运行时构造任意一个类的对象。
3.在运行时判断任意一个类所具有的成员变量和方法。
4.在运行时调用任意一个对象的方法。
5.生成动态代理。
6.获取泛型信息。
7.获取注解信息。
8.获取配置文件信息。
(二) 如何使用
1. Class对象的获取(类类型,描述其他类所具有的这些特性)。
try{
// 类的.class(最安全/性能最好)属性(不会自动初始化该Class对象),任何一个类都一个隐含的静态成员变量
Class c1 = User.class;
// 已知该类的对象通过getClass方法
Class c2 = user.getClass();
// 运用Class.forName(String className)动态加载类,className需要是类的全限定名(最常用)。
Class c3 = Class.forName(“business.User”);
// 我们完全可以通过类的类类型(一个类只有一个类类型)创建该类的实例对象
User user = (User)c1.newInstance()
}catch(ClassNotFoundExpcetion e){
e.printStackTrace();
}
2.从Class类获取信息(Class类提供了大量的实例方法来获取该Class对象所对应的详细信息,Class类大致包含如下方法,其中每个方法都包含多个重载版本) 。
1)Constructor<T>getConstructor(Class<?>... parameterTypes)。
2)Method getMethod(String name,Class<?>... parameterTypes)。
3)Field getField(String name)。
4)Method Constructor Field这些类都实现了java.lang.reflect.Member接口,程序可以通过Method对象来执行相应的方法,通过Constructor对象来调用对应的构造器创建实例,通过Filed对象直接访问和修改对象的成员变量值。
3.创建对象
1)使用Class对象的newInstance()方法来创建该Class对象对应类的实例(这种方式要求该Class对象的对应类有默认构造器)。
2)先使用Class对象获取指定的Constructor对象, 再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例(通过这种方式可以选择指定的构造器来创建实例)。
4.访问成员变量
1)通过Class对象的的getField()方法可以获取该类所包含的全部或指定的成员变量Field,Filed提供了如下两组方法来读取和设置成员变量值。
PS:getDeclaredXxx方法可以获取所有的成员变量,无论private/protected。
2)getXxx(Objectobj): 获取obj对象的该成员变量的值,此处的Xxx对应8中基本类型,如果该成员变量的类型是引用类型,则取消get后面的Xxx。
3)setXxx(Objectobj, Xxx val): 将obj对象的该成员变量值设置成val值.此处的Xxx对应8种基本类型,如果该成员类型是引用类型,则取消set后面的Xxx。
5.调用方法
1)当获取到某个类对应的Class对象之后,就可以通过该Class对象的getMethod来获取一个Method数组或Method对象,每个Method对象对应一个方法,在获得Method对象之后,就可以通过调用invoke方法来调用该Method对象对应的方法。
public class MethodDemo1(){
public static void main(String[] args){
// 要获取print方法就是获取类的信息,要先获取类的类类型
A a1 = new A();
Class c = a1.getClass();
/*
*获取方法由方法名和参数决定
*getMethod获取的是public方法
*getDeclaredMethod自己声明的方法
*/
try{
//Method m = c.getMethod(“print”, new Class[]{int.class, int.class});
Method m = c.getMethod(“print”, int.class, int.class);
// 方法的反射操作是作用于m对象来进行方法的调用达到和a1.print调用的效果相同
// a1.print(10,10);
// 如果方法有参数则放回具体参数,没有则放null
Object o = m.invoke(a1, 10, 10); // 输出20
} catch (Exception e){
e.printStackTrace();
}
}
}
class A(){
public void print(int a, int b){
System.out.println(a+b);
}
public void print(String a, String b){
System.out.println(a.toUpperCase()+”,”+b.toLowerCase());
}
}
三、Java反射了解集合泛型的本质
public class MethodDemo2(){
public static void main(String[] args){
ArrayList list = new ArrayList();
ArrayList<String> list1 = new ArrayList<String>();
Class c1 = list.getClass();
Class c2 = list.getClass();
System.out.printlf(c1 == c2);
//反射的操作都是在编译以后
/*
*返回结果为true则说明编译以后集合的泛型是去泛型化的;
*集合的泛型是为了防止输入错误,java的泛型只在编译阶段有效,编译以后无效
*/
try{
Method e = c2.getMethod(“add”,Object.class);
e.invoke(list1,10);
System.out.println(list1.size());
//会发现,size=1说明此时泛型失效了,并且注意list1此时不能用for循环遍历
}catch(Exception e){
e.printStackTrace();
}
}
}