Java Reflection

什么是Java反射

  • 反射reflection是在运行时动态访问类与对象的技术
  • 反射是JDK1.2版本后的高级特性,隶属于java.lang.reflect
  • 他将对象的创建时机从原本的编译时创建延迟到运行时创建
  • 大多数Java框架都基于反射实现参数配置,动态注入等特性
1
2
3
4
5
6
7
8
9
10
Scanner in = new Scanner(System.in);
System.out.println("please enter math operation");
String op = in.next();
System.out.println("please enter first number");
int a = in.nextInt();
System.out.println("please enter second number");
int b = in.nextInt();
MathOperation operation = null;

operation = (MathOperation) Class.forName(op).getDeclaredConstructor().newInstance();

反射的核心类

  • Class
  • Constructor
  • Method
  • Field

Class类

  • Class是JVM中代表类和接口的类
  • Class对象具体包含了某个特定类的结构信息
  • 通过Class类可以获取对应类的构造方法,方法,成员变量

Class核心方法

  • Class.forName() - 静态方法,用于获取指定Class对象
  • classObj.newInstance() - 通过默认构造方法创建新的对象 (在Java9之后被deprecated)
  • classObj.getConstructor() - 获取指定的public修饰构造方法Constructor对象
  • classObj.getMethod() - 获取指定的public修饰方法Method对象
  • classObj.getField() - 获取指定的public修饰的成员变量Field对象
1
2
3
4
5
6
// 将Employee类加载到JVM,并返回对应Class对象
Class employeeClass = Class.forName("entity.Employee");
System.out.println("Employee has been loaded to JVM");

// newInstance 调用默认构造方法创建新对象
Employee emp = (Employee) employeeClass.getDeclaredConstructor().newInstance();

创建Class对象时可能抛出的异常

  • InstantiationException
    • 实例化异常,对象无法被实例化
    • 例如abstract抽象对象
  • IllegalAccessException
    • 非法访问,在作用域外访问对象构造方法或成员变量
    • 例如尝试访问私有构造方法

Constructor类

  • 对Java类中的构造方法的抽象
  • Constructor对象包含了具体类的某个具体构造方法的声明
  • 通过Constructor对象调用带参构造方法创建对象

Constructor类的核心方法

  • classObj.getConstructor() - 获取指定public修饰的构造方法对象

  • constructorObj.newInstance() - 通过对应的构造方法创建对象

  • 例子,在通过Class对象创建Constructor对象时,需要提供每一个参数的Class类

  • 在Constructor对象创建Employee对象时,需要传入每一个参数

1
2
3
4
5
6
7
8
9
10
11
Class employeeClass = Class.forName("entity.Employee");
Constructor constructor = employeeClass.getConstructor(new Class[] {
Integer.class,
String.class,
Float.class,
String.class
});

Employee employee = (Employee) constructor.newInstance(new Object[] {
100, "yuan cheng", 3000f, "研发部"
});

创建Constructor对象时可能抛出的异常

  • InstantiationException
    • 实例化异常,对象无法被实例化
    • 例如abstract抽象对象
  • IllegalAccessException
    • 非法访问,在作用域外访问对象构造方法或成员变量
    • 例如尝试访问私有构造方法
  • InvocationTargetException
    • 当被调用的方法内部抛出了异常而没有被捕获时
  • NoSuchMethodException
    • 没有找到与之对应的构造方法

Method类

  • Method对象指代某个类中的方法的描述
  • Method对象使用classObj.getMethod()方法获取
  • 通过Method对象调用指定对象的对应方法

Method类核心方法

  • classObj.getMethod() - 获取指定public修饰的方法的对象
  • methodObj.invoke() - 调用指定对象的对应方法
1
2
3
4
5
6
7
8
9
10
11
Class employeeClass = Class.forName("entity.Employee");
Constructor constructor = employeeClass.getConstructor(new Class[] {
Integer.class, String.class, Float.class, String.class
});
Employee employee = (Employee) constructor.newInstance(new Object[] {
100, "yuan", 3000f, "研发部"
});

Method method = employeeClass.getMethod("updateSalary", Float.class);
Employee newEmpoyee = (Employee) method.invoke(employee, 1000f);
System.out.println(newEmpoyee);

Field类

  • 对应某个具体类中成员变量的声明
  • Field对象使用classObj.getField()方法获取
  • 通过Field对象可为某对象成员变量赋值/取值

Field类核心方法

  • classObj.getField() - 获取指定public修饰的成员变量对象
  • fieldObj.set() - 为某对象指定成员变量赋值
  • fieldObj.get() - 获取某对象指定成员变量数值

Field可能抛出的异常

  • NoSuchFieldException
    • 没有找到对应的Field
    • 当尝试访问private的Field时也会抛出这个异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Class employeeClass = Class.forName("entity.Employee");
Constructor constructor = employeeClass.getConstructor(new Class[] {
Integer.class, String.class, Float.class, String.class
});

Employee employee = (Employee) constructor.newInstance(new Object[] {
100, "Yuan", 4000f, "研发部"
});

Field enameField = employeeClass.getField("ename");
String ename = (String) enameField.get(employee);
System.out.println(ename);

enameField.set(employee, "new yuan");
System.out.println(employee);

getDeclared。。。系列方法

  • 之前的方法只能获取public对象
  • getDeclared。。系列方法可以获取非作用域内的构造方法,方法,成员变量 (private)

例子,如果我们想获取当前对象的所有成员变量的值

  • 不管是private还是public,我们需要用到getDeclaredFields()
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
Class employeeClass = Class.forName("entity.Employee");
Constructor constructor = employeeClass.getConstructor(new Class[] {
Integer.class, String.class, Float.class, String.class
});

Employee employee = (Employee) constructor.newInstance(new Object[] {
100, "Yuan", 4000f, "研发部"
});

// 获取当前类所有成员变量 private + public
Field[] fields = employeeClass.getDeclaredFields();
for (Field field : fields) {
if (field.getModifiers() == Modifier.PUBLIC) {
// public fields
Object val = field.get(employee);
System.out.println(field.getName() + ": " + val);
} else if (field.getModifiers() == Modifier.PRIVATE) {
// private fields
String methodName = "get" + field.getName().substring(0, 1).toUpperCase()
+ field.getName().substring(1);
Method getMethod = employeeClass.getMethod(methodName);
Object ret = getMethod.invoke(employee);
System.out.println(field.getName() + ": " + ret);
}
}

反射在项目中的应用

  • 切换网站的语言
  • 根据不用的设备切换网站layout
  • 不需要重新编译或者改变已有代码