Java反射详细介绍

发布于:2025-05-13 ⋅ 阅读:(14) ⋅ 点赞:(0)

 的反射(Reflection)是一种强大的机制,允许程序在运行时动态获取类的信息、操作类的成员(属性、方法、构造器),甚至修改类的行为。它是框架开发(如 Spring、MyBatis)、单元测试工具(如 JUnit)的核心技术之一。

一、认识反射

反射的核心是在运行时获取类的元数据(类的结构信息),突破了传统编程 “编译时确定类型” 的限制。例如:

运行时判断任意对象的所属类;

运行时构造任意类的对象;

运行时获取 / 修改类的属性、调用类的方法(包括私有成员)

运行时处理注解。

二、Class 类详解

在  中,Class 类是反射的入口。每个类被加载到 JVM 时,会生成唯一的 Class 对象,存储该类的所有元数据(如类名、父类、接口、属性、方法等)。

2.1 获取 Class 对象的 3 种方式

// 方式1:通过 类名.class(编译时已知类)

Class<String> stringClass = String.class;

// 方式2:通过 对象.getClass()(已知对象)

String str = "hello";Class<? extends String> strClass = str.getClass();

// 方式3:通过 Class.forName("全限定类名")(动态加载,最常用)

try {

Class<?> userClass = Class.forName("com.example.User"); }

catch (ClassNotFoundException e) {

e.printStackTrace();

}

2.2 Class 类的常用方法

方法

说明

getName()

获取类的全限定名(如 .lang.String)

getSimpleName()

获取类的简单名(如 String)

getSuperclass()

获取父类的 Class 对象

getInterfaces()

获取实现的接口数组

getFields()

获取所有公有属性(含父类)

getDeclaredFields()

获取所有属性(含私有,不含父类)

getMethods()

获取所有公有方法(含父类)

getDeclaredMethods()

获取所有方法(含私有,不含父类)

getConstructors()

获取所有公有构造器

getDeclaredConstructors()

获取所有构造器(含私有)

三、Class 类与多态

多态的本质是 “父类引用指向子类对象”,但反射可以突破多态的表象,直接操作子类或父类的真实信息。

示例:通过反射获取多态对象的真实类信息
假设有继承关系:Animal(父类)→ Dog(子类)。

class Animal {

 public void eat() {

 System.out.println("Animal eat");

  } 

}

class Dog extends Animal { 

@Override 

public void eat() { 

System.out.println("Dog eat"); 

}

 }

public class PolymorphismDemo {

    public static void main(String[] args) {

        Animal animal = new Dog(); // 多态:父类引用指向子类对象

        

        // 传统方式调用方法(表现多态)

        animal.eat(); // 输出:Dog eat

        

        // 反射获取真实类的信息

        Class<?> realClass = animal.getClass(); 

        System.out.println("真实类名:" + realClass.getSimpleName()); // 输出:Dog

        

        // 反射调用父类的方法(绕过多态)

        try {

            Method parentEat realClass.getSuperclass().getMethod("eat");

            parentEat.invoke(animal); // 输出:Animal eat(调用了父类的原始方法)

        } catch (Exception e) {

            e.printStackTrace();

        }

}

}

输出结果:

Dog eat

真实类名:Dog

Animal eat

四、反射创建类对象

通过反射可以动态创建类的实例,即使类的构造器是私有的(需设置 setAccessible(true))。

4.1 无参构造创建对象

class User {

    private String name;

    public User() { System.out.println("无参构造被调用"); }

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

}

public class CreateObjectDemo {

    public static void main(String[] args) {

        try {

            // 1. 获取User的Class对象

            Class<?> userClass = Class.forName("com.example.User");

            

            // 2. 通过无参构造创建实例(等价于 new User())

            User user = (User) userClass.getDeclaredConstructor().newInstance();

        } catch (Exception e) {

            e.printStackTrace();

        }

}

}

输出:无参构造被调用

4.2 有参构造创建对象

public class CreateObjectWithArgsDemo {

    public static void main(String[] args) {

        try {

            Class<?> userClass = Class.forName("com.example.User");

            

            // 获取有参构造器(参数类型为String)

            Constructor<?> constructor = userClass.getDeclaredConstructor(String.class);

            

            // 创建实例(等价于 new User("xxx"))

            User user = (User) constructor.newInstance("xxx");

        } catch (Exception e) {

            e.printStackTrace();

        }

}

}

4.3 私有构造器创建对象(突破访问限制)

class SecretClass {

private SecretClass() { System.out.println("私有构造被调用"); 

}

}

public class CreatePrivateObjectDemo {

    public static void main(String[] args) {

        try {

            Class<?> secretClass = Class.forName("com.example.SecretClass");

            

            // 获取私有构造器

            Constructor<?> privateConstructor = secretClass.getDeclaredConstructor();

            

            // 允许访问私有成员(关键!)

            privateConstructor.setAccessible(true);

            

            // 创建实例

            SecretClass instance = (SecretClass) privateConstructor.newInstance();

        } catch (Exception e) {

            e.printStackTrace();

        }

}

}

