当前位置: 首页 > news >正文

Java 反射体系

目录

1 反射机制的概述

2 获取Class类的对象 

3 反射获取构造方法并使用 

4 反射获取成员方法并使用

5 反射获取成员变量并使用

6 反射获取注解的值

7 反射优缺点


1 反射机制的概述

Java反射(Reflection)是Java语言的一个重要特性,它允许运行中的Java程序对自身进行检查,并且可以直接操作程序的内部属性。通过反射,我们可以在运行时获取类的信息、创建对象、调用方法以及访问和修改字段等。

Java反射体系主要包括以下几个核心类:

  • Class类:代表一个类或接口。每个类都有一个对应的Class对象,该对象包含了类的所有信息,如名称、父类、实现的接口、构造器、方法和字段等。可以通过以下几种方式获得Class对象:

    • 类名.class
    • 对象.getClass()
    • Class.forName("全限定类名")
  • Constructor类:代表类的构造方法。通过Class对象可以获取Constructor对象数组,进而创建实例。

    • 例如: Constructor<?>[] constructors = clazz.getConstructors();
  • Method类:代表类的方法。通过Class对象可以获取Method对象,从而调用方法。

    • 例如: Method method = clazz.getMethod("methodName", paramTypes);
  • Field类:代表类的字段(成员变量)。通过Class对象可以获取Field对象,用于访问或修改字段值。

    • 例如: Field field = clazz.getField("fieldName");
  • Modifier类:提供了一组静态方法来处理类和成员的修饰符。可以用来判断是否为public, private, static, final等。

    • 例如: boolean isPublic = Modifier.isPublic(field.getModifiers());

2 获取Class类的对象 

获取Class对象是Java反射机制中的一个基本操作。Class对象代表了运行时类的信息,通过它可以访问类的构造器、方法、字段等。下面详细介绍几种获取Class对象的方法,并提供相应的可运行示例。

获取Class对象的方式

  • 使用.class语法

    • 适用于任何类型(包括类、接口、数组、基本数据类型及void)。
    • 这是最简单和最常用的方法。
  • 使用对象的.getClass()方法

    • 只有当已经有了某个类的对象实例时才能使用这种方法。
    • 返回的是该对象的实际类型,如果对象被子类化,则返回的是子类的Class对象。
  • 使用Class.forName()方法

    • 需要传入类的全限定名(包名+类名)作为参数。
    • 通常用于加载配置文件中指定的类或在运行时动态加载类。
    • 如果找不到对应的类,会抛出ClassNotFoundException

示例代码: 这里给出几个简单的例子来演示如何获取Class对象

public class ClassExample {public static void main(String[] args) {// 1. 使用 .class 语法Class<?> stringClass = String.class;System.out.println("Using .class: " + stringClass.getName());// 2. 使用对象的 getClass() 方法String str = "Hello, World!";Class<?> strClass = str.getClass();System.out.println("Using getClass(): " + strClass.getName());// 3. 使用 Class.forName() 方法try {Class<?> forNameClass = Class.forName("java.lang.String");System.out.println("Using Class.forName(): " + forNameClass.getName());} catch (ClassNotFoundException e) {e.printStackTrace();}}
}

输出结果:

Using .class: java.lang.String
Using getClass(): java.lang.String
Using Class.forName(): java.lang.String

注意事项:

  • 当使用Class.forName()时,确保提供的类名是正确的,否则程序将抛出ClassNotFoundException异常。
  • .getClass()方法返回的是对象的实际类型,因此如果对象是某个子类的实例,那么.getClass()将返回子类的Class对象,而不是父类的。
  • 对于基本类型(如int, boolean等),可以直接使用int.classboolean.class等来获取对应的Class对象。对于它们的包装类型(如Integer, Boolean),可以使用相同的.class语法或Class.forName()方法。

3 反射获取构造方法并使用 

在Java中,反射不仅可以用来获取类的信息,还可以用来创建对象实例。通过Class对象,我们可以访问类的构造方法(Constructor),并使用这些构造方法来创建新的对象实例。下面我将详细介绍如何使用反射来获取构造方法,并给出一个可运行的例子。

获取构造方法:

  • 无参数构造方法

