java 反射(Reflection)-干货

看了很多关于java 反射的文章,自己把所看到的总结一下。对自己,对他人或多或少有帮助吧。

  • Java Reflection是什么?

首先来看看官方文档Oracle里面对Reflection的描述:

Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

简单的讲:

  • 反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运行的时候还可以动态地去操作他们。

再简单一点的讲:

  • 我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。

举个例子:

Java是面向对象语言,我们可以把老虎,狮子等具有相同性质的动物归类(抽象)为猫科动物,他们具有牙齿,胡须等一些属性。同时具有吃肉()的动作。

同样的道理,我们所接触到的类Class,也可以把他们抽象出来,有类名,成员变量,方法等。

那么既然能够把类看做是对象,那么java就可以对其进行处理。

  • Java反射(Reflection)框架主要提供以下功能:

  1. 在运行时判断任意一个对象所属的类;
  2. 在运行时构造任意一个类的对象;
  3. 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
  4. 在运行时调用任意一个对象的方法
  • Java反射(Reflection)的主要用途

  1. 工厂模式:Factory类中用反射的话,添加了一个新的类之后,就不需要再修改工厂类Factory了
  2. 数据库JDBC中通过Class.forName(Driver).来获得数据库连接驱动
  3. 分析类文件:毕竟能得到类中的方法等等
  4. 访问一些不能访问的变量或属性:破解别人代码
  • Java反射(Reflection)的基本运用

  获取Class有一下三种方法:

1. 使用Class类的forName静态方法

 1 //在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机),   
 2 //这通过java.lang.Class类的静态方法forName(String  className)实现。   
 3 //例如:   
 4 try{   
 5     //加载MySql的驱动类   
 6     Class.forName("com.mysql.jdbc.Driver") ;   
 7 }catch(ClassNotFoundException e){   
 8     //System.out.println("找不到驱动程序类 ,加载驱动失败!");   
 9     e.printStackTrace() ;   
10 }   
11 //成功加载后,会将Driver类的实例注册到DriverManager类中。

2. 直接获取某一个对象的class

1 Class<?> klass = int.class; 
2 Class<?> classInt = Integer.TYPE;

3. 调用某个对象的getClass()方法

