目录
什么是反射?
反射的实现原理
反射的使用
为什么需要反射?(反射的作用/应用场景)
反射的缺点
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
java类加载:java类加载就是类加载器根据类的全限定名把.class的二进制字节码代表的静态存储结构转化为方法区的运行时数据结构,然后在内存中生成代表该类的Class对象,一个类有且只有一个Class对象。每次生成Java对象实际上都是通过这个Class对象获取整个类的结构并生成相应的java对象。(接口和抽象类也会被加载为class对象)
所以如果能够在运行时拿到Class对象,就可以生成java对象并进行调用,这就是java反射的本质。
Java 中的 java.lang.reflect 包提供了反射功能。java.lang.reflect 包中的类都没有 public 构造方法。
/*
* 获取Class对象的3种方法:
1、Class.forName("pacakge.className")
2、Class.class
3、javaObject.getClass()
* */
clazz = Class.forName("com.meituan.data.springbootdemo.User");
// clazz = user.getClass();
// clazz = User.class;
System.out.println(clazz);
/*
* 判断是否为某个类的实例的2种方法:
1、instanceof 关键字
2、Class 对象的 isInstance 方法(它是一个 Native 方法)
* */
Boolean isInstance;
// isInstance = user instanceof User;
isInstance = clazz.isInstance(user);
System.out.println("user is instance of User: "+isInstance);
/*
* 通过反射来创建实例对象的2种方法:
1、Class 对象的 newInstance 方法。
2、Constructor 对象的 newInstance 方法。
* */
User userNew;
// userNew = (User) clazz.newInstance();
Constructor constructor = clazz.getConstructor(String.class,Integer.class,String.class);
userNew = (User) constructor.newInstance("ck",88,"234567");
System.out.println(userNew);
/*
* 获取代理类的方法Method,并调用该方法(invoke)
* */
Method method = clazz.getMethod("getAge",null);
System.out.println(method.invoke(user));
/*
* 获取代理类的构造器Constructor
* */
/*
* 获取代理类的注解Annotation,并根据不同的注解进行不同的行为
* */
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation:annotations){
System.out.println(annotation);
if(annotation instanceof MyPrintAnnotation){
System.out.println(user);
}
if(annotation instanceof MyAnnotation){
System.out.println(((MyAnnotation) annotation).msg());
}
}
反射的作用可以用一句话概括:反射赋予了jvm动态编译的能力。动态编译可以最大限度的体现Java的灵活性(多态)。
否则类的元信息只能通过静态编译的形式实现(在编译期确定类型,绑定对象),而不能实现动态编译(在运行期确定类型,绑定对象)。也就是说在编译以后,程序在运行时的行为就是固定的了,如果要在运行时改变程序的行为,就需要动态编译,在Java中就需要反射机制。
情景一:不得已而为之
有的类是我们在编写程序的时候无法使用new一个对象来实例化对象的。例如:
情景二:动态加载(可以最大限度的体现Java的灵活性,并降低类的耦合性:多态)
有的类可以在用到时再动态加载到jvm中,这样可以减少jvm的启动时间,同时更重要的是可以动态的加载需要的对象(多态)。例如:
情景三:避免将程序写死到代码里
因为java代码是先通过编译器将.java文件编译成.class的二进制字节码文件,因此如果我们使用new Person()来实例化对象person会出现的问题就是如果我们希望更换person的实例对象,就要在源代码种更改然后重新编译再运行,但是如果我们将person的实例对象类名等信息编写在配置文件中,利用反射的Class.forName(className)方法来实例化java对象(因为实例化java对象都是根据全限定名查找到jvm内存中的class对象,并根据class对象中的累信息实例化得到java对象,因此xml文件中只要包含了权限定类名就可以通过反射实例化java对象),那么我们就可以更改配置文件,无需重新编译。例如: