Java - 反射

Posted kehuaihan

tags:

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

大纲:
  Class类的使用;
  方法的反射;
  成员变量的反射;
  构造函数的反射;
  Java类加载机制;

在面向对象的世界里,万事万物皆对象;但其实静态的员、普通的数据类型不是对象,但我们最好看做万事万物皆对象;类也是对象,类是java.lang.Class类的实例对象;任何一个类都是Clss类的实例对象;

三种表示Class类的实例的方法:

 

技术分享图片
 1 /**
 2  * Class类的使用:Class类的实例对象的三种表示方法
 3  */
 4 package reflect;
 5 
 6 public class ClassTest01
 7 {
 8     public static void main(String[] args)
 9     {
10         // Foo类的实例对象:foo
11         Foo foo = new Foo();
12         
13         // Foo这个类也是一个实例对象,是java.lang.Class类的实例对象
14         // 任何一个类都是Class类的实例对象,这个实例对象有三种表示方法
15         
16         // 第一种表示方法:
17         // 实际告诉我们,任何一个类都有一个隐含的静态成员变量:class
18         Class c1 = Foo.class;
19         
20         // 第二种表示方法:已知该类的实例对象,通过getClass()方法获取
21         Class c2 = foo.getClass();
22         
23         // 官网描述:c1、c2表示Foo类的类类型(class type)
24         // 如何解释:Foo类本身就是一个实例对象,是Class类的实例对象
25         // 以此区分:Foo类的实例对象为foo,Foo类的类类型为c1、c2
26         
27         // c1、c2都是同一个类(Foo)的类类型,一个类只可能是Class类的一个实例对象
28         // 所以c1与c2完全相等
29         System.out.println(c1 == c2);
30         
31         // 第三种表示方法:
32         Class c3 = null;
33         try
34         {
35             // 写出类的全量名
36             c3 = Class.forName("reflect.Foo");
37         } 
38         catch (ClassNotFoundException e)
39         {
40             e.printStackTrace();
41         }
42         // c2与c3完全相等
43         System.out.println(c2 == c3);
44         
45         // 可以通过一个类的类类型创建该类的实例对象
46         try
47         {
48             // 前提:要有无参的构造方法
49             Foo f1 = (Foo) c1.newInstance();
50             // 此时可以调用类的方法了
51             f1.display();
52         } 
53         catch (InstantiationException e)
54         {
55             e.printStackTrace();
56         } 
57         catch (IllegalAccessException e)
58         {
59             e.printStackTrace();
60         }
61     }
62 }
63 
64 class Foo
65 {
66     void display()
67     {
68         System.out.println("abc");
69     }
70 }
Class类的使用:Class类的实例对象的三种表示方法

 

Class类动态加载类的用法:
  Class.forName(“类的全称”); 这种写法不仅表示了类的类类型,还表示了动态加载类;
  要区分编译和运行:
    编译时刻加载类是静态加载类、运行时刻加载类是动态加载类;
    new 创建对象:是静态加载类,在编译时刻就需要加载所有可能用到的类,不管这些类在后面是否真用到了;(通过动态加载类可以解决该问题);

静态加载类的方式:

 

技术分享图片
 1 /**
 2  * 静态加载类的方式:通过new创建对象
 3  */
 4 package reflect.static_load;
 5 
 6 public class Office
 7 {
 8     public static void main(String[] args)
 9     {
10         // Word类,Excel类必须都存在才能运行
11         if("Word".equals(args[0]))
12         {
13             // 通过new创建对象,是静态加载类,在编译时刻就需要加载所有可能使用到的类,不管你需不需要用;所以下面报错了,因为没有定义Word和Execl类;
14             // 现在的需求是:你想用哪个就加载哪个,哪个不用就不加载它,这就涉及到动态加载;
15             // 通过动态加载类可以解决该问题
16             Word word = new Word();  // 这里编译报错,因为没有Word类
17             word.start();
18         }
19         
20         if("Excel".equals(args[0]))
21         {
22             Excel excel = new Excel();  // 这里编译报错,因为没有Excel类
23             excel.start();
24         }
25     }
26 }
静态加载类的方式:通过new创建对象

 

动态加载类的方式:

