基本概念
反射(Reflection)是 Java 中的一种机制,它允许程序在运行时动态地访问类的结构和行为,包括类、方法、字段、构造函数等,从而实现动态调用和操作。
通过反射,程序不需要在编译时就确定使用的类、方法或字段,而是在运行时动态地加载和使用它们。反射广泛用于框架设计、工具开发和动态操作等场景。
OK,反射的概念可能比较抽象,让我们来举个例子。
应用场景
在许多产品中,需要支持用户通过插件扩展功能,而插件的具体实现通常是在运行时由用户提供的。这种场景下,程序在设计时并不确定会加载哪些插件类或调用哪些方法,而是在运行时动态加载插件类,并通过反射调用相关方法。
以IDEA插件举例
- 插件以 JAR 包形式存在
每个插件是一个 JAR 包,包含具体实现类和plugin.xml
配置文件,用于描述插件的入口类。 - 运行时动态加载插件
IDEA 使用 反射 配合 类加载器 动态加载插件入口类,实例化插件对象并调用其方法。
核心概念
- 运行时动态获取类型信息
- 通过反射,可以获取某个类的完整信息,例如其类名、父类、接口、构造函数、方法和字段等。
- 动态操作对象的属性和方法
- 即使在编译期并不知道具体的类和方法名,反射也可以在运行时访问和调用这些元素。
- 绕过访问限制
- 通过反射,可以访问私有字段和方法(通过设置
AccessibleObject.setAccessible(true)
),这为框架设计带来灵活性的同时,也可能引入安全隐患。
- 通过反射,可以访问私有字段和方法(通过设置
反射的实现方式
Java 反射主要依赖于 java.lang.reflect
包中的类,以下是常用的反射工具类:
Class
:表示类的字节码对象,可以用来加载类、获取类的信息。Field
:表示类的字段,可以获取和设置字段的值。Method
:表示类的方法,可以动态调用方法。Constructor
:表示类的构造函数,可以通过它创建对象。
Class
在 Java 中,Class
类和反射密切相关。反射机制允许程序在运行时动态地查询、访问和操作类的元数据,而 Class
类是进行反射操作的基础。可以通过 Class
类获取一个类的详细信息,甚至可以通过反射实例化类、调用方法、获取字段等。
Class
类是 Java 中每个类的模板,Java 中的每个类(包括用户定义的类)都有一个对应的 Class
对象,它是类元数据的载体。
每个类的定义都可以通过 Class
对象来引用,通过 Class
对象可以获取类的结构信息,比如:
- 类名
- 类的构造方法
- 类的字段和方法
- 类的父类和接口
示例类
class Person {
private String name;
private int age;
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 方法
public void introduce() {
System.out.println("My name is " + name + " and I am " + age + " years old.");
}
// 获取年龄
public int getAge() {
return age;
}
}
获取Class对象
有两种方法可以获取到Class对象
方式1
public static void main(String[] args) throws Exception {
// 获取 Person 类的 Class 对象
Class<?> clazz = Person.class;
}
在 Java 中,Person.class
是直接通过类字面量(Class Literal)获得 Person
类的 Class
对象。这个 Class
对象是 Java 类型系统中表示 Person
类的对象。
Person.class
表示Person
类本身(而非Person
的实例)。Person.class
获取的是一个Class<Person>
类型的对象,它表示Person
类的元数据。- 在这里,
Person.class
的类型是Class<Person>
,这意味着它是一个Class
类型对象,且其泛型参数为Person
类本身。
方式2
public static void main(String[] args) throws Exception {
// 获取 Person 类的 Class 对象
Class<? extends Person> clazz = Person.class.getClass();
}
Person.class.getClass()
调用的过程是先获取 Person.class
这个 Class
对象,然后对这个 Class
对象调用 getClass()
方法。getClass()
方法是 Object
类的方法,返回的是该对象的运行时类型的 Class
对象。
Person.class.getClass()
的目的是获取 Person.class
这个 Class
对象的类型。
Person.class
的类型是 Class<Person>
,而 Class<Person>
继承自 Class<?>
,因此 getClass()
返回的类型是 Class<? extends Class<?>>
,即表示 Class
类型的 Class
对象。
也就是说,Person.class.getClass()
返回的是 Class<? extends Class<?>>
,它表示的是 Class
的类(即 Class.class
的类)。
方式3
当你知道一个class
的完整类名,可以通过静态方法Class.forName()
获取
public static void main(String[] args) throws Exception {
Class cls = Class.forName("com.test.Person");
}
获取构造方法
通过Class实例获取Constructor的方法如下:
getConstructor(Class...)
:获取某个public
的Constructor
;getDeclaredConstructor(Class...)
:获取某个Constructor
;getConstructors()
:获取所有public
的Constructor
;getDeclaredConstructors()
:获取所有Constructor
。
调用非public
的Constructor
时,必须首先通过setAccessible(true)
设置允许访问。setAccessible(true)
可能会失败。
public static void main(String[] args) throws Exception {
// 获取 Person 类的 Class 对象
Class<?> clazz = Person.class;
// 获取public类型的所有构造方法
Constructor<?>[] constructors = clazz.getConstructors();
// 获取指定参数的Constructor
Constructor<Person> constructor = clazz.getConstructor(new Class[]{String.class, Integer.class});
// 获取所有构造方法,包括私有的
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
// 记得上边提到的写法吗,我们还能这么写
Class<? extends Class> clazz = new Person().getClass();
Constructor<? extends Class>[] constructors = clazz.getConstructors();
Constructor<? extends Class> constructor = clazz.getConstructor(new Class[]{String.class, Integer.class});
Constructor<? extends Class>[] declaredConstructors = clazz.getDeclaredConstructors();
}
获取字段
getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。 getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的声明字段。
public static void main(String[] args) throws Exception {
Person person = new Person("erosion2020", 14);
// 获取 Person 类的 Class 对象
Class<?> clazz = Person.class;
// 获取所有有访问权限的字段
Field[] pubFields = clazz.getFields();
// 获取所有字段,包括私有的
Field[] fields = clazz.getDeclaredFields();
// 因为Person中name是private的,如果这里没有访问权限的话是获取不到name这个字段的
Field name = clazz.getField("name");
// 这样就可以获取到name字段了(即便没有访问权限也可以通过getDeclaredField直接将字段拿到
name = clazz.getDeclaredField("name");
// 当name字段没有访问权限时,如果想要修改字段值,则需要设置
name.setAccessible(true);
// 然后就能越权给name设置值了
name.set(person, "AcidEtch")
}
获取方法
Class
类提供了以下几个方法来获取Method
:
Method getMethod(name, Class...)
:获取某个public
的Method
(包括父类)Method getDeclaredMethod(name, Class...)
:获取当前类的某个Method
(不包括父类)Method[] getMethods()
:获取所有public
的Method
(包括父类)Method[] getDeclaredMethods()
:获取当前类的所有Method
(不包括父类)
public static void main(String[] args) throws Exception {
Class<?> clazz = Person.class;
// 获取所有包含了访问权限的方法
Method[] pubMethods = clazz.getMethods();
// 获取所有方法,然后输出(即便这个方法是被禁止访问的
Method[] methods = clazz.getDeclaredMethods();
// 来看个例子,动态调用String.substring(int)方法
String name = "AcidEtch";
Method substring = String.class.getMethod("substring", int.class);
System.out.println(substring.invoke(name,3));
}
如果调用的方法是静态方法。那么invoke
方法传入的第一个参数永远为null
public static void main(String[] args) throws Exception {
// 获取Integer.parseInt(String)方法,参数为String:
Method m = Integer.class.getMethod("parseInt", String.class);
// 调用该静态方法并获取结果:
Integer n = (Integer) m.invoke(null, "114514");
System.out.println(n);
}
创建实例并调用方法
public static void main(String[] args) throws Exception {
Class<?> clazz = Person.class;
// 获取指定参数的构造方法
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
// 通过反射来创建对象
Object personInstance = constructor.newInstance("John", 30);
Method introduceMethod = clazz.getMethod("introduce");
// 通过反射调用打印方法
introduceMethod.invoke(personInstance);
}
反射执行exec命令
Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")),"cmd /c start");
修改被final修饰的字段
class Example {
private final String message = "Initial Value";
public String getMessage() {
return message;
}
}
public class ModifyFinalField {
public static void main(String[] args) throws Exception {
Example example = new Example();
// 输出原始值
System.out.println("Before modification: " + example.getMessage());
// 获取 Example 类中 message 字段的 Field 对象
Field messageField = Example.class.getDeclaredField("message");
// 设置为可访问
messageField.setAccessible(true);
// 修改 final 字段的值
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
// 清除 final 修饰符的影响
modifiersField.setInt(messageField, messageField.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
// 修改字段值
messageField.set(example, "Modified Value");
// 输出修改后的值
System.out.println("After modification: " + example.getMessage());
}
}
标签:反射,JAVA,String,Person,public,class,获取,Class
From: https://www.cnblogs.com/erosion2020/p/18559481