java中的反射详解

反射

前段时间,网易面试,面试官问我关于反射方面的知识,回答的不是特别好,今天特此补充学习一下~~~

简介

反射是java语言被视为动态或者准动态语言的一个关键性质,这个机制允许程序在运行时通过Reflection API获取任何一个已知名称的class的内部信息,包括修饰符(public static等)、字段或者方法的所有信息、父类以及实现的接口,并可在运行时改变字段的内容或者调用方法。
简单的说,反射机制可以在运行时动态的获取任意一个类的属性和方法,并可以对任意一个属性进行赋值,调用任意一个方法。(即便是私有方法,只要设置setAccessible)

Jdk中的Reflection API

这里写图片描述

Java反射案例

在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判段任意一个类所具有的成员变量和方法
在运行时调用任一个对象的任意一个方法和属性

在使用java反射功能时,首先要获取该类的Class对象,再通过Class对象获取其他对象。

1 获取Class对象
Student s = new Student();
//获取Class对象
//1 getClass方法
Class<?> clazz1 = s.getClass();
System.out.println(clazz1); //class com.wy.reflect.Student
//2 .class语法
System.out.println(Student.class); //class com.wy.reflect.Student
//3 Class.forName()
Class<?> clazz2 = Class.forName("com.wy.reflect.Student"); //class com.wy.reflect.Student
System.out.println(clazz2);
2 获取类的fields

通过反射机制获取某个类的某个属性,然后改变对应于这个类的某个实例的该属性值。
public FieldgetField(String name)
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口
的指定公共成员字段
public Field[] getFields()
返回一个包含某些 Field 对象的数组,这些对象反映此 Class
对象所表示的类或接口的所有可访问公共字段
public FieldgetDeclaredField(Stringname) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类
或接口的指定已声明字段
public Field[] getDeclaredFields()
返回 Field 对象的一个数组,这些对象反映此 Class 对象所
表示的类或接口所声明的所有字段

Field[] fields = clazz1.getFields();
        System.out.println("---公共成员字段------");
        for(Field f : fields) {
            System.out.println(f);
        }
        System.out.println("---所有成员字段------");
        fields = clazz1.getDeclaredFields();
        for(Field f : fields) {
            System.out.println(f+" ");
        }

返回的结果:

---公共成员字段------
public int com.wy.reflect.Student.score
public java.lang.String com.wy.reflect.Person.age
---所有成员字段------
private java.lang.String com.wy.reflect.Student.height 
public int com.wy.reflect.Student.score

总结:
getFileds返回的声明为public的属性,包括父类的属性。
getDeclaredFields返回了类中所有的属性,不包括父类的。

3 获取类的Method

