Java反射机制详解
时间:2022/11/03
一. 引出
在学习反射的时候,大家可能会纠结反射技术有什么作用,下面我们通过这里需求来说明反射是如何解决现有技术不能解决的问题的:
根据配置文件re.properties指定的信息,创建Cat对象并调用方法hi(这样的需求经常出现在框架的配置文件中,即通过外部配置文件,在不修改源码的情况下来控制程序,符合设计模式中的ocp原则(开闭原则:不修改源码,扩容功能)
我们首先定义一个Cat实体类:
1 package reflection; 2 3 public class Cat { 4 private String name; 5 6 public Cat(){ 7 8 } 9 10 public Cat(String name){ 11 this.name = name; 12 } 13 14 public String getName() { 15 return name; 16 } 17 18 public void setName(String name) { 19 this.name = name; 20 } 21 22 public void hi(){ 23 System.out.println("我是一只小猫"); 24 } 25 26 27 }
然后定义一个配置文件re.properties:
1 classfullpath = reflection.Cat 2 method = hi
然后在ReflectionQuestion类的main方法中编写实现逻辑,如下所示,我们可以通过Properties类来获取配置文件中对应字段的内容,但是由于此时获取的内容是String类型的,所以我们无法通过传统new的方式来生成一个对象,这里就说明通过传统的方式无法解决上面这个问题,所以我们这里用到了反射机制,我们可以将类的全路径传入Class类的forName方法来获取这个类的Class对象(这里的Class是一个类,可能比较绕),通过类的Class对象就可以获取到这个类的实例、方法和成员变量等(这里的方法和成员变量都是对象,体现了Java中万物皆对象的思想)。
1 package reflection; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.IOException; 7 import java.lang.reflect.Method; 8 import java.util.Properties; 9 10 public class ReflectionQuestion { 11 12 public static void main(String[] args) throws Exception { 13 // 1.使用Properties类,可以读写配置文件 14 // System.out.println(new File(".").getAbsolutePath()); // 查看当前路径 15 Properties properties = new Properties(); 16 properties.load(new FileInputStream("src/reflection/re.properties")); 17 String classfullpath = properties.get("classfullpath").toString(); 18 String methodName = properties.get("method").toString(); 19 // classfullpath: reflection.Cat 20 // method: hi 21 System.out.println("classfullpath: " + classfullpath); 22 System.out.println("method: " + methodName); 23 24 // 2.使用传统方式创建对象,行不通,因为上面通过配置文件得到的classfullpath是一个string,而不是一个类名 25 // new classfullpath(); 26 27 // 3.使用反射机制解决 28 // 3.1 加载类,返回一个Class类型的对象 29 Class cls = Class.forName(classfullpath); 30 // 3.2 通过cls得到reflection.Cat类的对象实例,在这里当然可以通过强制类型转换将Object类型转为Cat类型,但是这样做没有意义, 31 // 因为按照上面的问题,你是通过配置文件读取的classfullpath,在这里你并不知道classfullpath: reflection.Cat,你在这里只知道要调用 32 // 配置文件中classfullpath类的method方法,具体classfullpath具体是哪一个类,method是哪一个方法,你都不知道 33 Object o = cls.newInstance(); 34 // 3.3 通过cls得到配置文件中要求的methodName方法,在这里也就是hi方法 35 // 在反射中,可以把方法视为对象,即万物皆对象 36 Method method = cls.getMethod(methodName); 37 // 3.4 铜鼓method对象调用方法 38 System.out.println("==================="); 39 method.invoke(o); // 传统方式:对象.方法() 在反射中:方法.invoke(对象) 40 41 42 } 43 }
上面这个示例对反射存在的意义进行了说明,现在很多框架的底层都使用了反射机制。通过反射机制,程序可以在执行期借助Reflection API获取任何类的内部信息(比如成员变量、构造器、成员方法等),并能操作对象的属性以及方法。
二. 反射机制原理
Java反射机制原理图:
在上图的第二阶段,即加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象就可以得到类的结构。这个Class对象就像一面镜子,透过这个镜子可以看到类的结构,所以,形象的称之为:反射。
三. 反射的相关类与作用
Java反射机制可以完成:
1. 在运行时判断任意一个对象所属的类。
2. 在运行时构造任意一个类的对象。
3. 在运行时得到任意一个类所具有的成员变量和方法。
4. 在运行时调用任意一个对象的成员变量和方法。
5. 生成动态代理。
与反射相关的主要类(在java.lang.reflection包中):
1. java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象。
2. java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法。
3. java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量。
4. java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器。
反射机制还可以关闭访问检查,提高反射效率:Method和Field、Constructor对象都有setAccessible()方法,该方法的作用是启动和禁用访问安全检查。通过传入true,可以访问 private 构造器/方法/属性。
由于Class类是在类加载阶段被创建的,所以需要了解一下类加载的时机,主要有如下几个:
1. 当创建对象时(new)-静态加载
2. 当子类被加载时,父类也被加载-静态加载
3. 调用类中的静态成员时-静态加载
4. 通过反射-动态加载
下面我们对反射中各个类的主要方法及其作用进行说明:
1. java.lang.Class类:
2. java.lang.reflect.Field类:
3. java.lang.reflect.Method类:
4. java.lang.reflect.Constructor类
反射的作用及其对应操作:
1. 通过反射创建对象
2. 通过反射访问类中的属性
3. 通过反射访问类中的方法