主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。在java中,只要给定类的名字, 那么就可以通过反射机制来获得类的所有信息。
反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高!
类中有什么信息,利用反射机制就能可以获得什么信息,不过前提是得知道类的名字。
首先要搞清楚为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念。 静态编译:在编译时确定类型,绑定对象,即通过。 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
反射机制的优点:可以实现动态创建对象和编译,体现出很大的灵活性(特别是在J2EE的开发中它的灵活性就表现的十分明显)。通过反射机制我们可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。
比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
反射机制的缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它 满足我们的要求。这类操作总是慢于只直接执行相同的操作。
添加一句:所有类的对象其实都是Class的实例。
1 package Reflect; 2 3 class Demo{ 4 //other codes... 5 } 6 7 class hello{ 8 public static void main(String[] args) { 9 Demo demo=new Demo(); 10 System.out.println(demo.getClass().getName()); 11 } 12 } 13 //【运行结果】:Reflect.Demo但是注意一下,当我们把Person中的默认的无参构造函数取消的时候,比如自己定义只定义一个有参数的构造函数之后,会出现错误:
比如定义了一个构造函数:
1 public Person(String name, int age) { 2 this.age=age; 3 this.name=name; 4 }然后继续运行上面的程序,会出现:
java.lang.InstantiationException: Reflect.Person at java.lang.Class.newInstance0(Class.java:340) at java.lang.Class.newInstance(Class.java:308) at Reflect.hello.main(hello.java:39) Exception in thread "main" java.lang.NullPointerException at Reflect.hello.main(hello.java:47)所以大家以后再编写使用Class实例化其他类的对象的时候,一定要自己定义无参的构造函数。
(以下几个例子,都会用到这个例子的Person类,所以为节省篇幅,此处不再粘贴Person的代码部分,只粘贴主类hello的代码)
首先来看看如何获得类加载器:
View Code其实在java中有三种类类加载器。
1)Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。
2)Extension ClassLoader 用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类
3)AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。
如果想要完成动态代理,首先需要定义一个InvocationHandler接口的子类,已完成代理的具体操作。
View Code类的生命周期
在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM。在程序执行中JVM通过装载,链接,初始化这3个步骤完成。
类的装载是通过类加载器完成的,加载器将.class文件的二进制文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象。用来封装数据。 但是同一个类只会被类装载器装载以前
链接就是把二进制数据组装为可以运行的状态。
链接分为校验,准备,解析这3个阶段:
校验一般用来确认此二进制文件是否适合当前的JVM(版本),
准备就是为静态成员分配内存空间,。并设置默认值
解析指的是转换常量池中的代码作为直接引用的过程,直到所有的符号引用都可以被运行程序使用(建立完整的对应关系)。
完成之后,类型也就完成了初始化,初始化之后类的对象就可以正常使用了,直到一个对象不再使用之后,将被垃圾回收。释放空间。当没有任何引用指向Class对象时就会被卸载,结束类的生命周期。
Spring中的IoC的实现原理就是工厂模式加反射机制。
当我们在添加一个子类的时候,就需要修改工厂类了。如果我们添加太多的子类的时候,改的就会很多。
现在就算我们添加任意多个子类的时候,工厂类就不需要修改。
使用反射机制的工厂模式可以通过反射取得接口的实例,但是需要传入完整的包和类名。而且用户也无法知道一个接口有多少个可以使用的子类,所以我们通过属性文件的形式配置所需要的子类。
首先创建一个fruit.properties的资源文件:
1 apple=Reflect.Apple 2 orange=Reflect.Orange然后编写主类代码:
1 package Reflect; 2 3 import java.io.*; 4 import java.util.*; 5 6 interface fruit{ 7 public abstract void eat(); 8 } 9 10 class Apple implements fruit{ 11 public void eat(){ 12 System.out.println("Apple"); 13 } 14 } 15 16 class Orange implements fruit{ 17 public void eat(){ 18 System.out.println("Orange"); 19 } 20 } 21 //操作属性文件类 22 class init{ 23 public static Properties getPro() throws FileNotFoundException, IOException{ 24 Properties pro=new Properties(); 25 File f=new File("fruit.properties"); 26 if(f.exists()){ 27 pro.load(new FileInputStream(f)); 28 }else{ 29 pro.setProperty("apple", "Reflect.Apple"); 30 pro.setProperty("orange", "Reflect.Orange"); 31 pro.store(new FileOutputStream(f), "FRUIT CLASS"); 32 } 33 return pro; 34 } 35 } 36 37 class Factory{ 38 public static fruit getInstance(String ClassName){ 39 fruit f=null; 40 try{ 41 f=(fruit)Class.forName(ClassName).newInstance(); 42 }catch (Exception e) { 43 e.printStackTrace(); 44 } 45 return f; 46 } 47 } 48 49 class hello{ 50 public static void main(String[] a) throws FileNotFoundException, IOException{ 51 Properties pro=init.getPro(); 52 fruit f=Factory.getInstance(pro.getProperty("apple")); 53 if(f!=null){ 54 f.eat(); 55 } 56 } 57 } 58 //【运行结果】:Apple
参考:http://blog.sina.com.cn/s/blog_636415010100v4gr.html
http://blog.csdn.net/liujiahan629629/article/details/18013523
http://www.cnblogs.com/jqyp/archive/2012/03/29/2423112.html
http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html
标签: Spring