输出:私有构造被调用

五、反射调用类方法

通过反射可以调用任意对象的方法(包括私有方法),甚至可以调用未实现的方法(动态代理的基础)。

5.1 调用公有方法

class Calculator {

public int add(int a, int b) { return a + b; }

}

public class InvokeMethodDemo {

    public static void main(String[] args) {

        try {

            Calculator calc = new Calculator();

            Class<?> calcClass = calc.getClass();

            

            // 获取add方法(参数类型为int, int)

            Method addMethod = calcClass.getMethod("add", int.class, int.class);

            

            // 调用方法(等价于 calc.add(3, 5))

            int result = (int) addMethod.invoke(calc, 3, 5);

            System.out.println("计算结果:" + result); // 输出:8

        } catch (Exception e) {

            e.printStackTrace();

        }

}

}

5.2 调用私有方法(突破访问限制)

class PrivateMethodClass {

    private String formatName(String name) {

        return "Hello, " + name + "!";

}

}

public class InvokePrivateMethodDemo {

    public static void main(String[] args) {

        try {

            PrivateMethodClass obj = new PrivateMethodClass();

            Class<?> clazz = obj.getClass();

            

            // 获取私有方法(方法名、参数类型)

            Method privateMethod = clazz.getDeclaredMethod("formatName", String.class);

            

            // 允许访问私有成员

            privateMethod.setAccessible(true);

            

            // 调用方法(等价于 obj.formatName("xxx"))

            String result = (String) privateMethod.invoke(obj, "xxx");

            System.out.println(result); // 输出:Hello, xxx!

        } catch (Exception e) {

            e.printStackTrace();

        }

}

}

六、反射修改类属性

通过反射可以直接修改对象的属性值(包括私有属性),甚至绕过 setter 方法。

6.1 修改公有属性

class Book {

public String title = "默认书名";

}

public class ModifyFieldDemo {

    public static void main(String[] args) {

        try {

            Book book = new Book();

            Class<?> bookClass = book.getClass();

            

            // 获取公有属性title

            Field titleField = bookClass.getField("title");

            

            // 修改属性值(等价于 book.title = "反射详解")

            titleField.set(book, "反射详解");

            

            System.out.println(book.title); // 输出:反射详解

        } catch (Exception e) {

            e.printStackTrace();

        }

}

}

6.2 修改私有属性(突破访问限制)

class User {

    private String password = "123456";}

public class ModifyPrivateFieldDemo {

    public static void main(String[] args) {

        try {

            User user = new User();

            Class<?> userClass = user.getClass();

            

            // 获取私有属性password

            Field passwordField = userClass.getDeclaredField("password");

            

            // 允许访问私有成员

            passwordField.setAccessible(true);

            

            // 修改属性值(等价于 user.password = "new_password")

            passwordField.set(user, "new_password");

            

            // 验证修改结果

            System.out.println("新密码:" + passwordField.get(user)); // 输出:new_password

        } catch (Exception e) {

            e.printStackTrace();

        }

}

}

七、类加载器

类加载器(Class Loader)负责将 .class 文件加载到 JVM 中,生成对应的 Class 对象。 采用双亲委派模型,确保类的唯一性和安全性。

7.1 类加载器的层级

引导类加载器(Bootstrap Class Loader):加载 JDK 核心类(如 .lang.*),由 C++ 实现,无法通过  代码获取。

扩展类加载器(Extension Class Loader):加载 jre/lib/ext 目录下的 JAR 包。

应用类加载器(Application Class Loader):加载用户项目中的类(classpath 下的类)。

7.2 示例:查看类的加载器

public class ClassLoaderDemo {

    public static void main(String[] args) {

        // 获取String类的加载器(引导类加载器,输出null)

        ClassLoader stringLoader = String.class.getClassLoader();

        System.out.println("String类的加载器:" + stringLoader); // 输出:null

        

        // 获取当前类的加载器(应用类加载器)

        ClassLoader selfLoader = ClassLoaderDemo.class.getClassLoader();

        System.out.println("当前类的加载器:" + selfLoader); 

        // 输出:sun.misc.Launcher$AppClassLoader@18b4aac2

        

        // 获取应用类加载器的父加载器(扩展类加载器)

        ClassLoader parentLoader = selfLoader.getParent();

        System.out.println("父加载器:" + parentLoader); 

        // 输出:sun.misc.Launcher$ExtClassLoader@1b6d3586

}

}

7.3 双亲委派模型的作用

当加载一个类时,类加载器会先委托父类加载器尝试加载,直到引导类加载器。如果父类无法加载,才由当前类加载器加载。
好处:避免重复加载,防止核心类被篡改(如自定义 .lang.String 不会被加载)。

反射是  的 “动态之魂”,但过度使用会降低代码可读性和安全性(如破坏封装性)。实际开发中,框架(如 Spring)已封装了反射的复杂操作,开发者只需理解原理即可。建议结合源码(如 Spring 的 BeanFactory)深入学习反射的应用。


网站公告

今日签到

点亮在社区的每一天
去签到