Java 反射

本文最后更新于:2024年3月18日 凌晨

Java 反射

  • Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

获得反射对象

  • 加载完类之后,在堆内存的方法区中就产生了一个Class 类型的对象(一个类只有一个Class类型对象),这个对象包含了完整的类的结构信息。

通过类的 class 属性

  • 通过类的class属性获取反射对象,该方法最为安全可靠,程序性能最高。
1
Class strClass=String.class

通过Class类的静态方法

  • Class.forName("包名。类名");
  • 通过Class类的forName方法获得Class对象。
1
Class<?> strClass = Class.forName("java.lang.String");

通过对象

  • 由于所有类都是继承Object类,所以可以调用任何对象的getClass()方法获得Class对象。
1
2
String string=new String();
Class  strClass=string.getClass();

通过包装类的TYPE属性

  • 基本内置类型的包装类都有一个Type属性。
1
Class  intClass = Integer.TYPE

Class类

  • Class对象只能由系统创建对象。
  • 对于每个类而言,JRE都为其保留一个不变的Class 类型的对象,一个Class对象对应的是一个加载到JVM中的一个.class文件。
  • 每个类的实例都会记得自己是由哪个Class实例所生成。

那些类型可以有Class对象?

  • class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类,Class类。
  • interface:接口。
  • []:数组。
  • enum:枚举。
  • annotation:注解。
  • primitive type:基本数据类型。
  • void

常用方法

方法 目的
static ClassforName(String className) 使用参数className来指定具体的类,来获得相关的类描述对象,该方法有可能抛出类加载异常(ClassNotFoundException),必须捕捉
Object newInstance() 调用缺省构造函数返回Class对象的一个实例
Class getSuperclass() 获得当前类描述对象的父类的Class对象
String getName() 返回当前类描述对象的类名称
String getSimpleName() 返回当前类描述对象的简单类名称
Class[] getinterfaces() 获取当前Class对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Constructor getConstructor(Class<?> paramTypes) 返回一个Constructor对象(本类的public构造器)
Constructor getDeclaredConstructor(Class<?> paramTypes) 返回一个Constructor对象
Constructor[] getConstructors() 返回一个包含Constructor对象的数组(本类的public构造器)
Constructor[] getDeclaredConstructors() 返回一个包含Constructor对象的数组(本类的构造器)
Method getMethod(String name, Class<?> paramTypes) 返回一个Method对象,此对象的形参类型为paramTypes(本类和父类的public方法)
Method getDeclaredMethod(String name, Class<?> paramTypes) 返回一个Method对象,此对象的形参类型为paramTypes
Method getMethods() 返回一个Method对象的一个数组(本类和父类的public方法)
Method getDeclaredMethod() 返回一个Method对象的一个数组(本类的方法)
Field getField() 返回Field某个对象(只包括public属性)
Field getDeclaredField() 返回Field某个对象
Field[] getFields() 返回Field对象的一个数组(只包括public属性)
Field[] getDeclaredFields() 返回Field对象的一个数组(本类的属性)

实例

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
import java.lang.reflect.Field;
import java.lang.reflect.Method;

class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class<?> strClass = Class.forName("java.lang.String");
Class<?> superClass = strClass.getSuperclass();
System.out.println("String的Class类:" + strClass);
System.out.println("String的父类的Class类:" + superClass);

System.out.println("String的简单类名字" + strClass.getSimpleName());
System.out.println("String的类名" + strClass.getName());

// 获得类的全部public属性。
Field[] fields = strClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
// 获得类的全部属性。
fields = strClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}

// 获得本类和父类的全部public方法。
Method[] methods = strClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
// 获得类的全部方法。
methods = strClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}

// 获得指定方法。
Method valueOf = strClass.getMethod("valueOf", int.class);
System.out.println(valueOf);

}
}

动态创建对象

  • User.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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class User {
private String name;
private int age;
private String sex;

public User() {
}

public User(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}

调用Class对象的newInstance()方法

  • 类必须有一个无参数的构造器。
  • 类的构造器拥有足够的访问权限。
1
2
3
4
5
6
7
class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Class<User> userClass = User.class;
User user = userClass.newInstance();
System.out.println(user);
}
}

通过构造器创建对象

  1. 通过Class类的getDeclaredConstructor方法取得本类的指定形参类型的构造器。
  2. 向构造器的形参中传递一个对象数组,里面包含了构造器中所需的各个参数。
  3. 通过Constructor实例化对象。
1
2
3
4
5
6
7
8
9
10
11
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<User> userClass = User.class;
Constructor<User> userConstructor = userClass.getDeclaredConstructor(String.class, int.class, String.class);
User user = userConstructor.newInstance("Test", 10, "male");
System.out.println(user);
}
}

