java反射基础面试题,以及代码示例

1:什么是 Java 反射?反射的核心作用是什么?

Java 反射是指程序在运行时可以获取自身的信息,并且可以操作类或对象的属性、方法、构造器等成员。核心作用是打破封装性,在运行时动态操作类的成员,这也是框架(如 Spring、MyBatis)的核心底层技术。

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

// 定义测试类
class User {
    private String name;
    public int age;

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

    public void sayHello() {
        System.out.println("Hello, " + name);
    }

    private String getName() {
        return name;
    }
}

public class ReflectionDemo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式1:通过Class.forName获取Class对象
        Class<?> clazz = Class.forName("User");
        
        // 1. 获取类名
        System.out.println("类的全限定名:" + clazz.getName());
        System.out.println("类的简单名:" + clazz.getSimpleName());
        
        // 2. 获取所有公有字段
        Field[] publicFields = clazz.getFields();
        System.out.println("\n公有字段:");
        for (Field field : publicFields) {
            System.out.println(field.getName() + " - " + field.getType().getName());
        }
        
        // 3. 获取所有构造器(包括私有)
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        System.out.println("\n所有构造器:");
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        
        // 4. 获取所有方法(包括私有)
        Method[] methods = clazz.getDeclaredMethods();
        System.out.println("\n所有方法:");
        for (Method method : methods) {
            System.out.println(method.getName() + " - " + method.getReturnType().getName());
        }
    }
}

image

2:如何通过反射调用私有方法和修改私有字段?

反射可以突破访问修饰符的限制,通过setAccessible(true)关闭访问检查,从而操作私有成员。这是反射最常用的场景,也是框架实现依赖注入、ORM 映射的关键。

import java.lang.reflect.Field;
import java.lang.reflect.Method;

class Person {
    private String name = "默认名称";
    private int age = 18;

    private String getInfo() {
        return "姓名:" + name + ",年龄:" + age;
    }

    private void setName(String name) {
        this.name = name;
    }
}

public class ReflectionDemo2 {
    public static void main(String[] args) throws Exception {
        // 1. 获取Class对象
        Class<?> clazz = Person.class;
        // 2. 创建实例(即使构造器私有也可以,这里用默认构造器)
        Object person = clazz.newInstance();

        // ========== 操作私有字段 ==========
        // 获取私有字段name
        Field nameField = clazz.getDeclaredField("name");
        // 关闭访问检查
        nameField.setAccessible(true);
        // 修改私有字段值
        nameField.set(person, "张三");
        
        // 获取私有字段age
        Field ageField = clazz.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(person, 25);

        // ========== 调用私有方法 ==========
        // 获取私有方法getInfo(无参数)
        Method getInfoMethod = clazz.getDeclaredMethod("getInfo");
        getInfoMethod.setAccessible(true);
        // 调用方法
        String info = (String) getInfoMethod.invoke(person);
        System.out.println("调用私有方法getInfo的结果:" + info);

        // 获取私有方法setName(有参数)
        Method setNameMethod = clazz.getDeclaredMethod("setName", String.class);
        setNameMethod.setAccessible(true);
        // 调用方法
        setNameMethod.invoke(person, "李四");
        // 再次调用getInfo查看结果
        info = (String) getInfoMethod.invoke(person);
        System.out.println("修改姓名后调用getInfo的结果:" + info);
    }
}

image

3:如何通过反射创建对象?对比不同创建方式的区别

反射创建对象有两种核心方式:

image

import java.lang.reflect.Constructor;

class Student {
    private String name;
    private int id;

    // 无参构造器
    public Student() {
        this.name = "未知";
        this.id = 0;
    }

    // 有参构造器(私有)
    private Student(String name, int id) {
        this.name = name;
        this.id = id;
    }

    @Override
    public String toString() {
        return "Student{name='" + name + "', id=" + id + "}";
    }
}

public class ReflectionDemo3 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Student.class;

        // 方式1:使用Class.newInstance()(Java 9+已过时)
        Student student1 = (Student) clazz.newInstance();
        System.out.println("方式1创建的对象:" + student1);

        // 方式2:使用Constructor.newInstance()(推荐)
        // 获取私有有参构造器
        Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
        constructor.setAccessible(true);
        // 调用有参构造器创建对象
        Student student2 = (Student) constructor.newInstance("小明", 1001);
        System.out.println("方式2创建的对象:" + student2);
    }
}

image

4:反射的优缺点是什么?在实际开发中哪些场景会用到反射?

反射是一把 “双刃剑”,既有灵活性,也有性能和安全的代价,需要结合实际场景合理使用。

import java.lang.reflect.Field;

// 自定义注解模拟Spring的@Autowired
@interface Autowired {}

// 服务类
class UserService {
    public void say() {
        System.out.println("UserService执行方法");
    }
}

// 控制器类
class UserController {
    // 标记需要注入的字段
    @Autowired
    private UserService userService;

    public void test() {
        userService.say();
    }
}

// 模拟Spring的依赖注入实现
public class ReflectionDemo4 {
    public static void main(String[] args) throws Exception {
        // 1. 创建控制器实例
        UserController controller = new UserController();
        Class<?> clazz = controller.getClass();

        // 2. 遍历所有字段,找到带有@Autowired注解的字段
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Autowired.class)) {
                // 3. 关闭访问检查
                field.setAccessible(true);
                // 4. 通过反射创建依赖对象并注入
                Class<?> fieldType = field.getType();
                Object instance = fieldType.newInstance();
                field.set(controller, instance);
            }
        }

        // 5. 测试注入是否成功
        controller.test();
    }
}

image

image

5:如何通过反射获取泛型类型信息?请编写代码演示

Java 的泛型在编译期会被擦除(类型擦除),但反射可以通过ParameterizedType获取泛型的实际类型参数,这是很多框架(如 MyBatis)处理泛型的关键。

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

class GenericDemo {
    // 定义不同泛型类型的字段
    private List<String> stringList;
    private Map<Integer, User> userMap;
    private List<? extends Number> numberList;
}

public class ReflectionDemo5 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = GenericDemo.class;

        // 1. 获取stringList字段的泛型信息
        Field stringListField = clazz.getDeclaredField("stringList");
        Type stringListType = stringListField.getGenericType();
        // 判断是否为参数化类型(即带有泛型的类型)
        if (stringListType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) stringListType;
            // 获取泛型实际类型参数
            Type[] actualTypeArguments = pt.getActualTypeArguments();
            System.out.println("stringList的泛型类型:" + actualTypeArguments[0].getTypeName());
        }

        // 2. 获取userMap字段的泛型信息
        Field userMapField = clazz.getDeclaredField("userMap");
        Type userMapType = userMapField.getGenericType();
        if (userMapType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) userMapType;
            Type[] actualTypeArguments = pt.getActualTypeArguments();
            System.out.println("userMap的key泛型类型:" + actualTypeArguments[0].getTypeName());
            System.out.println("userMap的value泛型类型:" + actualTypeArguments[1].getTypeName());
        }

        // 3. 获取numberList字段的泛型信息
        Field numberListField = clazz.getDeclaredField("numberList");
        Type numberListType = numberListField.getGenericType();
        if (numberListType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) numberListType;
            Type[] actualTypeArguments = pt.getActualTypeArguments();
            System.out.println("numberList的泛型类型:" + actualTypeArguments[0].getTypeName());
        }
    }
}

image

posted @ 2026-03-12 10:51  白鹿为溪  阅读(34)  评论(0)    收藏  举报