Java基础-反射

什么是反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为反射。简单地说:反射就是根据Java类的实例化对象或Java的类名来获取类的信息(类名,类成员,包名,类方法),然后将这些信息映射为一个个对象(因为在Java中,万物皆对象,类名,类成员,包名,类方法 都是对象).

Class类

因为反射机制是要在程序运行时获取类的属性,而Java类在运行时是被编译成字节码文件的,所以要使用反射,必须获取Java类的字节码文件,而Class类就用来获取Java类的字节码文件. Class类是没有公共构造函数的,无法由我们手动地创建,Class类对象是在JVM将class文件(要分清class和Class,class文件是指一个Java类经过编译后产生的文件)读入内存的时候自动创建的

需要注意的是每个Java类在编译后有且只有一个class文件,所以每一个Java类的Class对象都是只有一个–无论Class对象是由实例对象(即使不是同一个对象)获取还是由Java类获取,这个Java类的Class对象都是同一个

如果还是不太清楚的话,可以看下面的例子:

public static void main(String[] args) throws Exception {
	String str1 = "abc";
	String str2 = "aaa";
	//通过对象.getClass()获取
	Class class1 = str1.getClass();
	// 通过类.class获取
	Class class2 = String.class;
	//通过Class.forName("类名")或
	Class class3 = Class.forName("java.lang.String");
	Class class4 = str2.getClass();
	System.out.println(class1==class2);
	System.out.println(class2==class3);
	System.out.println(class1==class4);
    }

上面三个例子都是true,他们的Class类对象都是同一个.
同时也可以看到获取一个类的Class对象有三种方式:

  1. Object . getClass();
  2. Java类.class , (包括基本数据类型)
  3. forName(String className)

使用反射获取构造方法

创建一个User类来演示如何使用反射来获取类的构造方法
public class User {
    //类成员属性
    private String name;
    private int password;
   //无参构造函数
    public User(){
	System.out.println("null");
    }
    
    //一个参数的有参构造函数
    public User(String name){
	this.name = name;
	System.out.println(name);
    }
    
    private User(int password){
	this.password = password;
	System.out.println(password);
    }
    
    //两个参数的有参构造函数
    public User(String name,int password){
	this.password = password;
	this.name = name;
	System.out.println(name+"-"+password);
    }
}

使用Class对象获取Java类的所有构造函数

 public static void main(String[] args) throws Exception {
	
	//获取User类的Class对象
	Class userClass = User.class;
	//获取User类的所有构造方法(包括公有和私有的)
	Constructor[] constructors = userClass.getConstructors();
	for (Constructor constructor : constructors) {
	    System.out.println(constructor);
	}
}
public cn.QEcode.day01.User(java.lang.String,int)
public cn.QEcode.day01.User(java.lang.String)
public cn.QEcode.day01.User()

Class类还提供了其他获取构造函数的方法:
public Constructor[] getConstructors():所有公有构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
public Constructor getConstructor(Class… parameterTypes):获取公有的构造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):获取构造方法包括(私有,受保护、默认、公有)

使用反射调用构造方法

//获取User类中,参数为string型的构造函数
	Constructor constructor = userClass.getConstructor(String.class);
	//调用构造方法,实例化一个对象
	Object user1 = constructor.newInstance("user");

在使用反射调用构造方法的时候要注意:

  • 使用反射来调用构造方法,在实例化对象时,如果调用的构造方法参数类型和获取的构造方法的参数类型不一致时,Java是不会报错的,只有运行的时候,才会抛出异常
   //获取User类中,参数为string型的构造方法
   Constructor constructor = userClass.getConstructor(String.class);
   //调用构造方法,但传入的参数类型为double型,可以编译,但运行时报错
   Object user2 = constructor.newInstance(1.0);
  • 调用私有的构造方法需要暴力访问
	Constructor constructor = 	userClass.getConstructor(int.class);
	//暴力访问
	constructor.setAccessible(true);
	Object user = constructor.newInstance(1);

使用反射获取类属性