1 public class Test{
2     public static void main(String[] args) {
3         Demo demo=new Demo();
4         System.out.println(demo.getClass().getName());
5     }
6 }

 下面是一个完整的Demo展示Java Reflection的操作:

  1 package com.b510.hongten.test.reflex;
  2 
  3 import java.lang.reflect.Constructor;
  4 import java.lang.reflect.Field;
  5 import java.lang.reflect.InvocationTargetException;
  6 import java.lang.reflect.Method;
  7 import java.lang.reflect.Modifier;
  8 
  9 public class ReflectionDemo {
 10     /**
 11      * 为了看清楚Java反射部分代码,所有异常我都最后抛出来给虚拟机处理!
 12      * 
 13      * @param args
 14      * @throws ClassNotFoundException
 15      * @throws InstantiationException
 16      * @throws IllegalAccessException
 17      * @throws InvocationTargetException
 18      * @throws IllegalArgumentException
 19      * @throws NoSuchFieldException
 20      * @throws SecurityException
 21      * @throws NoSuchMethodException
 22      */
 23     public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchFieldException, NoSuchMethodException {
 24         // Demo1. 通过Java反射机制得到类的包名和类名
 25         Demo1();
 26         System.out.println("===============================================");
 27 
 28         // Demo2. 验证所有的类都是Class类的实例对象
 29         Demo2();
 30         System.out.println("===============================================");
 31 
 32         // Demo3. 通过Java反射机制,用Class 创建类对象[这也就是反射存在的意义所在],无参构造
 33         Demo3();
 34         System.out.println("===============================================");
 35 
 36         // Demo4: 通过Java反射机制得到一个类的构造函数,并实现构造带参实例对象
 37         Demo4();
 38         System.out.println("===============================================");
 39 
 40         // Demo5: 通过Java反射机制操作成员变量, set 和 get
 41         Demo5();
 42         System.out.println("===============================================");
 43 
 44         // Demo6: 通过Java反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等
 45         Demo6();
 46         System.out.println("===============================================");
 47 
 48         // Demo7: 通过Java反射机制调用类中方法
 49         Demo7();
 50         System.out.println("===============================================");
 51 
 52         // Demo8: 通过Java反射机制获得类加载器
 53         Demo8();
 54         System.out.println("===============================================");
 55 
 56     }
 57 
 58     /**
 59      * Demo1: 通过Java反射机制得到类的包名和类名
 60      */
 61     public static void Demo1() {
 62         Person person = new Person();
 63         System.out.println("Demo1: 包名: " + person.getClass().getPackage().getName() + "," + "完整类名: " + person.getClass().getName());
 64         
 65         /**
 66         运行结果:
 67         Demo1: 包名: com.b510.hongten.test.reflex,完整类名: com.b510.hongten.test.reflex.Person
 68         */
 69     }
 70 
 71     /**
 72      * Demo2: 验证所有的类都是Class类的实例对象 
 73      * 
 74      * @throws ClassNotFoundException
 75      */
 76     public static void Demo2() throws ClassNotFoundException {
 77         // 定义两个类型都未知的Class , 设置初值为null, 看看如何给它们赋值成Person类
 78         Class<?> class1 = null;
 79         Class<?> class2 = null;
 80 
 81         // 写法1, 可能抛出 ClassNotFoundException [多用这个写法]
 82         class1 = Class.forName("com.b510.hongten.test.reflex.Person");
 83         System.out.println("Demo2:(写法1) 包名: " + class1.getPackage().getName() + "," + "完整类名: " + class1.getName());
 84 
 85         /**
 86         运行结果:
 87         Demo2:(写法1) 包名: com.b510.hongten.test.reflex,完整类名: com.b510.hongten.test.reflex.Person
 88         */
 89         
 90         // 写法2
 91         class2 = Person.class;
 92         System.out.println("Demo2:(写法2) 包名: " + class2.getPackage().getName() + "," + "完整类名: " + class2.getName());
 93         
 94         /**
 95         运行结果:
 96         Demo2:(写法2) 包名: com.b510.hongten.test.reflex,完整类名: com.b510.hongten.test.reflex.Person
 97         */
 98     }
 99 
100     /**
101      * Demo3: 通过Java反射机制,用Class 创建类对象[这也就是反射存在的意义所在]
102      * 
103      * @throws ClassNotFoundException
104      * @throws IllegalAccessException
105      * @throws InstantiationException
106      */
107     public static void Demo3() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
108         Class<?> class1 = null;
109         class1 = Class.forName("com.b510.hongten.test.reflex.Person");
110         // 由于这里不能带参数,所以你要实例化的这个类Person,一定要有无参构造函数哈~
111         Person person = (Person) class1.newInstance();
112         person.setAge(20);
113         person.setName("Hongten");
114         System.out.println("Demo3: " + person.getName() + " : " + person.getAge());
115         
116         /**
117         运行结果:
118         Demo3: Hongten : 20
119         */
120     }
121 
122     /**
123      * Demo4: 通过Java反射机制得到一个类的构造函数,并实现创建带参实例对象
124      * 
125      * @throws ClassNotFoundException
126      * @throws InvocationTargetException
127      * @throws IllegalAccessException
128      * @throws InstantiationException
129      * @throws IllegalArgumentException
130      */
131     public static void Demo4() throws ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
132         Class<?> class1 = null;
133         Person person1 = null;
134         Person person2 = null;
135 
136         class1 = Class.forName("com.b510.hongten.test.reflex.Person");
137         // 得到一系列构造函数集合
138         Constructor<?>[] constructors = class1.getConstructors();
139 
140         person1 = (Person) constructors[0].newInstance();
141         person1.setAge(30);
142         person1.setName("Hongten");
143 
144         person2 = (Person) constructors[1].newInstance(20, "Hongten");
145 
146         System.out.println("Demo4: " + person1.getName() + " : " + person1.getAge() + "  ,   " + person2.getName() + " : " + person2.getAge());
147 
148         /**
149         运行结果:
150         Demo4: Hongten : 30  ,   Hongten : 20
151         */
152     }
153 
154     /**
155      * Demo5: 通过Java反射机制操作成员变量, set 和 get
156      * 
157      * @throws IllegalAccessException
158      * @throws IllegalArgumentException
159      * @throws NoSuchFieldException
160      * @throws SecurityException
161      * @throws InstantiationException
162      * @throws ClassNotFoundException
163      */
164     public static void Demo5() throws IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException, InstantiationException, ClassNotFoundException {
165         Class<?> class1 = null;
166         class1 = Class.forName("com.b510.hongten.test.reflex.Person");
167         Object obj = class1.newInstance();
168 
169         Field personNameField = class1.getDeclaredField("name");
170         personNameField.setAccessible(true);
171         personNameField.set(obj, "HONGTEN");
172 
173         System.out.println("Demo5: 修改属性之后得到属性变量的值:" + personNameField.get(obj));
174 
175         /**
176         运行结果:
177         Demo5: 修改属性之后得到属性变量的值:HONGTEN
178         */
179     }
180 
181     /**
182      * Demo6: 通过Java反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等
183      * 
184      * @throws ClassNotFoundException
185      */
186     public static void Demo6() throws ClassNotFoundException {
187         Class<?> class1 = null;
188         class1 = Class.forName("com.b510.hongten.test.reflex.SuperMan");
189 
190         // 取得父类名称
191         Class<?> superClass = class1.getSuperclass();
192         System.out.println("Demo6:  SuperMan类的父类名: " + superClass.getName());
193 
194         /**
195         运行结果:
196         Demo6:  SuperMan类的父类名: com.b510.hongten.test.reflex.Person
197         */
198         
199         System.out.println("===============================================");
200 
201         Field[] fields = class1.getDeclaredFields();
202         for (int i = 0; i < fields.length; i++) {
203             System.out.println("类中的成员: " + fields[i]);
204         }
205         /**
206         运行结果:
207         类中的成员: private boolean com.b510.hongten.test.reflex.SuperMan.BlueBriefs
208         */
209         
210         System.out.println("===============================================");
211 
212         // 取得类方法
213         Method[] methods = class1.getDeclaredMethods();
214         for (int i = 0; i < methods.length; i++) {
215             System.out.println("Demo6,取得SuperMan类的方法:");
216             System.out.println("函数名:" + methods[i].getName());
217             System.out.println("函数返回类型:" + methods[i].getReturnType());
218             System.out.println("函数访问修饰符:" + Modifier.toString(methods[i].getModifiers()));
219             System.out.println("函数代码写法: " + methods[i]);
220         }
221 
222         /**
223         运行结果:
224         Demo6,取得SuperMan类的方法:
225         函数名:isBlueBriefs
226         函数返回类型:boolean
227         函数访问修饰符:public
228         函数代码写法: public boolean com.b510.hongten.test.reflex.SuperMan.isBlueBriefs()
229         Demo6,取得SuperMan类的方法:
230         函数名:setBlueBriefs
231         函数返回类型:void
232         函数访问修饰符:public
233         函数代码写法: public void com.b510.hongten.test.reflex.SuperMan.setBlueBriefs(boolean)
234         Demo6,取得SuperMan类的方法:
235         函数名:fly
236         函数返回类型:void
237         函数访问修饰符:public
238         函数代码写法: public void com.b510.hongten.test.reflex.SuperMan.fly()
239         Demo6,取得SuperMan类的方法:
240         函数名:walk
241         函数返回类型:void
242         函数访问修饰符:public
243         函数代码写法: public void com.b510.hongten.test.reflex.SuperMan.walk(int)
244         */
245         
246         System.out.println("===============================================");
247 
248         // 取得类实现的接口,因为接口类也属于Class,所以得到接口中的方法也是一样的方法得到哈
249         Class<?> interfaces[] = class1.getInterfaces();
250         for (int i = 0; i < interfaces.length; i++) {
251             System.out.println("实现的接口类名: " + interfaces[i].getName());
252         }
253         
254         /**
255         运行结果:
256         实现的接口类名: com.b510.hongten.test.reflex.ActionInterface
257         */
258 
259     }
260 
261     /**
262      * Demo7: 通过Java反射机制调用类方法
263      * 
264      * @throws ClassNotFoundException
265      * @throws NoSuchMethodException
266      * @throws SecurityException
267      * @throws InvocationTargetException
268      * @throws IllegalAccessException
269      * @throws IllegalArgumentException
270      * @throws InstantiationException
271      */
272     public static void Demo7() throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
273         Class<?> class1 = null;
274         class1 = Class.forName("com.b510.hongten.test.reflex.SuperMan");
275 
276         System.out.println("Demo7: 
调用无参方法fly()");
277         Method method = class1.getMethod("fly");
278         method.invoke(class1.newInstance());
279 
280         System.out.println("调用有参方法walk(int m)");
281         method = class1.getMethod("walk", int.class);
282         method.invoke(class1.newInstance(), 100);
283         
284         /**
285         运行结果:
286         Demo7: 
287         调用无参方法fly()
288         fly method....
289         调用有参方法walk(int m)
290         fly in 100 m
291         */
292         
293     }
294 
295     /**
296      * Demo8: 通过Java反射机制得到类加载器信息
297      * 
298      * 在java中有三种类类加载器。[这段资料网上截取]
299      * 
300      * 1)Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。
301      * 
302      * 2)Extension ClassLoader 用来进行扩展类的加载,一般对应的是jrelibext目录中的类
303      * 
304      * 3)AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。
305      * 
306      * @throws ClassNotFoundException
307      */
308     public static void Demo8() throws ClassNotFoundException {
309         Class<?> class1 = null;
310         class1 = Class.forName("com.b510.hongten.test.reflex.SuperMan");
311         String nameString = class1.getClassLoader().getClass().getName();
312 
313         System.out.println("Demo8: 类加载器类名: " + nameString);
314         
315         /**
316         运行结果:
317         Demo8: 类加载器类名: sun.misc.Launcher$AppClassLoader
318         */
319     }
320 
321 }
322 
323 
324 class Person {
325     private int age;
326     private String name;
327 
328     public Person() {
329 
330     }
331 
332     public Person(int age, String name) {
333         this.age = age;
334         this.name = name;
335     }
336 
337     public int getAge() {
338         return age;
339     }
340 
341     public void setAge(int age) {
342         this.age = age;
343     }
344 
345     public String getName() {
346         return name;
347     }
348 
349     public void setName(String name) {
350         this.name = name;
351     }
352 }
353 
354 class SuperMan extends Person implements ActionInterface {
355     private boolean BlueBriefs;
356 
357     public void fly() {
358         System.out.println("fly method....");
359     }
360 
361     public boolean isBlueBriefs() {
362         return BlueBriefs;
363     }
364 
365     public void setBlueBriefs(boolean blueBriefs) {
366         BlueBriefs = blueBriefs;
367     }
368 
369     public void walk(int m) {
370         System.out.println("fly in " + m + " m");
371     }
372 }
373 
374 interface ActionInterface {
375     public void walk(int m);
376 }
  •  说说工厂模式和Java 反射(Reflection)机制

 如果在工厂模式下面,我们不使用Java 反射(Reflection)机制,会是什么样子呢?

 1 package com.b510.hongten.test.reflex;
 2 
 3 /**
 4  * @author hongten
 5  * @created Apr 18, 2018
 6  */
 7 public class FactoryTest {
 8 
 9     public static void main(String[] args) {
10         Cats cats = FactoryTest.getInstance("Tiger");
11         cats.eatMeat();
12 
13         /*
14          * The Result : The tiger eat meat.
15          */
16     }
17 
18     public static Cats getInstance(String name) {
19         Cats cats = null;
20         if ("Tiger".equals(name)) {
21             cats = new Tiger();
22         }
23         if ("Lion".equals(name)) {
24             cats = new Lion();
25         }
26         return cats;
27     }
28 }
29 
30 interface Cats {
31     public abstract void eatMeat();
32 }
33 
34 class Tiger implements Cats {
35 
36     public void eatMeat() {
37         System.out.println("The tiger eat meat.");
38     }
39 
40 }
41 
42 class Lion implements Cats {
43 
44     public void eatMeat() {
45         System.out.println("The lion eat meat.");
46     }
47 
48 }