    • 使用getConstructor()方法来获取公共的(public)无参数构造方法,如果没有找到匹配的构造方法,会抛出NoSuchMethodException
    • 使用getDeclaredConstructor()方法来获取所有可见性的无参数构造方法,如果没有找到匹配的构造方法,会抛出NoSuchMethodException
  • 带参数的构造方法

    • 使用getConstructor(Class<?>... parameterTypes)来获取特定签名的公共构造方法(public),如果没有找到匹配的构造方法,会抛出NoSuchMethodException
    • 使用getDeclaredConstructor(Class<?>... parameterTypes)来获取特定签名的所有可见性构造方法,如果没有找到匹配的构造方法,会抛出NoSuchMethodException
  • 所有构造方法

    • 使用getConstructors()来获取所有的公共(public)构造方法,不会抛出NoSuchMethodException,但如果该类没有公共构造方法,则返回的数组长度为0。
    • 使用getDeclaredConstructors()来获取所有可见性的构造方法。

创建对象实例:

一旦获取了构造方法,就可以使用newInstance(Object... initargs)方法来创建对象实例。需要注意的是,如果构造方法是私有的或有其他访问限制,需要先调用setAccessible(true)来允许访问。

示例代码:

import java.lang.reflect.Constructor;public class ReflectionConstructorExample {public static void main(String[] args) {try {// 1. 获取Class对象Class<?> personClass = Class.forName("om.miracle.service.reflex.Person");// 2. 获取无参数构造方法Constructor<?> noArgsConstructor = personClass.getConstructor();// 使用无参数构造方法创建对象Object person1 = noArgsConstructor.newInstance();System.out.println("Created object with no-arg constructor: " + person1);// 3. 获取带参数的构造方法Constructor<?> fullArgsConstructor = personClass.getConstructor(String.class, int.class);// 使用带参数的构造方法创建对象Object person2 = fullArgsConstructor.newInstance("Alice", 30);System.out.println("Created object with full-arg constructor: " + person2);// 4. 获取私有构造方法并创建对象Constructor<?> privateConstructor = personClass.getDeclaredConstructor(int.class);privateConstructor.setAccessible(true); // 设置为可访问Object person3 = privateConstructor.newInstance(25);System.out.println("Created object with private constructor: " + person3);// 5.获取所有公共构造方法Constructor<?>[] constructors = personClass.getDeclaredConstructors();for (Constructor<?> c : constructors) {System.out.println("Public constructor: " + c);}} catch (Exception e) {e.printStackTrace();}}
}class Person {private String name;private int age;public Person() {this.name = "Unnamed";this.age = 0;}public Person(String name, int age) {this.name = name;this.age = age;}private Person(int age) {this.name = "Unnamed";this.age = age;}@Overridepublic String toString() {return "Person{name='" + name + "', age=" + age + "}";}
}

 输出结果:

Created object with no-arg constructor: Person{name='Unnamed', age=0}
Created object with full-arg constructor: Person{name='Alice', age=30}
Created object with private constructor: Person{name='Unnamed', age=25}
Public constructor: private com.miracle.service.reflex.Person(int)
Public constructor: public com.miracle.service.reflex.Person(java.lang.String,int)
Public constructor: public com.miracle.service.reflex.Person()

4 反射获取成员方法并使用

在Java中,反射不仅可以用来获取类的构造方法和创建对象实例,还可以用来访问和调用类中的成员方法。通过Class对象,我们可以获取类的方法(Method),并使用这些方法来执行相应的操作。下面我将详细介绍如何使用反射来获取成员方法,并给出一个可运行的例子。

获取成员方法:

  • 获取公共方法

    • 使用getMethod(String name, Class<?>... parameterTypes)方法来获取特定签名的公共方法。
    • 使用getMethods()方法来获取所有公共方法(包括继承自父类和实现接口的方法)。
  • 获取所有可见性的方法

    • 使用getDeclaredMethod(String name, Class<?>... parameterTypes)方法来获取特定签名的所有可见性方法(不包括继承的方法)。
    • 使用getDeclaredMethods()方法来获取所有可见性的方法(不包括继承的方法)。

调用成员方法:

一旦获取了方法对象,就可以使用invoke(Object obj, Object... args)方法来调用该方法。如果方法是静态的,则第一个参数可以是null。如果方法是非静态的,则需要提供一个有效的对象实例作为第一个参数。

示例代码:

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;public class ReflectionMethodExample {public static void main(String[] args) {try {// 1. 获取Class对象Class<?> personClass = Class.forName("om.miracle.service.reflex.Person");// 2. 创建Person对象Constructor<?> constructor = personClass.getConstructor(String.class, int.class);Person person = (Person) constructor.newInstance("Alice", 30);// 3. 获取公共方法sayHello()Method sayHelloMethod = personClass.getMethod("sayHello");// 调用sayHello()方法String greeting = (String) sayHelloMethod.invoke(person);System.out.println(greeting); // 输出: Hello, Alice!// 4. 获取带参数的方法setAge(int)Method setAgeMethod = personClass.getMethod("setAge", int.class);// 调用setAge(int)方法setAgeMethod.invoke(person, 35);// 5. 获取私有方法getPrivateInfo()Method getPrivateInfoMethod = personClass.getDeclaredMethod("getPrivateInfo");getPrivateInfoMethod.setAccessible(true); // 设置为可访问String privateInfo = (String) getPrivateInfoMethod.invoke(person);System.out.println(privateInfo); // 输出: Private info for Alice, 35} catch (Exception e) {e.printStackTrace();}}
}class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String sayHello() {return "Hello, " + name + "!";}public void setAge(int age) {this.age = age;}private String getPrivateInfo() {return "Private info for " + name + ", " + age;}@Overridepublic String toString() {return "Person{name='" + name + "', age=" + age + "}";}
}

输出结果:

Hello, Alice!
Private info for Alice, 35

5 反射获取成员变量并使用

在Java中,反射还可以用来访问和操作类中的成员变量(字段)。通过Class对象,我们可以获取类的字段(Field),并使用这些字段来读取或设置其值。下面我将详细介绍如何使用反射来获取成员变量,并给出一个可运行的例子。

获取成员变量:

  • 获取公共字段

    • 使用getField(String name)方法来获取特定名称的公共字段。
    • 使用getFields()方法来获取所有公共字段(包括继承自父类和实现接口的字段)。
  • 获取所有可见性的字段

    • 使用getDeclaredField(String name)方法来获取特定名称的所有可见性字段(不包括继承的字段)。
    • 使用getDeclaredFields()方法来获取所有可见性的字段(不包括继承的字段)。

读取和设置成员变量:

一旦获取了字段对象,就可以使用以下方法来读取或设置字段值:

  • get(Object obj):获取指定对象上该字段的值。
  • set(Object obj, Object value):设置指定对象上该字段的值为给定的新值。

如果字段是私有的或者有其他访问限制,需要先调用setAccessible(true)来允许访问。

示例代码:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;public class ReflectionFieldExample {public static void main(String[] args) {try {// 1. 获取Class对象Class<?> personClass = Class.forName("om.miracle.service.reflex.Person");// 2. 创建Person对象Constructor<?> constructor = personClass.getConstructor(String.class, int.class);Person person = (Person) constructor.newInstance("Alice", 30);// 3. 获取公共字段nameField nameField = personClass.getField("name");// 读取name字段的值String name = (String) nameField.get(person);System.out.println("Name: " + name); // 输出: Name: Alice// 4. 设置name字段的值nameField.set(person, "Bob");System.out.println("New Name: " + person.getName()); // 输出: New Name: Bob// 5. 获取私有字段ageField ageField = personClass.getDeclaredField("age");ageField.setAccessible(true); // 设置为可访问// 读取age字段的值int age = (int) ageField.get(person);System.out.println("Age: " + age); // 输出: Age: 30// 6. 设置age字段的值ageField.set(person, 35);System.out.println("New Age: " + person.getAge()); // 输出: New Age: 35} catch (Exception e) {e.printStackTrace();}}
}class Person {public String name; // 公共字段private int age; // 私有字段public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}@Overridepublic String toString() {return "Person{name='" + name + "', age=" + age + "}";}
}

输出结果:

Name: Alice
New Name: Bob
Age: 30
New Age: 35

这个例子展示了如何使用反射来动态地访问和修改类中的成员变量,即使这些变量是私有的。注意,在实际开发中,应该谨慎使用反射来直接访问私有字段,因为这可能会破坏封装性和安全性。

6 反射获取注解的值

反射还可以用来访问类、方法或字段上的注解。通过Class对象、Method对象或Field对象,我们可以检查这些元素上是否存在特定的注解,并且可以读取注解中的值。下面我将详细介绍如何使用反射来获取注解的值,并给出一个可运行的例子。

获取注解:

  • 检查注解的存在

    • 使用isAnnotationPresent(Class<? extends Annotation> annotationClass)方法来检查某个元素上是否存在指定类型的注解。
  • 获取注解实例

    • 使用getAnnotation(Class<T> annotationClass)方法来获取特定类型的注解实例。
    • 使用getAnnotations()方法来获取所有注解的数组。
    • 使用getDeclaredAnnotations()方法来获取所有直接声明的注解(不包括继承的注解)。
  • 读取注解的值

    • 一旦获取了注解实例,就可以通过调用注解接口中定义的方法来读取注解中的值。

示例代码:

import java.lang.annotation.*;
import java.lang.reflect.Method;// 定义一个自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyAnnotation {String value();int count() default 1;
}class MyClass {@MyAnnotation(value = "Hello", count = 5)public void myMethod() {System.out.println("This is a method with an annotation.");}
}public class ReflectionAnnotationExample {public static void main(String[] args) {try {// 1. 获取Class对象Class<?> myClass = Class.forName("MyClass");// 2. 获取myMethod方法Method myMethod = myClass.getMethod("myMethod");// 3. 检查myMethod方法上是否存在MyAnnotation注解if (myMethod.isAnnotationPresent(MyAnnotation.class)) {// 4. 获取MyAnnotation注解实例MyAnnotation myAnnotation = myMethod.getAnnotation(MyAnnotation.class);// 5. 读取注解的值String value = myAnnotation.value();int count = myAnnotation.count();// 输出注解的值System.out.println("Value: " + value); // 输出: Value: HelloSystem.out.println("Count: " + count); // 输出: Count: 5} else {System.out.println("The method does not have the specified annotation.");}} catch (Exception e) {e.printStackTrace();}}
}

输出结果:

Value: Hello
Count: 5

这个例子展示了如何使用反射来动态地获取方法上的注解,并读取注解中的具体值。同样的方法也可以应用于类和字段上的注解。注意,为了能够在运行时通过反射访问注解,注解的保留策略必须是RetentionPolicy.RUNTIME

7 反射优缺点

优点

  • 高度灵活:能够在运行时发现并使用类型信息。
  • 支持动态编程:允许程序在运行时改变行为。
  • 便于调试和测试:可以方便地查看内部结构,对于复杂系统的调试非常有用。

缺点

  • 性能开销:反射比直接调用慢得多,因为JVM需要做额外的工作来解析字符串形式的类名、方法名等。
  • 安全问题:由于反射可以绕过正常的访问控制,可能会带来一些安全隐患。
  • 可读性和维护性降低:过度使用反射会使代码变得难以理解和维护。

总之,反射提供了一种强大而灵活的方式来操作运行时的数据结构,但在使用时应该考虑到其潜在的缺点,并确保只在确实需要的地方使用。 


http://www.mrgr.cn/news/35827.html

相关文章:

  • 【伺服】Servo入坑学习记录①
  • 图解Transformer就这30页PPT,你们真不看啊
  • 双端搭建个人博客
  • Vue3 tsx文件中如何实现页面跳转
  • sql server 官方学习网站
  • vue3腾讯云实时音视频通话 ui集成方案TUIcallkit
  • 编码器分辨率、精度和重复精度的定义
  • 线性判别分析 (LDA)中目标函数的每个部分的具体说明
  • 【P1320 压缩技术(续集版)】
  • 优化理论及应用精解【11】
  • Prompt输出限制怎么写?用CCoT限制输出长度的推理,大幅提高LLM准确性
  • 在pycharm中怎样调试HTML网页程序
  • C语言课程设计题目二:图书信息管理系统设计
  • vulnhub靶场Matrix-win全流程
  • 【设计模式-策略】
  • 双十一有哪些好物值得入手?五款超值数码好物分享!
  • C# 用统一代码动态查询数据库并显示数据
  • 芒果TV《航海少年团》强强联合,优质少儿动画乘风起航
  • W39-02-jmeter中如何实现:下一个请求是需要根据前一个请求返回值进行循环请求
  • Latex学习