技术分享图片
 1 /**
 2  * 动态加载类
 3  */
 4 package reflect.dynamic_load;
 5 
 6 public class OfficeBetter
 7 {
 8     public static void main(String[] args)
 9     {
10         // 实例一
11         try
12         {
13             // 动态加载类,在运行时刻加载;就算此时没有定义Execl类,编译也没有报错;只有在运行时才会报错
14             Class c1 = Class.forName("reflect.dynamic_load.Excel");
15         } 
16         catch (ClassNotFoundException e)
17         {
18             e.printStackTrace();
19         }
20         
21         // 实例二
22         // 动态加载类,在运行时刻加载
23         try
24         {
25             Class c2 = Class.forName("reflect.dynamic_load.Excel");
26             // 如下直接创建具体类的类对象不合适,如果你想要的是Office类对象,但加载的可能是Excel类对象
27             Word word = (Word) c2.newInstance();  // 运行时报错,因为c2是Excel类的类类型
28             word.show();
29             Excel excel = (Excel) c2.newInstance();
30             excel.show();
31         } 
32         catch (ClassNotFoundException e)
33         {
34             e.printStackTrace();
35         }
36         catch (InstantiationException e)
37         {
38             e.printStackTrace();
39         } 
40         catch (IllegalAccessException e)
41         {
42             e.printStackTrace();
43         }
44         
45         // 实例三
46         try
47         {
48             // 动态加载类,在运行时刻加载
49             Class c3 = Class.forName("reflect.dynamic_load.Excel");
50             // 通过类类型创建具体类的实例对象
51             // 统一标准,优化实例二
52             OfficeAble oa = (OfficeAble)c3.newInstance();
53             oa.show();
54         } 
55         catch (ClassNotFoundException e)
56         {
57             e.printStackTrace();
58         }
59         catch (InstantiationException e)
60         {
61             e.printStackTrace();
62         } 
63         catch (IllegalAccessException e)
64         {
65             e.printStackTrace();
66         }
67     }
68 }
动态加载类
技术分享图片
1 package reflect.dynamic_load;
2 
3 public interface OfficeAble
4 {
5     public void show();
6 }
View Code
技术分享图片
1 package reflect.dynamic_load;
2 
3 public class Word implements OfficeAble
4 {
5     public void show()
6     {
7         System.out.println("word...work");
8     }
9 }
View Code
技术分享图片
1 package reflect.dynamic_load;
2 
3 public class Excel implements OfficeAble
4 {
5     public void show()
6     {
7         System.out.println("excel...work");
8     }
9 }
View Code

 

基本数据类型对应的类类型:

 

技术分享图片
 1 /**
 2  * 基本数据类型和一些关键字都存在对应的类类型
 3  */
 4 package reflect;
 5 
 6 public class ClassTest02
 7 {
 8     public static void main(String[] args)
 9     {
10         Class c1 = int.class; // int基本类型对应的类类型
11         Class c2 = double.class;
12         // 打印出类类型的名称
13         System.out.println(c1.getName());  // 结果:int 
14         System.out.println(c2.getName());  // 结果:double
15         
16         Class c3 = Double.class; // Double类的类类型
17         Class c4 = String.class;
18         System.out.println(c3.getName());  // 结果:java.lang.Double
19         System.out.println(c4.getName());  // 结果:java.lang.String
20         System.out.println(c4.getSimpleName());  // 结果:String,不带包名的类名称
21         
22         // 只要是在类里面声明的返回值啊什么的基本都有对应的类类型
23         Class c5 = void.class; // void关键字对应的类类型
24         System.out.println(c5.getName());
25     }
26 }
基本数据类型和一些关键字都存在对应的类类型

 

Class类的基本API:获取类的常用(全部)信息:

 

