Java 反射浅谈

Java反射机制允许运行时动态获取类信息并操作类或对象,核心类包括`Class`、`Constructor`、`Field`和`Method`。反射支持动态创建对象、调用方法及修改字段,但性能较低。优点是灵活性高(如框架底层依赖),缺点是解释执行影响速度。通过`Class.forName()`、`getDeclaredMethod()`等方法实现动态加载与操作,适用于配置化编程和扩展功能场景。

作者头像
LumiBee
10 天前 · 60 0
分享

反射机制

Java反射机制是Java提供的一种在运行时动态获取类信息并操作类或对象的机制。通过反射,可以在程序运行时获取类的构造方法,成员变量,方法等信息,并调用他们。

反射的优点与缺点

  1. 优点:可以动态地创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑
  2. 缺点:使用反射基本是解释执行,对执行速度有影响

反射的核心类

  • Class类:表示类的元数据,表示某个类加载后在堆中的对象,是反射的入口
  • Constructor类:表示类的构造方法
  • Field类:表示类的成员变量
  • Method类:表示类的方法

底层运作

image-20250105105407854

反射的主要功能

  1. 获取类的信息:
    1. 获取类的名称,修饰符,父类,接口,注解等
    2. 获取类的构造方法,成员变量,方法等信息
  2. 动态创建对象:
    1. 通过 Class.newInstance()Constructor.newInstance() 动态创建对象
  3. 动态调用方法:
    1. 通过 Method.invoke() 动态调用对象的方法。
  4. 动态访问和修改字段:
    1. 通过 Field.get()Field.set() 访问和修改对象的字段。
  5. 操作数组:
    1. 通过 Array 类动态创建和操作数组。

反射相关类

  1. java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
  2. java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法
  3. java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
  4. java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示某个类的构造方法

常用方法

  1. 获取Class对象
//通过类的全限定名获取Class对象(编译阶段)
//应用场景:多用于配置文件,读取类全路径,加载类
Class.forName("String className")
//通过类字面常量获取Class对象(加载阶段)
//应用场景:多用于参数传递,比如通过反射得到对应地构造器对象
类.class
//通过对象实例获取Class对象(运行阶段)
//应用场景:通过创建好地对象,获取Class对象
对象.getClass()
  1. 获取构造函数
//获取指定的公共构造函数
getConstructor(Class<?>... parameterTypes)
//获取指定的构造函数(包括私有构造函数)
getDeclaredConstructor(Class<?> parameterTypes)
//获取所有公共构造函数   
getConstructors()
//获取所有构造函数(包括私有构造函数)
getDeclaredConstructors()
  1. 获取方法
//获取指定的公共方法
getMethod(String name, Class<?>... parameterTypes)
//获取指定的方法(包括私有方法)
getDeclaredMethod(String name, Class<?>... parameterTypes)
//获取所有公共方法
getMethods()
//获取所有方法(包括私有方法)
getDeclaredMethods()
  1. 获取字段
//获取指定的公共字段
getField(String name)
//获取指定的字段(包括私有字段)
getDeclaredField(String name)
//获取所有公共字段
getFields()
//获取所有字段(包括私有字段)
getDeclaredFields()
  1. 调用方法
//调用方法
method.invoke(Object obj, Object... args)
  1. 创建实例
//通过指定的构造函数创建实例
//需要先获得构造方法,然后才能创建实例
Constructor<?> constructor = cls.getConstructor(Class<?>... parameterTypes);
Object instance = constructor.newInstance(Object... initargs);
//法二
Object instance = cls.getConstructor().newInstance();

动态和静态加载

静态加载

定义:静态加载是指在程序启动或初始化时,一次性将所有资源加载到内存中

特点

  1. 启动时加载:程序启动时即加载所有资源。
  2. 内存占用高:所有资源常驻内存,占用较大。
  3. 响应快:资源已预加载,使用时无需等待。
  4. 适用场景:适合资源少、内存充足或对响应速度要求高的场景。

优点

  • 使用资源时无需额外加载,响应迅速。

缺点

  • 启动慢,内存占用高,资源利用率低。

动态加载

