Java反射介绍
Java反射是指在程序运行时动态获取类的信息、创建对象、调用方法等操作。其基本原理是通过Java虚拟机在运行时动态加载类,并通过类的信息获取类的结构信息,包括构造方法、字段、方法等,并对这些结构进行访问和操作。
在Java中,每一个类都对应一个Class对象,Class对象包含了该类的结构信息,包括类的构造方法、字段、方法等。Java反射机制就是通过Class对象来获取类的结构信息,并对其进行操作。
Java反射机制主要涉及以下几个类:
- Class类:表示一个类的结构信息,包括构造方法、字段、方法等。
- Constructor类:表示一个构造方法。
- Field类:表示一个类的成员变量。
- Method类:表示一个类的方法。
通过反射机制,可以动态地创建对象、获取对象的成员变量、调用对象的方法等。例如,可以通过反射机制实现对象的序列化和反序列化、动态代理等功能。但是,由于反射机制的使用需要大量的资源,所以在实际开发中应该尽量避免滥用反射。
Java反射机制主要的作用包括:
- 动态加载类和创建对象:反射机制可以在运行时动态加载类和创建对象,可以根据条件动态创建不同的对象,提高程序的灵活性和可扩展性。
- 获取类的信息:反射机制可以获取类的属性、方法、构造方法等信息,可以根据这些信息进行对象的操作,比如调用方法、获取和修改属性值等。
- 调用对象的方法:反射机制可以调用对象的方法,可以根据方法名和参数列表获取对应的方法,并执行该方法,实现动态调用对象的方法。
- 动态修改对象的属性值:反射机制可以获取和修改对象的属性值,可以根据属性名获取对应的属性,并动态修改属性值。
- 动态代理:反射机制可以实现动态代理,可以在运行时生成代理对象,并通过代理对象调用原始对象的方法,实现AOP编程。
总之,Java反射机制为Java程序提供了一种强大的动态编程方式,使得程序更加灵活、动态和可扩展。本来写死的类和方法可以增加复用性
Java中反射的优点:
- 灵活性:反射提供了一种灵活的机制,在运行时检查和修改程序的行为,而无需重新编译代码。
- 代码重用:反射可以用于创建可重用的代码,可以与任何对象或类一起使用,而不需要在编译时知道类型的具体细节。
- 调试:反射可用于调试复杂程序,允许开发人员在运行时检查和修改对象的状态。
Java中反射的缺点:
- 性能:由于动态检查和修改对象的开销,反射可能比直接访问对象的属性或方法要慢。
- 安全风险:反射可以用于访问和修改类的私有或受保护成员,如果不正确使用,可能会导致安全漏洞。
- 复杂性:反射可能会使代码更加复杂和难以理解,特别是如果过度或不正确使用。
注意:要有选择的使用反射功能,如果可以直接执行操作,那么最好不要使用反射。
Java反射获取Class的方法
- 使用对象的getClass()方法获取,例如:Object obj = new Object(); Class<?> objClass = obj.getClass();
- 使用类名.class获取,例如:Class<?> objClass = Object.class;
- 使用Class.forName()方法获取,例如:Class<?> objClass = Class.forName("java.lang.Object");
注意:
使用Class.forName()方法时需要指定类的全限定名(包括包名),否则会抛出ClassNotFoundException异常。此外,还可以使用ClassLoader来获取Class对象,例如:Class<?> objClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.Object");
Java反射中比较常用的类和方法
类 | 描述 |
---|---|
Class | 表示一个Java类,可以获取和设置类的信息。 |
Constructor | 表示Java类的构造函数,可以创建新的对象。 |
Field | 表示Java类的字段,可以获取和设置字段的值。 |
Method | 表示Java类的方法,可以调用方法并获取返回值。 |
Modifier | 提供了访问修饰符的方法,例如public、private、static等。 |
方法 | 使用类型 | 返回值 | 描述 |
---|---|---|---|
newInstance() | Class |
T | 默认调用无参构造创建一个Java类的新实例,但是可以通过下面两个方法调用newInstance() 创建对象 |
getConstructor() | Class |
Constructor |
获取Java类public的特定构造函数。 |
getDeclaredConstructor() | Class |
Constructor |
获取Java类的特定构造函数。 |
getField() | Class |
Field | 参数获取指定字段,后面加s获取Java类的public字段。 |
getDeclaredField() | Class |
Field | 参数获取指定字段,后面加s获取Java类的所有字段。 |
getMethod() | Class |
Method | 获取指定方法,后面加s获取Java类的public方法(包括从父类继承的)。 |
getDeclaredMethod() | Class |
Method | 获取指定方法,后面加s获取Java类的所有方法。 |
getModifiers() | Member,Field,Method | int | 获取Java类、字段或方法的访问修饰符(整型常量)。 |
getName() | Member,Field,Method | String | 获取Java类、字段或方法的名称。 |
getSimpleName() | Member,Field,Method | String | 获取Java类、字段或方法的简单名称(没有包名)。 |
getType() | Field | Class<?> | 获取Java字段的数据类型。 |
getValue() | Field | Object | 获取Java字段的值。 |
invoke() | Method | Object | 调用Java方法并返回其返回值。 |
setAccessible() | AccessibleObject | void | 可以用来将一个成员变量或成员方法的可访问性改为可访问,不一定是改为 public 访问权限 |
getAnnotation(注解类型) | Member,Field,Method | 注解类型 | getAnnotation方法只能获取到直接作用于当前元素上的注解,无法获取到间接作用于当前元素上的注解(例如继承、实现、重载等情况下的注解),后面加s获取所有注解。 |
Java反射案例
import java.lang.reflect.*;
public class ReflectionExample {
public static void main(String[] args) {
// 通过Class.forName获取Person类的Class对象
Class<?> clazz = null;
try {
clazz = Class.forName("Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 通过Class对象获取Constructor对象并创建实例
Constructor<?> constructor = null;
try {
constructor = clazz.getConstructor(String.class, int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Person person = null;
try {
person = (Person) constructor.newInstance("张三", 20);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
// 通过Class对象获取Method对象并调用方法
Method method = null;
try {
method = clazz.getMethod("sayHello");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
method.invoke(person);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
// 通过Class对象获取Field对象并修改属性值
Field field = null;
try {
field = clazz.getDeclaredField("name");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
field.setAccessible(true);
try {
field.set(person, "李四");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
System.out.println("修改后的名字为:" + person.getName());
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void sayHello() {
System.out.println("Hello, I'm " + name + ", " + age + " years old.");
}
public String getName() {
return name;
}
}
反射案例解析
通过Class.forName获取Person类的Class对象
Class<?> clazz = Class.forName("Person")
Class<?>
:表示一个未知类型的类,用于表示任意类型的类,包括基本数据类型和引用类型Class.forName("Person")
:Java 中的一个静态方法,它返回一个类或接口的 Class
对象
通过Class对象获取Constructor对象
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Constructor<?>
使用可以调用一个类的构造方法来创建一个对象的Constructor对象clazz.getConstructor(String.class, int.class)
:通过clazz
对象获取该类的构造方法,比如案例中返回Person(String name, int age)
这个构造方法
通过Constructor对象创建实例
Person person = (Person) constructor.newInstance("张三", 20);
Person
:实体类constructor.newInstance("张三", 20)
:调用该类的构造方法创建对象
通过Class对象获取Method对象
Method method = clazz.getMethod("sayHello");
Method
:一个类,用于表示类的方法。一个方法对象包含了一个方法的所有信息,包括方法的名称、返回类型、参数类型、修饰符等。clazz.getMethod("sayHello")
:使用 Java 反射机制获取类中指定方法的实例对象,其中 "sayHello" 是方法名。该方法返回一个 Method 对象,可以用来调用该方法。
通过Method对象调用方法person对象的方法
method.invoke(person)
:使用 method
对象(根据method
中的方法信息) 调用 person对象的方法
通过Class对象获取Field对象
Field field = clazz.getDeclaredField("name");
Field
:表示一个类的成员变量clazz.getDeclaredField("name")
:获取clazz
类的name
属性字段
修改属性值
field.setAccessible(true);
:将一个私有属性的可访问性改为public
( true
)。但是这种做法是有风险的,非必要最好不要使用field.set(person, "李四");
:通过set修改属性的值
Java反射学习路线
Java反射是Java中一个重要的概念,能够让你在程序运行期间获取类的信息、访问对象的属性和方法,实现代码的灵活性和扩展性。下面是学习Java反射的一些路线:
- 首先,你需要对Java的基本语法和面向对象编程有一定的了解,掌握类、对象、方法、属性等概念。
- 然后学习Java反射的基本概念和原理,了解反射是如何在运行期间获取和使用类信息的。
- 掌握Class类和其常用方法,如forName()方法、newInstance()方法等。
- 了解Constructor、Field、Method等反射API的基本使用方法,掌握其在实际开发中的应用。
- 学习注解和反射的结合使用,了解注解的原理和使用方法,学习如何通过反射读取和解析注解信息。
- 学习泛型和反射的结合使用,了解泛型的基本概念和使用方法,学习如何通过反射读取和使用泛型信息。
- 了解Java反射的局限性和使用注意事项,如反射的性能问题、安全问题等。
- 最后,结合实际项目,学习Java反射的实际应用方法和技巧,提高自己的开发效率和代码质量。