Java反射

Java反射机制是在运行过程中,对于任意一个类,能够动态的获取对象的属性和方法

作用

  1. 获取类的变量,调用类的私有方法
  2. 增加代码的灵活性

获取类的Class对象

通过对象的getClass()方法

1
2
3
String str = "Hello World";
Class<?> clazz = str.getClass();
System.out.println(clazz);

输入结果

1
class java.lang.String

通过类的.class属性

1
2
Class<?> clazz2 = String.class;
System.out.println(clazz2);

输出结果

1
class java.lang.String

通过Class类的静态方法forName(String className)

1
2
Class<?> clazz3 = Class.forName("java.lang.String");
System.out.println(clazz3);

输出结果

1
class java.lang.String

注意:使用这种方法,需要写上包名,不需要写.class

应用场景

先创建一个实体类User,具体内容如下

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
public class User {

private int id;
private String name;
private String password;
private int level;
private String phone;

public User() {
}

public User(int id, String name, String password, int level, String phone) {
super();
this.id = id;
this.name = name;
this.password = password;
this.level = level;
this.phone = phone;
}

public User(int id, String name, String password) {
super();
this.id = id;
this.name = name;
this.password = password;
}

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

获取所有的构造方法

  • public Constructor<?>[] getConstructors()

获得所有 public 访问权限的构造方法

1
2
3
4
5
6
7
Class<?> clazz = Class.forName("org.spirit.lemon.reflect.User");

System.out.println("获取所有的公共构造方法:");
Constructor<?>[] constructors = clazz.getConstructors();
for (int i = 0; i < constructors.length; i++) {
System.out.println(constructors[i]);
}

输出结果

1
2
3
4
获取所有的公共构造方法:
public org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String,java.lang.String)
public org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String,java.lang.String,int,java.lang.String)
public org.spirit.lemon.reflect.User()

可以看出,只输出了三个public修饰的构造方法,私有的构造方法并未输出

  • public Constructor getConstructor(Class<?>… parameterTypes)

获得指定的构造方法,注意只能获得 public 权限的构造方法,其他访问权限的获取不到

1
2
3
4
5
6
7
8
System.out.println("获取指定的公共构造方法:");
Constructor<?> constructor = clazz.getConstructor(Integer.class, String.class, String.class);
System.out.println(constructor);
System.out.println();

System.out.println("私有构造方法测试:");
constructor = clazz.getConstructor(Integer.class, String.class);
System.out.println(constructor);

输出结果

1
2
3
4
5
6
7
8
获取指定的公共构造方法:
public org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String,java.lang.String)

私有构造方法测试:
Exception in thread "main" java.lang.NoSuchMethodException: org.spirit.lemon.reflect.User.<init>(java.lang.Integer, java.lang.String)
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getConstructor(Class.java:1825)
at org.spirit.lemon.reflect.Test.main(Test.java:37)

可以看到,只能获取public修饰的构造方法,在获取private修饰的构造方法时,抛出了异常信息

  • public Constructor<?>[] getDeclaredConstructors()

获得所有的构造方法,包括(public, private,protected,默认权限的)

1
2
3
4
5
System.out.println("获取所有的公共构造方法(包括私有构造方法):");
Constructor[] constructors = clazz.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
System.out.println(constructors[i]);
}

输出结果

1
2
3
4
5
获取所有的公共构造方法(包括私有构造方法):
private org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String)
public org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String,java.lang.String)
public org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String,java.lang.String,int,java.lang.String)
public org.spirit.lemon.reflect.User()

不仅输出了public修饰的构造方法,private的构造方法也有输出

  • public Constructor getDeclaredConstructor(Class<?>… parameterTypes)

获得指定的构造方法,注意可以获取到任何访问权限的构造方法。

1
2
3
System.out.println("获取指定的构造方法(私有构造方法也可以获取):");
Constructor constructor = clazz.getDeclaredConstructor(Integer.class, String.class);
System.out.println(constructor);