技术分享图片
  1 /**
  2  * Class类的基本API:获取类的常用信息;
  3  * 获取类的信息,要先获取类的类类型
  4  */
  5 package reflect;
  6 
  7 import java.lang.reflect.Constructor;
  8 import java.lang.reflect.Field;
  9 import java.lang.reflect.Method;
 10 
 11 public class ClassTest03
 12 {
 13     public static void main(String[] args)
 14     {
 15         
 16     }
 17     
 18     // 获取方法的信息
 19     public static void getMethodsMessage(Object obj)
 20     {
 21         // 先获取类的类类型
 22         // 因为我们有了类的对象,所以通过这种方法获取类的类类型
 23         // 传递的是哪个子类的对象,获取的就是那个子类的类类型
 24         Class c = obj.getClass();  
 25         
 26         // 获取类的完整名称
 27         System.out.println(c.getName());
 28         
 29         // 获取类的名称(不是完整的名称)
 30         System.out.println(c.getSimpleName());
 31         
 32         // 获取类的方法
 33         // 万事万物都是对象,方法也是对象
 34         // 一个成员方法就是一个java.lang.reflect.Method类的对象,Method类里面封装了关于成员方法的操作
 35         // getMethods():获取所有的public修饰的函数,包括父类继承而来的
 36         Method[] ms = c.getMethods();
 37         // getDeclaredMethods():获取所有该类自己声明的方法,不问访问权限,不包括继承来的方法
 38         Method[] ms2 = c.getDeclaredMethods();
 39         
 40         // 下面获取方法的信息
 41         for(int i=0;i<ms.length;i++)
 42         {
 43             // 获取方法的返回值类型的类类型
 44             Class returnType = ms[i].getReturnType();
 45             // 获得方法的返回值类型的类类型的名字
 46             System.out.println(returnType.getName());
 47             
 48             // 获取方法的名称
 49             System.out.println(ms[i].getName());
 50             
 51             // 获取方法的参数列表的类型的类类型
 52             Class[] parameterTypes = ms[i].getParameterTypes();
 53             for(int j=0;j<parameterTypes.length;j++)
 54             {
 55                 // 获取方法的参数类型的类类型的名字
 56                 System.out.println(parameterTypes[j].getName());
 57             }
 58         }
 59     }
 60     
 61     // 获取成员变量的信息
 62     private static void getFieldsMessage(Object obj)
 63     {
 64         // 先获取类的类类型
 65         Class c = obj.getClass();
 66         
 67         // 成员变量也是对象,是java.lang.reflect.Field类的对象,Field类里面封装了关于成员变量的操作
 68         // getFields():获取所有的public的成员变量的信息
 69         Field[] fs = c.getFields();
 70         // getDeclaredFields():获取该类自己声明的成员变量的信息
 71         Field[] fs2 = c.getDeclaredFields();
 72         for(Field f:fs)
 73         {
 74             // 获取成员变量的类型的类类型
 75             Class fieldTypes = f.getType();
 76             // 获取成员变量的类型的类类型的名字
 77             String typeName = fieldTypes.getName();
 78             System.out.println(typeName);
 79             // 获取成员变量的名字
 80             String fieldName = f.getName();
 81             System.out.println(fieldName);
 82         }
 83     }
 84     
 85     // 获取对象的构造函数的信息
 86     public static void getConstructorMessage(Object obj)
 87     {
 88         // 先获取类的类类型
 89         Class c = obj.getClass();
 90         
 91         // 构造函数也是对象,是java.lang.reflect.Constructor类的对象,Constructor类中封装了构造函数的信息
 92         // c.getConstructors():获得所有的public的构造函数
 93         Constructor[] con = c.getConstructors();
 94         // getDeclaredConstructors():获得所有的构造函数;也可以说获得所有自己声明的构造方法,因为构造方法都是必须要自己声明的;
 95         Constructor[] con2 = c.getDeclaredConstructors();
 96         for(Constructor constr:con2)
 97         {
 98             // 获取构造函数的参数列表的类型的类类型
 99             Class[] parametersTypes = constr.getParameterTypes();
100             for(Class parametersType:parametersTypes)
101             {
102                 // 获取构造函数的参数列表的类型的类类型的名称
103                 System.out.println(parametersType.getName());
104             }
105             
106             // 获取构造函数的名字
107             System.out.println(constr.getName());
108         }
109     }
110 }
Class类的基本API:获取类的常用信息

 

方法的反射的基本操作:
1、如何获取某个方法:
  方法的名称和方法的参数列表才能唯一决定某个方法;
2、方法反射的操作:
  method.invoke(对象,参数列表);

 

技术分享图片
 1 /**
 2  * 方法的反射操作
 3  * 要获取一个方法,就是获取类的信息,获取类的信息首先要获取类的类类型
 4  */
 5 package reflect;
 6 
 7 import java.lang.reflect.InvocationTargetException;
 8 import java.lang.reflect.Method;
 9 
