反射
可以JVM运行时动态的加载类,还可以调用类的方法或者修改其属性。
反射原理
Class.forName()
-
时序图
-
源码
clazz.getDeclaredMethod()
-
时序图
-
Class反射的元数据对象:
-
源码
method.invoke()
-
时序图
-
源码:
-
比较GenerateMethodAccessorImpl
- 对比
- 该方法调用15次前,每次都是jvm本地方法调用。
- 15次后,说明该方法经常使用,专门用asm生成该方法执行类的字节码文件,然后用类加载器加载进来,每次就可以在内存种调用。
- 对比
补充
反射的优缺点
优点
-
增加程序的灵活性,避免将程序写死到代码里。
package cn.yonyong.reflection.testdemo; interface Fruit { //水果接口 public void eat() ; //吃水果 } class Apple implements Fruit{ //定义苹果 public void eat() { System.out.println("**吃苹果。"); } } class Orange implements Fruit{ public void eat() { System.out.println("**吃橘子。"); } } /** * 反射创造实例的工厂 */ class Factory{ public static Fruit getInstance(String className){ Fruit fruit = null ; try{ fruit = (Fruit) Class.forName(className).newInstance() ; }catch(Exception e ){ e.printStackTrace() ; } return fruit ; } } public class FactoryDemo{ public static void main(String args[]){ //通过工厂类取得接口实例,传入完整的包.类名称 Fruit f = Factory.getInstance("cn.yonyong.reflection.testdemo.Apple") ; if(f!=null){ //判断是否取得接口实例 f.eat() ; } } }
-
对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
缺点:
- 性能问题。避免在经常被 执行的代码或对性能要求很高的程序中使用反射。
- 使用反射会模糊程序内部逻辑。无法在源代码中看到程序的逻辑,带来维护问题。
- 安全限制。使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如Applet,那么这就是个问题了
- 内部暴露。由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
反射是否真的会让你的程序性能降低?
- 反射大概比直接调用慢50~100倍,但是需要你在执行100万遍的时候才会有所感觉
- 判断一个函数的性能,你需要把这个函数执行100万遍甚至1000万遍
- 如果你只是偶尔调用一下反射,请忘记反射带来的性能影响
- 如果你需要大量调用反射,请考虑缓存。
- 你的编程的思想才是限制你程序性能的最主要的因素
单例模式如何实现防反射?
-
存在问题方法:修改构造器,让它在被要求创建第二个实例的时候抛出异常(即加一个static boolean flag字段)。存在的问题是flag字段仍可以通过反射修改。
-
正确解决方法:实现单例需编写一个包含单个元素的枚举类型
public enum SingletonClass { INSTANCE("instance"); private String name; SingletonClass(String name){ this.name = name; } public void test() { System.out.println("The Test!"); } }
-
枚举原理:编译之后,每个枚举变量会被修适为public static final类型,并且在静态代码块里初始化,所以不可能被修改
.Class和Class.forName、obj.getClass得到的Class实例是同一个实例还是不同的实例?
相同,但是.Class和Class.forName是编译时决定, 而.getClass()是运行时决定
- .Class: JVM将使用类的类装载器, 将类装入内存(前提是:类还没有装入内存),不对类做类的初始化工作.返回类的Class的对象(转, 不是很懂)
- .getClass(): 返回对象运行时的真正对象(有可能存在向上转换)所属的类
- Class.forName(): 装入类, 并做类的初始化