java-反射深度剖析

Java反射是Java语言一个非常重要的特征,简单剖析下反射的定义、原理、使用、性能及应用场景。

(一)定义

程序执行时,同意修改程序结构或变量类型,这样的语言称为动态语言。java不属于动态语言,但提供了RTTI(Run-time Type Identification)执行时类别识别。

RTTI分为两种方式。一种是编译执行时已知悉类型,一种是反射机制。


(二)原理

《深入java虚拟机》中提到,java文件被编译成class文件,JVM类载入器载入class字节码到方法区,然后在堆中生成Class类,Class类能够訪问到类的基本信息。如类简单名、类包括路径全名、訪问修饰符、字段、方法等信息。

反射中须要使用到的类:

Field类:提供有关类或接口的属性的信息,以及对它的动态訪问权限。反射的字段可能是一个类(静态)属性或实例属性,简单的理解能够把它看成一个封装反射类的属性的类。
Constructor类:提供关于类的单个构造方法的信息以及对它的訪问权限。

这个类和Field类不同,Field类封装了反射类的属性,而Constructor类则封装了反射类的构造方法。
Method类:提供关于类或接口上单独某个方法的信息。

所反映的方法可能是类方法或实例方法(包含抽象方法)。 
Class类:类的实例表示正在执行的 Java 应用程序中的类和接口。枚举是一种类,凝视是一种接口。每一个数组属于被映射为 Class 对象的一个类,全部具有同样元素类型和维数的数组都共享该 Class 对象。



(三)使用

(1)获取Class

方法一:Class c=Class.forName("java.lang.String") 
方法二:对于基本数据类型能够用形如Class c=int.class或Class c=Integer.TYPE的语句 

