Java反射学习:深入学习Java反射机制

Posted 小风微灵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java反射学习:深入学习Java反射机制相关的知识,希望对你有一定的参考价值。

一、Java反射的理解(反射是研究框架的基础之一)

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

二、逐步分析

参考:https://blog.csdn.net/u012585964/article/details/52011138

 

1、关于Class

  1、Class是一个类,一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Filed,描述构造器的Constructor等属性
    2、对象照镜子后(反射)可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。
    3、对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。 一个 Class 对象包含了特定某个类的有关信息。
    4、Class 对象只能由系统建立对象
    5、一个类在 JVM 中只会有一个Class实例

下面创建一个例子:后面的分析均以此案例为基准。

创建一个空接口:(后面会不断补充) 

1 package com.xfwl.reflection;
2 
3 public interface IHuman {
4 
5 }

 创建一个空基类:(后面会不断补充) 

1 package com.xfwl.reflection;
2 
3 public class Human {
4     
5 }

 创建一个子类:(后面会不断补充) 

 1 package com.xfwl.reflection;
 2 
 3 public class Person extends Human implements IHuman {
 4     /**
 5      * 默认default修饰
 6      */
 7     String name;
 8     /**
 9      * private修饰
10      */
11     private int age;
12     /**
13      * public修饰
14      */
15     public char sex=\'M\';
16     /**
17      * 无参构造
18      */
19     public Person(){
20         System.out.println("无参构造!!!");
21     }
22     /**
23      * 有参构造
24      */
25     public Person(String name,int age,char sex){
26         System.out.println("有参构造!!!");
27         this.name=name;
28         this.age=age;
29         this.sex=sex;
30     }
31     public String getName() {
32         return name;
33     }
34     public void setName(String name) {
35         this.name = name;
36     }
37     public int getAge() {
38         return age;
39     }
40     public void setAge(int age) {
41         this.age = age;
42     }
43     public char getSex() {
44         return sex;
45     }
46     public void setSex(char sex) {
47         this.sex = sex;
48     }
49     public String toString() {  
50         return "Person{" +  
51                 "name=\'" + name + \'\\\'\' +  
52                 ", age=" + age  +  
53                 ", sex=\'" + sex + \'\\\'\' +  
54                 \'}\';  
55     }  
56 }

 2、反射获取类对象的三种方式(通过一个Junit测试来说明) 

 1 package com.xfwl.reflection;
 2 
 3 import org.junit.Test;
 4 /**
 5  * 测试类
 6  * @function  
 7  * @author 小风微凉
 8  * @time  2018-6-3 下午12:28:38
 9  */
10 public class TestAction {
11     /** 
12      * 反射机制获取类有三种方法 
13      */ 
14     @Test
15     public void testGetClass() throws ClassNotFoundException {  
16          Class clazz = null; 
17          
18         //1 直接通过类名.Class的方式得到  
19         clazz = Person.class;  
20         System.out.println("通过类名: " + clazz);  
21          
22         //2 通过对象的getClass()方法获取,这个使用的少(一般是传的是Object,不知道是什么类型的时候才用)  
23         Object obj = new Person();  
24         clazz = obj.getClass();  
25         System.out.println("通过getClass(): " + clazz);   
26          
27         //3 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常  
28         clazz = Class.forName("com.xfwl.reflection.Person");  
29         System.out.println("通过全类名获取: " + clazz);           
30     }
31 }

 运行结果: 

通过类名: class com.xfwl.reflection.Person
无参构造!!!
通过getClass(): class com.xfwl.reflection.Person
通过全类名获取: class com.xfwl.reflection.Person

 特别注意:(以下2中方式不会调用构造方法,因为没有实例化操作)  

//1 直接通过类名.Class的方式得到  
clazz = Person.class; 
//3 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常  
clazz = Class.forName("com.xfwl.reflection.Person");

3、利用newInstance创建对象:调用的类必须有无参的构造器

 1 /**
 2      * Class类的newInstance()方法,创建类的一个对象。
 3      * @throws ClassNotFoundException
 4      * @throws IllegalAccessException
 5      * @throws InstantiationException
 6      */
 7     @Test
 8     public void testNewInstance()  
 9             throws ClassNotFoundException, IllegalAccessException, InstantiationException {  
10       
11         Class clazz = Class.forName("com.xfwl.reflection.Person");  
12       
13         //使用Class类的newInstance()方法创建类的一个对象  
14         //实际调用的类的那个 无参数的构造器(这就是为什么写的类的时候,要写一个无参数的构造器,就是给反射用的)  
15         //一般的,一个类若声明了带参数的构造器,也要声明一个无参数的构造器  
16         Object obj = clazz.newInstance();  
17         System.out.println(obj);  
18     }  

 测试结果:

