【Java基础面经】Java 反射机制

文章目录


反射

反射就是程序运行时获取任意类的内部信息

反射(Reflection) 是 Java 语言的一种动态特性,它允许程序在运行时获取任意类的内部信息(如构造方法、成员变量、方法、注解等),并且可以动态地创建对象、调用方法、访问字段,甚至修改私有成员。


在没有反射的情况下,我们写代码时对类的引用是静态的、确定的。比如 new UserService(),编译器必须知道 UserService 这个类存在。但有些场景下,我们无法在编译时确定要使用的类,例如:

  • 框架开发:Spring 框架需要读取你写的类(如 @Controller、@Service),动态创建对象并管理它们。框架在编写时并不知道你的业务类名,只能在运行时通过反射去加载、实例化。

  • 配置文件驱动:将类名写在配置文件中,程序运行时读取配置,然后使用反射加载该类。

  • 动态代理:JDK 动态代理在运行时生成代理类,需要反射调用目标方法。

  • 开发工具:IDE 的代码提示、调试器查看变量值等,底层都用到了反射。

反射API

Java 的反射 API 提供了一系列的类和接口来操作 Class 对象。主要的类包括:

  • java.lang.Class:表示类的对象。提供了方法来获取类的字段、方法、构造函数等。
  • java.lang.reflect.Field:表示类的字段(属性)。提供了访问和修改字段的能力。
  • java.lang.reflect.Method:表示类的方法。提供了调用方法的能力。
  • java.lang.reflect.Constructor:表示类的构造函数。提供了创建对象的能力。

Class类

Java文件编译成.class文件后,JVM会读取.class文件,并将其转化为java.lang.Class类的实例。这样,每个运行中的Java类都对应一个Class对象。通过反射机制可以访问并修改这个类的属性和方法,让类在运行时具有动态特性。

Class类中的相关方法如下,

  • 获得类相关的方法
方法 用途
getClassLoader() 获得类的加载器
getDeclaredClasses() 返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有的)
forName(String className) 根据类名返回类的对象
newInstance() 创建类的实例
getName() 获得类的完整路径名字
  • 获得类中属性相关的方法
方法 用途
getField(String name) 获得某个公有的属性对象
getFields() 获得所有公有的属性对象
getDeclaredField(String name) 获得某个属性对象
getDeclaredFields() 获得所有属性对象
  • 获得类中属性相关的方法
方法 用途
getAnnotation(Class<A> annotationClass) 返回该类中与参数类型匹配的公有注解对象
getAnnotations() 返回该类所有的公有注解对象
getDeclaredAnnotation(Class<A>annotationClass) 返回该类中与参数类型匹配的所有注解对象
getDeclaredAnnotations() 返回该类所有的注解对象
  • 获得类中构造器相关的方法
方法 用途
getConstructor(Clas...<?>parameterTypes) 获得该类中与参数类型匹配的公有构造方法
getConstructors() 获得该类的所有公有构造方法
getDeclaredConstructor(Class...<?>parameterTypes) 获得该类中与参数类型匹配的构造方法
getDeclaredConstructors() 获得该类所有构造方法
  • 获得类中方法相关的方法
方法 用途
getMethod(String name, Class...<?>parameterTypes) 获得该类某个公有的方法
getMethods() 获得该类所有公有的方法
getDeclaredMethod(String name,Class...<?> parameterTypes) 获得该类某个方法
getDeclaredMethods() 获得该类所有方法

获取一个类的 Class 对象

Java 反射的入口是 java.lang.Class 类。获取一个类的 Class 对象有以下三种方式

  • 通过对象.getClass()
  • 通过类名.class
  • 通过 Class.forName("全限定类名")