我们会发现:

当我们在添加一个子类(Puma-美洲狮)的时候,就需要修改工厂类了。如果我们添加太多的子类的时候,改的就会很多。

我们对程序进行修改,加入反射机制。

 1 package com.b510.hongten.test.reflex;
 2 
 3 /**
 4  * @author hongten
 5  * @created Apr 18, 2018
 6  */
 7 public class FactoryTest {
 8 
 9     public static void main(String[] args) {
10         //Cats cats = FactoryTest.getInstance("com.b510.hongten.test.reflex.Lion");
11         Cats cats = FactoryTest.getInstance("com.b510.hongten.test.reflex.Tiger");
12         cats.eatMeat();
13 
14         /*
15          * The Result : The tiger eat meat.
16          */
17     }
18 
19     public static Cats getInstance(String name) {
20         Cats cats = null;
21         try {
22             try {
23                 //use Class.forName() with java reflection
24                 cats = (Cats) Class.forName(name).newInstance();
25             } catch (InstantiationException e) {
26                 e.printStackTrace();
27             } catch (IllegalAccessException e) {
28                 e.printStackTrace();
29             }
30         } catch (ClassNotFoundException e) {
31             e.printStackTrace();
32         }
33         return cats;
34     }
35 }
36 
37 interface Cats {
38     public abstract void eatMeat();
39 }
40 
41 class Tiger implements Cats {
42 
43     public void eatMeat() {
44         System.out.println("The tiger eat meat.");
45     }
46 
47 }
48 
49 class Lion implements Cats {
50 
51     public void eatMeat() {
52         System.out.println("The lion eat meat.");
53     }
54 
55 }