通过反射机制得到某个类的某个方法,然后调用对应于这个类的某个实例的该方法。
public Method getMethod(String name,Class

Method[] methods = clazz1.getMethods();
        System.out.println("---公共成员方法------");
        for(Method m : methods) {
            System.out.println(m);  //包含父类中public方法
        }
        methods = clazz1.getDeclaredMethods();
        System.out.println("---所有成员方法------");
        for(Method m : methods) {  
            System.out.println(m);  //不包含父类中方法
        }

输出结果:
—公共成员方法——

public void com.wy.reflect.Student.getScore()
public void com.wy.reflect.Person.getName()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
---所有成员方法------
private void com.wy.reflect.Student.setScore(int)
public void com.wy.reflect.Student.getScore()
private void com.wy.reflect.Student.getHeight()
4 获取类的构造器

通过反射机制获取某个类的构造器,然后调用该构造器创建该类的一个实例。
public Constructor getConstructor(Class

Constructor<?>[] constructors = clazz1.getConstructors();
        System.out.println("---公共构造方法------");
        for(Constructor<?> c : constructors) {
            System.out.println(c);  //不包含父类中的构造方法
        }
        constructors = clazz1.getDeclaredConstructors();
        System.out.println("---所有构造方法------");
        for(Constructor<?> c : constructors) {
            System.out.println(c);  //不包含父类中的构造方法
        }

结果:

---公共构造方法------
public com.wy.reflect.Student()
---所有构造方法------
public com.wy.reflect.Student()
private com.wy.reflect.Student(java.lang.String)
5 创建类的实例对象
  • 调用Class对象的newInstance()方法

该方法会调用对象的默认构造器,如果没有默认构造器,会调用失败。

//有默认构造器,且为public
System.out.println(clazz1.newInstance());
  • 调用默认Constructor对象的newInstance方法

    Constructor<?> con = clazz1.getConstructor();
    System.out.println(con.newInstance());
    
  • 调用带参的构造方法

Constructor<?> con2 = clazz1.getConstructor(int.class);
Student stu = (Student) con2.newInstance(12);
System.out.println(stu + " " + stu.score);

注意:这里的构造器方法必须是public

6 调用类的函数

通过反射获取类的Method对象,调用Field的Invoke方法调用函数。

Method setScore = clazz1.getDeclaredMethod("setScore",int.class);
        Student s = (Student) clazz1.newInstance();
        //因为setScore是private方法,必须设置为true 表示在反射时取消java语言的反射检查
        setScore.setAccessible(true);
        setScore.invoke(s, 100);
        System.out.println(s.score);
7 设置 获取类的属性值

通过反射获取类的Field对象,调用Field方法设置或获取值

//设置
        Field f = clazz1.getDeclaredField("height");
        f.setAccessible(true);
        f.set(s, "100");
        //获取
        System.out.println(f.get(s));
<think>嗯,用户想了解Java反射机制的详细解释以及如何在代码和实际项目中使用。首先,我需要回顾反射的基本概念。根据引用[3],反射是把Java类的各个成分映射成对象,比如成员变量、方法、构造方法等。反射允许程序在运行时动态地获取类的信息并操作类或对象。 接下来,应该解释反射的核心原理。Java反射机制主要依赖于Class类,每个类被加载后都会生成一个Class对象,里面包含类的结构信息。用户可能需要知道如何获取Class对象,比如通过类名.class、对象.getClass()或Class.forName()方法。例如,Class<?> clazz = Class.forName("com.example.User"); 这样可以动态加载类。 然后,需要介绍反射的主要功能,比如获取构造方法、成员变量和方法。引用[2]中的代码示例展示了如何获取构造方法数组,这可以作为例子。比如使用getDeclaredConstructors()获取所有构造方法,并通过newInstance()创建实例。同时,要注意访问私有成员时需要setAccessible(true),这里可以提到反射的灵活性但也可能破坏封装性。 在代码实现部分,应该分步骤说明如何动态创建对象、调用方法和访问字段。例如,动态加载类、获取指定构造方法并实例化对象,调用setter和getter方法,以及修改私有字段的值。这些例子能帮助用户理解反射的实际应用。 实际应用场景方面,引用[1]提到反射虽然看似与日常业务无关,但实际广泛应用。比如框架设计(Spring的IoC)、动态代理(AOP)、注解处理和数据库ORM映射。需要具体说明每个场景中反射的作用,比如Spring通过反射创建Bean,Hibernate通过反射映射实体类和数据库表。 还要讨论反射的优缺点。优点包括灵活性和动态性,缺点则是性能开销、安全限制和代码复杂度。引用[1]和[3]都提到了反射的基础性和潜在的性能问题,需要提醒用户权衡使用。 最后,总结反射的使用建议,强调在必要时合理使用,并注意性能优化,比如缓存Class对象。用户可能还关心如何避免反射的缺点,可以提到使用反射时遵循最佳实践,如尽量减少反射调用次数。 检查是否需要生成相关问题,根据示例,用户要求生成相关问题,所以最后需要添加相关问题部分,比如反射的应用场景、优缺点、性能优化方法等。确保问题与内容相关,帮助用户深入理解。 整个过程中,要确保使用正确的中文表达,行内公式如变量用$...$,但这里反射相关的内容可能较少涉及数学公式,但如果有涉及的地方需要正确应用。例如,如果有动态代理的示例,可能需要代码块展示,但根据用户提供的引用内容,主要集中于类、方法的结构,所以可能不需要数学公式。确保代码示例正确,如Python示例中的语法是否正确,但用户的问题是关于Java的,所以应使用Java代码示例。 需要确认所有引用标记正确对应,引用[1]、[2]、[3]的内容是否准确应用到回答中。例如,引用[1]提到反射的优缺点,引用[2]提供代码示例,引用[3]解释反射的基本概念。在回答中适当的位置添加引用标识,如讨论优缺点时引用[1]和[3],代码示例引用[2]。 最后,确保回答结构清晰,分点讲解原理、核心功能、代码实现、应用场景和优缺点,并附上相关问题。使用用户提供的示例中的格式,比如代码块使用反引号,引用标识放在相关段落的末尾。</think>### Java反射机制详解及其应用案例 #### 一、反射机制的基本原理 Java反射机制允许程序在**运行时**动态获取类的信息(如成员变量、方法、构造方法等)并操作类或对象[^3]。其核心是通过`Class`类实现:每个类加载后,JVM会为其生成一个唯一的`Class`对象,存储类的元数据。例如: ```java Class<?> clazz = Class.forName("com.example.User"); // 动态加载类 ``` #### 二、反射的核心功能 1. **获取构造方法** 可通过`getDeclaredConstructors()`获取所有构造方法,并通过`newInstance()`创建实例: ```java Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class); Object user = constructor.newInstance("Alice", 25); // 调用有参构造方法[^2] ``` 2. **操作成员变量** 可读写对象的字段值,包括私有字段: ```java Field ageField = clazz.getDeclaredField("age"); ageField.setAccessible(true); // 突破私有权限限制 ageField.set(user, 30); // 修改字段值 ``` 3. **调用方法** 动态执行对象的方法: ```java Method setNameMethod = clazz.getMethod("setName", String.class); setNameMethod.invoke(user, "Bob"); // 调用setName方法 ``` #### 三、实际应用场景 1. **框架设计** - **Spring的IoC容器**:通过反射创建Bean实例并注入依赖[^1]。 - **Hibernate ORM**:利用反射将数据库字段映射到实体类的属性。 2. **动态代理** AOP(面向切面编程)通过反射生成代理对象,实现日志记录或事务管理: ```java Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxy, method, args) -> { System.out.println("执行方法前记录日志"); return method.invoke(target, args); } ); ``` 3. **注解处理器** 反射可读取并处理注解信息,例如JUnit通过`@Test`注解标记测试方法。 #### 四、反射的优缺点 | **优点** | **缺点** | |-----------------------------------|-----------------------------------| | 1. 动态加载类,提高代码灵活性 | 1. 性能开销大(比直接调用慢10倍以上) | | 2. 突破封装性,访问私有成员 | 2. 可能引发安全问题(如绕过权限检查) | | 3. 支持泛型擦除后获取类型参数 | 3. 代码可读性降低 | #### 五、使用建议 - **适用场景**:框架开发、动态配置、插件化系统。 - **规避缺点**:通过缓存`Class`对象、限制反射调用频率优化性能[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值