Java的9种常用反射操作详解

Java反射是Java编程语言的一种强大功能,它能够检查和修改运行时对象的行为。我们将详细探讨Java反射中的9个主要操作。

1. 获取Class对象

在Java反射中,所有操作起点都是获取对应的Class对象,由于每个实例对象都属于某个类,所以我们最常见的就是从一个实例对象获取其对应类的Class对象。

Java提供了两种方式来获取一个类的Class对象:使用getClass()方法或使用Class.forName()静态方法。

1.1 通过实例对象获取Class对象

getClass()是一个实例方法,它存在于所有Java对象中,因为每个对象都继承自Object,而Object类提供了此方法。当在一个特定对象上调用该方法,将返回该对象所属的Class对象。

考虑以下的例子,我们有一个String对象,并调用getClass()方法获取该对象的Class对象:

String str = "Hello, World!";
Class strClass = str.getClass();

在这个例子中,由于字符串"Hello, World!"是一个String对象,所以getClass()方法返回的是String类的Class对象。

1.2 通过类的全限定名获取Class对象

如果你只知道类的全限定名,即包含包名的类名,如java.lang.String,可以通过Class类的静态方法forName(String className)来获取该类的Class对象。这种方式常用于动态加载类。

例如,我们如下获取String类的Class对象:

Class strClass = Class.forName("java.lang.String"); //动态加载
Class<?> strClass = String.class; //静态加载

此方法将加载名为"java.lang.String"的类,并返回该类的Class对象。

总结起来,获取Class对象的主要方式就是通过实例对象的getClass()方法和Class.forName()静态方法,而后续的反射操作,都是基于这个Class对象进行的。

2. 创建对象

利用反射,我们可以动态地创建一个对象。通过在获取到的Class对象上调用newInstance()方法,可以创建该类的新实例,等价于使用new关键字。

2.1 使用newInstance()创建对象

Class类中的newInstance()方法会调用类的无参构造函数来创建该类的新实例。这等价于使用new关键字和无参构造函数创建对象。

例如,假设我们有一个Person类有一个无参构造函数,我们可以像下面这样创建Person的新实例:

Class<?> personClass = Person.class;
Person person = (Person) personClass.newInstance();

这里,我们首先获取到Person类的Class对象,然后通过调用newInstance()方法创建了Person的新实例。

2.2 注意事项与限制

  • newInstance()方法只能调用无参构造函数,如果需要使用带参数的构造函数创建对象,需要使用Constructor类的newInstance(Object... initargs)方法。

  • 调用newInstance()方法时,无参构造函数的访问权限需要是可访问的。如果无参构造函数是private,那么调用newInstance()方法就会抛出IllegalAccessException

  • 如果无参构造函数在执行过程中抛出了异常,那么调用newInstance()方法也会抛出InstantiationException

在使用newInstance()方法的时候,对上述限制和异常需要有一定的注意和处理。

虽然可以使用newInstance()方法,但因为其使用的是无参构造函数,所以在需要参数构造的情况下,推荐使用Constructor.newInstance(Object... initargs)方法。

要注意,从Java 9开始,Class类的newInstance()方法已经被弃用。取而代之的是,应该使用Class类的getDeclaredConstructor()方法获取Constructor对象,然后调用Constructor对象的newInstance()方法来创建类的实例。

以下是使用getDeclaredConstructor().newInstance()方法创建实例的示例:

Class<?> personClass = Person.class;
Person person = (Person) personClass.getDeclaredConstructor().newInstance();

使用这种方式创建对象,我们可以选择调用哪个构造函数并传入合适的参数,因此它提供了更大的灵活性。

另外,当使用反射创建对象时,可能会抛出各种异常(比如ClassNotFoundExceptionInstantiationExceptionIllegalAccessExceptionNoSuchMethodExceptionInvocationTargetException等),因此在使用反射时,需要进行恰当的异常处理。

3. 获取和操作成员变量

我们可以通过getDeclaredField(String name)方法从某个Class对象中获取指定的字段,然后可以使用Field类的set(Object obj, Object value)方法来修改此字段的值。

3.1 获取成员变量

