码迷,mamicode.com
首页 > 编程语言 > 详细

Java反射再学习

时间:2014-08-02 20:42:44      阅读:318      评论:0      收藏:0      [点我收藏+]

标签:style   blog   color   java   使用   io   文件   for   

在最初学习Java的时候觉得反射真的好难,并不是技术负责,而是思想复杂,无法接受。随着工作经验的增多,今日偶然间又看见某智的一个视频,感觉茅塞顿开。顺便在此系统整理一下反射的知识。

一言以蔽之:反射就是将Java类的各个组成部分转换为对应的Java对象。

我们知道,一切皆对象,那么这个“一切”必然也包含了Java类啊,Java类也是一种事物,那么他是什么的对象呢?毫无疑问,Java类是Class类的对象。(PS:那么Class类又是谁的对象呢?求大神指教?这问题貌似无穷无尽啊 %>_<% )

换句话说,Class类对象代表的就是Java类编译后的那份字节码,类文件里面有的,字节码文件里面肯定有,所以通过Class来操作字节码,就相当于操作类文件,只是多了一个中间步骤而已。

所以,要使用反射技术,首先我们要在程序中拿到这份字节码,有三种方法:

-- 类名.class

--对象名.getClass()

-- Class.forName("完整包名")

1 public class Test {
2     public static void main(String[] args) {
3         Person p = new com.test.Person();
4         Class clazz1 = Person.class;
5         Class clazz2 = p.getClass();
6         Class clazz3 = Class.forName("com.test.Person");
7     }
8 }

依据实际开发中,在使用反射的那个时间能够拿到的是类、对象还是包名来选择使用哪种方式加载字节码进入内存。

当我们拿到Class对象以后,也就是将字节码载入内存后,哇咔咔,我们就可以随便搞啦~~么么哒!

一个Java类里包含的主要东西有:

-- 包 Package 

-- 成员变量 Field

-- 方法 Method

-- 构造方法 Constructor

-- 注解 Annotation

-- 类加载器 ClassLoader

-- 泛型相关(先忽略)

 

示例Person类如下:

 1 package com.test.reflecttest;
 2 
 3 public class Person {
 4 
 5     public String name;
 6     
 7     private int age;
 8 
 9     public Person() {
10         super();
11     }
12 
13     public Person(String name, int age) {
14         super();
15         this.name = name;
16         this.age = age;
17     }
18 
19     public int getAge() {
20         return age;
21     }
22 
23     public void setAge(int age) {
24         this.age = age;
25     }
26     
27     private void say() {
28         System.out.println(this.name + " >>>>> " + this.age);
29     }
30     
31 }

 

 

首先是拿到成员变量:

 1 public class Test {
 2 
 3     public static void main(String[] args) throws Exception {
 4         Class<Person> clazz = Person.class;
 5         //通过指定的成员变量名来获得一个成员变量对象
 6         Field field = clazz.getField("name");
 7         //获得私有成员变量对象
 8         Field field2 = clazz.getDeclaredField("age");
 9         //获得所有非私有成员变量对象
10         Field[] fields = clazz.getFields();
11         //获得所有私有、非私有成员变量对象
12         Field[] fields2 = clazz.getDeclaredFields();
13     }
14 
15 }

从例子中可以看出,只有调用getDeclaredXxx()等类似方法才能够获取到私有类成员。

获得类方法的代码类似获得成员变量:

 1 public class Test {
 2 
 3     public static void main(String[] args) throws Exception {
 4         Class<Person> clazz = Person.class;
 5         //通过指定的方法名来获得一个方法对象
 6         Method method = clazz.getMethod("setName");
 7         //获得所有非私有方法对象
 8         Method[] methods = clazz.getMethods();
 9         //获得所有私有、非私有方法对象
10         Method[] methods2 = clazz.getDeclaredMethods();
11     }
12 }

获得构造器并使用构造器创建对象的方法:

 1 public class BlogTest {
 2 
 3     public static void main(String[] args) throws Exception {
 4         Class<Person> clazz = Person.class;
 5         //获得无参构造器
 6         Constructor<Person> c1 = clazz.getConstructor();
 7         //使用无参构造器创建类对象
 8         Person p = c1.newInstance();
 9         //通过指定参数列表的参数类型来获得构造器
10         Constructor<Person> c2 = clazz.getConstructor(String.class, int.class);
11         //传入参数,使用有参构造器创建类对象
12         Person p2 = c2.newInstance("Lucas",20);
13     }
14 }