输出结果

1
2
获取指定的构造方法(私有构造方法也可以获取):
private org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String)

可以看出,private修饰的构造方法也可以获取到

通过构造方法构建实例对象

获取到构造方法Constructor的对象之后,可以通过Constructor对象创建类的实例

1
2
3
Constructor constructor = clazz.getDeclaredConstructor(Integer.class, String.class, String.class);
User user = (User)constructor.newInstance(1, "lemon", "a12345+");
System.out.println(user.toString());

输出结果

1
User [id=1, name=lemon, password=a12345+, level=null, phone=null]

不过私有构造方法好像是不能通过这个方式创建对象的

1
2
3
Constructor constructor = clazz.getDeclaredConstructor(Integer.class, String.class);
User user = (User)constructor.newInstance(1, "lemon");
System.out.println(user.toString());

输出结果

1
2
3
4
5
6
Exception in thread "main" java.lang.IllegalAccessException: Class org.spirit.lemon.reflect.Test can not access a member of class org.spirit.lemon.reflect.User with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Constructor.newInstance(Constructor.java:413)
at org.spirit.lemon.reflect.Test.main(Test.java:55)

抛出异常,对象创建失败

通过Class对象创建实例对象

1
2
User user2 = (User)clazz.newInstance(); //User.class.newInstance()也可以实现
System.out.println(user2.toString());

输出结果

1
User [id=0, name=null, password=null, level=null, phone=null]

更多创建对象的方法可以参考Java 创建对象的几种方式

获取属性

添加Student类,继承User的属性

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
public class Student extends  User{

private String cardNo;
private Integer profession;
public String cls;

public Student() {

}

public Student(String cardNo, Integer profession) {
super();
this.cardNo = cardNo;
this.profession = profession;
}

public String getCardNo() {
return cardNo;
}

public void setCardNo(String cardNo) {
this.cardNo = cardNo;
}

public Integer getProfession() {
return profession;
}

public void setProfession(Integer profession) {
this.profession = profession;
}

public String getCls() {
return cls;
}

public void setCls(String cls) {
this.cls = cls;
}

@Override
public String toString() {
return "Student [cardNo=" + cardNo + ", profession=" + profession + ", cls=" + cls + ", id=" + id + ", name=" + name
+ ", password=" + password + ", level=" + level + ", phone=" + phone + "]";
}
}
  • public Field[] getDeclaredFields()

返回 Field 对象的一个数组,包括public、private、protected和default属性,但是不包括继承的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Class<?> clazz = Student.class;	

Student student = new Student();
student.setCardNo("1001");
student.setProfession(11055225);
student.setCls("数媒2班");

Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
field.setAccessible(true); //设置属性可访问,否则私有属性无法获取值,抛出IllegalAccessException

System.out.println(field);//属性字段对象

System.out.println(field.getName());//属性名
System.out.println(field.getType());//属性的类型

System.out.println(field.get(student));//获取对象指定属性的值

System.out.println("-------------------------");
}

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private java.lang.String org.spirit.lemon.reflect.Student.cardNo
cardNo
class java.lang.String
1001
-------------------------
private java.lang.Integer org.spirit.lemon.reflect.Student.profession
profession
class java.lang.Integer
11055225
-------------------------
public java.lang.String org.spirit.lemon.reflect.Student.cls
cls
class java.lang.String
数媒2
-------------------------
  • public Field[] getFields()

获取类的公共属性,包括继承的属性;私有属性无法获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Field[] fields = clazz.getFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
field.setAccessible(true);

System.out.println(field);

System.out.println(field.getName());
System.out.println(field.getType());

System.out.println(field.get(student));

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
public java.lang.String org.spirit.lemon.reflect.Student.cls
cls
class java.lang.String
数媒2
-------------------------
public int org.spirit.lemon.reflect.User.id
id
int
0
-------------------------
public java.lang.String org.spirit.lemon.reflect.User.name
name
class java.lang.String
null
-------------------------
public java.lang.String org.spirit.lemon.reflect.User.password
password
class java.lang.String
null
-------------------------
public java.lang.Integer org.spirit.lemon.reflect.User.level
level
class java.lang.Integer
null
-------------------------
public java.lang.String org.spirit.lemon.reflect.User.phone
phone
class java.lang.String
null
-------------------------