那么,如果删除Person.java中的无参构造,继续测试,结果如下:

4、ClassLoader类加载器


类加载器详解:
http://blog.csdn.net/ochangwen/article/details/51473120
 
 1     /** 
 2      * ClassLoader类装载器 
 3      */  
 4     @Test  
 5     public void testClassLoader1() throws ClassNotFoundException, IOException {  
 6         //1、获取一个系统的类加载器  
 7         ClassLoader classLoader = ClassLoader.getSystemClassLoader();  
 8         System.out.println("系统的类加载器-->" + classLoader);  
 9       
10         //2、获取系统类加载器的父类加载器(扩展类加载器(extensions classLoader))  
11         classLoader = classLoader.getParent();  
12         System.out.println("扩展类加载器-->" + classLoader);  
13       
14         //3、获取扩展类加载器的父类加载器  
15         //输出为Null,无法被Java程序直接引用  
16         classLoader = classLoader.getParent();  
17         System.out.println("启动类加载器-->" + classLoader);  
18       
19         //4、测试当前类由哪个类加载器进行加载 ,结果就是系统的类加载器  
20         classLoader = Class.forName("com.xfwl.reflection.Person").getClassLoader();  
21         System.out.println("当前类由哪个类加载器进行加载-->"+classLoader);  
22       
23         //5、测试JDK提供的Object类由哪个类加载器负责加载的  
24         //输出为Null,无法被Java程序直接引用
25         classLoader = Class.forName("java.lang.Object").getClassLoader();  
26         System.out.println("JDK提供的Object类由哪个类加载器加载-->" + classLoader);  
27     } 

 测试结果: 

系统的类加载器-->sun.misc.Launcher$AppClassLoader@18b4aac2
扩展类加载器-->sun.misc.Launcher$ExtClassLoader@614c5515
启动类加载器-->null
当前类由哪个类加载器进行加载-->sun.misc.Launcher$AppClassLoader@18b4aac2
JDK提供的Object类由哪个类加载器加载-->null

 5、反射机制通过加载器获取流对象:getResourceAsStream方法 

 1 /**
 2      * 反射机制通过加载器获取流对象:getResourceAsStream方法
 3      * @throws ClassNotFoundException
 4      * @throws IOException
 5      */
 6      @Test  
 7     public void testGetResourceAsStream() throws ClassNotFoundException, IOException {  
 8         //调用getResourceAsStream 获取类路径下的文件对应的输入流 
 9          /**
10           * 特别说明:
11           * getResourceAsStream("path"),path的路径和new Person()的位置有关
12           */
13          
14         InputStream in = new Person().getClass().getClassLoader()  
15                 .getResourceAsStream("com/xfwl/reflection/test.properties");  
16         System.out.println("in: " +in);  
17   
18         Properties properties = new Properties(); 
19         properties.load(in);  
20         System.out.println("文件内容:"+properties);
21         System.out.println("name: "+properties.getProperty("name"));  
22         System.out.println("age: " + properties.getProperty("age"));
23         System.out.println("sex: "+properties.getProperty("sex"));  
24         System.out.println("desc: " + properties.getProperty("desc")); 
25     }  

 test.properties文件内容如下:文件编码格式:ISO-8859-1 

name=\\u5C0F\\u98CE\\u5FAE\\u51C9\\u0087\\u0089
age=23
sex=M
desc=\\u53CD\\u5C04\\u673A\\u5236\\u5B66\\u4E60

 运行结果:(直接解析会出现乱码问题,这个可以通过new String(乱码格式处理参数)来处理)

无参构造!!!
in: java.io.BufferedInputStream@215be6bb
文件内容:{age=23, name=小风微凉??, sex=M, desc=反射机制学习}
name: 小风微凉??
age: 23
sex: M
desc: 反射机制学习

 6、反射机制获取类中的方法:Method: 对应类中的方法 

