Java反射

什么是反射

在运行状态中,对于任意一个的类,都能通过反射知道或者获取这个类的所有属性和方法,不管是public还是private。反射能把Java类钟的各种成分映射成一个个Java对象,本质就是获取类的字节码文件,即.class文件,获取/调用类的属性或者方法。

重要方法

获取构造方法

方法 功能描述
public Constructor getConstructor(Class<?>… parameterTypes) 获取指定的构造方法,但是只能获得public权限的构造方法
public Constructor getDeclaredConstructor(Class<?>… parameterTypes) 获取指定的构造方法,包括private和protected
public Constructor<?>[] getConstructors() throws SecurityException 获取所用的public定义的构造方法,返回的是集合
public Constructor<?>[] getDeclaredConstructors() throws SecurityException 真正意义上获取所有的构造方法,包括private和protected,返回的是集合

获取属性及方法

方法 功能描述
public Field[] getFields() 获取所有public权限的属性
public Field getField(String name) 获取指定的属性
public native Field[] getDeclaredFields() 获取所有的属性,包括private和protected的属性集合
public Method[] getMethods() 获取所有public权限的方法
public Method getMethod(String name, Class<?>… parameterTypes) 获取指定的方法
public Method[] getDeclaredMethods() 获取所有的属性,包括private和protected的方法集合

综上可以看出方法名尾缀有s的返回都是集合,有Declared的都是能获取所有属性或是方法。

反射具体实现

测试属性和构造方法

定义一个Person类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Person {
public String name;
private int id;

public Person() {
System.out.println("无参构造方法");
}

private Person(String name, int id) {
this.id = id;
this.name = name;
}

private void eat() {
System.out.println("private-------------eat");
}

public void drink() {
System.out.println("public-------------drink");
}
}

使用测试

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
//三种获取Class对象的方式
Person person = new Person();
Class c1 = person.getClass();

Class c2 = Person.class;

Class c3 = Class.forName("com.zhouk.roomlocaltion.MainActivityTest.Person");


//获得类完整的名字
String className = c2.getName();
System.out.println("className:"+className);//输出com.ys.reflex.Person


//获得类的public类型的属性。
Field[] fields = c2.getFields();
for (Field field : fields) {
System.out.println("public属性:"+field.getName());
}

//获得类的所有属性。包括私有的
Field[] allFields = c2.getDeclaredFields();
for (Field field : allFields) {
System.out.println("全部属性:"+field.getName());
}

//获得类的public类型的方法。这里包括 Object 类的一些方法
Method[] methods = c2.getMethods();
for (Method method : methods) {
System.out.println("public方法:"+method.getName());//work waid equls toString hashCode等
}

//获得类的所有方法。
Method[] allMethods = c2.getDeclaredMethods();
for (Method method : allMethods) {
System.out.println("全部方法:"+method.getName());//work say
}

//获得指定的public属性
Field f1 = c2.getField("name");
System.out.println("获取name属性:"+f1);
//获得指定的私有属性
Field f2 = c2.getDeclaredField("id");
//启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消
f2.setAccessible(true);
System.out.println("获取id属性:"+f2);

//获取构造方法
Constructor[] constructors = c2.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("获取所有构造方法:"+constructor.toString());
}

输出控制台结果
可以看出无论私有还是公有,都可通过对应得方法来获取,那么利用反射如何调用私有方法呢?

用反射调用方法

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
public class Person {
public String name;
private int id;

public Person() {
System.out.println("无参构造方法");
}

private Person(String name, int id) {
this.id = id;
this.name = name;
}

private void eat(String food) {
System.out.println("private-------------eat"+food);
}

public void drink() {
System.out.println("public-------------drink");
}
}
// ... ...

//main方法下
Class c2 = Person.class;
//调用public方法
Object object = c2.newInstance();
Method m = c2.getDeclaredMethod("drink");
m.invoke(object);
//调用私有方法
Object o = c2.newInstance();
Method method = c2.getDeclaredMethod("eat",String.class);
method.setAccessible(true);//解除私有限定
method.invoke(o,"苹果");

需要注意得是,调用私有方法必须要method.setAccessible(true)来接触私有限制。
输出控制台结果

总结

可以看出在反射机制面前,类得私有变量、方法都能访问的到,通过反射我们可以业务关系解耦中发挥很大的作用,不暴露内部实现,又能拓展新业务,将开闭原则运用到极致,很多框架中的注解很中意使用反射。
另外再说下反射与private之间的矛盾,为什么Java允许private私有化变量或者方法禁止他类访问,而又出现反射机制来打破这种规定呢?其实对于private来讲,更大的意义是面向对象编程,它并不能解决安全的问题。一旦获取到代码,始终还是有办法查看或者改变代码的。表现安全更多的是无法让他人获取/查看代码。
所以,private比较类似生活中遇到的“禁止通行”的路标,但是脚在你腿上,你想走还是可以走的,但是后果就自负了。