10 public class MethodTest01
11 {
12     public static void main(String[] args)
13     {
14         // 获取需要的方法:方法名+参数列表唯一决定一个方法
15         
16         // 先获取类类型
17         A a = new A();
18         Class c = a.getClass();
19         
20         // 再获取方法:方法名+参数列表
21         try
22         {
23             // 获取方法
24             // getMethod():获取public方法
25             // getDeclaredMethod():获取自己声明的方法
26             Method m = c.getMethod("show", new Class[]{int.class,int.class});
27             // 也可以这样写
28             Method m2 = c.getMethod("show", int.class,int.class);
29             
30             // 获取方法后,进行方法的反射操作
31             // 以前调用方法是这样写的,而方法的反射操作是用n对象来进行方法调用,但两者的效果是一样的
32             // a.show(1,2);
33             // 方法的反射操作
34             // 方法如果没有返回值,返回null,有具体的返回值就返回具体的返回值
35             Object o = m.invoke(a, new Object[]{1,2});
36             // 也可以这样写
37             Object o2 = m2.invoke(a, 1,2);
38             
39             Method m3 = c.getMethod("show", new Class[]{String.class,String.class});
40             Method m4 = c.getMethod("show", String.class,String.class);
41             Object o3 = m3.invoke(a, new Object[]{"hello",",world"});
42             Object o4 = m4.invoke(a, "hello",",world");
43             
44             // 方法的形参为空场景
45             Method m5 = c.getMethod("show", new Class[]{});
46             // 或者没有就不传
47             Method m6 = c.getMethod("show");
48             Object o5 = m5.invoke(a, new Object[]{});
49             Object o6 = m6.invoke(a);
50         } 
51         catch (NoSuchMethodException e)
52         {
53             e.printStackTrace();
54         } 
55         catch (SecurityException e)
56         {
57             e.printStackTrace();
58         }
59         catch (IllegalAccessException e)
60         {
61             e.printStackTrace();
62         } 
63         catch (IllegalArgumentException e)
64         {
65             e.printStackTrace();
66         } 
67         catch (InvocationTargetException e)
68         {
69             e.printStackTrace();
70         }
71     }
72 }
73 
74 class A
75 {
76     public void show(int i,int j)
77     {
78         System.out.println(i + j);
79     }
80     
81     public void show(String i,String j)
82     {
83         System.out.println(i.toUpperCase() + "," + j.toUpperCase());
84     }
85     
86     public void show()
87     {
88         
89     }
90 }
方法的反射操作

 

通过反射了解集合泛型的本质:

 

技术分享图片
 1 /**
 2  * 通过反射了解集合泛型的本质
 3  */
 4 package reflect;
 5 
 6 import java.lang.reflect.InvocationTargetException;
 7 import java.lang.reflect.Method;
 8 import java.util.ArrayList;
 9 
10 public class MethodTest02
11 {
12     public static void main(String[] args)
13     {
14         ArrayList list = new ArrayList();
15         ArrayList<String> list1 = new ArrayList<String>();
16         
17         list1.add("123");
18         // 下面这样写是错误的,因为泛型限制的加入的对象类型
19         // list1.add(23);
20         
21         Class c1 = list.getClass();
22         Class c2 = list1.getClass();
23         
24         // 反射的操作都是编译之后(运行时)的操作
25         // 结果为true,说明编译之后的集合的泛型是去泛型化的,就是说编译之后集合就没有泛型了
26         // java中集合的泛型是防止错误输入的,只在编译时有效,绕过编译就无效了
27         // 验证:通过方法的反射绕过编译
28         System.out.println(c1 == c2);  // true
29         
30         // 验证:通过方法的反射操作绕过编译
31         try
32         {
33             Method m = c2.getMethod("add", Object.class);
34             m.invoke(list1, 1); // 绕过编译操作就绕过了泛型,能添加int类型数据
35             System.out.println(list1.size());
36             System.out.println(list1);
37             
38             // 现在就不能用String类型遍历了,因为有了int类型
39         } 
40         catch (NoSuchMethodException e)
41         {
42             e.printStackTrace();
43         } 
44         catch (SecurityException e)
45         {
46             e.printStackTrace();
47         }
48         catch (IllegalArgumentException e)
49         {
50             e.printStackTrace();
51         } 
52         catch (InvocationTargetException e)
53         {
54             e.printStackTrace();
55         }
56         catch (IllegalAccessException e)
57         {
58             e.printStackTrace();
59         }
60     }
61 }
通过反射了解集合泛型的本质

 

 

 














以上是关于Java - 反射的主要内容,如果未能解决你的问题,请参考以下文章

反射机制入门

反射机制入门

反射机制入门

使用反射在外部JAR / CLASS上调用包含Hibernate事务的方法(Java EE)

为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?

OpenGL片段着色器不照亮场景