Java中的反射和类装载器

来源:互联网 发布:java 静态变量 泛型 编辑:程序博客网 时间:2024/04/30 21:42

首先通过一个简单的例子看一下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内部表示的对象组件,大致经过如下步骤:

  1. 装载:查找和导入Class文件
  2. 链接:执行教研、准备和解析步骤,解析步骤可选
    • 校验:检查载入Class文件数据的正确性
    • 准备:给类的静态变量分配存储空间
    • 解析:讲符号引用转换成直接引用
  3. 初始化:对类的静态变量、静态代码块执行初始化工作。

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。

0 0