OK,通过上面三个例子我们已经知道了怎么获得类的构造器、成员变量、方法,现在使用这三者来做一个综合示例(预警:下面的示例极度无聊,祝各位安好)

示例说明:我们使用反射调用Person类的两个成员变量的setXxx方法为他们赋值,然后使用反射获得赋值后的成员变量值。

 1 public class BlogTest {
 2 
 3     public static void main(String[] args) throws Exception {
 4         //获得Class对象,将Person类的字节码载入内存
 5         Class<Person> clazz = Person.class;
 6         //获得Person类的无参构造器
 7         Constructor<Person> c = clazz.getConstructor();
 8         //使用无参构造器创建Person对象
 9         Person p = c.newInstance();
10 
11         /*
12          * 获得成员变量 —— name
13          * 由于我们在Peron类中没有提供name的getter&setter方法
14          * 所以直接使用下面的方式进行赋值
15          */
16         Field fn = clazz.getField("name");
17         /*
18          * 为name赋值
19          * 赋值方法需要传入name成员变量所在的类对象
20          * 所以我们在前面创建了Person类对象p
21          */
22         fn.set(p, "Jack");
23         
24         //获得成员变量age的setAge方法,程序通过参数列表来区分方法,所以传入与调用方法参数列表顺序一致的参数类型
25         Method ma = clazz.getMethod("setAge", int.class);
26         /*
27          * invoke方法用于执行调用者(调用者即某个要执行的方法)
28          * 需要传入执行的方法所属的类对象和参数列表
29          */
30         ma.invoke(p, 20);
31         
32         /*
33          * 使用反射获得赋值后的age成员变量的值
34          * 由于age是private修饰的,所以要使用getDeclaredXxx类似方法来获取
35          */
36         Field fa = clazz.getDeclaredField("age");
37         //对于所有被private修饰的类成员,要进行访问或操作,都需要调用setAccessible方法将访问权限改为true
38         fa.setAccessible(true);
39         //反射是通用的一套技术,所以这里get方法默认返回Object类型,需要强制转换为int
40         int age = (int) fa.get(p);
41         System.out.println(age);  //输出 20
42         
43         //最后调用被private修饰的say()方法打印出姓名和年龄
44         Method say = clazz.getDeclaredMethod("say");
45         say.setAccessible(true);
46         say.invoke(p); //输出 Jack >>>>> 20
47     }
48 }

通过上面的例子,对于反射的基本应用差不多就搞定了。特别说明的是,对于被private修饰的类成员,在执行访问或操作时都需要调用其setAccessible(boolean flag);方法将该成员的访问权限设置为true。否则程序将抛出非法参数异常。

在使用反射调用方法时,特别注意,当被调用的方法接受的参数为数组时,需要特别处理。

我们提供一个类如下:这个类的test方法接受一个String类型的数组,并打印出所有数组元素。

1 public class ArrayMethod {
2 
3     public void test(String[] strs) {
4         for (String s : strs) {
5             System.out.println(s);
6         }
7     }
8 }

我们现在使用反射调用这个方法,并为其传参:

 1 public class Test {
 2 
 3     public static void main(String[] args) throws Exception {  
 4         ArrayMethod a = new com.test.reflecttest.ArrayMethod();
 5         Method m = a.getClass().getMethod("test", String[].class);
 6         String[] strs = new String[]{"abc","def","ghi","jkl"};
 7         /*
 8          * 执行以下操作会抛出异常:
 9          * java.lang.IllegalArgumentException: wrong number of arguments
10          */
11         //m.invoke(a, strs);
12         
13         //正确的赋值操作如下
14         m.invoke(a, (Object) strs);   
15     }
16 
17 }

为什么会报错提示参数数量错误呢?其实这个一个向后兼容的天坑。在jdk 1.4的时候,当传入一个数组参数进入方法时,程序会自动将数组中的元素拆分出来当做多个参数进行操作。而在jdk 1.5开发,数组参数被规定为单独的一个参数,所以就产生了这个问题。所以,只需要将数组参数强制转换为Object类型就可以解决了,这样就等于是直接告诉JVM,这个数组参数是一个单独参数,不用拆分。

Java反射再学习,布布扣,bubuko.com

Java反射再学习

标签:style   blog   color   java   使用   io   文件   for   

原文地址:http://www.cnblogs.com/jasongan/p/3887281.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!