黑马程序员--Java基础加强(3)--反射
2013-11-25 17:28
483 查看
------------------
android培训、java培训、期待与您交流! ---------------------
反射不是JDK1.5的新特性,从JDK1.2就有了。Struts,Hibernate,Spring包括JUnit等框架都用到了反射。
一、 反射的基础—Class类
1、什么是Class类?
任何事物都可以看做一个对象,相同的一类对象就抽象成为一类。那么Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。
通俗类比:众多的人用什么类表示:Person。那么众多的Java类用什么类表示呢:Class 。
Class类中的方法getName()获取该类的名字,getMethods()获取该类中的方法,getInterface()获取该类实现的接口,都是所有类共有的属性。
2、Class类的实例对象代表内存中的一份字节码。
什么是字节码?
JVM想要使用某个类,先将这个类的二进制文件加载进内存获得其字节码文件,再使用这个字节码文件生产出一个个该类的对象来。每一个类都在内存中只有一份字节码,而类的字节码就是Class的实例对像。具体到内存,就是一个个存储一份份字节码的存储空间。
3、得到Class 字节码的三种方式
(1)类名.class,如Person.class;
(2)类的对象名.getClass();如 Person p = new Person(); p.getClass();
(3)Class.forName("具体的类名如:java.lang.String");
注:第三种方式用的较多,因为可以在类还未加载到内存中时,源程序中就可以使用,不需要事先知道该类的名字。可以将forname的参数作为一个未知字符串,等程序运行时再临时传进去,比较方便。
4、九个预定义的Class实例对象
八个基本数据类型的字节码文件(boolean,byte,char,short,int,long,float,double) + void对应的字节码文件。
注意:八个基本数据类型对应的类的字节码,并不是预定义的Class实例对象。
5、Class类的isPrimitive()方法 :是不是原始类型。
例如String.class.isPrimitive()返回false,int.class.isPrimitive()返回true, Integer.TYPE.isPrimitive()返回true, int[].class.isPrimitive()返回false,int[].class.isArray返回true。注意查看API。
二、反射及应用
1、反射就是把java类中的各个成分映射成相应的java类。
就是说,java类中的每一个成分,都能解析成为一个相应的类,比如,变量能解析成Field类,方法能解析成Method类,构造函数能解析成Constructor类。而java类中的每一个成分都能用相应的类的对象表示。Java类的class文件可以通过getField方法获取该类中某个变量的Field对象,然后加以操作。
2、构造函数的反射应用(Constructor)
(1)Constructor类代表某个类中的一个构造方法。
(2)得到某个类所有的构造方法,例如:Constructor[] constructors = Class.forName("java.lang.String").getConstructors();(注意构造函数必须是public的才能这样获得)
(3)得到某类某一个构造方法:例如:Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);//第二个参数表示想要获得的构造方法的参数类型。有什么样的参数类型,就传入什么类型的class文件。
(4)获得构造方法后,有很多用处,比如创建新的实例对象:
Constructor constructor = String.class.getConstructor(StringBuffer.class);
String str2 = (String)constructor.newInstance(new StringBuffer("abc"));//如果使用带参数的构造函数创建实例对象,前后参数要一致,前面用的是该参数类型类的class文件,后面用的是该参数类型类的实例。注意这里为什么要(String)强制转型?因为Constructor本来是有泛型的,这里没加,它不知道生成的实例到底是什么类型,所以需要强转一下。
(5)另外一种创建实例的方法Class的newInstance()方法
用该方法创建String对象:String obj = (String)String.class.newInstance();
该方法内部先得到默认的构造方法,然后用该默认构造方法创建实例对象。用到了缓存机制来保存默认构造方法的实例对象。
3、成员变量的反射(Field)
(1)Field类代表某个类中的一个成员变量。
(2)获得类的某个成员变量:Field fName = Person.class.getField(“name”);//获取Person类的成员变量name。
得到的Field对象是对应到类上面的成员变量,代表的是成员变量的定义,而不是具体的值,但是可以通过这个定义,获得某个对象的该变量的值。
举例如下:
(3)暴力反射
如果成员变量是私有的,那么getField方法将获取不到该变量,这时需要方法getDeclaredField方法获取,但是获取了该变量也不能取出指定对象对应的变量值,这时需要设置标志.setAccessible(true);这样就能获得对象的变量值了。这个过程是强取,即暴力反射。
(4)、构造函数和成员变量反射的代码示例:
4、成员方法的反射(Method)
(1)Method类代表类中的成员方法。
(2)如何获取成员方法并调用?先得到某个方法的Method对象,然后再针对调用该方法的对象调用该方法。
例子:获得String类中的charAt方法并调用。
5、数组的反射应用
(1)具有相同的元素类型和相似维度的数组反射出来的class都是同一个字节码文件。
(2)数组与Object类的关系。
数组可以看做一个Object对象,但是基本数据类型int不可以看成一个Object对象。
int一维数组、二维数组和String数组的不同。
举例说明:
注意,Object数组中存储的应当是Object对象。
II对是因为, int二维数组赋值给Object[]数组,相当于object数组中每个元素都是一个int一维数组,每个int[]相当于一个Object对象,这是可以的。
III对事因为,String数组赋值给Object[]数组,相当于Object数组中每个元素是一个String字符串,每个String可以看成一个Object对象,所以这也是可以的。
I错是因为,int一维数组赋值给Object[]数组,相当于Object数组中每个元素存储的是一个int值,而int值并不是Object的子类对象,所以不对。
由以上区分,引申出,Arrays工具类中的asList方法处理int[]和String[]时的差异。如果传入的是String数组,将会将数组中的字符串元素打印出来,如果传入的是int型一维数组,则打印的还是该数组的哈希地址值,因为它会把int[]看成一个整体作为Object对象传入。
(3)位于java.lang.reflect包中的Array工具类用于完成对数组的反射操作。
举例:用于打印数组中的元素:
(4)如何得到数组中元素的类型?
答案是无法获得,但是我们可以获得某个元素的类型,因为有可能数组中元素类型并不一定一样,
比如,
6、反射的作用
(1)反射的作用实现框架功能.
好处一,利用反射,对象参数可以在调用时才传入,不需要必须事先写好。调用者类先写好,被调用类只要调用类运行前写好就行。
例如,Struts框架,框架先写好,自己的代码后写好
(2)反射使用举例:调用运行时接收的参数类main方法:
注意运行时配置要传入的类参数,如:
别忘了Person类中加上主函数:
(3)另外,反射还可以从配置文件中读取信息,以达到用户使用,比如配置文件,键为String,值为类名,这样利用Properties根据键值获得类名,利用Class.forName()获得类的class文件,就可以进行相关操作。这样有时就可以只修改配置文件就能达到目的,功能更强大。
配置文件一般写上绝对路径,其路径最好是能让客户自己设置,然后用get方法获取的。
android培训、java培训、期待与您交流! ---------------------
反射不是JDK1.5的新特性,从JDK1.2就有了。Struts,Hibernate,Spring包括JUnit等框架都用到了反射。
一、 反射的基础—Class类
1、什么是Class类?
任何事物都可以看做一个对象,相同的一类对象就抽象成为一类。那么Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。
通俗类比:众多的人用什么类表示:Person。那么众多的Java类用什么类表示呢:Class 。
Class类中的方法getName()获取该类的名字,getMethods()获取该类中的方法,getInterface()获取该类实现的接口,都是所有类共有的属性。
2、Class类的实例对象代表内存中的一份字节码。
什么是字节码?
JVM想要使用某个类,先将这个类的二进制文件加载进内存获得其字节码文件,再使用这个字节码文件生产出一个个该类的对象来。每一个类都在内存中只有一份字节码,而类的字节码就是Class的实例对像。具体到内存,就是一个个存储一份份字节码的存储空间。
3、得到Class 字节码的三种方式
(1)类名.class,如Person.class;
(2)类的对象名.getClass();如 Person p = new Person(); p.getClass();
(3)Class.forName("具体的类名如:java.lang.String");
注:第三种方式用的较多,因为可以在类还未加载到内存中时,源程序中就可以使用,不需要事先知道该类的名字。可以将forname的参数作为一个未知字符串,等程序运行时再临时传进去,比较方便。
4、九个预定义的Class实例对象
八个基本数据类型的字节码文件(boolean,byte,char,short,int,long,float,double) + void对应的字节码文件。
注意:八个基本数据类型对应的类的字节码,并不是预定义的Class实例对象。
5、Class类的isPrimitive()方法 :是不是原始类型。
例如String.class.isPrimitive()返回false,int.class.isPrimitive()返回true, Integer.TYPE.isPrimitive()返回true, int[].class.isPrimitive()返回false,int[].class.isArray返回true。注意查看API。
二、反射及应用
1、反射就是把java类中的各个成分映射成相应的java类。
就是说,java类中的每一个成分,都能解析成为一个相应的类,比如,变量能解析成Field类,方法能解析成Method类,构造函数能解析成Constructor类。而java类中的每一个成分都能用相应的类的对象表示。Java类的class文件可以通过getField方法获取该类中某个变量的Field对象,然后加以操作。
2、构造函数的反射应用(Constructor)
(1)Constructor类代表某个类中的一个构造方法。
(2)得到某个类所有的构造方法,例如:Constructor[] constructors = Class.forName("java.lang.String").getConstructors();(注意构造函数必须是public的才能这样获得)
(3)得到某类某一个构造方法:例如:Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);//第二个参数表示想要获得的构造方法的参数类型。有什么样的参数类型,就传入什么类型的class文件。
(4)获得构造方法后,有很多用处,比如创建新的实例对象:
Constructor constructor = String.class.getConstructor(StringBuffer.class);
String str2 = (String)constructor.newInstance(new StringBuffer("abc"));//如果使用带参数的构造函数创建实例对象,前后参数要一致,前面用的是该参数类型类的class文件,后面用的是该参数类型类的实例。注意这里为什么要(String)强制转型?因为Constructor本来是有泛型的,这里没加,它不知道生成的实例到底是什么类型,所以需要强转一下。
(5)另外一种创建实例的方法Class的newInstance()方法
用该方法创建String对象:String obj = (String)String.class.newInstance();
该方法内部先得到默认的构造方法,然后用该默认构造方法创建实例对象。用到了缓存机制来保存默认构造方法的实例对象。
3、成员变量的反射(Field)
(1)Field类代表某个类中的一个成员变量。
(2)获得类的某个成员变量:Field fName = Person.class.getField(“name”);//获取Person类的成员变量name。
得到的Field对象是对应到类上面的成员变量,代表的是成员变量的定义,而不是具体的值,但是可以通过这个定义,获得某个对象的该变量的值。
举例如下:
ReflectPoint po = new ReflectPoint(3,5); Field fieldY = po.getClass().getField("y");//fieldY的值是多少,是5吗?不是。FieldY不是对象身上的变量,而是类上的代表该变量的字节码,需要指定某个对象才能得到对应的值: System.out.println(fieldY.get(po));//将打印出po对象的y变量的值5。
(3)暴力反射
如果成员变量是私有的,那么getField方法将获取不到该变量,这时需要方法getDeclaredField方法获取,但是获取了该变量也不能取出指定对象对应的变量值,这时需要设置标志.setAccessible(true);这样就能获得对象的变量值了。这个过程是强取,即暴力反射。
(4)、构造函数和成员变量反射的代码示例:
import java.lang.reflect.Constructor; import java.lang.reflect.Field; public class MyReflectTest { public static void main(String[] args) throws Exception{ Constructor[] pConstructors = Person.class.getConstructors();//获取Person类中的所有构造函数 Constructor pConstructor1 = Person.class.getConstructor(null);//获取Person类的无参构造函数 Constructor pConstructor2 = Person.class.getConstructor(String.class,int.class,String.class);//获取Person类含有三个参数的构造函数 for(Constructor con:pConstructors){//增强for循环,打印出获得的每个构造函数 System.out.println(con); } //使用反射所得的构造函数创建Person实例对象 Person p1 = (Person)pConstructor1.newInstance(null); Person p2 = (Person)pConstructor2.newInstance(new String("zhangsan"),34,new String("beijing")); System.out.println(p1); System.out.println(p2); //取出特定对象的变量值name Field pFieldName = Person.class.getField("name"); System.out.println(pFieldName.get(p2)); //以下演示暴力反射,取出私有的age属性值 Field pFieldAge = Person.class.getDeclaredField("age"); pFieldAge.setAccessible(true); System.out.println(pFieldAge.get(p2)); //成员变量反射应用练习:将成员变量所有字符串中的‘b’变成‘a’ Person p3 = new Person("qibaishi",75,"beijing"); Field[] fields = p3.getClass().getFields();//先将p3中的所有成员变量取出 for(Field field:fields){//遍历所有变量 if(field.getType()==String.class){//判断是不是String类型 String oldStr = (String)field.get(p3);//如果是String类型,取出,并替换 String newStr = oldStr.replace('b', 'a'); field.set(p3, newStr);//将替换后的String反馈给p3 } } System.out.println(p3);//打印p3看结果 } }
4、成员方法的反射(Method)
(1)Method类代表类中的成员方法。
(2)如何获取成员方法并调用?先得到某个方法的Method对象,然后再针对调用该方法的对象调用该方法。
例子:获得String类中的charAt方法并调用。
String str1 = "abc"; Method methodCA = String.class.getMethod("chartAt",int.class);//两个变量分别为该方法的名字和该方法的参数列表 char c = methodCA.invkoe(str1,1);//invoke是调用的意思,两个参数分别表示调用该方法的对象和传入该方法的参数。如果第一个表示对象的参数为null,则说明该方法是静态的。
5、数组的反射应用
(1)具有相同的元素类型和相似维度的数组反射出来的class都是同一个字节码文件。
(2)数组与Object类的关系。
数组可以看做一个Object对象,但是基本数据类型int不可以看成一个Object对象。
int一维数组、二维数组和String数组的不同。
举例说明:
int[] a1 = new int[]{1,2,3}; int[][] a2 = new int[][]{{3,4,6}{7,8,9}{11,3,44}}; String[] str = new String[]{“ghjhtg”,”dssd”,”dfg”}; Object o1 = a1;//对,一个int一维数组可以作为一个Object子类对象 Object o2 = a2;//对,一个int二维数组可以作为一个Object子类对象 Object o3 =str;//对,一个String数组可以作为一个Object子类对象 Object[] or1 = a1;//错I Object[] or2 =a2;//对II Object[] or3 =str;//对III
注意,Object数组中存储的应当是Object对象。
II对是因为, int二维数组赋值给Object[]数组,相当于object数组中每个元素都是一个int一维数组,每个int[]相当于一个Object对象,这是可以的。
III对事因为,String数组赋值给Object[]数组,相当于Object数组中每个元素是一个String字符串,每个String可以看成一个Object对象,所以这也是可以的。
I错是因为,int一维数组赋值给Object[]数组,相当于Object数组中每个元素存储的是一个int值,而int值并不是Object的子类对象,所以不对。
由以上区分,引申出,Arrays工具类中的asList方法处理int[]和String[]时的差异。如果传入的是String数组,将会将数组中的字符串元素打印出来,如果传入的是int型一维数组,则打印的还是该数组的哈希地址值,因为它会把int[]看成一个整体作为Object对象传入。
(3)位于java.lang.reflect包中的Array工具类用于完成对数组的反射操作。
举例:用于打印数组中的元素:
public static void printObject(Object obj){ Class class = obj.getClass(); if(class.isArray){ int len = Array.getLength(obj);//getLength(obj)用于获得数组的长度 for(int i=0;i<len;i++){//如果是数组就用Array工具操作 System.out.println(Array.get(obj,i));//get(obj,i)获取obj数组中脚标为i的元素 } }else{ System.out.println(obj);} }
(4)如何得到数组中元素的类型?
答案是无法获得,但是我们可以获得某个元素的类型,因为有可能数组中元素类型并不一定一样,
比如,
Object obj = new Object[]{"a",1}; obj.getClass().getName();//可以通过这种方式获得0元素的类型。
6、反射的作用
(1)反射的作用实现框架功能.
好处一,利用反射,对象参数可以在调用时才传入,不需要必须事先写好。调用者类先写好,被调用类只要调用类运行前写好就行。
例如,Struts框架,框架先写好,自己的代码后写好
(2)反射使用举例:调用运行时接收的参数类main方法:
注意运行时配置要传入的类参数,如:
import java.lang.reflect.Method; public class ReflectApply { public static void main(String[] args) throws Exception{ String className = args[0];//将传入的参数类名接收到className中 Class clazz = Class.forName(className);//将接收的类名获得其class文件 Method mainMethod = clazz.getMethod("main", String[].class);//获得传入类的main方法 mainMethod.invoke(null, (Object)new String[]{"aaa","bbb","ccc"});//为这个类的main方法传入参数new String[]{"aaa","bbb","ccc"} //注意参数String数组要封装成Object,否则会被JVM自动拆包,把每一个String元素都作为参数,这样会报参数类型不符错误。 } }
别忘了Person类中加上主函数:
public static void main(String[] args){ for(String arg:args){//遍历主函数传入的参数,并打印 System.out.println(arg); } }
(3)另外,反射还可以从配置文件中读取信息,以达到用户使用,比如配置文件,键为String,值为类名,这样利用Properties根据键值获得类名,利用Class.forName()获得类的class文件,就可以进行相关操作。这样有时就可以只修改配置文件就能达到目的,功能更强大。
配置文件一般写上绝对路径,其路径最好是能让客户自己设置,然后用get方法获取的。
相关文章推荐
- 黑马程序员_java_基础加强_静态导入_反射_枚举_注解_内省_泛型
- 黑马程序员--Java基础加强学习笔记之Class类、反射(Reflect)
- 黑马程序员 java基础加强--反射
- 黑马程序员-Java基础加强之反射
- 黑马程序员_java基础加强-枚举和反射
- 黑马程序员--Java基础加强--14.利用反射操作泛型III【解析关于泛型类型的细节信息的获取方法】【Method与泛型相关的方法】【个人总结】
- 黑马程序员_Java基础加强_反射
- 黑马程序员_java基础加强之(反射)
- 黑马程序员-Java基础加强-反射
- 黑马程序员--Java基础加强--13.利用反射操作泛型II【TypeVariable】【GenericArrayType】【WildcardType】【Type及其子接口的来历】【个人总结】
- 黑马程序员—Java基础加强(反射)
- 黑马程序员java学习<基础加强>—反射
- 黑马程序员---java基础加强 反射的深入理解
- 黑马程序员-->Java基础加强-->反射(Reflection)
- 黑马程序员_Java基础加强之反射
- 黑马程序员---java基础加强---反射
- 黑马程序员-JAVA基础加强-反射1
- 黑马程序员--java基础加强-反射
- 黑马程序员--Java基础加强--15.利用反射操作泛型IV【通过反射Method解析泛型方法思路】【通过Method对四种Type子接口类型进行解剖】【使用递归对任意复合泛型类型进行彻底解剖】【个人
- 黑马程序员--Java基础加强--16.利用反射操作泛型V【通过Constructor反射解析泛型构造方法】【通过Field反射解析泛型成员变量】【个人总结】