我们会发现利用了Java Reflection以后,现在就算我们添加任意多个子类的时候,工厂类就不需要修改。我们只需要传递工厂类的实现类的名称即可。

然而,在这样的情况下面,如果我们需要运行Lion实例的时候,我们还需要去修改java代码。

1 Cats cats = FactoryTest.getInstance("com.b510.hongten.test.reflex.Lion");

上面的代码虽然可以通过反射取得接口的实例,但是需要传入完整的包和类名。而且用户也无法知道一个接口有多少个可以使用的子类,所以我们通过属性文件的形式配置所需要的子类。

能不能不需要修改java代码,同样可以完成相同的操作呢?是可以的。

我们可以让工厂模式结合属性文件(如:*.properties, *.xml文件)

 属性文件:/reflex/cats.properties

1 lion=com.b510.hongten.test.reflex.Lion
2 tiger=com.b510.hongten.test.reflex.Tiger

测试文件:/reflex/FactoryTest.java

 1 package com.b510.hongten.test.reflex;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.FileNotFoundException;
 6 import java.io.FileOutputStream;
 7 import java.io.IOException;
 8 import java.util.Properties;
 9 
10 /**
11  * @author hongten
12  * @created Apr 18, 2018
13  */
14 public class FactoryTest {
15 
16     public static void main(String[] args) {
17         Cats cats;
18         try {
19             cats = FactoryTest.getInstance(getPro().getProperty("tiger"));
20             if (cats != null) {
21                 cats.eatMeat();
22             }
23         } catch (FileNotFoundException e) {
24             e.printStackTrace();
25         } catch (IOException e) {
26             e.printStackTrace();
27         }
28 
29         /*
30          * The Result : The tiger eat meat.
31          */
32     }
33 
34     public static Cats getInstance(String name) {
35         Cats cats = null;
36         try {
37             try {
38                 // use Class.forName() with java reflection
39                 cats = (Cats) Class.forName(name).newInstance();
40             } catch (InstantiationException e) {
41                 e.printStackTrace();
42             } catch (IllegalAccessException e) {
43                 e.printStackTrace();
44             }
45         } catch (ClassNotFoundException e) {
46             e.printStackTrace();
47         }
48         return cats;
49     }
50 
51     public static Properties getPro() throws FileNotFoundException, IOException {
52         Properties pro = new Properties();
53         File f = new File("cats.properties");
54         if (f.exists()) {
55             pro.load(new FileInputStream(f));
56         } else {
57             pro.setProperty("lion", "com.b510.hongten.test.reflex.Lion");
58             pro.setProperty("tiger", "com.b510.hongten.test.reflex.Tiger");
59             pro.store(new FileOutputStream(f), "FRUIT CLASS");
60         }
61         return pro;
62     }
63 }
64 
65 interface Cats {
66     public abstract void eatMeat();
67 }
68 
69 class Tiger implements Cats {
70 
71     public void eatMeat() {
72         System.out.println("The tiger eat meat.");
73     }
74 
75 }
76 
77 class Lion implements Cats {
78 
79     public void eatMeat() {
80         System.out.println("The lion eat meat.");
81     }
82 
83 }

作为开发者,我们只需要配置cats.properties属性文件即可。极大的提高了程序的扩展性能。

  • Java反射(Reflection)的一些注意事项

  1. 由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射。
  2. 另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

========================================================

More reading,and english is important.

I'm Hongten

 

大哥哥大姐姐,觉得有用打赏点哦!多多少少没关系,一分也是对我的支持和鼓励。谢谢。
Hongten博客排名在100名以内。粉丝过千。
Hongten出品,必是精品。

E | hongtenzone@foxmail.com  B | http://www.cnblogs.com/hongten

========================================================

原文地址:https://www.cnblogs.com/hongten/p/hongten_java_reflection.html