Chinaunix首页 | 论坛 | 博客
  • 博客访问: 32426
  • 博文数量: 18
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 122
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-05 16:09
文章分类
文章存档

2014年(6)

2013年(12)

我的朋友

分类: Java

2013-12-29 15:17:11

一、关于反射需要了解的一些基本概念
1、java类:Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,
 至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。

2、类的生命周期
在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM。在程序执行中JVM通过装载,链接,初始化这3个步骤完成。
类的装载是通过类加载器完成的,加载器将.class文件的二进制文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象。用来封装数据。
但是同一个类只会被类装载器装载一次。链接就是把二进制数据组装为可以运行的状态。
 
链接分为校验,准备,解析这3个阶段
校验一般用来确认此二进制文件是否适合当前的JVM(版本),
准备就是为静态成员分配内存空间,。并设置默认值
解析指的是转换常量池中的代码作为直接引用的过程,直到所有的符号引用都可以被运行程序使用(建立完整的对应关系)
完成之后,类型也就完成了初始化,初始化之后类的对象就可以正常使用了,直到一个对象不再使用之后,将被垃圾回收。释放空间。
当没有任何引用指向Class对象时就会被卸载,结束类的生命周期。

3、反射:由java类的基本概念,我们可以知道,java程序中各个java类属于同一类事物,而描述这类事物的java类名就是Class。
说白了,反射就是把java类中的各个成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:
成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。
表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,
它们是Field、Method、Contructor、Package等等。 


4、内存中的思考:一个类被加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同类的字节码是不同的,所以他们在内存中
的内容也是不同的,这一个个的空间分别用一个个的对象来表示,这些对象具有相同的类型,这个类型就是Class.这些字节码只被装载一次。


二、对于反射的具体实现
1、在进入反射的代码使用时,我们需要先了解一下,jdk为反射提供了那些类与方法
在jdk中,主要由一下类来实现Java的反射机制,这些类都位于java.lang.reflect包中
Class类:代表一个类(类的模板)
Field类:代表类的成员变量(即类的属性)
Method类:代表类的方法。
Constructor类:代表类的构造方法
Arrays类:动态创建数组,以及访问数组元素的静态方法(数组的排序sort(),数组中的最大值getMax()等)
其中Class类是Reflaction API的核心类。

类中的方法和和具体的作用
a.获得一个类的类模板
Class clazz = 对象名.getClass();
Class clazz = Class.forName(包名+类名);这是我们经常用到的,比如说在jdbc中获得某个数据库的驱动器的类
Class clazz = 类名.class(方法中以Class类型作为参数时,经常用到) 
b.根据类模板获取类的信息:
Field类 ---clazz.getFiled(String pname)  获得指定属性(公共字段)
Filed类----clazz.getDeclearedFiled(String pname) 获得指定属性(所有权限字段)
Filed[]----clazz.getFileds(String pname)  获得所有属性(公共字段)
Filed[]----clazz.getDeclearedFileds(String pname) 获得所有属性(所有权限字段)
通过以上方法获得的Field的类的对象是属于类的即clazz,而不是这个类所代表的的具体的对象的属性,
如果要利用反射获得对象的属性,需要用到Filed类提供的public Object get(Object obj)的方法,
返回指定对象上此 Field 表示的字段的值(obj就是要得到属性的对象)。如果该值是一个基本类型值,则自动将其包装在一个对象中。 
c.获取类的方法:
Method类----clazz.getMethod(StringmethodName,class[] params)  获得指定方法(公共方法)
Method类----clazz.getDeclearedMethod(StringmethodName,class[] params)  获得指定方法(所有方法)
Method[]----clazz.getMethods(StringmethodName,class[] params)  获得所有方法(公共方法)
Method[]----clazz.getDeclearedMethods(StringmethodName,class[] params)  获得所有方法(所有权限方法)


通过以上方法获得类模板的方法并不包括类的构造方法,上面只是获得方法,如果要执行某个对象的方法,
则要用到Method类中的public Object invoke(Object obj,Object... args),在使用这个方法时有两点要注意:
1。如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。 
2.如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。 
d.获取类的构造方法:
Constructors类---getConstructor(Class... parameterTypes)  获得指定构造方法(公共方法)
Constructors[]---public Constructor[] getConstructors() 获取所有类的构造方法(公共的)
Constructors类---public Constructor getDeclaredConstructor(Class... parameterTypes)
Class 对象所表示的类或接口的指定构造方法
Constructors[]---public Constructor[] getDeclaredConstructors() 获取所有类的构造方法(公共、保护、默认(包)访问和私有构造方法)
通过以上方法可以获得类的所有的构造方法,如果要创建类的具体的实例,则需要用到Constructors类中的public T newInstance(Object... initargs),
注意点:如果底层构造方法所需形参数为 0,则所提供的 initargs 数组的长度可能为 0 或 null。 
2、介绍完所有的类和方法,下面就是演示案例了