现在给Person类添加一个private 方法、一个public 方法、一个defaut 方法、一个protected方法 

 1 /**
 2      * Java权限有四个,分别为public,protected,默认,private,其开放程度依次降低
 3      *    public可供所有类访问
 4      *    protected继承可见
 5      *    private只能类本身内部的方法可以访问
 6      */    
 7     public void method_public(){
 8         System.out.println("method_public");
 9     }
10     public void method_public_2(String name,int age,char sex){//public  带参数
11         System.out.println("method_public_2");
12         String info="Person{" +  
13         "name=\'" + name + \'\\\'\' +  
14         ", age=" + age  +  
15         ", sex=\'" + sex + \'\\\'\' +  
16         \'}\';
17         System.out.println(info);
18     }
19     protected void method_protected(){
20         System.out.println("method_protected");
21     }
22     protected void method_protected_2(String info){//protected  带参数
23         System.out.println("method_protected_2:"+info);
24     }
25     void method_default(){
26         System.out.println("method_default");
27     }
28     void method_default_2(String info){//默认修饰符   带参数
29         System.out.println("method_default_2:"+info);
30     }
31     private void method_private(){
32         System.out.println("method_private");
33     }
34     private void method_private_2(String info){//private  带参数
35         System.out.println("method_private_2:"+info);
36     }

 开始测试如何通过反射机制使用这些方法 

 1 /**
 2      * 如何通过反射机制使用这些方法
 3      * @throws ClassNotFoundException
 4      * @throws NoSuchMethodException
 5      * @throws IllegalAccessException
 6      * @throws InstantiationException
 7      * @throws InvocationTargetException
 8      */
 9     @Test  
10     public void testMethod() throws ClassNotFoundException, NoSuchMethodException,   
11             IllegalAccessException, InstantiationException, InvocationTargetException {  
12         Class clazz = Class.forName("com.xfwl.reflection.Person");  
13       
14         //1、得到clazz 对应的类中有哪些方法,不能获取private方法  
15         Method[] methods =clazz.getMethods();  
16         System.out.println("通过反射机制可以拿到的方法:clazz.getMethods()");  
17         for (Method method : methods){  
18             System.out.println(method.getName());  
19         } 
20         System.out.println("<-------------------------->"); 
21       
22         //2、获取所有的方法(且只获取当着类声明的方法,包括private方法)  
23         Method[] methods2 = clazz.getDeclaredMethods(); 
24         System.out.println("通过反射机制可以拿到的方法:clazz.getDeclaredMethods()");  
25         for (Method method : methods2){  
26             System.out.println(method.getName());  
27         }  
28         System.out.println("<-------------------------->"); 
29         System.out.println("通过反射机制可以拿到指定的方法:clazz.getDeclaredMethod()"); 
30         //3、获取指定的方法  
31         Method  method1= clazz.getDeclaredMethod("method_private");
32         System.out.println("private 无参:"+method1);  
33       
34         Method method2 = clazz.getDeclaredMethod("method_private_2",String.class);//第一个参数是方法名,后面的是方法里的参数  
35         System.out.println("private 有参:"+method2);  
36       
37         Method method3 = clazz.getDeclaredMethod("method_public_2",String.class,int.class,char.class);//第一个参数是方法名,后面的是方法里的参数  
38         System.out.println("public 有参:"+method2);  
39       
40         //4、执行方法!  
41         Object obj = clazz.newInstance();  
42         method3.invoke(obj, "小风微凉", 23,\'M\');  //执行方法:invoke(类对象)
43     }  

 测试结果: 

通过反射机制可以拿到的方法:clazz.getMethods():不能获取private/protected/default方法 
toString
getName
setName
method_public_2
setAge
method_public
getSex
getAge
setSex
wait
wait
wait
equals
hashCode
getClass
notify
notifyAll
<-------------------------->
通过反射机制可以拿到的方法:clazz.getDeclaredMethods():获取所有修饰权限的方法
toString
getName
setName
method_private_2
method_private
method_public_2
setAge
method_public
method_default
method_default_2
getSex
getAge
setSex
method_protected
method_protected_2
<-------------------------->
通过反射机制可以拿到指定的方法:clazz.getDeclaredMethod()
private 无参:private void com.xfwl.reflection.Person.method_private()
private 有参:private void com.xfwl.reflection.Person.method_private_2(java.lang.String)
public 有参:private void com.xfwl.reflection.Person.method_private_2(java.lang.String)
无参构造!!!
method_public_2
Person{name=\'小风微凉\', age=23, sex=\'M\'}

 继续分析一下:

JDK中的获取方法

获取方法:默认只能获取public修饰的方法 

1 @CallerSensitive
2     public Method[] getMethods() throws SecurityException {
3         checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
4         return copyMethods(privateGetPublicMethods());
5     }

 获取方法:所有修饰权限的方法都可以获得 

@CallerSensitive
    public Method[] getDeclaredMethods() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyMethods(privateGetDeclaredMethods(false));
    }

 获取方法:获取指定的方法(所有修饰权限) 

 1     /**
 2      * @jls 8.2 Class Members
 3      * @jls 8.4 Method Declarations
 4      * @since JDK1.1
 5      */
 6     @CallerSensitive
 7     public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
 8         throws NoSuchMethodException, SecurityException {
 9         checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
10         Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
11         if (method == null) {
12             throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
13         }
14         return method;
15     }

   分析一下上面这个方法:

    String name:方法的名称

       Class<?>... parameterTypes:一个或多个方法参数的类型,注意要一一对应,否则会报错的哦

执行方法:invoke(方法对象,方法实际参数) 

 1 @CallerSensitive
 2     public Object invoke(Object obj, Object... args)
 3         throws IllegalAccessException, IllegalArgumentException,
 4            InvocationTargetException
 5     {
 6         if (!override) {
 7             if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
 8                 Class<?> caller = Reflection.getCallerClass();
 9                 checkAccess(caller, clazz, obj, modifiers);
10             }
11         }
12         MethodAccessor ma = methodAccessor;             // read volatile
13         if (ma == null) {
14             ma = acquireMethodAccessor();
15         }
16         return ma.invoke(obj, args);
17     }

 7、反射机制获取类中的方法:Method: 对应基类或接口中的方法

