------- 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