首先通过一个简单的例子看一下Java中的反射,如下,是一个Car类:
Car.java
public class Car {
private String brand;
private String color;
private int maxSpeed;
public Car(){System.out.println("init car!!");}
public Car(String brand,String color,int maxSpeed){
this.brand = brand;
this.color = color;
this.maxSpeed = maxSpeed;
}
public void introduce() {
System.out.println("brand:"+brand+";color:"+color+";maxSpeed:"+maxSpeed);
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
通常我们可以使用如下方式创建Car的实例:
Car car = new Car();
car.setBrand("红旗");
或:
Car car = new Car("红旗", "黑色");
我们也可以通过Java中的反射机制以更加通用的方式间接的操作目标类:
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectTest {
public static Car initByDefaultConst() throws Throwable
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass("com.baobaotao.reflect.Car");
// Class clazz = Class.forName("com.baobaotao.beans.Car1");
Constructor cons = clazz.getDeclaredConstructor((Class[])null);
Car car = (Car)cons.newInstance();
Method setBrand = clazz.getMethod("setBrand",String.class);
setBrand.invoke(car,"红旗CA72");
Method setColor = clazz.getMethod("setColor",String.class);
setColor.invoke(car,"黑色");
Method setMaxSpeed = clazz.getMethod("setMaxSpeed",int.class);
setMaxSpeed.invoke(car,200);
return car;
}
public static Car initByParamConst() throws Throwable{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass("com.baobaotao.reflect.Car");
Constructor cons = clazz.getDeclaredConstructor(new Class[]{String.class,String.class,int.class});
Car car = (Car)cons.newInstance(new Object[]{"吉利TOPMIX","绿色",120});
return car;
}
public static void main(String[] args) throws Throwable {
Car car1 = initByDefaultConst();
Car car2 = initByParamConst();
car1.introduce();
car2.introduce();
}
}
如上,首先获取到当前线程的ClassLoader,然后通过指定全限定类装载Car类对应的反射实例,然后通过反射类对象获取Car的构造函数对象cons,然后通过构造函数对象的newInstance()方法实例化Car对象,后面通过Car的反射类对象的getMethod(String methodName, Class paramClass)方法获取属性的Setter方法对象,然后通过invoke(Object obj, Object param)方法调用目标类的方法,通过如上方法操作目标类的元信息。
反射机制的通用性在于,我们可以将这些信息以配置文件的方式提供,然后编写通用的代码来进行实例化操作。一些框架就使用到了这些技术。
接下来看一下类装载器的工作机制:
类装载器就是寻找类的字节码文件并构造出类在JVM内部表示的对象组件,大致经过如下步骤:
- 装载:查找和导入Class文件
- 链接:执行教研、准备和解析步骤,解析步骤可选
- 校验:检查载入Class文件数据的正确性
- 准备:给类的静态变量分配存储空间
- 解析:讲符号引用转换成直接引用
- 初始化:对类的静态变量、静态代码块执行初始化工作。
JVM在运行时会产生三个ClassLoader:根装载器,ExtClassLoader(扩展类装载器)和AppClassLoader(系统类装载器)。其中,根装载器不是ClassLoader的子类,它使用C++编写,因此在Java中看不到。
根装载器负责JRE的核心类库,ExtClassLoader和AppClassLoader都是ClassLoader的子类。其中ExtClassLoader负责装载JRE扩展目录的ext中的JAR类包;AppClassLoader负责装载Classpath路径下的类包。
这三个类装载器之间存在父子层级关系,即根装载器是ExtClassLoader的父装载器,ExtClassLoader是AppClassLoader的父装载器。默认使用AppClassLoader装载应用程序的类。
在JVM中出于安全考虑,装载类时使用“全盘负责委托机制”,委托机制是指先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。
即就是优先查找系统中已有的类,找不到才会使用用户自定义的类。
类实例、类描述对象以及类装载器之间的关系如下:
如上图,当类文件被装载并解析后,在JVM内将拥有一个对应的java.lang.Class类描述对象,该类的实例都拥有指向这个类描述对象的引用,而类描述对象又拥有指向关联ClassLoader的引用。
通过反射机制还可以访问private和protected的成员变量和方法(当JVM安全机制允许),如下:
PrivateCar.java
public class PrivateCar {
private String color;
protected void drive(){
System.out.println("drive private car! the color is:"+color);
}
}
color变量和drive()方法都是私有的,通过类实例变量无法在外部访问,通过反射机制可以绕开这个限制,如下:
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class PrivateCarReflect {
public static void main(String[] args) throws Throwable{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass("com.baobaotao.reflect.PrivateCar");
PrivateCar pcar = (PrivateCar)clazz.newInstance();
//Field:类的成员变量的反射类
Field colorFld = clazz.getDeclaredField("color");
//取消Java语言访问检查以访问private变量
colorFld.setAccessible(true);
colorFld.set(pcar,"红色");
Method driveMtd = clazz.getDeclaredMethod("drive",(Class[])null);
//Method driveMtd = clazz.getDeclaredMethod("drive"); JDK5.0下使用
driveMtd.setAccessible(true);
driveMtd.invoke(pcar,(Object[])null);
}
}
如上,在通过反射机制访问private、protected成员变量和方法时必须通过setAccessible(boolean access)方法取消Java语言检查,否则将抛出IllegalAccessException。如果JVM的安全管理器设置了相应的安全机制,调用该方法讲排除SecurityException。