Java反射提供了从Class对象中获取指定字段的方法。具体来说,通过如下几个方法:

  • getDeclaredField(String name):返回一个Field对象,它反映此Class对象所表示的类或接口的指定已声明字段。这包括所有的字段,无论其访问权限如何。
  • getDeclaredFields():返回此Class对象表示的类或接口所声明的所有字段的Field对象数组。
  • getField(String name):返回一个Field对象,它反映此Class对象所表示的类或接口的指定公开字段,包括其父类或父接口中的字段。
  • getFields():返回包含此Class对象表示的类或接口的所有可访问公共字段的Field对象的数组。

例如,假设我们有一个Person类:

public class Person {
    private String name;

    // getters and setters
}

我们可以使用以下几种方法获取"name"字段以及其他字段:

// 获取单个字段
Class<?> personClass = Person.class;
Field nameField = personClass.getDeclaredField("name");

// 获取所有声明的字段(不包括父类)
Field[] declaredFields = personClass.getDeclaredFields();

// 获取单个公有字段(包括父类)
Field nameFieldPublic = personClass.getField("name");

// 获取所有公有字段(包括父类)
Field[] publicFields = personClass.getFields();

注意:getDeclaredFieldgetDeclaredFields方法会返回所有字段,无论其是否公开;而getFieldgetFields方法只会返回公开的字段,包括从父类继承的字段。

3.2 操作成员变量

获取到Field对象后,我们可以对其进行操作。Field类提供了多个方法来设置或获取字段的值。

例如,我们可以使用set(Object obj, Object value)方法来修改字段的值。此方法接受两个参数:第一个参数是要修改的对象,第二个参数是新的字段值。

假设我们有一个Person对象person,我们可以这样修改其"name"字段的值:

nameField.setAccessible(true); // necessary for private fields
nameField.set(person, "Alice");

这段代码首先调用setAccessible(true)方法允许访问私有字段,然后调用set(Object obj, Object value)方法将person的"name"字段值设为"Alice"。

注意,setAccessible方法是用于削弱Java语言访问控制的,所以需要谨慎使用。此外,对于final字段,反射不允许改变其值。

总的来说,Java反射提供了灵活的方式来操作类的字段,实现了在运行时动态地获取和设置字段的值。

4. 获取构造函数并创建对象

除了使用newInstance()方法,我们也可以通过获取类的特定构造函数来创建新对象。这可以通过调用getDeclaredConstructor(Class<?>... parameterTypes)方法并传入参数类型来实现。

4.1 获取构造函数

Java反射提供了从Class对象中获取构造函数的功能。具体的方式主要有以下几种:

  • getDeclaredConstructor(Class... parameterTypes):返回 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定声明构造函数。该方法接受一个或多个Class类型的参数,表示你要获取的构造函数的参数类型。
  • getDeclaredConstructors():返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类或接口的所有已声明构造函数。
  • getConstructor(Class... parameterTypes):返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定公开(public)构造函数。
  • getConstructors():返回包含一个数组,该数组包含 Constructor 对象反映由此 Class 对象表示的类的所有公开(public)构造函数。

例如,假设我们有一个Person类,它有一个接受String类型参数的构造函数:

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    // getters and setters
}

我们可以使用以下方法来获取该Person类的构造函数:

// 获取单个指定参数类型的构造函数
Class<?> personClass = Person.class;
Constructor<?> constructor = personClass.getDeclaredConstructor(String.class);

// 获取所有已声明的构造函数
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();

// 获取单个公开(public)的构造函数
Constructor<?> publicConstructor = personClass.getConstructor(String.class);

// 获取所有公开(public)的构造函数
Constructor<?>[] publicConstructors = personClass.getConstructors();

注意,getDeclaredConstructorgetDeclaredConstructors方法返回的都是类自身声明的构造函数,无论其访问权限如何;而getConstructorgetConstructors方法返回的都是类的公开(public)构造函数,不包括类自身的私有(private)和受保护(protected)构造函数。

4.2 使用构造函数创建对象

获取到Constructor对象后,我们可以使用它来创建类的新实例。具体来说,Constructor类的newInstance(Object... initargs)方法可以创建一个新的对象。这个方法接受一系列参数,用于传递给构造函数。

使用上述Person类的例子,我们可以这样创建一个新的Person对象:

Person person = (Person) constructor.newInstance("Alice");

这段代码将创建一个新的Person对象,其"name"字段的值为"Alice"。

