Java中的反射和类装载器

首先通过一个简单的例子看一下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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值