------- android培训、java培训、期待与您交流! ----------
反射
一、反射的基石-->Class类
1、java类的作用:是用来描述一类事物的共性,有什么属性,没有什么属性,至于属性的值是什么,则由这个类的实例对象来确定的,而不同的 实例对象就有不同的属性值。
2、Class类的产生:java程序中的各个java类也属于同一类事物,所以也可以用一个类来描述这些事物,这个类就是Class。
例如:众多的人需要建一个Person类,同理众多的类需要建一个Class类。
二、Class类介绍
1、创建Class类的的引用:Class class = 字节码(Person.class);
字节码:每个类编译后会产生一个.class文件,该文件就是字节码。
1)类名.class,如:System.class;
2)对象.getClass(),如:new Date().getClass();
3)Class.forName("类名"),如:Class.forName("java.lang.String");该方法在反射中常用,用时将类名作为变量传入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class ReflectionClass { public static void main( String [] args) throws ClassNotFoundException { String str = "abc" ; //获取Class类实例对象的三种方式 Class cls1 = str.getClass(); Class cls2 = String . class ; Class cls3 = Class.forName( "java.lang.String" ); //三种方式产生的对象相同 System.out.println(cls1 == cls2); //true System.out.println(cls1 == cls3); //true //判断该类是否是基本数据类型 System.out.println(cls1.isPrimitive()); //false System.out.println( int . class .isPrimitive()); //true // int和Integer是不同的对象 System.out.println( int . class == Integer. class ); //false //Integer.TYPE代表Integer中封装的类型,其他基本数据类型的使用一样 System.out.println( int . class == Integer.TYPE); //true System.out.println( int []. class .isPrimitive()); //false //判断是否是数组的方法 System.out.println( int []. class .isArray()); //true } } |
2、九个预定义Class实例对象:8个基本数据类型,加一个void.class;
三、反射
1、定义:反射其实是将java类中的各种成分映射成相应的类。
比如每个类可以用Class类的对象表示,同样每个类中的成员变量、函数、包等信息都可以用一个个类来表示。而表示Java类的Class类提供一些方法来获取其中的变量,方法,修饰符,包等信息,这些信息就是用相对应的类的实例对象来表示,如Field(成员变量),Method(方法),Constructor(构造函数),Package(包)等等;
2、Constructor类(构造方法)
1) 对象的获取:
a、得到某个类中的所有构造方法;
Constructor [] cons = Class.forName("java.lang.String").getConstructors();
b、得到某个类中的某个构造方法;
//getConstructor(字节码)方法里传的字节码表示编译的时候调用哪个构造方法
Constructor c = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
//newInstence()括弧里传入的是编译时需要生成的对象,返回的是Object对象,需要向下转型
String string = (String) c.newInstence(new StringBuffer("a"));//调用newInstance方法来传入该构造方法对应的参数
注意:如果想要得到类中的无参构造方法时,可以直接Class.newInstance()来实现。
3、Field类(成员变量)
1) 获取某个类中的成员变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
class Person{ //定义一个公有的变量 public int id; //定义一个私有的变量 private int age; public Person( int id, int age) { super (); this .id = id; this .age = age; } } public class ReflectionClass { public static void main( String [] args) throws Exception{ //创建一个Person对象 Person p = new Person( 01 , 25 ); //获取Penson类中的公有变量:id //首先获取p引用属于哪个类,返回一个类对象,然后用该对象获取其自己的成员变量名,返回一个成员变量对象 Field fId = p.getClass().getField( "id" ); //用该成员变量对象获取该类的具体哪个对象的变量值,想获取哪个对象的就将该对象传入,比如在建个对象p2,就传入p2 System.out.println(fId. get (p)); //获取类中的私有变量,方法不同 Field fAge = p.getClass().getDeclaredField( "age" ); //设置访问权限为true,如果不设置运行时会报错 fAge.setAccessible( true ); System.out.println(fAge. get (p)); } } |
2) 用反射将一个类中的字符串类型的成员变量中的字母替换
将下面Test类中的字符串中的'a'替换为'b'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
class Test { public String str1 = "ababababababa" ; public String str2 = "bbbbbbbaaaaaa" ; public String str3 = "aaaaaaabbbbbb" ; public Test() {} public String toString(){ return " str1:" +str1 + " str2:" + str2 + " str3:" + str3; } } public class ReflectionClass { public static void main( String [] args) throws Exception{ Test t = new Test(); method(t); System.out.println(t); } //定义一个方法,实现替换一个类中字符串的字符 public static void method( Object obj) throws Exception{ //获取该类的全部成员变量,返回一个Field类型的数组 Field [] fields = obj.getClass().getFields(); //遍历数组 for (Field field : fields) { //判断是否是String类型的变量,此处用==来判断,因为他们指向同一字节码。 if (field.getType() == String . class ){ //如果是,得到该变量的值 String oldStr = ( String ) field. get (obj); //替换 String newStr = oldStr.replace( 'a' , 'b' ); //将替换完的字符串设置到该成员变量中 field. set (obj, newStr); } } } } |
4、method类(方法)
1) 获取Method对象,调用某个类的方法.例:获取String类中的charAt()方法
//定义一个字符串。如果类里已经有了String类的字节码,那么下面可以直接使用String.class
String str1 = "abc";
//获取Method对象,getMethod("类中的某个方法","该方法所需的参数类型的字节码,可以是多参数")
Method method = Class.forName("java.lang.String").getMethod("charAt",int.class);
//将给方法作用到某个对象上,invoke(该方法作用于哪个对象,该方法操作的参数对应int.class);
//如果想要调用某个静态方法,那么invoke(null,该静态方法对应的参数);
method.invoke(str1,1);//也可以表示为method.invoke(str1,new Object [] {1});
2) 调用某个类的main方法
a、普通方法:类名.main(new String [] {"111","222","333"});
b、反射方法:为什么要用反射调用?当某个程序执行过程中,需要调用其他类的main方法,但是不知道该main所属的类名,只能将该类名 作为参数传入,这是需要用反射代码来完成
需求:用反射自定义一个方法调用某个类的main方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
//定义一个Test类,提供main方法 class Test { public static void main( String [] args){ for ( String arg : args) { System.out.println(arg); } } } //该类调用Test中的main方法 class ReflectionClass{ public static void main( String [] args) throws Exception{ //普通方法,当知道要main方法属于Test类时 //Test.main(new String [] args {"111","222","333"}); //反射方法,运行中不知道调用Test类中的main方法 //定义一个字符串变量,接收位未知的类名 String className = args[ 0 ]; //获取Method对象 Method mainMethod = Class.forName(className).getMethod( "main" , String []. class ); //调用该静态方法,并传入参数 mainMethod.invoke( null , ( Object ) new String []{ "111" , "222" , "333" }); /*代码详解:关于(Object)new String []{"111","222","333"}为什么要向上转型,首先invoke()需要传入两个参数,第一个就是需要调用的类的对象或者null; 第二个需要传入的是main方法的参数,该参数在1.4版本前是Object数组,数组中的每个元素对应一个main方法的参数。1.5版本后有了可变参数,就需要传入Object类型的数组, 当我们传入一个String类型的数组后,编译器是兼容1.4的,它会按1.4的语法进行处理,将数组中的每个元素拆出来作为参数,所以会产生参数个数不匹配异常。 解决办法就是将该数组向上转型成Object,或者new Object [] {{new String [] {"111","222","333"}}},将String数组作为一个整体传入Object数组中。*/ } } |
5、数组与Object的关系及其反射类型
1) 通过代码来说明数组与Object的关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
class ReflectionClass{ public static void main( String [] args){ int [] arr1 = new int []{ 1 , 2 , 3 }; int [] arr2 = new int [ 4 ]; int [][] arr3 = new int [][]{{ 1 , 2 , 3 },{ 4 , 5 , 6 },{ 7 , 8 , 9 }}; String [] arr4 = new String []{ "a" , "b" , "c" }; System.out.println(arr1.getClass() == arr2.getClass()); //true,说明两个数组是同一份儿字节码 //System.out.println(arr1.getClass() == arr3.getClass());//编译不通过,说明不是同一份儿字节码 //System.out.println(arr1.getClass() == arr4.getClass());//同上,说明类型相同且具有相同维度的数组用的是同一份儿字节码 System.out.println(arr1.getClass().getName()); //[I 代表Integer的数组,详见Class类中的getName方法介绍 System.out.println(arr4.getClass().getName()); //[Ljava.lang.String System.out.println(arr1.getClass().getSuperclass().getName()); //java.lang.Object System.out.println(arr4.getClass().getSuperclass().getName()); //java.lang.Object说明每个数组的父类都是Object //通过以上说明下面代码成立 Object obj1 = arr1; Object obj2 = arr4; Object [] obj3 = arr3; Object [] obj4 = arr4; //但是下面代码就不成立了 //Object [] obj5 = arr1;//改行代码编译不通过,说明基本数据类型的一维数组不能传入Object[]中,但是String类型的可以 //打印数组中的内容 //对于字符串的数组,可以转换成List列表并且能直接打印出来内容 System.out.println(Arrays.asList(arr4)); //[a, b, c] //而对于整数的数组来说,只能打印出地址值 System.out.println(Arrays.asList(arr1)); //[[I@5e55ab] System.out.println(Arrays.asList(arr3)); //[[I@15093f1, [I@120bf2c, [I@e6f7d2] /*上述情况差异的原因:因为 1.4 版本之前,asList()方法需要传入的参数是 Object [], String []与 Object 类型匹配,所以可以将 String []中的每个字符串作为一个 Object 对象取出来打印,类似于 161 行代码。 而 int [] 在 191 行证明不匹配与 Object [],只能用 1.5 的asList()方法来处理,此方法传入的是可变参数的 Object 对象,而 int []只能作为一个 Object 对象传入,所以只能打印该数组的地址值。 } } |
2)反射在数组的应用
需求:定义一个打印Object对象的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class ReflectionClass{ public static void main( String [] args){ //printObject(obj); } public static void printObject( Object obj){ //获取该对象的字节码 Class cl = obj.getClass(); //如果是数组,打印出数组中的每个元素,否则,直接输出 if (cl.isArray()) { //获取长度 int len = Array .getLength(obj); for ( int i = 0 ; i < len; i++) { //获取每个元素,并打印 System.out.println( Array . get (obj, i)); } } else { System.out.println(obj); } } } |
四、反射的作用:实现框架功能
1、什么是框架
开发商盖好房子,卖给用户,由用户自己装好门窗空调等,房子就是框架,用户想要用框架,就得把门窗插入到开发商提供的框架中。
框架和工具类是有区别的,框架是要调用用户提供的类,好比门窗。而工具类是被用户调用的类,好比门上的锁。
2、框架要解决的核心问题
因为在写框架的时候,无法知道要调用哪个类,所以,程序无法直接new某个类的实例对象,只能用反射来实现了。
需求:写一个小框架,用反射获取对象.该对象定义在配置文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class ReflectionClass{ public static void main( String [] args) throws Exception{ //创建一个读取流,关联配置文件 InputStream is = new FileInputStream( "config.properties" ); //创建properties对象 Properties p = new Properties(); //加载文件中的内容 p.load( is ); is .close(); //获取配置信息中键值对的值,得到类名 String className = p.getProperty( "className" ); //创建该类的对象 Collection collections = (Collection) Class.forName( "className" ).newInstance(); collections.add( "abc" ); collections.add( "bbc" ); collections.add( "abc" ); System.out.println(collections.size()); } } |
------- android培训、java培训、期待与您交流! ----------
来源:https://www.cnblogs.com/guoyanjun2013/archive/2013/04/07/3013379.html