前言:
java语言允许通过程序化的方式间接对Class进行操作,Class文件由类装载器装载后,在jvm中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息,如构造函数,属性和方法等,
java允许用户借由这个与Class相关的元信息对象间接调用Class对象的功能,这就为使用程序化方式操作Class对象开辟了途径。
实例:
下面一个Car类拥有两个构造函数,一个方法及三个属性
package reflect; public class Car { private String brand; //品牌 private String color; //颜色 private int maxSpeed; //转速 public Car(){} public Car(String brand, String color, int maxSpeed){ this.brand = brand; this.color = color; this.maxSpeed = maxSpeed; } public void say(){ System.out.println("brand:"+ brand + ", color:"+ color +", maxSpeed:"+ maxSpeed); } //set和get 方法省略
一般情况下,我们会使用如下代码创建Car的实例:
Car car = new Car(); car.setBrand("红旗");
这种是采用传统方式直接调用目标类,下面我们可以使用java反射机制以一种间接的方式操控目标类:
public class ReflectTest { public static Car initByDefaultConst() throws Throwable{ // 1. 通过类装载器获取Car类的对象 ClassLoader loader = Thread.currentThread().getContextClassLoader(); //使用全限定名来找class类 Class clazz = loader.loadClass("reflect.Car"); /*Class clazz = Class.forName("reflect.Car");*/ //另一种方式获取class // 2. 获取类的默认构造器对象并通过它实例化Car Constructor cons = clazz.getDeclaredConstructor((Class[])null); Car car = (Car)cons.newInstance(); // 3. 通过反射方法设置属性 Method setBrand = clazz.getMethod("setBrand", String.class); setBrand.invoke(car,"红旗"); Method setColor = clazz.getMethod("setColor", String.class); setColor.invoke(car,"黑色"); Method setMaxSpeed = clazz.getMethod("setMaxSpeed", int.class); setMaxSpeed.invoke(car,200); return car; } @Test public void test() throws Throwable{ Car car = initByDefaultConst(); car.say(); } }
运行以上程序,在控制台打印如下: brand:红旗, color:黑色, maxSpeed:200
上述的程序只是用了很少的api,java反射包为我们提供了很多的方法,可以细查。
其余扩展:
类装载器ClassLoader的工作机制:
类装载器就是寻找类的节码文件并构造出类在jvm内部表示对象的组件,在java中,类装载器把一个类装入jvm中,需要经过如下步骤:
1. 装载:查找和导入Class文件
2. 链接:执行校验,准备和解析步骤,其中解析步骤是可以选择的
2.1. 校验:检查载入Class文件数据的正确性
2.2. 准备: 给类的静态变量分配存储空间
2.3. 解析:将符号引用转换成直接引用
3. 初始化:对类的静态变量,静态代码块执行初始化工作
JVM装载类使用 “全盘负责委托机制”,
“全盘负责”是指当一个ClassLoader装载一个类时(除非显式的使用另一个ClassLoader),该类所依赖及引用的类也由这个ClassLoader载入;
“委托机制”是指先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。
这一点是从安全角度考虑的,试想,如果有一个人编写了一个恶意的基础类(如java.lang.String)并装载到JVM中,将会引起多么可怕的后果,
但是由于有了“全盘负责委托机制”,java.lang.String永远是根装载器来装载的,这样就避免了上述安全隐患的发生。