目录
- 一、什么是反射
- 二、反射的核心接口和类
- 三、测试代码 Bean 类和目录结构
- Person 类
- 代码目录结构
- 四、获取 Class 对象
- 五、获取构造方法 Constructor 并使用
- 六、获取成员变量 Field 并使用
- 七、获取成员方法 Method 并使用
- 八、练习
- 1. 使用反射获取String类的所有公有方法,并把方法名打印出来。
- 2. 使用反射创建一个对象,并调用其无参构造方法。
- 3. 使用反射修改一个对象的私有字段值。
- 4. 使用反射获取一个ArrayList的所有父类(包括间接父类)。
- 5. 使用反射调用一个类的静态方法。
- 6. 使用反射获取某个类的所有公有成员变量,并打印出每个成员变量的名称和类型。
- 7. 使用反射获取某个类的所有成员方法,并打印出每个方法变量的名称和返回值类型。
- 8. 使用反射调用一个对象的公有方法,并传递参数。
一、什么是反射
解释一:
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个属性和方法。这种动态获取信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
解释二:
Java 反射是指在运行时动态检查和操作类的能力。通过反射,(对于一个对象)程序可以在运行时获取关于类、方法、属性、构造函数等的详细信息,并且可以动态地创建对象、调用方法以及访问和修改字段。反射提供了一种灵活的机制,使得程序可以在编译时不知道确切类型的情况下操作这些类型。
二、反射的核心接口和类
Java 反射主要涉及以下几个核心类和接口,它们位于包 java.lang.reflect
中:
Class
:每个类和接口在 JVM 中都表示为一个 Class 对象。通过 Class 对象,程序可以获取类的全限定名、实现的接口、父类、构造函数、方法、字段等信息。
Constructor
:表示类的构造函数。通过 Constructor 对象,程序可以创建类的新实例。
Field
:表示类的属性。通过 Field 对象,程序可以获取或修改属性的值。
Method
:表示类的方法。通过 Method 对象,程序可以调用方法。
三、测试代码 Bean 类和目录结构
Person 类
该Person
类有name
和age
属性,无参构造方法
、有参构造方法
、getter
、setter
、toString
以及自定义的sayHello
和sayGoodbye
方法。
public class Person {
private String name;
private int age;
public Person() {
this.name = "unknown";
this.age = 0;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 省略getter()和setter()
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// public类型
public void sayHello() {
System.out.println("Hello, my name is " + this.name);
}
// private类型
private void sayGoodbye() {
System.out.println("Goodbye, my name is " + this.name);
}
}
代码目录结构
四、获取 Class 对象
Class 对象包含了类的结构信息,是反射的入口点。获取 Class 对象有三种方法,如下所示:
1. 类.class
2. 对象.getClass()
3. Class.forName()
public class Main {
public static void main(String[] args) {
// 方式1: 类.class语法
Class<?> cls1 = Person.class;
System.out.println(cls1); // class Person
System.out.println(cls1.getName()); // Person
// 方式2: 对象.getClass()
Class cls2 = new Person().getClass();
System.out.println(cls2.getName()); // Person
// 方式3: 使用静态方法Class.forName(),需要捕获ClassNotFoundException
try {
Class<?> cls3 = Class.forName("Person");
System.out.println(cls3.getName()); // Person
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
五、获取构造方法 Constructor 并使用
cls.getDeclaredConstructor()
获取 Class 对象的所有构造方法
cls.getConstructor()
获取 Class 对象的公有构造方法
constructor.newInstance()
使用 Class 构造方法创建对象
import java.lang.reflect.Constructor;
public class Main {
public static void main(String[] args) {
try {
// 获取Person类的Class对象
Class cls = Class.forName("Person");
// Class personClass = Person.class;
// Class personClass = new Person().getClass();
// 获取无参构造方法
Constructor<?> noArgsConstructor = cls.getConstructor();
// 创建对象
Object person1 = noArgsConstructor.newInstance();
// 重写了Person的toString方法直接打印即可
System.out.println(person1); // Person{name='unknown', age=0}
// 获取带参数的构造方法
Constructor<?> paramArgsConstructor = cls.getConstructor(String.class, int.class);
// 创建对象,并传递参数
Object person2 = paramArgsConstructor.newInstance("Alice", 30);
System.out.println(person2); // Person{name='Alice', age=30}
} catch (Exception e) {
e.printStackTrace();
}
}
}
六、获取成员变量 Field 并使用
cls.getDeclaredField(name)
获取 Class 对象的所有成员变量
cls.getField(name)
获取 Class 对象的所有公有成员变量
field.setAccessible(true)
设置 Class 对象的属性值可访问
field.get()
获取 Class 对象的属性值
field.set()
设置 Class 对象的属性值
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) {
try {
// 获取Person类的Class对象
Class cls = Class.forName("Person");
// Class personClass = Person.class;
// Class personClass = new Person().getClass();
// 创建Person对象
Person person = new Person("Alice",30);
// 获取name字段
Field nameField = cls.getDeclaredField("name");
// 设置可访问性,因为name是私有的
nameField.setAccessible(true);
// 获取name字段的值
String name = (String) nameField.get(person);
System.out.println("Name: " + name); // Name: Alice
// 获取age字段
Field ageField = cls.getDeclaredField("age");
// 设置可访问性,因为age是私有的
ageField.setAccessible(true);
// 获取age字段的值
int age = ageField.getInt(person);
System.out.println("Age: " + age); // Age: 30
// 修改age字段的值
ageField.setInt(person, 31);
// 再次获取age字段的值,验证修改是否成功
age = ageField.getInt(person);
System.out.println("Updated Age: " + age); // Updated Age: 31
} catch (Exception e) {
e.printStackTrace();
}
}
}
七、获取成员方法 Method 并使用
cls.getDeclaredMethod(name)
获取 Class 对象的所有成员方法
cls.getMethod(name)
获取 Class 对象的公有成员方法
method.setAccessible(true)
设置 Class 对象的方法可访问
method.invoke()
调用 Class 对象的成员方法
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) {
try {
// 获取Person类的Class对象
Class<?> cls = Class.forName("Person");
// 创建Person对象
Person person = (Person) cls
.getDeclaredConstructor(String.class, int.class)
.newInstance("Alice", 30);
//或 Person person =new Person("Alice", 30);
// 获取sayHello方法
Method sayHelloMethod = cls.getMethod("sayHello");
// 调用sayHello方法
sayHelloMethod.invoke(person);
// 获取sayGoodbye方法
Method sayGoodbyeMethod = cls.getDeclaredMethod("sayGoodbye");
// 设置可访问性,因为sayGoodbye是私有的
sayGoodbyeMethod.setAccessible(true);
// 调用sayGoodbye方法
sayGoodbyeMethod.invoke(person);
} catch (Exception e) {
e.printStackTrace();
}
}
}
八、练习
1. 使用反射获取String类的所有公有方法,并把方法名打印出来。
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) {
Class<String> cls = String.class;
Method[] methods = cls.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
}
}
2. 使用反射创建一个对象,并调用其无参构造方法。
import java.lang.reflect.Constructor;
public class Main {
public static void main(String[] args) {
Class<String> cls = String.class;
try {
Constructor<String> constructor = cls.getConstructor(String.class);
// 指定了String就可以不用Object
String s = constructor.newInstance("Hello world!");
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. 使用反射修改一个对象的私有字段值。
import java.lang.reflect.Field;
class Employee {
private int age;
public Employee() {
}
public Employee(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private void printAge() {
System.out.println(this.age);
}
}
public class Main {
public static void main(String[] args) {
Employee employee = new Employee(30);
Class cls = employee.getClass();
try {
Field ageField = cls.getDeclaredField("age");
ageField.setAccessible(true);
int age = ageField.getInt(employee);
System.out.println(age); // 30
ageField.setInt(employee,31);
System.out.println(employee.getAge()); // 31
} catch (Exception e) {
e.printStackTrace();
}
}
}
4. 使用反射获取一个ArrayList的所有父类(包括间接父类)。
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
Class cls = ArrayList.class;
while (cls != null) {
System.out.println(cls.getName());
cls = cls.getSuperclass();
}
}
}
5. 使用反射调用一个类的静态方法。
import java.lang.reflect.Method;
class MyMath {
public static <T extends Number> T add(T a, T b) {
if (a instanceof Integer) {
return (T) Integer.valueOf(a.intValue() + b.intValue());
} else if (a instanceof Double) {
return (T) Double.valueOf(a.doubleValue() + b.doubleValue());
} else {
throw new IllegalArgumentException("Unsupported number type");
}
}
}
public class Main {
public static void main(String[] args) {
Class cls = MyMath.class;
try {
// 由于泛型擦除,需要指定方法的确切参数类型
Method method = cls.getDeclaredMethod("add", Number.class, Number.class);
// 静态方法必须指定null
Object invoke = method.invoke(null, 1, 2);
System.out.println(invoke); // 3
} catch (Exception e) {
e.printStackTrace();
}
}
}
6. 使用反射获取某个类的所有公有成员变量,并打印出每个成员变量的名称和类型。
import java.lang.reflect.Field;
class People {
public int id;
public int age;
private String name;
}
public class Main {
public static void main(String[] args) {
Class<People> cls = People.class;
Field[] PeopleFields = cls.getFields();
for (Field peopleField : PeopleFields) {
System.out.println(peopleField.getName() + " => " + peopleField.getType());
}
//id => int
//age => int
}
}
7. 使用反射获取某个类的所有成员方法,并打印出每个方法变量的名称和返回值类型。
import java.lang.reflect.Method;
class People {
public void printHello() {
System.out.println("Hello, Java");
}
public String getHello(String Hello) {
return Hello + ", Java";
}
private int getMoney() {
return 0;
}
}
public class Main {
public static void main(String[] args) {
Class<People> cls = People.class;
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName() + " => " + method.getReturnType());
}
//printHello => void
//getHello => class java.lang.String
//getMoney => int
}
}
8. 使用反射调用一个对象的公有方法,并传递参数。
import java.lang.reflect.Method;
class MyMath {
public int add(int a, int b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Class<MyMath> myMathClass = MyMath.class;
try {
Method addMethod = myMathClass.getMethod("add", int.class, int.class);
MyMath myMath = new MyMath(); // 创建 MyMath 类的实例
Object invoke = addMethod.invoke(myMath, 1, 2); // 传递 MyMath 类的实例
System.out.println(invoke); // 3
} catch (Exception e) {
e.printStackTrace();
}
}
}