反射调用方法

  1. 通过Class对象的getDeclaredMethod方法获得想调用的方法的Method对象。
  2. 调用该Method对象的invoke方法为某个类调用方法,并向invoke方法中传递要设置的参数信息。
1
2
3
4
5
6
7
8
9
10
11
12
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<User> userClass = User.class;
User user = userClass.newInstance();
Method setName = userClass.getDeclaredMethod("setName", String.class);
setName.invoke(user, "Test");
System.out.println(user);
}
}

invoke()

1
Object invoke(Object obj, Object... args)
  • Object对应原方法的返回值,若原方法无返回值,此时返回null
  • 若原方法为静态方法,此时形参Object obj可为null
  • 若原方法形参列表为空,则Object[] args为null

反射操作属性

  1. 通过Class对象的getDeclaredField方法获得想调用的方法的Field对象。
  2. 调用该Field对象的set方法为某个属性赋值,或者通过get方法取得属性的值。
1
2
3
4
5
6
7
8
9
10
class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class<User> userClass = User.class;
User user = userClass.newInstance();
Field name = userClass.getDeclaredField("name");
name.setAccessible(true);// 设置可访问性。
name.set(user,"Test");
System.out.println(user);
}
}

setAccessible

  • Method和Field,Constructor对象都有setAccessible()方法。
  • setAccessible()的作用是启动和禁用访问安全检查的开关。
  • 参数值为true:指示反射的对象在使用时应该取消Java语言访问检查。
    • 提高反射的效率,如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true
    • 使得原本无法访问的私有成员也可以访问。
  • 参数值为false:指示反射的对象应该实施Java语言访问检查。
1
2
3
4
5
6
7
8
9
10
class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class<User> userClass = User.class;
User user = userClass.newInstance();
Field name = userClass.getDeclaredField("name");
name.setAccessible(true);// 设置可访问性。
name.set(user,"Test");
System.out.println(user);
}
}

反射操作泛型

  • Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。
  • 为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariableWildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
    • ParameterizedType:表示一种参数化类型,比如Collection<String>
    • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型。
    • TypeVariable:是各种类型变量的公共父接口。
    • WildcardType:代表一种通配符类型表达式。
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
import java.lang.reflect.*;
import java.util.List;
import java.util.Map;

class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Method test1 = Test.class.getMethod("test1", Map.class, List.class);
// 获得该方法的泛型参数类型。
Type[] genericParameterTypes = test1.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println("#: " + genericParameterType);
// 判断该泛型参数类型是否为参数化类型。
if (genericParameterType instanceof ParameterizedType) {
// 获得真实类型参数。
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}

Method test2 = Test.class.getMethod("test2", null);
Type genericReturnType = test2.getGenericReturnType();
System.out.println("#: " + genericReturnType);
// 判断该泛型返回类型是否为参数化类型。
if (genericReturnType instanceof ParameterizedType) {
// 获得真实类型参数。
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}

public void test1(Map<String ,User> map, List<User> list){
System.out.println("Test1");
}

public Map<String, User> test2(){
System.out.println("Test2");
return null;
}
}

反射操作注解

  • 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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableUser {
String value();
}


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldUser {
String columnName();

String type();

int length();
}

@TableUser("db_User")
class User {
@FieldUser(columnName = "db_name", type = "varchar", length = 10)
private String name;
@FieldUser(columnName = "db_age", type = "varchar", length = 10)
private int age;
@FieldUser(columnName = "db_sex", type = "int", length = 10)
private String sex;

public User() {
}

public User(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}

获得所有注解

  • 调用getAnnotations()方法。
  • 返回的是Annotation对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class<User> userClass = User.class;
// 获得类的注解。
Annotation[] userTable = userClass.getAnnotations();
for (Annotation annotation : userTable) {
System.out.println(annotation);
}
// 获得属性的注解。
Field name = userClass.getDeclaredField("name");
Annotation[] nameField = name.getAnnotations();
for (Annotation annotation : nameField) {
System.out.println(annotation);
}
}
}

获得指定注解

  • 调用getAnnotation()方法。
  • 需要传入指定注解的字节码,返回的是注解对象。
  • 可以通过注解对象获得注解值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class<User> userClass = User.class;

// 获得指定的类注解。
TableUser tableUser = userClass.getAnnotation(TableUser.class);
// 输出注解的值。
System.out.println(tableUser.value());

// 获得指定的属性注解。
Field name = userClass.getDeclaredField("name");
FieldUser nameField = name.getAnnotation(FieldUser.class);
// 输出注解的值。
System.out.println(nameField.columnName());
System.out.println(nameField.type());
System.out.println(nameField.length());
}
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!