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:[email protected]
create singleton
singleton2:[email protected]
create singleton
singleton3:[email protected]
singleton4:[email protected]

由测试结果可知,

单例模式可以用反射创建多个对象。

(七)总结

目前的计算机系统的速度,应用开发已不在过于在意性能,而更为注重系统的可维护性和扩展性以及快速开发效率上。上述的测试结果是在大量操作基础上产生的。而在通常的一次业务请求中,反射使用的次数应该是非常少的,只在框架级基础上被使用,在一个高负载的系统中,业务处理的性能将是关键点,而不在于使用的这些反射所带来的性能影响上。而使用反射所带来的开发便利与可维护性可扩展性的提升所带来的价值,是远远高于其所损耗的性能的。

java-反射深度剖析

时间: 2024-11-01 14:08:52

java-反射深度剖析的相关文章

Java反射机制剖析(四)-深度剖析动态代理原理及总结

动态代理类原理(示例代码参见java反射机制剖析(三)) a)  理解上面的动态代理示例流程 a)  理解上面的动态代理示例流程 b)  代理接口实现类源代码剖析 咱们一起来剖析一下代理实现类($Proxy0)的源代码和整个动态代理的流程. $Proxy0生成的代码如下: import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; impo

Java反射机制剖析(三)-简单谈谈动态代理

通过Java反射机制剖析(一)和Java反射机制剖析(二)的学习,已经对反射有了一定的了解,这一篇通过动态代理的例子来进一步学习反射机制. 1.     代理模式 代理模式就是为其他对象提供一种代理来控制对这个对象的访问.其实代理模式是在访问的对象时引入一定程度的间接性,这种间接性可以附加多种用途. 它 的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会 存在关联关系,一个代理类的对象与一个委托类的对象

java反射机制剖析(三)—类类型Class

为了区别于我们常用的Class,因此给了个中文名类类型.目的就是为了知道此Class非彼Class.上一篇博客已经介绍了Class Loader,它的作用是根据提供的信息来加载类到内存中.我之前有提过这个类信息的提供者就是本篇博客要介绍的Class.提醒:一个类的Class对象和它的Instance是不一样的,切记,不然你会混乱的.开始了! 概念 Class类是所有类(注意是对象)的共有信息的抽象,比如该类实现的接口.对应的加载器.类名等等.一句话,类类型保存了每个类所对应的类型信息.每一个类都

【54】Java反射机制剖析

java反射机制: 1.指的是可以于运行时加载,探知和使用编译期间完全未知的类. 2.程序在运行状态中, 可以动态加载一个只有名称的类, 对于任意一个已经加载的类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能调用他的任意一个方法和属性; 3.加载完类之后, 在堆内存中会产生一个Class类型的对象(一个类只有一个Class对象), 这个对象包含了完整的类的结构信息,而且这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以被称之为:反射. 4.每个类被加载进入内存之后,系统

java反射机制剖析(二)— Class Loader

上一篇博客简要的提了一下java反射机制中涉及到的一些相关知识,那么ClassLoader就是其中之一.本篇博客就详细的对ClassLoader做一个相对深入的了解.作为了解需要知道的是,其实类类型(Class Class)是和ClassLoader分不开的,因为ClassLoader需要的信息是由它提供的.类类型将在下一篇博客介绍. 简介 ClassLoader是负责加载类的对象,作用是根据Jvm请求提供的类信息,将请求的类加载的内存中或者说加载到Jvm中.另外,每一个类的Class对象(注意

Java反射之剖析方法

继上篇反射构造函数后,今剖析函数方法. 要通过反射执行方法: 1.可以先反射出类或构造函数,创建实例对象,通过对象调用方法(反射通过构造函数创建对象参见上篇). 2.可以通过类反射出方法,传入关联对象,从而实现对象调用方法. 以下是一个简单测试类: 包含两个文件: 1.Person.java,用于反射操作 2.Deom:Demo_MtdRft.java,反射的测试代码 测试的情况包括:1.公有方法,2.私有方法,3.无参数方法,4.有参数方法,5静态方法 下面是两个测试代码文件: Person.

Java反射之剖析main方法

上篇反射了类的成员方法,main方法其实也应该包含在其中,因为main方法就是一个静态方法. 但实际mian的反射稍有不同,其原因主要是为在升级过程中保持1.5兼容1.4版本:推理如下 1.main方法的参数为一个类型为String[]的参数 2.1.5支持可变参数,所以是可以可变参数形式来读取参数的, 如:main(String[]{"arg1","arg2"})===> main("arg1","arg2") 3.但

Java反射之剖析构造函数

反射通俗理解 (根据字符串)解析出一个类的各个组成部分(构造函数,字段,方法). 主要用来做框架 以下是一个简单的测试类 首先是一个测试实体,在此为一个Person类.类里面有五个构造函数.里面的构造有两种类型的: 1.公开的 2.私有的 其中每种类型又包含无参数的,和有参数的(一个或多个) 然后是一个测试类,主要测试了上面所说的几种类型组合的构造函数的获取,以及创建对象并输出测试代码.测试是用Junit来测试的. **** 反射创建对象时,有两种方式. 关于第一种方式,获取私有构造函数需要ge

Java反射之剖析字段

通过反射获取字段: -- 反射方法并调用时,静态方法可以不传递对象. 字段有点小不同,即使是静态字段也需要传递对象 以下是测试类:包括一个测试实体类:Girl.java,一个反射测试Demo.java Girl.java: 1 package cn.rt.gwq; 2 3 public class Girl { 4 5 private int age = 18; 6 public int height = 168; 7 public static String name = "alice&quo

java反射机制剖析(一)—简介

由之前动态代理的学习再次接触到反射这个知识点,第二次接触了所以做了一些稍微深入的了解.那么,对于反射这部分的内容我打算分三篇博客来总结.本篇博客先对反射做一个大概的了解,包括反射有关的RTTI.定义的理解以及涉及到的其他知识的简介. 回顾 java之前我接触反射这个知识,是在大话设计中的抽象工厂模式里,通过反射+配置文件来优化抽象工厂提高其应对需求变更的灵活性.当时对于反射的认知仅仅是它是一种技术,一种实例化对象的技术,一种实例化对象不依赖于写死的代码的技术.简单的说就是,它是一种可以摆脱用NE