和获取构造方法一样,Class类为我们提供四种获取类属性的方法 Field[] getFields():获取所有的 ==公有== 属性 Field[] getDeclaredFields():获取所有属性,包括:私有、受保护、默认、公有 public Field getField(String fieldName):获取某个==公有的==属性; public Field getDeclaredField(String fieldName):获取某个属性(可以是私有的)

为了方便演示,将User类中的name改为public.

public String name;

获取所有公有属性

//获取User类的Class对象
	Class userClass = User.class;
	//获取公有属性
	Field[] field =  userClass.getFields();
	
	for (Field f : field) {
	    System.out.println(f);
	}
public java.lang.String cn.QEcode.day01.User.name

设置字段的值
Field.public void set(Object obj,Object value):
第一个参数:要赋值的实例化对象
第二个参数:要赋的值
这里要理解为什么传入的是实例化对象?
因为除非是static属性,否则每个实例化对象的属性都不是同一个,所以我们要赋值的是某个实例化对象的属性,而不是一个class类的属性.这个要注意.

要注意:如果要获取的属性是私有的,要设置Field.setAccessible(true).

	//获取User类的Class对象
	Class userClass = User.class;
	//获取公有属性
	Field field =  userClass.getField("name");
	//获取User对象
	Object user = userClass.getConstructor().newInstance();
	//给name赋值
	field.set(user, "user");
	User user1 = (User) user;
	System.out.println(user1.name);

使用反射获取类方法

public Method[] getMethods():获取所有公有方法;(包含了父类的方法也包含Object类)
public Method[] getDeclaredMethods():获取所有的成员方法
public Method getMethod(String name,Class<?>… parameterTypes): 获取一个公有成员方法
public Method getDeclaredMethod(String name,Class<?>… parameterTypes): 获取一个成员方法

//获取User类的Class对象
Class userClass = User.class;
//获取User对象
Object user = userClass.getConstructor().newInstance();
//第一个参数是方法的明珠,第二个参数是方法参数的类型的Class对象
Method method =  userClass.getMethod("test",int.class);
//调用方法,第一个参数是实例化对象,第二个参数是方法参数
Object i = method.invoke(user,1);
System.out.println(i);

**要注意:如果要获取的属性是私有的,要设置Method.setAccessible(true).还有要理解为什么调用方法时method.invoke(object,object) ,传入的第一个参数是一个实例化对象.但是…static 方法是例外,在调用static方法的时候,第一个参数可以是null,即method.invoke(null,object)
**