[java] view plaincopy
  1. package cn.yusheng.reflaction  
  2. /* 
  3. *这是反射main方法所用到的类 
  4. */  
  5. public class Test {  
  6.     public static void main(String[] args) {  
  7.           
  8.         System.out.println("我是Test类的main()方法");  
  9.           
  10.     }  
  11.   
  12. }  
  13.   
  14.   
  15. package cn.yusheng.reflaction  
  16.   
  17. import java.lang.reflect.Constructor;  
  18. import java.lang.reflect.Field;  
  19. import java.lang.reflect.Method;  
  20. import java.util.Date;  
  21.   
  22. public class ReflectTest {  
  23.     public static void main(String[] args) throws Exception {  
  24.         //得到Class实例对象的三种形式  
  25.         Class cls1 = String.class;  
  26.         Class cls2 = new String().getClass();  
  27.         Class cls3 = Class.forName("java.lang.String");  
  28.         System.out.println(cls1.getName()+"--"+cls2.getName()+"--"+cls3.getName());  
  29.         //从打印结果可以看出三者都获得了java.lang.String的类模板  
  30.           
  31.         //判定指定的 Class 对象是否表示一个基本类型。   
  32.         System.out.println(Integer.TYPE == int.class);  
  33.         //打印结果为true,说明两者的.class文件是同一个  
  34.           
  35.         //反射构造方法Stri.ng:String(StringBuffer buffer)   
  36.         Class cls4 = Class.forName("java.lang.String");  
  37.         Constructor cons = cls4.getConstructor(StringBuffer.class);  
  38.         //利用String类的模板通过反射所获的构造方法创建String类的实例对象  
  39.         String str = (String) cons.newInstance(new StringBuffer("我是通过反射出的构造方法创建的String实例"));  
  40.         //因为只有当运行时才知道创建的实例对象的类型,而在编译期间是不知道的  
  41.         //所以在此处要加强转  
  42.         System.out.println(str);  
  43.           
  44.           
  45.         //用Clss类提供的newInstance()方法创建Stringh实例,但底层还是通过反射出类模板的构造方法去创建  
  46.         //实例的,只是利用的黑丝默认的无参数构造方法  
  47.         Class cls5 = Class.forName("java.lang.String");  
  48.         String str2 = (String) cls5.newInstance();  
  49.           
  50.         //反射字段  
  51.         ReflectPoint r1 = new ReflectPoint(12);  
  52.         Class cls6 = Class.forName("ReflectPoint");  
  53. //      Field x = cls6.getField("x");//类或接口的指定公共成员字段,访问私有的属性时会把错  
  54.         Field x = cls6.getDeclaredField("x");  
  55.         x.setAccessible(true);//暴力反射  
  56.         int x1 = (Integer)x.get(r1);  
  57.         System.out.println(x1);  
  58.           
  59.         //反射String类的charAt(int index)方法并执行  
  60.         String str3 = "hello world";  
  61.         Method charAt = Class.forName("java.lang.String").getMethod("charAt"int.class);  
  62.         //第一个参数是方法的名字,第二个参数是方法的参数,jdk5支持可变参数  
  63.         System.out.println("hello world中的第2个字符是:"+charAt.invoke(str3, 1));  
  64.           
  65.         //反射执行MainClass的main的方法  
  66.         Class cls7 = Class.forName("Test");  
  67.         Method main = cls7.getMethod("main", String[].class);  
  68.         main.invoke(cls7, (Object)new String[]{"12","13"});  
  69.         /* 
  70.          * 在反射main方法时出现的问题:启动Java程序的main方法的参数是一个字符串数组, 
  71.          * 即public static void main(String[] args),通过反射方式来调用这个main方法时, 
  72.          * 如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法, 
  73.          * 数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时, 
  74.          * javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理, 
  75.          * 即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时, 
  76.          * 不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解, 
  77.          * 而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。 
  78.          *  
  79.          * 解决方法:mainMethod.invoke(null,new Object[]{new String[]{"xxx"}}); 
  80.                     mainMethod.invoke(null,(Object)new String[]{"xxx"}); , 
  81.                     编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了 
  82.  
  83.          */  
  84.     }  
  85. }  
  86.   
  87.   
  88. //定义一个做实验所需要的类,  
  89. class ReflectPoint{  
  90.     private int x;  
  91.     public int y;  
  92.     public ReflectPoint(int x, int y) {  
  93.         super();  
  94.         this.x = x;  
  95.         this.y = y;  
  96.     }  
  97.       
  98. }  

动态创建和访问数组
java.lang.Array 类提供了动态创建和访问数组元素的各种静态方法
例程ArrayTester1 类的main()方法创建了一个长度为10 的字符串数组,接着把索引位置为5 的
元素设为“hello”,然后再读取索引位置为5 的元素的值


代码实现:
[java] view plaincopy
  1. public class ArrayTester1 {  
  2.     public static void main(String args[]) throws Exception {  
  3.         Class classType = Class.forName("java.lang.String");  
  4.         // 创建一个长度为10的字符串数组  
  5.         Object array = Array.newInstance(classType, 10);  
  6.         // 把索引位置为5的元素设为"hello"  
  7.         Array.set(array, 5"hello");  
  8.         // 获得索引位置为5的元素的值  
  9.         String s = (String) Array.get(array, 5);  
  10.         System.out.println(s);  
  11.     }  
  12.   
  13. }  

三、总结与思考
反射的优缺点:
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念,
    静态编译:在编译时确定类型,绑定对象,即通过。
    动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多
    态的应用,有以降低类之间的藕合性。
    一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中
    它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编
    译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如
    这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能
    的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功
    能。
       它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它
    满足我们的要求。这类操作总是慢于只直接执行相同的操作。


写到这,关于反射的知识算是总节的差不多了,还好这次写博客用了2个小时,算是有点进步吧。其实对于javaSE的
东西我以前都是看过的,如今只是复习,进度算是蛮快的,但这样边写博客边复习,却让我发现自己还有好多的知识点
掌握的不牢固,还需要加深,不过还有时间,慢慢来,静下心来,赶不上第一批录取,就力争第二批吧,只要我努力,
我相信一定会通过的。
阅读(321) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~