美文网首页
再谈Java反射

再谈Java反射

作者: sugaryaruan | 来源:发表于2018-02-13 08:39 被阅读26次

    在日常开发中尽量不要用反射,如果需要,先考虑通过复制原始类的形式来避免反射,还不行再考虑通过反射。

    反射能帮我们做什么?

    1. 反射构建出无法直接访问的类
    2. set或get到无法访问的类变量
    3. 调用不可访问的方法

    每个包装器原始数据类型类具有名为 TYPE 的静态字段
    int.class 和 Integer.TYPE 指的是同一个类对象。

    上一篇我在Java反射札记里说了如何反射一个类,代参实例对象,反射属性和方法。这次我们来看一个开源类库

    jOOR 传送门 (https://github.com/jOOQ/jOOR)

    我们一起学习下这个库的代码

    这个开源库适配了Java6,Java8 和2017年11月份已经发布的Java9,作者在开发这个类库使,借鉴了已有的项目,做了调研和测试。

    github上给出的Demo代码段:

    String world = on("java.lang.String")  // Like Class.forName()
                    .create("Hello World") // Call most specific matching constructor
                    .call("substring", 6)  // Call most specific matching substring() method
                    .call("toString")      // Call toString()
                    .get();  
    

    上述代码采用链式调用,on方法 create方法,call方法内部都是调用的on的重载方法,返回Reflection类,最后的get()内部采用了泛型返回,如下:

    /**
     * Get the wrapped object
     *
     * @param <T> A convenience generic parameter for automatic unsafe casting
     */
    @SuppressWarnings("unchecked")
    public <T> T get() {
        return (T) object;
    }
    

    关于泛型的使用,可以查阅之前的两篇:

    1. 重识Java泛型 上
    2. 重识Java泛型 下

    在Android项目中,findViewById经过这样封装后,再也不用强转类型了。

    类加载机制

    JVM里java的类的加载流程图

    这部分内容已在:Java反射札记 这篇讲解了,这里不重复了

    Loading -> Linking -> Initialization

    类一定会初始化的五大情况

    1. 使用new字节码指令创建类的实例,或者使用getstatic、putstatic读取或设置一个静态字段的值(放入常量池中的常量除外),或者调用一个静态方法的时候,对应类必须进行过初始化。

    2. 通过java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则要首先进行初始化。

    3. 当初始化一个类的时候,如果发现其父类没有进行过初始化,则首先触发父类初始化。

    4. 当虚拟机启动时,用户需要指定一个主类(包含main()方法的类),虚拟机会首先初始化这个类。

    5. 使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、RE_invokeStatic的方法句柄,并且这个方法句柄对应的类没有进行初始化,则需要先触发其初始化。

    关于第五点,没有接触过

    现在通过代码实践来试试看吧

    我定义了SuperClass类和SubClass类

    SuperClass类

    public class SuperClass {
    
        public static String sValue = "666";
    
        public static final String HELLO_WORLD = "hello world!";
    
        static {
            Out.println("SuperClass init");
        }
    
        public String getContent(){
            return "This is String result";
        }
    
        public static void setsValue(String value){
            sValue = value;
        }
    }
    

    SubClass

    public class SubClass extends SuperClass {
    
        public static String sTemp = "";
    
        static {
            sTemp = "sub class temp";
            Out.println("Subclass init");
        }
    
        public void instanceTest(){
            Out.println("instanceTest");
        }
    }
    

    测试下第一点,new的时候

    /**
     * 类初始化
     */
    private static void testVersion1_2() {
        SubClass subClass = new SubClass();
    }
    

    输出:

    SuperClass init
    Subclass init
    

    子类SubClass和SuperClass都经历了类初始化阶段

    测试下第一点里提到的引用静态字段时

    /**
     * 类初始化3
     */
    private static void testVersion1_3() {
        Out.println(SubClass.sTemp);
    }
    

    输出:

    SuperClass init
    Subclass init
    sub class temp
    

    再看下这段代码

    /**
     * 类初始化1 主动引用/被动引用
     */
    private static void testVersion1_1() {
        Out.println(SubClass.sValue);
    }
    

    你觉得SubClass类会调用static静态代码块么?

    输出:

    SuperClass init
    666
    

    答案是不会,神奇的一幕,SubClass没有经过类初始化

    Java“相等”判定相关方法

    我们最熟悉的是instanceof方法,这个是实例对象的方法,判断当前对象是否是某个类的实例。

    如果我要比较class类与类之间是否子类关系呢?要比较class对象和一个实例对象呢?

    Java反射为我们提供了解决方法

    /**
     * 相等判断 除了 instanceof
     */
    private static void testVersion2_1() {
        Out.println(SuperClass.HELLO_WORLD);
    }
    
    private static void testVersion3_1() {
        boolean isAssignable = SuperClass.class.isAssignableFrom(SuperClass.class);
        Out.println("isAssignable = " + isAssignable);
    }
    
    private static void testVersion3_2() {
        SubClass subClass = new SubClass();
        boolean isInstance = SuperClass.class.isInstance(subClass);
        Out.println("isInstance = " + isInstance);
    }
    

    说明:使用isInstance,isAssignableFrom是需要注意判断的逻辑关系。比如拿testVersion3_1方法举例,boolean isAssignable = SuperClass.class.isAssignableFrom(SuperClass.class);说的是SuperClass是否是SuperClass的子类啊?isAssignable的布尔值就是答案

    参考资料

    欢迎关注CodeThings

    相关文章

      网友评论

          本文标题:再谈Java反射

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