相比直接使用Class类的newInstance()方法,使用Constructor对象创建新实例的好处是可以选择调用哪个构造函数,并传入合适的参数。

5. 获取和调用成员方法

我们也可以使用反射来获取并调用类的成员方法。通过getDeclaredMethod(String name, Class<?>... parameterTypes)方法可以获取指定的方法,然后通过调用Method类的invoke(Object obj, Object... args)方法来调用此方法。

5.1 获取成员方法

Java反射提供了获取类的成员方法的功能。具体的方式主要有以下几种:

  • getDeclaredMethod(String name, Class... parameterTypes):返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定已声明的方法。此方法接收两个参数:一个是要获取的方法的名称,另一个是该方法参数的类型。
  • getDeclaredMethods():返回包含 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法, 不包括继承的方法。
  • getMethod(String name, Class... parameterTypes):返回一个 Method 对象,它反映此 Class 对象所代表的类或接口的指定公共(public)成员方法,包括其继承的方法。
  • getMethods():返回包含 Method 对象的一个数组,这些对象反映此 Class 对象所代表的类或接口的所有公共(public)成员方法,包括其继承的方法。

例如,假设我们有一个Person类,有一个名为"sayHello"的方法:

public class Person {
    public void sayHello(String message) {
        System.out.println("Hello, " + message);
    }
}

我们可以使用以下方法来获取Person类的方法:

// 获取单个指定参数类型的方法
Class<?> personClass = Person.class;
Method sayHelloMethod = personClass.getDeclaredMethod("sayHello", String.class);

// 获取所有已声明的方法
Method[] declaredMethods = personClass.getDeclaredMethods();

// 获取单个公开(public)的方法,包括父类的方法
Method sayHelloMethodPublic = personClass.getMethod("sayHello", String.class);

// 获取所有公开(public)的方法,包括父类的方法
Method[] publicMethods = personClass.getMethods();

注意,getDeclaredMethodgetDeclaredMethods方法会返回类自身声明的所有方法,无论其访问权限如何,而getMethodgetMethods方法返回的都是类的公开(public)方法,包括从父类继承的方法。

5.2 调用成员方法

获取到Method对象后,可以使用它来调用类的成员方法。具体使用Method类的invoke(Object obj, Object... args)方法实现。invoke方法接收两个参数:第一个参数是要调用方法的对象,第二个参数是调用方法时传入的参数。

使用前面Person类的例子,我们可以这样调用"sayHello"方法:

Person person = new Person();
sayHelloMethod.invoke(person, "Alice");

执行这段代码后,控制台将输出:Hello, Alice

此外,对于访问权限的问题,如私有方法,也是可以通过setAccessible(true)来关闭Java语言的访问权限检查,从而调用私有方法。此处需要格外注意,这么做可能会导致安全问题,必须要谨慎对待。

6. 获取类名

我们可以通过调用getName()方法来获取类的全限定名(包括包名)。

在Java反射中,Class类的getName()方法可用于获取类的全限定名,也就是类名包含包名。

例如,假设我们有一个完全限定名为"com.example.Person"的类,我们可以这样获取该类的全限定名:

Class<?> personClass = Person.class;
String className = personClass.getName();

执行这段代码后,className的值将是:“com.example.Person”。

此外,Class类还有其他一些方法可以获取类的信息:

  • getSimpleName():返回类的简单名字,不包括包名。对于上述例子,getSimpleName()将返回"Person"。
  • getPackage():返回包含此类的Package对象。可以用来获取包级别的注解、声明的版本等信息。
  • getSuperclass():返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class
  • getInterfaces():确定此 Class 对象所表示的类或接口实现的接口。

这就是关于Java反射获取类名和其他相关类信息的内容。

7. 获取父类

我们可以通过调用getSuperclass()方法获取类的父类。

在Java反射中,Class类的getSuperclass()方法可以获取类的父类信息。

例如,假设我们有一个名为"Person"的类,它继承自"Human"类,我们可以这样获取"Person"类的父类信息:

Class<?> personClass = Person.class;
Class<?> superClass = personClass.getSuperclass();

执行这段代码后,superClass的值将是代表"Human"类的Class对象。