可以看出,cardNo和profession两个私有属性没有输出

  • public Field getDeclaredField(String name)

获取指定属性的Field对象,包括private属性;不可以获取继承自父类的属性

1
2
3
4
5
6
7
8
9
Field field = clazz.getDeclaredField("cls");
field.setAccessible(true);

System.out.println(field);

System.out.println(field.getName());
System.out.println(field.getType());

System.out.println(field.get(student));

输出结果

1
2
3
4
public java.lang.String org.spirit.lemon.reflect.Student.cls
cls
class java.lang.String
数媒2班
  • public Field getField(String name)

获取指定属性的Field对象,包括继承自父类的属性;只能获取public修饰的属性

1
2
3
4
5
6
7
8
9
Field field = clazz.getField("cls");
field.setAccessible(true);

System.out.println(field);

System.out.println(field.getName());
System.out.println(field.getType());

System.out.println(field.get(student));

输出结果

1
2
3
4
public java.lang.String org.spirit.lemon.reflect.User.name
name
class java.lang.String
null

获取方法

在User类中添加方法

1
2
3
public void test() {
System.out.println("User");
}

在Student类中添加私有方法

1
2
3
private void test2() {
System.out.println("Student");
}
  • public Method[] getDeclaredMethods()

获取所有的方法,包括private方法,但是不包括继承自父类的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
System.out.println(method);
System.out.println(method.getName());//方法名
System.out.println(method.getReturnType());//返回值

if (method.getName().contains("test2")) {
method.setAccessible(true);//设置可访问,否则私有方法执行报错
System.out.println("执行test2方法:");
method.invoke(student, args);//执行方法
}
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
public java.lang.String org.spirit.lemon.reflect.Student.toString()
toString
class java.lang.String
---------分割线----------
public void org.spirit.lemon.reflect.Student.setCls(java.lang.String)
setCls
void
---------分割线----------
private void org.spirit.lemon.reflect.Student.test2()
test2
void
执行test2方法:
Student
---------分割线----------
public void org.spirit.lemon.reflect.Student.setCardNo(java.lang.String)
setCardNo
void
---------分割线----------
public void org.spirit.lemon.reflect.Student.setProfession(java.lang.Integer)
setProfession
void
---------分割线----------
public java.lang.String org.spirit.lemon.reflect.Student.getCardNo()
getCardNo
class java.lang.String
---------分割线----------
public java.lang.String org.spirit.lemon.reflect.Student.getCls()
getCls
class java.lang.String
---------分割线----------
public java.lang.Integer org.spirit.lemon.reflect.Student.getProfession()
getProfession
class java.lang.Integer
---------分割线----------

可以看出,User的方法test并没有获取到

  • public Method[] getMethods()

获取类的方法,包括继承自父类的方法;但是只能获取public修饰的方法,私有方法不能获取