(tips:int.class = Integer.TYPE !=Integer.class

方法三:Class c=MyClass.class

(2)调用Class中的方法得到你想得到的信息集合,如调用getDeclaredFields()方法得到类全部的属性

Field field = classInstance.getDeclaredField("TEST_TIMES");
            int times = (Integer) field.get(classInstance);
            System.out.println(times);


(四)性能

反射的性能是低于直接调用的。下次通过測试验证这个结果,測试中尽量避免对象创建等干扰因素。

我们将測试直接訪问的耗时、直接反射的耗时、缓存须要查找的函数反射的耗时、使用ReflectAsm的反射耗时。

/**
 * 測试反射性能
 *
 * @author peter_wang
 * @create-time 2014-6-13 下午12:54:52
 */
public class ReflectPerformanceDemo {
    private static final int TEST_TIMES = 1000000;

    private long mNum;
    private long mSum;

    /**
     * @param args
     */
    public static void main(String[] args) {
        normalInvoke();
        normalReflectInvoke();
        cacheReflectInvoke();
        asmReflectInvoke();
    }

    /**
     * 正常调用方法
     */
    private static void normalInvoke() {
        ReflectPerformanceDemo demo = new ReflectPerformanceDemo();
        long time1 = System.currentTimeMillis();
        for (long i = 0; i < TEST_TIMES; i++) {
            demo.setmNum(i);
            demo.mSum += demo.getmNum();
        }
        long time2 = System.currentTimeMillis();
        System.out.println("normal invoke time:" + (time2 - time1));
    }

    /**
     * 常规反射调用方法
     */
    private static void normalReflectInvoke() {
        ReflectPerformanceDemo demo = new ReflectPerformanceDemo();
        long time1 = System.currentTimeMillis();
        try {
            for (long i = 0; i < TEST_TIMES; i++) {
                Class<?> c = Class.forName("com.peter.demo.process.reflect.ReflectPerformanceDemo");
                Method method = c.getMethod("setmNum", Long.TYPE);
                method.invoke(demo, i);
                demo.mSum += demo.getmNum();
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        long time2 = System.currentTimeMillis();
        System.out.println("normal reflect invoke time:" + (time2 - time1));
    }

    /**
     * 缓存反射调用方法
     */
    private static void cacheReflectInvoke() {
        ReflectPerformanceDemo demo = new ReflectPerformanceDemo();
        try {
            Class<?> c = Class.forName("com.peter.demo.process.reflect.ReflectPerformanceDemo");
            Method method = c.getMethod("setmNum", Long.TYPE);
            long time1 = System.currentTimeMillis();
            for (long i = 0; i < TEST_TIMES; i++) {
                method.invoke(demo, i);
                demo.mSum += demo.getmNum();
            }
            long time2 = System.currentTimeMillis();
            System.out.println("cache invoke time:" + (time2 - time1));
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * asm反射调用方法
     */
    private static void asmReflectInvoke() {
        ReflectPerformanceDemo demo = new ReflectPerformanceDemo();
        try {
            MethodAccess ma = MethodAccess.get(ReflectPerformanceDemo.class);
            int index = ma.getIndex("setmNum");
            long time1 = System.currentTimeMillis();
            for (long i = 0; i < TEST_TIMES; i++) {
                ma.invoke(demo, index, i);
                demo.mSum += demo.getmNum();
            }
            long time2 = System.currentTimeMillis();
            System.out.println("asm invoke time:" + (time2 - time1));
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    public long getmNum() {
        return mNum;
    }

    public void setmNum(long mNum) {
        this.mNum = mNum;
    }

}

測试结果:

normal invoke time:7
normal reflect invoke time:1499
cache invoke time:32
asm invoke time:20

带缓存的反射调用方法速度明显慢于直接调用,採用asm第三方反射库,速度有少量提升。

反射慢的原因:Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications. 


(五)应用场景

(1)“基于构件的编程”,在这样的编程方式中,将使用某种基于高速应用开发(RAD)的应用构建工具来构建项目。这是如今最常见的可视化编程方法,通过代表不同组件的图标拖动到图板上来创建程序,然后设置构件的属性值来配置它们。

这样的配置要求构件都是可实例化的,而且要暴露其部分信息,使得程序猿能够读取和设置构件的值。

(2)可以提供在跨网络的远程平台上创建和执行对象的能力,实现java语言的网络可移动性。

这被成为远程调用(RMI),它同意一个Java程序将对象分步在多台机器上,这样的分步能力将帮助开发者执行一些须要进行大量计算的任务。充分利用计算机资源,提高执行速度。


(六)范例

破解最可靠的单例模式,在这个样例中,最可靠的又能够lazy loading的是第五种单例模式创建。可是能够通过反射机制破除安全性。

/**
 * 安全的单例模式
 * 
 * @author peter_wang
 * @create-time 2014-6-10 下午4:45:20
 */
public class SingletonSafe {
    private SingletonSafe() {
        System.out.println("create singleton");
    }

    private static class StaticSingleton {
        private static SingletonSafe instance = new SingletonSafe();
    }

    public static SingletonSafe getInstance() {
        return StaticSingleton.instance;
    }
}
/**
 * 測试反射
 * 
 * @author peter_wang
 * @create-time 2014-6-10 下午5:08:58
 */
public class ReflectDemo {

    /**
     * @param args
     */
    public static void main(String[] args) {
        try {
            Class classInstance = Class.forName("com.peter.demo.process.reflect.SingletonSafe");
            System.out.println(classInstance.getName());

            Constructor cons = classInstance.getDeclaredConstructor(null);
            cons.setAccessible(true);
            SingletonSafe singletonSafe1 = (SingletonSafe) cons.newInstance(null);
            System.out.println("singleton1:" + singletonSafe1.toString());
            SingletonSafe singletonSafe2 = (SingletonSafe) cons.newInstance(null);
            System.out.println("singleton2:" + singletonSafe2.toString());

            Method method1 = classInstance.getDeclaredMethod("getInstance", null);
            SingletonSafe singletonSafe3 = (SingletonSafe) method1.invoke(classInstance, null);
            System.out.println("singleton3:" + singletonSafe3.toString());
            Method method2 = classInstance.getDeclaredMethod("getInstance", null);
            SingletonSafe singletonSafe4 = (SingletonSafe) method2.invoke(classInstance, null);
            System.out.println("singleton4:" + singletonSafe4.toString());
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

}

測试结果:

com.peter.demo.process.reflect.SingletonSafe
create singleton
singleton1:com.peter.demo.process.reflect.SingletonSafe@2a9931f5
create singleton
singleton2:com.peter.demo.process.reflect.SingletonSafe@2f9ee1ac
create singleton
singleton3:com.peter.demo.process.reflect.SingletonSafe@5f186fab
singleton4:com.peter.demo.process.reflect.SingletonSafe@5f186fab

由測试结果可知,
单例模式能够用反射创建多个对象。


(七)总结

眼下的计算机系统的速度,应用开发已不在过于在意性能,而更为注重系统的可维护性和扩展性以及高速开发效率上。上述的測试结果是在大量操作基础上产生的。而在通常的一次业务请求中,反射使用的次数应该是很少的,仅仅在框架级基础上被使用,在一个高负载的系统中,业务处理的性能将是关键点。而不在于使用的这些反射所带来的性能影响上。

而使用反射所带来的开发便利与可维护性可扩展性的提升所带来的价值,是远远高于其所损耗的性能的。



原文地址:https://www.cnblogs.com/gavanwanggw/p/6721780.html