定义: 动态加载是在程序运行时,按需加载资源。

特点

  1. 按需加载:资源在使用时才加载。
  2. 内存占用低:只加载当前需要的资源,节省内存。
  3. 响应延迟:首次加载资源时可能有延迟。
  4. 适用场景:适合资源多、内存有限或资源不常用的场景。

优点

  • 启动快,内存占用低,资源利用率高。

缺点

  • 首次加载资源时可能有延迟,增加运行时复杂性。

动态加载的使用场景

  • 使用Class.forName()动态加载类,在运行时根据类名加载类,并返回对应的class对象

类加载

类加载是 JVM将类的字节码文件(.class 文件)加载到内存中,并转换为 Class 对象的过程。

image-20250114160834130

类加载的过程

类加载的过程可以分为以下几个阶段:

  1. 加载(Loading)
    • 通过类的全限定名(包名 + 类名)查找字节码文件。
    • 将字节码文件加载到内存中,并生成一个 java.lang.Class 对象。
    • 加载可以由 JVM 自带的类加载器完成,也可以由用户自定义的类加载器完成。
  2. 验证(Verification)
    • 确保加载的字节码文件符合 JVM 规范,防止恶意代码破坏 JVM。
    • 验证内容包括文件格式、元数据、字节码和符号引用等。
  3. 准备(Preparation)
    • 为类的静态变量分配内存,并设置默认初始值(如 0null 等)。
    • 如果是常量(final static),则直接赋值为代码中定义的值。
  4. 解析(Resolution)
    • 将常量池中的符号引用(Symbolic Reference)转换为直接引用(Direct Reference)。
    • 符号引用是类、方法、字段的名称,直接引用是具体的内存地址。
  5. 初始化(Initialization)
    • 执行类的静态代码块(static {})和静态变量的赋值操作。
    • 这是类加载的最后一步,JVM 会保证类的初始化在多线程环境下是线程安全的。

获取类结构信息

  1. 获取Class对象
// 方式 1:通过类名.class
Class<?> clazz = MyClass.class;

// 方式 2:通过对象的 getClass() 方法
MyClass obj = new MyClass();
Class<?> clazz = obj.getClass();

// 方式 3:通过 Class.forName() 动态加载类
Class<?> clazz = Class.forName("com.example.MyClass");
  1. 获取类的结构信息

    1. 获取类的基本信息
    //获取类名
    String className = clazz.getName();//全限定名(包名+类名)
    String simpleName = clazz.getSimpleName();//简单类名
    
    //获取包名
    Package pkg = clazz.getPackage();
    
    /**获取类的修饰符(public,final,absetract等)
    /*public : 1, private : 2, protected : 4, static : 8, final : 16
    /*synchronized : 32, volatile(表示字段是易变的): 64, transient(表示字段不会被序列化): 128, native(表示字段是本地方法): 256
    /*interface : 512, abstract : 1024, strictfp(表示类或者方法使用严格的浮点计算): 2048
    **/
    int modifiers = clazz.getModifiers();
    //将修饰符对应的二进制再转成对应的修饰符
    String modifierStr = Modifier.toString(modifiers);
    
    1. 获取类的字段(成员变量)信息
    // 获取所有 public 字段(包括父类的 public 字段)
    Field[] publicFields = clazz.getFields();
    // 获取所有字段(包括私有字段,但不包括父类的字段)
    Field[] allFields = clazz.getDeclaredFields();
    
    for (Field field : allFields) {
        String fieldName = field.getName();          // 字段名
        Class<?> fieldType = field.getType();        // 字段类型
        int fieldModifiers = field.getModifiers();   // 字段修饰符
        String modifierStr = Modifier.toString(fieldModifiers);
    }
    
    1. 获取类的方法信息
    // 获取所有 public 方法(包括父类的 public 方法)
    Method[] publicMethods = clazz.getMethods();
    
    // 获取所有方法(包括私有方法,但不包括父类的方法)
    Method[] allMethods = clazz.getDeclaredMethods();
    
    for (Method method : allMethods) {
        String methodName = method.getName();          // 方法名
        Class<?> returnType = method.getReturnType();  // 返回值类型
        int methodModifiers = method.getModifiers();   // 方法修饰符
        String modifierStr = Modifier.toString(methodModifiers);
        // 获取方法的参数类型
        Class<?>[] parameterTypes = method.getParameterTypes();
    }
    
    1. 获取类的构造器信息
    // 获取所有 public 构造器
    Constructor<?>[] publicConstructors = clazz.getConstructors();
    
    // 获取所有构造器(包括私有构造器)
    Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();
    
    for (Constructor<?> constructor : allConstructors) {
        String constructorName = constructor.getName();          // 构造器名
        int constructorModifiers = constructor.getModifiers();   // 构造器修饰符
        String modifierStr = Modifier.toString(constructorModifiers);
    
        // 获取构造器的参数类型
        Class<?>[] parameterTypes = constructor.getParameterTypes();
    
    }
    
    1. 获取类的父类和接口
    // 获取父类
    Class<?> superClass = clazz.getSuperclass();
    
    // 获取实现的接口
    Class<?>[] interfaces = clazz.getInterfaces();
    
  2. 反射操作示例

