Java反射

Class类的使用

除了基本的数据类型、静态成员不是对象,万事万物皆对象,而基本的数据类型可封装为包装类。
类是java.lang.Class类的实例对象,任何一个类都是Class的实例对象。

类的实例化表示方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Food food = new Food();
//第一种
Class c1 = food.getClass();
//第二种,任何一个都有一个隐含的静态成员class
Class c2 = Food.class;
//第三种
Class c3 = null;
try {
c3 = Class.forName("com.Reflectest.Food");
//需要加上类的包名
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//c1、c2、c3表示Food类的类类型(class type)
//一个类只可能是Class类的一个实例对象
System.out.println(c1 == c2);//true
System.out.println(c2 == c3);//true

c1、c2、c3表示Food类的类类型(class type)
一个类只可能是Class类的一个实例对象

通过类类型实例化对象

1
2
3
Food food1 = (Food) c1.newInstance();
//注意异常
//返回值为Object类型,需要转换

基本数据类型、包装类、void关键字等都存在类类型

1
2
3
4
5
6
7
8
9
10
11
Class cl1 = int.class;
Class cl2 = String.class;
Class cl3 = void.class;
Class cl4 = Double.class;
Class cl5 = double.class;//cl4和cl5不一样
System.out.println(cl1.getName()); //int
System.out.println(cl2.getName()); //java.lang.String
System.out.println(cl3.getName()); //void
System.out.println(cl4.getName()); //java.lang.Double
System.out.println(cl5.getName()); //double

动态加载类

编译时刻加载类是静态加载类,运行时刻加载类是动态加载类

  • new创建对象:是静态加载类,在编译时刻就需要加载所有的可能使用到的类。有一个类有问题(如不存在),都不能通过编译,会报错。
  • Class.forName("类的全称");不仅代表了类的类型,还代表了动态加载类。Class.forName()通过动态加载类,用到一个类时,才进行加载。动态加载类,编译时不会出错,只有运行时可能出错
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 静态加载类
* 如果Word类和Excel类不存在,则编译的时候会报错,IDE直接报错
*/
public class Office{
public static void main(String[] args){
Word word = new Word();
word.start();
Excel excel = new Excel();
excel.start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 动态加载类
* 编译时不会抛出异常
*/
public class OfficeBetter{
public static void main(String[] args) {
try {
Class cl = Class.forName(args[0]);
Excel e = (Excel)cl.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

功能性的类尽量使用动态加载,并对新添的类实现功能性接口(标准),这样就不用重新编译

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
public interface OfficeAble {
public void start();
}
public class Word implements OfficeAble {
@Override
public void start() {
System.out.println("Word");
}
}
public class Excel implements OfficeAble {
@Override
public void start() {
System.out.println("Excel");
}
}
public class OfficeBetter{
public static void main(String[] args) {
try {
Class cl = Class.forName(args[0]);
OfficeAble oa = (OfficeAble) cl.newInstance();
oa.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}

获取类的信息

通过反射可以获取类的方法、属性、构造函数、包等信息

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
public class MethodClass {
public static void main(String[] args){
Animal animal = new Animal();
MethodClass.printClassMethodMessage(animal);
MethodClass.printClassFieldMessage(animal);
MethodClass.printClassConstructorMessage(animal);
Integer i = 1;
MethodClass.printClassMethodMessage(i);
MethodClass.printClassFieldMessage(i);
MethodClass.printClassConstructorMessage(i);
String s = "Hello";
MethodClass.printClassMethodMessage(s);
MethodClass.printClassFieldMessage(s);
MethodClass.printClassConstructorMessage(s);
}
/**
* 获取类的方法信息
* 通过反射获取类的方法名称、返回值类型、参数列表
* 所有方法都是java.lang.reflect.Method类的对象
* 一个成员方法就是一个Method对象
*/
public static void printClassMethodMessage(Object obj){
//获取obj对象的类类型
Class cl = obj.getClass();
//获取类的名称,包含包名
System.out.println("类的名称,包含包名:\t"+cl.getName());
//获取类的名称,不包含包名
System.out.println("类的名称,不包含包名:\t"+cl.getSimpleName());
//获取所有public的方法,包括继承来的
Method[] ms = cl.getMethods();
//获取该类所有自己声明的方法,不论访问权限
Method[] ms1 = cl.getDeclaredMethods();
System.out.println("Method类的方法:");
for (Method m:ms1) {
//获取方法的返回值类型的类类型(int.clss\String.class,而不是int\String)
Class returnType = m.getReturnType();
System.out.print(returnType.getSimpleName()+"\t");
//获取方法的名称
System.out.print(m.getName()+"\t");
//获取参数类型(和返回值一样,得到的都是类类型)
Class[] paramTypes = m.getParameterTypes();
for(Class c:paramTypes){
System.out.print(c.getSimpleName()+",");
}
System.out.println();
}
System.out.println();
}
/**
* 获取类的成员变量信息
* 通过反射获取类的属性名称、属性类型
* 成员变量是java.lang.reflect.Field的对象
*/
public static void printClassFieldMessage(Object obj){
Class cl = obj.getClass();
//获取所有public的成员变量信息,包括继承
Field[] fields = cl.getFields();
//获取该类所有自己声明的成员变量信息,不论访问权限
Field[] fields1 = cl.getDeclaredFields();
for(Field field:fields){
//得到成员变量类型的类类型(int.clss\String.class)
Class fieldType = field.getType();
String typeName = fieldType.getName();
//得到成员变量的名称
String fieldName = field.getName();
System.out.println(typeName+"\t"+fieldName);
}
System.out.println();
}
/**
* 获取类的构造方法信息
* 通过反射获取构造函数的名称、参数列表
* 构造函数是java.lang.Constructor类的对象
*/
public static void printClassConstructorMessage(Object obj){
Class cl = obj.getClass();
//获得所有公有构造方法信息,包括继承
Constructor[] constructors = cl.getConstructors();
//获取自己声明的构造方法,不论访问权限
Constructor[] constructors1 = cl.getDeclaredConstructors();
for(Constructor constructor:constructors1){
//获取构造函数的名称
String constructorName = constructor.getName();
System.out.print(constructorName + " ");
//获取构造函数的参数
Class[] parameterTypes = constructor.getParameterTypes();
for(Class parameterType:parameterTypes){
System.out.print(parameterType.getSimpleName()+",");
}
System.out.println();
}
System.out.println();
}
}

方法的反射

通过方法的反射调用方法

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
37
38
public class ClassDemo5 {
public static void main(String[] args){
A a = new A();
Class cl = a.getClass();
try {
//无参无返回值
//1. 获取方法,也可以使用getDeclaredMethod()
Method m1 = cl.getMethod("print");
//Method m1 = cl.getDeclaredMethod("print");
//2. 调用方法,method.invok(对象,参数列表); 以下两种方式效果一样
m1.invoke(a);
a.print();
//有参有返回值
//参数列表为可变参数,使用以下两种方式均可
Method m2 = cl.getMethod("add",new Class[]{int.class,int.class});
//Method m1 = cl.getMethod("add",int.class,int.class);
//参数列表为可变参数,使用以下两种方式均可
//方法如果没有返回值则返回null,有返回值则返回具体的返回值,需要类型转换
int i = (int)m2.invoke(a,1,2);
//int i = (int)m2.invoke(a,new Object[]{1,2});
} catch (Exception e) {
e.printStackTrace();
}
}
}
class A{
public void print(){
System.out.println("hello");
}
public int add(int a,int b){
int c = a+b;
System.out.println(c);
return c;
}
}

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

Java中集合的泛型是防止错误输入的,只在编译阶段有效,如果绕开编译就无效了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ClassDemo6 {
public static void main(String[] args){
//list1可以添加任意类型,list2只能添加String类型
ArrayList list1 = new ArrayList();
ArrayList<String> list2 = new ArrayList<>();
//反射的操作过都是编译之后的操作
//c1 == c2 返回true说明编译之后集合的泛型是去泛型化的
Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1 == c2); //true
//Java中集合的泛型是防止错误输入的,只在编译阶段有效,如果绕开编译就无效了
//验证:通过方法的反射绕过编译
Method m = null;
try {
m = c2.getMethod("add",Object.class);
m.invoke(list2,100);
System.out.println(list2);
} catch (Exception e) {
e.printStackTrace();
}
}
}