美文网首页
Java反射机制

Java反射机制

作者: Specime | 来源:发表于2018-05-15 19:59 被阅读0次

    1、概念

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
    换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields赋值、或调用其methods。

    2、示例

    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 void setBrand(String brand) {
            this.brand = brand;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        public void setMaxSpeed(int maxSpeed) {
            this.maxSpeed = maxSpeed;
        }
    }
    

    测试

    public class ReflectTest {
        
        public static Car initByDefaultConst() throws Throwable{
            //①通过类加载器获取Car类对象
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            Class<?> clazz = loader.loadClass("com.java.reflect.Car");
            
            //②获取类的默认构造器对象并通过它实例化Car
            Constructor<?> constructor = clazz.getDeclaredConstructor((Class[])null);
            Car car = (Car) constructor.newInstance();
            
            //③通过反射方法设置属性
            Method setBrand = clazz.getMethod("setBrand", String.class);
            setBrand.invoke(car, "volvo XC90");
            Method setColor = clazz.getMethod("setColor", String.class);
            setColor.invoke(car, "黑色");
            Method setMaxSpeed = clazz.getMethod("setMaxSpeed", int.class);
            setMaxSpeed.invoke(car, 199);
            
            return car;
        }
        
        public static void main(String[] args) throws Throwable {
            Car car = initByDefaultConst();
            car.introduce();
        }
    
    }
    

    结果

    init car!!
    brand:volvo XC90;color:黑色;maxSpeed:199
    

    3、分析

    以上结果与直接通过Car的构造方法赋值效果上没有差别,构造方法的方式是直接调用,反射是间接调用。
    反射常用的几个反射类包括:ClassLoaderClassConstrucorMethod,通过这些类就可以间接调用目标Class了。 在①处获取当前线程的ClassLoader,然后通过指定全限定类名"com.java.reflect.Car"装载Car类对应的反射实例。在②处通过Car的反射类对象获取Car的构造方法对象constructor ,通过构造方法对象的newInstance()方法实例化Car对象,其效果等同于new Car()。在③处通过Car的反射类对象的getMethod(String methodName,Class paramClass)获取属性的Setter()方法对象,其中第一个参数是Class的方法名,第二个参数是方法的入参对象类型。获取反射对象后,通过invoke(Object obj,Object param)方法调用目标类的方法,第一个参数是操作的目标类对象实例,第二个参数是目标方法的入参。

    4、反射机制

    Class反射对象描述类语义结构,可以从Class对象中获取构造函数、成员变量、方法类等类元素的反射对象,并以编程的方式通过这些反射对象对目标类对象进行操作。这些反射对象类在java.reflect包中定义。以下介绍3个主要反射类。统一使用以下代码获取的Class对象为例:

      ClassLoader loader = Thread.currentThread().getContextClassLoader();
      Class<?> clazz = loader.loadClass("全限定类名");
    

    Constructor: 类的构造器反射类

    • clazz.getDeclaredConstructor();接收Class类型的可变参数,以便获取对应的有参构造器,返回Constructor对象,使用该返回对象调用newInstance();方法创建反射类的实例对象,相当于new关键字。

    Method: 类方法的反射类

    • clazz.getDeclaredMethods();获取所有在类中声明的方法,包括private和static方法,但不包括继承自父类(未重写)的方法,返回Method数组。
    • clazz.getMethods();获取所有方法,包括继承自父类的方法。
    • clazz.getDeclaredMethod(String name, Class<?>... parameterTypes);获取指定方法,仅限在本类中声明的方法。第一个参数:方法名,传入String类型,第二个可变参数,传入需要获取方法的参数列表的参数类类型(不是类名),如:
    Method setBrand = clazz.getDeclaredMethod("setBrand", String.class);
    
    • clazz.getMethod(String name, Class<?>... parameterTypes);获取指定方法,包括继承自父类的方法,参数与getDeclaredMethod();相同。
    • Method常用方法:
    ① Class[] getParameterTypes(); 获取方法的入参类型数组
    ② Class getReturnTypes(); 获取方法的返回值类型
    ③ Class[] getExceptionTypes(); 获取方法的异常类型数组
    ④ Annotation[][] getParameterAnnotation(); 获取方法的注解信息
    

    Field:类的成员变量的反射类

    • clazz.getDeclaredFields();获取类的成员变量反射对象数组,仅限本类声明的成员变量;
    • clazz.getDeclaredField(String name);获取类的某个特定名称的成员变量反射对象,仅限本类声明的成员变量;
    • clazz.getFields();获取类的成员变量反射对象数组,包括继承自父类的成员变量;
    • clazz.getField(String name);获取类的某个特定名称的成员变量反射对象,包括继承自父类的成员变量。

    Field常用方法:

    set(Object obj, Object value);参数obj表示操作的目标对象,即获取的Field对象,参数value 表示要给这个Field赋的值
    

    其他
    通过Class还可以获取内部类信息,
    Java还提供了Package、AnnotateElement反射类。使用频率较低,自行补充。

    5、类加载器ClassLoader

    1、工作机制

    类加载器就是寻找类的字节码文件并构造出类在JVM内部表示对象的组件。在Java中,类加载器把一个类装入JVM中需要经过以下几个步骤:

    • (1)装载:查找和导入Class文件
    • (2)链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的
      1 校验:检查载入Class文件数据的正确性
      2 准备:给类的静态变量分配存储空间
      3 解析:将符号引用转为直接引用
    • (3)初始化:对类的静态变量、静态代码块执行初始化操作
      类装载工作由ClassLoader及其子类负责,ClassLoader是一个重要的Java运行时系统组件,它负责运行时查找和装入Class字节码文件,JVM在运行时会产生三个ClassLoader:根装载器、ExtClassLoader(扩展类装载器)和AppClassLoader(应用类装载器)。其中根装载器不是ClassLoader的子类,它使用C++编写,因而在Java中看不到它,它负责装载rt.jar、charsets.jar等JRE的核心类库。ExtClassLoader和AppClassLoader都是ClassLoader的子类。
      这3个类装载器之间存在父子层级关系,即根装载器是ExtClassLoader的父装载器,ExtClassLoader是AppClassLoader的父装载器。在默认情况下,使用AppClassLoader装载应用程序的类。

    2、JVM全盘负责委托机制

    • 全盘负责:当一个ClassLoader装载一个类时,除非显示地使用另一个ClassLoader,该类所依赖及引用的类有这个ClassLoader负责加载。
    • 委托机制:先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并载入目标类。

    3、ClassLoader的重要方法

    Java中ClassLoader是一个抽象类,位于java.lang包中。
    除了JVM默认的3个ClassLoader外,用户可以编写自己的第三方类装载器,以实现一些特殊的需求。

    • Class loadClass(String name): name参数指定类装载器需要装载类的名字,必须为全限定类名。 该方法有一个重载的方法,loadClass(String name,boolean resolve),resolve参数告诉类装载器是否需要解析该类。 在初始化类之前,应该考虑进行类解析的工作,但并不是所有的类都需要解析。如JVM只需要知道该类是否存在或找出该类的超类,那么就不需要进行解析。
    • Class defineClass(String name,byte[] b,int off,int len):把类文件的字节数组转换成JVM内部的java.lang.Class对象。字节数组可以从本地文件系统、远程网络获取,参数name为字节数组对应的全限定类名。
    • Class findSystemClass(String name): 从本地文件系统载入Class文件。如果本地文件系统不存在该Class文件,则抛出ClassNotFoundException异常。该方法是JVM默认使用的装载机制。
    • Class findLoadedClass(String name): 通过调用该方法来查看ClassLoader是否已载入某个类。如果已载入,那么返回该类的Class对象;否则返回null。如果强行装载已存在的类,那么将会抛出链接错误。
    • ClassLoader getParent(): 获取类装载器的父装载器。除根装载器外,所有的类装载器都有且仅有一个父装载器。ExtClassLoader的父装载器是根装载器,因为根装载器是C++语言写的,所以无法获得,返回null。

    4、说明

    类文件被装载并解析后,在JVM内将拥有一个对应的java.lang.Class类描述对象,该类的实例都拥有指向这个类描述对象的引用,而类描述对象又拥有指向关联ClassLoader的引用,如下图:

    ClassLoader.png

    每个类在JVM中都拥有一个对应的java.lang.Class对象,它提供了类结构信息的描述。数组、枚举、注解及基本类型(int、double等),甚至void都有对应的Class对象。Class没有public的构造方法。Class对象是在装载类时由JVM通过类装载器中的defineClass()方法自动构造的。

    参考自《精通Spring4.x企业应用开发实战》

    相关文章

      网友评论

          本文标题:Java反射机制

          本文链接:https://www.haomeiwen.com/subject/bpbydftx.html