java 复制代码
class Student {
    //私有属性name
    private String name = "bit";
    //公有属性age
    public int age = 18;
    //不带参数的构造⽅法
    public Student(){
        System.out.println("Student()");
    }
    private Student(String name,int age) {
        this.name = name;
        this.age = age;
        System.out.println("Student(String,name)");
    }
    private void eat(){
        System.out.println("i am eat");
    }
    public void sleep(){
        System.out.println("i am pig");
    }
    private void function(String str) {
        System.out.println(str);
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class TestDemo {
    public static void main(String[] args) {
        /*
        1.通过getClass获取Class对象
        */
        Student s1 = new Student();
        Class c1 = s1.getClass();
        /*
        2.直接通过 类名.class 的⽅式得到,该⽅法最为安全可靠,程序性能更⾼
        这说明任何⼀个类都有⼀个隐含的静态成员变量 class
        */
        Class c2 = Student.class;
        /*
        3、通过 Class 对象的 forName() 静态⽅法来获取,⽤的最多,
        但可能抛出 ClassNotFoundException 异常
        */
        Class c3 = null;
        try {
            //注意这⾥是类的全路径,如果有包需要加包的路径
            c3 = Class.forName("Student");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //⼀个类在 JVM 中只会有⼀个 Class 实例,即我们对上⾯获取的
        //c1,c2,c3进⾏ equals ⽐较,发现都是true
        System.out.println(c1.equals(c2));
        System.out.println(c1.equals(c3));
        System.out.println(c2.equals(c3));
    }
}

反射的使用

创建对象调用的是Student(),输出默认属性Student{name='bit',age=18}

java 复制代码
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectClassDemo {
    // 创建对象
    public static void reflectNewInstance() {
        try {
            Class<?> classStudent = Class.forName("Student");
            Object objectStudent = classStudent.newInstance();
            Student student = (Student) objectStudent;
            System.out.println("获得学⽣对象:"+student);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    // 反射私有的构造⽅法 屏蔽内容为获得公有的构造⽅法
    public static void reflectPrivateConstructor() {
        try {
            Class<?> classStudent = Class.forName("Student");
            //注意传⼊对应的参数
            Constructor<?> declaredConstructorStudent =
            classStudent.getDeclaredConstructor(String.class,int.class);
            //Constructor<?> declaredConstructorStudent = classStudent.getConstructor();
            //设置为true后可修改访问权限
            declaredConstructorStudent.setAccessible(true);
            Object objectStudent = declaredConstructorStudent.newInstance("⾼博",15);
            //Object objectStudent = declaredConstructorStudent.newInstance();
            Student student = (Student) objectStudent;
            System.out.println("获得私有构造哈数且修改姓名和年龄:"+student);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    // 反射私有属性
    public static void reflectPrivateField() {
        try {
            Class<?> classStudent = Class.forName("Student");
            Field field = classStudent.getDeclaredField("name");
            field.setAccessible(true);
            //可以修改该属性的值
            Object objectStudent = classStudent.newInstance();
            Student student = (Student) objectStudent;
            field.set(student,"⼩明");
            String name = (String) field.get(student);
            System.out.println("反射私有属性修改了name:"+ name);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    // 反射私有⽅法
    public static void reflectPrivateMethod() {
        try {
            Class<?> classStudent = Class.forName("Student");
            Method methodStudent =
            classStudent.getDeclaredMethod("function",String.class);
            System.out.println("私有⽅法的⽅法名为:"+methodStudent.getName());
            //私有的⼀般都要加
            methodStudent.setAccessible(true);
            Object objectStudent = classStudent.newInstance();
            Student student = (Student) objectStudent;
            methodStudent.invoke(student,"我是给私有的function函数传的参数");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    public static void main(String[] args) {
        //reflectNewInstance();
        //reflectPrivateConstructor();
        //reflectPrivateField();
        reflectPrivateMethod();
    }
}

反射的优缺点

优点:

  • 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法。
  • 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力。
  • 反射已经运用在了很多流行框架如:Struts、Hibernate、Spring 等等。

缺点:

  • 使用反射会有效率问题,会导致程序效率降低;
  • 反射技术绕过了源代码的技术,因而会带来维护问题,反射代码比相应的直接代码更复杂。

反射在实际框架中的应用

  1. JDBC 加载数据库驱动:反射 + 类加载器

通过 Class.forName 动态加载驱动类

java 复制代码
Class.forName("com.mysql.cj.jdbc.Driver");
  1. Spring IoC 容器:反射 + 注解 + 动态代理

Spring 在启动时会扫描包路径下的类,读取 @Component、@Service 等注解,然后通过反射创建 Bean 实例,并通过反射注入依赖(@Autowired)

  1. 配置文件驱动的应用(如 Spring Boot 的自动配置)

读取 META-INF/spring.factories 中的类名列表,通过反射实例化并注册

相关推荐
YXWik62 小时前
Langchain4j(1)基础对话+连续对话+工具调用 + 流式响应+结构化 JSON 输出
java
广州山泉婚姻2 小时前
VSCode中切换Python虚拟环境失败的原因
python
m0_694845572 小时前
UVdesk部署教程:企业级帮助台系统实践
服务器·开发语言·后端·golang·github
泉飒2 小时前
C2001: 常量中有换行符-QT解决办法-逆向思路
开发语言·qt
96772 小时前
什么是 Thymeleaf?
java
Dream_sky分享2 小时前
找类中字段属性不同工具类
java
Ulyanov2 小时前
从零构建现代化Python音频播放器:ttk深度应用与皮肤系统设计
python·架构·音视频·数据可视化
ghie90902 小时前
基于学习的模型预测控制(LBMPC)MATLAB实现指南
开发语言·学习·matlab
咚为2 小时前
Rust 经典面试题255道
开发语言·面试·rust