1
2
3
4
5
6
7
8
9
Method[] methods = clazz.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
System.out.println(method);
System.out.println(method.getName());//方法名
System.out.println(method.getReturnType());//返回值

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
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
public java.lang.String org.spirit.lemon.reflect.Student.toString()
toString
class java.lang.String
---------分割线----------
public void org.spirit.lemon.reflect.Student.setProfession(java.lang.Integer)
setProfession
void
---------分割线----------
public void org.spirit.lemon.reflect.Student.setCardNo(java.lang.String)
setCardNo
void
---------分割线----------
public void org.spirit.lemon.reflect.Student.setCls(java.lang.String)
setCls
void
---------分割线----------
public java.lang.String org.spirit.lemon.reflect.Student.getCardNo()
getCardNo
class java.lang.String
---------分割线----------
public java.lang.Integer org.spirit.lemon.reflect.Student.getProfession()
getProfession
class java.lang.Integer
---------分割线----------
public java.lang.String org.spirit.lemon.reflect.Student.getCls()
getCls
class java.lang.String
---------分割线----------
public void org.spirit.lemon.reflect.User.test()
test
void
---------分割线----------
public final void java.lang.Object.wait() throws java.lang.InterruptedException
wait
void
---------分割线----------
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
wait
void
---------分割线----------
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
wait
void
---------分割线----------
public boolean java.lang.Object.equals(java.lang.Object)
equals
boolean
---------分割线----------
public native int java.lang.Object.hashCode()
hashCode
int
---------分割线----------
public final native java.lang.Class java.lang.Object.getClass()
getClass
class java.lang.Class
---------分割线----------
public final native void java.lang.Object.notify()
notify
void
---------分割线----------
public final native void java.lang.Object.notifyAll()
notifyAll
void
---------分割线----------

可以看到Student的私有方法test2()并没有打印出来

  • public Method getDeclaredMethod(String name, Class<?>… parameterTypes)

获取本类指定的方法,包括私有方法;但是不能获取继承自父类的方法

1
2
3
4
5
6
7
Method method = clazz.getDeclaredMethod("test2", null);
System.out.println(method);
System.out.println(method.getName());//方法名
System.out.println(method.getReturnType());//返回值

method.setAccessible(true);
method.invoke(student, args);

输出结果

1
2
3
4
private void org.spirit.lemon.reflect.Student.test2()
test2
void
Student
  • public Method getMethod(String name, Class<?>… parameterTypes)

获取指定的方法,包括继承自父类的方法,但是只能获取public方法

1
2
3
4
5
Method method = clazz.getMethod("test", null);
System.out.println(method);
System.out.println(method.getName());//方法名
System.out.println(method.getReturnType());//返回值
method.invoke(student, args);

输出结果

1
2
3
4
public void org.spirit.lemon.reflect.User.test()
test
void
User

获取注解

通过Class对象也可以获取对应的类的注解信息。自定义注解就可以通过获取类/方法/属性的注解信息来判定是否需要进行必要的逻辑处理。

在类Student的声明上添加两个注解,如下:

1
2
3
4
5
@Description("test")
@Deprecated
public class Student extends User{

}
  • public Annotation[] getAnnotations()

获取类的注解信息,返回注解类的对象数组;可以获取到继承自父类的注解信息(@Inherited修饰的注解才行)

1
2
3
4
5
Annotation[] annotations = clazz.getAnnotations();
for (int i = 0; i < annotations.length; i++) {
Annotation annotation = annotations[i];
System.out.println(annotation);
}

输出结果

1
2
@com.sun.org.glassfish.gmbal.Description(key=, value=test)
@java.lang.Deprecated()

可以看出,获取到了类的注解信息

  • public Annotation[] getDeclaredAnnotations()

获取直接作用在类上的注解信息,不包含继承的注解

获取类的指定类型的注解信息,如果该类配置了指定注解,返回注解的对象信息,否则返回null

1
2
3
4
5
Annotation annotation = clazz.getDeclaredAnnotation(Deprecated.class);
System.out.println(annotation);

annotation = clazz.getDeclaredAnnotation(Documented.class);
System.out.println(annotation);

输出结果

1
2
@java.lang.Deprecated()
null

Student声明了Deprecated注解,未声明Documented注解,所以第二行输出null

其他

也可以通过Class类获取包(Package)、修饰符、父类、泛型等其他信息,此处不做介绍了。

总结

个人在开发过程中用到反射的地方并不是很多,但是使用的框架中(如Spring)大量的应用了反射技术,了解反射的使用方法有助于了解这些框架的实现;

此外,反射会额外的消耗系统资源,效率上较直接创建对象、方法调用较差,但是更加灵活、方便。

待补充

  • 反射的实现原理