上面分析了,如何通过反射拿到当前本类里面的各个修饰权限的方法,下面来继续分析一下,如何读取父类或实现的接口中的方法:

现在,给父类添加一些方法,在接口中定义一些方法:

接口中的方法声明: 

1 package com.xfwl.reflection;
2 
3 public interface IHuman {
4 
5     void eat();
6     void eat(String info);
7 }

 Person.java实现接口方法

1     public void eat() {
2         System.out.println("实现接口的方法:eat()无参:");
3     }
4     public void eat(String info) {
5         System.out.println("实现接口的方法:eat()有参:"+info);        
6     }

 父类中的方法定义: 

 1 package com.xfwl.reflection;
 2 
 3 public class Human {
 4     public void play_public(){
 5         System.out.println("public无参:play_public");
 6     }
 7     public void play_public_2(String info){
 8         System.out.println("public有参:play_public2:"+info);
 9     }
10     protected void play_protected(){
11         System.out.println("protected无参:play_protected");
12     }
13     protected void play_protected_2(String info){
14         System.out.println("protected有参:play_protected_2:"+info);
15     }
16     void play_default(){
17         System.out.println("默认修饰符无参:play_default");
18     }
19     void play_default_2(String info){//默认修饰符   带参数
20         System.out.println("默认修饰符有参:play_default_2:"+info);
21     }
22     private void play_private(){
23         System.out.println("private无参:play_private");
24     }
25     private void play_private_2(String info){
26         System.out.println("private有参:play_private_2:"+info);
27     }
28 }

 开始测试:

1、拿到当前Person类反射对象,能否得到接口中的方法 

 1 /**
 2      * 反射机制获取类中的方法:Method: 对应基类或接口中的方法
 3      * @throws ClassNotFoundException 
 4      * @throws SecurityException 
 5      * @throws NoSuchMethodException 
 6      * @throws InstantiationException 
 7      * @throws InvocationTargetException 
 8      * @throws IllegalArgumentException 
 9      * @throws IllegalAccessException 
10      */
11     @Test
12     public void testInterfaceOrSupperClass() throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{
13          Class clazz = Class.forName("com.xfwl.reflection.Person"Java 反射机制

Java中的反射机制

深入理解Java:类加载机制及反射

深入理解Java:类加载机制及反射

Java-反射机制学习

JAVA 学习笔记 - 反射机制