除此之外,Class类提供了一些其他方法来获取类继承相关的信息:

  • getGenericSuperclass():返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type。如果超类是参数化的,则返回的类型是参数化类型。
  • getInterfaces():确定此 Class 对象所表示的类或接口实现的接口。
  • getGenericInterfaces():返回表示此 Class 所表示的实体(类、接口、基本类型或 void)实现的接口的 Type

例如,获取"Person"类的所有实现的接口:

Type[] interfaces = personClass.getInterfaces();

以上就是关于Java反射中获取类的父类和其他继承相关信息的方法。

8. 获取实现的接口

我们也可以通过使用getInterfaces()方法获取一个类实现的所有接口。

在Java反射中,Class类的getInterfaces()方法可以获取一个类实现的所有接口。它返回一个包含表示这些接口的Class对象的数组。

例如,如果我们有一个Person类实现了RunnableComparable接口:

public class Person implements Runnable, Comparable<Person> {
    // ...
}

我们可以这样获取Person类实现的所有接口:

Class<?> personClass = Person.class;
Class<?>[] interfaceClasses = personClass.getInterfaces();

执行这段代码后,interfaceClasses将是一个包含表示Runnable类和Comparable类的Class对象的数组。

除了getInterfaces()方法,Class类还提供了getGenericInterfaces()方法,它返回一个Type对象的数组,这些对象代表这个类所实现的所有接口。相较于getInterfaces()getGenericInterfaces()考虑了泛型接口。

对于前面的Person类,我们可以这样获取泛型接口:

Type[] genericInterfaces = personClass.getGenericInterfaces();

执行这代码后,genericInterfaces将是一个包含表示Runnable接口和Comparable<Person>接口的Type对象的数组。

以上就是在Java反射中获取类所实现接口的方法。

9. 获取类的修饰符

我们可以通过getModifiers()方法获取类的修饰符,比如public、private、static等。然后我们可以使用java.lang.reflect.Modifier类的静态方法来解析这些修饰符。

在Java反射中,我们可以通过Class类的getModifiers()方法获取类的修饰符,该方法返回一个整数,表示类的修饰符。这个整数值表示的修饰符包括public、private、protected、static、final、abstract、interface等。例如,如下的获取方式:

Class<?> personClass = Person.class;
int modifiers = personClass.getModifiers();

然后,我们可以使用java.lang.reflect.Modifier类的一系列静态方法来解析这个整数值,以此来判断类的修饰符。Modifier类提供如下一些方法:

  • isAbstract(int mod):判断是否为抽象类。
  • isFinal(int mod):判断是否为final类。
  • isInterface(int mod):判断是否为接口。
  • isPrivate(int mod):判断是否为private类。
  • isProtected(int mod):判断是否为protected类。
  • isPublic(int mod):判断是否为public类。
  • isStatic(int mod):判断是否为static类。

如,判断"Person"类是否为public和abstract:

boolean isPublic = Modifier.isPublic(modifiers);
boolean isAbstract = Modifier.isAbstract(modifiers);

以上主要介绍了如何通过反射获取类的修饰符,以及如何解析获取到的修饰符。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Java反射机制是指在运行时动态地获取一个类的信息,并可以操作类的属性、方法和构造器等。Java反射机制可以使程序员在运行时动态地调用类的方法和属性,扩展类的功能,并可以实现注解、工厂模式以及框架开发等。 Java反射机制的原理如下:首先,Java编译器将Java源代码编译为字节码文件,字节码文件中包含着类的信息,这些信息包括类的名称、方法、属性和构造器等等。接着,Java虚拟机将字节码文件加载到内存中,然后通过类加载器将类加载到内存中形成一个类对象,这个类对象可以操作字节码文件中的信息。 使用Java反射机制的过程如下:首先获取类对象,通过类对象来获取类的构造器、属性、方法等信息,然后调用构造器来创建对象,通过属性获取和设置类的成员属性,通过方法调用类的方法等。 Java反射机制的优点是可以在运行时动态地得到类的信息,使得程序员在程序运行时能够对类进行更加灵活的操作,并可以使得程序更加通用化,同时也存在着一定的性能问题,因为Java反射机制需要Java虚拟机进行一定的额外处理,所以在程序运行时需要进行额外的时间和资源消耗。 总之,Java反射机制是Java语言的一项重要特性,在Java开发中广泛应用,在代码编写、框架开发以及API开发中具有重要作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_31188147

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值