public class Main {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        //获取Student类的Class对象
        Class<Student> stdCls = Student.class;
        //获取Student类的有参构造器
        Constructor<Student> declaredConstructor = stdCls.getDeclaredConstructor(String.class, int.class);
        //通过有参构造器实例化对象
        Student student1 = declaredConstructor.newInstance("xiaomi", 20);
        //获取Student类的无参构造器
        Constructor<Student> constructor = stdCls.getConstructor();
        //通过无参构造器实例化对象
        Student student = constructor.newInstance();

        //获取Student类的name属性
        Field name = stdCls.getField("name");
        //设置student对象的name属性值
        name.set(student, "xiaoming");
        //获取Student类的age属性
        Field age = stdCls.getDeclaredField("age");
        //由于age是private修饰的,需要设置可访问性
        age.setAccessible(true);
        //设置student对象的age属性值
        age.set(student, 30);

        //获取Student类的hi方法并调用
        stdCls.getMethod("hi").invoke(student);
        //获取Student类的privateMethod方法
        Method privateMethod = stdCls.getDeclaredMethod("privateMethod", String.class, int.class);
        //由于privateMethod是private修饰的,需要设置可访问性
        privateMethod.setAccessible(true);
        //调用privateMethod方法
        int res = (int)privateMethod.invoke(student, "xiaomi", 30);
    }
}

class Student {
    public String name;
    private int age;

    public Student(){}

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void hi() {
        System.out.println("hi");
    }

    private int privateMethod(String name, int age) {
        ++this.age;
        return age;
    }

}
阅读量: 60

评论区

登录后发表评论

正在加载评论...
相关阅读

MyBatis-Plus讲解

# 介绍 ​ [MyBatis-Plus](https://github.com/baomidou/mybatis-plus) 是一个 [MyBatis](https://www.mybati...

139
0

Redis06_SpringBoot整合Redis源码解析

# SpringBoot整合 ## 默认客户端 Spring Boot在2.0版本后,将默认的Redis客户端从Jedis切换到了Lettuce 以下是主要原因: 1. **线程模型和连...

110
0

Spring Security实战-构建安全的Web应用

Spring Security 作为 Spring 生态系统中不可或缺的一员,提供了一套全面且可扩展的机制来处理身份验证和授权。本文将结合实际应用场景,深入剖析 Spring Security ...

123
0

Java单列集合学习

# Collection Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的 * List接口的实现类 * ArrayList:动态数组实现,允许重复元素 ...

93
1

一个草稿箱功能的实现

# **从零到一:一个“自动保存草稿”功能的实现** 在现代Web应用中,用户体验至上。没有什么比用户在精心编辑长篇内容后,因意外关闭浏览器或网络问题而丢失所有心血更令人沮丧的了(一位站友就...

121
1

正则表达式

# 正则表达式 正则表达式在搜索,替换,检查和解析字符串方面有着高效且简洁的优点 ## 基本概念和用法 1. 普通字符 普通字符就是普通字符,例如,正则表达式abc会匹配包含“abc”的...

100
0