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());
}
}
}

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);
}
}

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

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);
}
}

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();
}
}


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());
}
}
}


浙公网安备 33010602011771号