<think>嗯,用户之前问过关于`int`和`integer`的区别,现在转向Java基础中的反射。可能用户正在学习Java,之前的问题涉及基本数据类型和包装类,现在深入到了更高级的主题。我需要先确定用户对Java基础掌握程度,反射通常是面向对象之后的内容,所以用户可能已经有一定的Java基础。 用户的需求是了解Java反射基础知识,可能需要知道反射是什么、为什么用它、以及如何使用。深层需求可能包括实际应用场景或常见问题,比如性能影响或者安全性考虑。需要结构清晰,分点解释,帮助用户逐步理解。 首先,反射的定义,必须用简单明了的语言,避免术语过多。然后核心类,比如Class、Constructor、Method、Field,这些是反射的关键类,需要举例说明。应用场景部分,用户可能想知道反射的实际用途,比如框架开发或动态加载,要举常见的例子如Spring的IoC。 接下来要讲实现步骤,获取Class对象的三种方式,实例化对象的方法,访问字段和调用方法。每个步骤都需要代码示例,但用户可能对代码的细节不太清楚,所以需要解释每个步骤的作用。例如,`Class.forName`可能会抛出异常,需要提及异常处理,但可能不需要深入,保持基础知识的介绍。 优缺点部分需要平衡,用户可能想知道为什么有候不推荐使用反射,所以性能和安全问题是重点。最后,实际案例,比如动态加载类,或者简单的框架应用,帮助用户将理论联系实际。 还要注意用户可能存在的误区,比如滥用反射导致代码难以维护,或者性能问题。需要提醒在必要使用,并遵循最佳实践。此外,检查是否需要提到Java版本的变化对反射的影响,比如模块化系统带来的访问限制,但可能用户刚入门,暂不需要深入。 最后总结,强调反射的灵活性和代价,帮助用户权衡使用场景。整体结构需要逻辑清晰,从定义到实现再到应用,逐步深入,确保用户能够跟随思路,逐步掌握反射基础知识。</think>好的!以下是关于 **Java反射(Reflection)** 的基础知识介绍,以逐步分析的方式展开: --- ### 一、反射的定义 **反射(Reflection)** 是Java提供的一种**运行动态机制**,允许程序在**不预先知道类结构**的情况下,通过`Class`对象获取类的信息(如字段、方法、构造器等),并操作这些成员。简单来说:**通过反射,程序可以“观察”和修改自身的行为**。 --- ### 二、反射的核心类 Java反射通过以下核心类实现: 1. **`Class`**:表示类或接口的元数据。 2. **`Constructor`**:表示类的构造方法。 3. **`Method`**:表示类的方法。 4. **`Field`**:表示类的字段(属性)。 5. **`Modifier`**:解析成员的修饰符(如`public`、`private`)。 --- ### 三、反射的核心用途 1. **动态获取类信息** 例如:获取类名、父类、接口、注解等。 ```java Class<?> clazz = String.class; System.out.println("类名:" + clazz.getName()); // 输出:java.lang.String ``` 2. **动态创建对象** 即使没有显式定义构造方法,也可以通过反射实例化对象。 ```java Class<?> clazz = Class.forName("java.util.ArrayList"); Object list = clazz.newInstance(); // 创建ArrayList实例(已过,推荐用构造器) ``` 3. **动态调用方法/访问字段** 可以绕过访问权限(如调用私有方法)。 ```java Method method = clazz.getDeclaredMethod("privateMethod"); method.setAccessible(true); // 突破private限制 method.invoke(obj); ``` 4. **实现通用框架** 如Spring的依赖注入(IoC)、MyBatis的ORM映射等。 --- ### 四、反射的实现步骤(以调用方法为例) 1. **获取`Class`对象** 三种方式: ```java Class<?> clazz1 = Class.forName("全限定类名"); // 通过类路径加载 Class<?> clazz2 = Object.class; // 通过类字面量 Class<?> clazz3 = obj.getClass(); // 通过对象实例 ``` 2. **获取方法或字段** ```java Method method = clazz.getDeclaredMethod("方法名", 参数类型.class); // 包括私有方法 Field field = clazz.getDeclaredField("字段名"); // 包括私有字段 ``` 3. **设置访问权限(可选)** ```java method.setAccessible(true); // 突破private限制 ``` 4. **执行操作** ```java Object result = method.invoke(对象实例, 参数值); // 调用方法 field.set(对象实例, 值); // 修改字段值 ``` --- ### 五、反射的优缺点 | **优点** | **缺点** | |------------------------------|-----------------------------------| | 动态性高(如插件化开发) | 性能较差(比直接调用慢约10-100倍)| | 可访问私有成员(灵活性强) | 代码可读性降低 | | 通用性高(框架开发必备) | 绕过类型安全检查,可能引发异常 | --- ### 六、实际应用案例 1. **动态加载类**(如插件机制): ```java Class<?> pluginClass = Class.forName("com.example.Plugin"); Object plugin = pluginClass.newInstance(); Method runMethod = pluginClass.getMethod("run"); runMethod.invoke(plugin); ``` 2. **Spring框架的依赖注入**: ```java // 通过反射创建Bean并注入属性 Class<?> beanClass = Class.forName("com.example.UserService"); Object bean = beanClass.newInstance(); Field daoField = beanClass.getDeclaredField("userDao"); daoField.setAccessible(true); daoField.set(bean, new UserDaoImpl()); // 注入依赖 ``` --- ### 七、注意事项 1. **性能优化**:避免在频繁调用的代码中使用反射。 2. **安全限制**:Java 9+的模块系统可能限制反射访问(需在`module-info.java`中声明`opens`)。 3. **代码健壮性**:反射调用需处理大量异常(如`ClassNotFoundException`、`NoSuchMethodException`)。 --- ### 总结 反射Java中强大的动态编程工具,**核心价值是灵活性**,但需谨慎使用。掌握反射后,可以深入理解框架设计原理(如Spring、Hibernate),但日常开发中应优先考虑直接调用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值