有反射必然就有正射,而正射就是我们平时写代码的正常逻辑。直接通过类和对象调用其方法或访问其属性,这种方式是静态的,意味着编译时就已经确定了方法的调用以及对象的属性访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package org.example;

class MyClass {
public void sayHello() {
System.out.println("Hello, World!");
}
}

public class Main {
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.sayHello();
}
}
Quote

Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.

反射

反射就是一开始并不知道我们要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这是 Java 提供的一种动态机制,它允许程序在运行时获取有关类的信息,并能在不事先知道类或对象类型的情况下,动态地调用它们的方法或访问它们的属性

通过使用反射我们不仅可以获取到任何类的成员方法 (Methods)、成员变量 (Fields)、构造方法 (Constructors) 等信息,还可以动态创建 Java 类实例、调用任意的类方法、修改任意的类成员变量值等

反射的基本使用

获取 Class 对象

主要有四种方法:

  1. 使用 Class.forName() 静态方法
  2. 使用类的 .class 方法
  3. 使用实例对象的 getClass() 方法
  4. 使用类加载器的 loadClass() 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.example;  

public class Main {
public static void main(String[] args) throws Exception{
String className = "java.lang.Runtime";

Class class1 = Class.forName(className);
System.out.println(class1.getName());

Class class2 = Runtime.class;
System.out.println(class2.getName());

Runtime runtime = Runtime.getRuntime();
Class class3 = runtime.getClass();
System.out.println(class3.getName());

Class class4 = ClassLoader.getSystemClassLoader().loadClass(className);
System.out.println(class4.getName());
}
}

反射创建对象

主要是通过 Class 和 Constructor 的 newInstance() 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.example;  

import java.lang.reflect.Constructor;

class MyClass {
public void sayHello() {
System.out.println("Hello, World!");
}
}

public class Main {
public static void main(String[] args) throws Exception{
String className = "org.example.MyClass";

Class class1 = Class.forName(className);
MyClass myClass = (MyClass) class1.newInstance();
System.out.println(myClass);

Constructor constructor = class1.getDeclaredConstructor();
MyClass myClass1 = (MyClass) constructor.newInstance();
System.out.println(myClass1);
}
}

反射获取类的构造器

getConstructor() 返回当前类单个 public 构造器,getConstructors() 返回当前类所有的 public 的构造器,是一个数组
getDeclaredConstructor() 返回当前类指定参数类型的单个构造器,无视权限,getDeclaredConstructors() 返回当前类中所有声明的构造器,是一个数组
注意这里是当前类,因为与方法不同,构造器不会被继承

反射获取类的方法

方法名 访问权限 范围 返回类型
getMethod() 仅 Public 包含父类/接口 Method
getDeclaredMethod() 所有 (Public/Private/…) 仅当前类 Method
getMethods() 仅 Public 包含父类/接口 Method[]
getDeclaredMethods() 所有 (Public/Private/…) 仅当前类 Method[]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.example;  

import java.lang.reflect.Method;

class MyClass {
public void sayHello() {
System.out.println("Hello, World!");
}
}

public class Main {
public static void main(String[] args) throws Exception{
String className = "org.example.MyClass";

Class class1 = Class.forName(className);

Method[] methods = class1.getMethods();
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i]);
}

}
}


这里 org. Example. MyClass 继承了 Object 类中的所有公共方法

1
2
Method method = class1.getMethod("sayHello");  
method.invoke(class1.getDeclaredConstructor().newInstance());

使用 invoke() 调用具体方法,第一个参数必须是类实例对象,如果调用的是 static 方法那么第一个参数值可以传 null

反射获取成员变量

方法名 访问权限 范围 返回类型
getField(String name) 仅 Public 包含父类/接口 Field
getDeclaredField(String name) 所有 (Public/Private/…) 仅当前类 Field
getFields() 仅 Public 包含父类/接口 Field[]
getDeclaredFields() 所有 (Public/Private/…) 仅当前类 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
package org.example;  

import java.lang.reflect.Field;

class MyClass {
public Integer age;
private String email;

public void sayHello() {
System.out.println("Hello, World!");
}
}

public class Main {
public static void main(String[] args) throws Exception{
String className = "org.example.MyClass";

Class class1 = Class.forName(className);

Field[] fields = class1.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
System.out.println(fields[i]);
}

}
}

1
2
3
Field field1 = class1.getField("age");  
Object value = field1.get(class1.getDeclaredConstructor().newInstance());
System.out.println("Value of age: " + value);

使用 get(类实例对象) 获取具体值

反射 java. Lang. Runtime

java.lang.Runtime 类使用的是单例模式,意味着 Java 设计者为了保证全系统只有一个运行环境实例,将它的构造方法设置为了 private。而 Class.newInstance() 方法只能调用类的 public 无参构造方法,因此我们有两种方式来获取 Runtime 实例
使用标准的单例模式:
通过提供的静态方法 getRuntime() 来获取实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package org.example;  

import java.lang.reflect.Method;

public class Main {
public static void main(String[] args) throws Exception{
String className = "java.lang.Runtime";

Class class1 = Class.forName(className);
Method method = class1.getMethod("getRuntime");

Object runtime = method.invoke(null);
Method execMethod = class1.getMethod("exec", String.class);
execMethod.invoke(runtime, "calc.exe");

}
}

通过强行破坏单例模式,获取构造方法(通过设置 setAccessible(true) 在 Java 9 即以上的版本中会发生报错,主要原因还是 JVM 对权限的检查,防止滥用)

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
package org.example;  

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Main {
public static void main(String[] args) throws Exception{
String className = "java.lang.Runtime";

Class class1 = Class.forName(className);

// 获取构造方法
Constructor constructor = class1.getDeclaredConstructor();
constructor.setAccessible(true);

// 创建Runtime类实例
Object runtime = constructor.newInstance();

// 获取Runtime的exec(String cmd)方法
Method execMethod = class1.getDeclaredMethod("exec", String.class);

// 调用exec方法并获取执行结果
Process process = (Process) execMethod.invoke(runtime, "calc.exe");
InputStream inputStream = process.getInputStream();

System.out.println(org.apache.commons.io.IOUtils.toString(inputStream, "UTF-8"));

}
}