Java的反射讲解

本文主要介绍Java反射机制,包括其概念、作用、优缺点,并给出反射例子。还阐述了Java的三种类加载器,即Bootstrap、Extension和AppClassLoader。此外,讲解了类的生命周期,包含装载、链接、初始化等步骤,以及对象使用和回收过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章最前: 我是Octopus,这个名字来源于我的中文名--章鱼;我热爱编程、热爱算法、热爱开源。所有源码在我的个人github ;这博客是记录我学习的点点滴滴,如果您对 Python、Java、AI、算法有兴趣,可以关注我的动态,一起学习,共同进步。

相关文章:

  1. java类加载过程
  2. java运行数据区域
  3. JVM 垃圾回收算法及回收器详解
  4. Java的反射讲解

 文章目录:

1.反射的概念

2.反射的作用

3.反射机制的优点与缺点

4.反射的例子:

5.java中有三种类加载器。

6.类的生命周期


1.反射的概念

      在java中,只要提供类名,就可以通过反射机制来获取类的所有信息。

      反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接,但是反射使用不当会降低程序的效率。


2.反射的作用

2.1 在运行时判断任意一个对象所属的类;

2.2 在运行时获取类的对象;

2.3 在运行时访问java对象的属性,方法,构造方法等。


3.反射机制的优点与缺点

缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它 满足我们的要求。

优点:可以实现动态创建对象和编译,体现出很大的灵活性(特别是在J2EE的开发中它的灵活性就表现的十分明显)。通过反射机制可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。


4.反射的例子:

4.1 一个被反射的Student的bean:

package testreflect;

import java.util.Date;

/**
*类描述:student的bean
*@author: 张宇
*@date: 日期: 2018年8月30日 时间: 上午11:13:42
 */
public class Student {

	private String studentID;
	private String studentName;
	private Date birthday;
	private int score;

	public String getStudentID() {
		return studentID;
	}

	public void setStudentID(String studentID) {
		this.studentID = studentID;
	}

	public String getStudentName() {
		return studentName;
	}

	public void setStudentName(String studentName) {
		this.studentName = studentName;
	}

	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	public int getScore() {
		return score;
	}

	public void setScore(int score) {
		this.score = score;
	}

	public void finishTask(String taskName) {
		System.out.println(this.studentName + "完成了"+taskName);
	}
}

4.2 反射的例子:

package testreflect;

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

/**
*类描述: 反射得到student的bean中的对象,属性,方法
*@author: 张宇
*@date: 日期: 2018年8月30日 时间: 下午2:09:03
 */

public class ReflectTest {
	public static void main(String[] args) {
		try {
			// 获取类的字节码文件:包名+类名
			Class<?> clazz = Class.forName("testreflect.Student");
			// 通过类的字节码文件获取所有字段名的数组
			Field[] fields = clazz.getDeclaredFields();
			// 遍历fileds数组,并打印出来它的字段,修饰符,类型,属性名
			for(Field field:fields){ 
				System.out.println(field);
			    System.out.println("修饰符:"+Modifier.toString(field.getModifiers()));
			    System.out.println("类型:"+field.getType());
			    System.out.println("属性名:"+field.getName());
			    System.out.println("----------------------------------------------"); 
			}
	
			// 获取该类的所有方法,其中getDeclaredMethods是强制获取,不管是共有的还是私有的方法
			Method[] methods = clazz.getDeclaredMethods();
			for (Method method : methods) {
				System.out.println(method);
				System.out.println("修饰符:"+ Modifier.toString(method.getModifiers()));
				System.out.println("方法名:" + method.getName());
				System.out.println("返回类型:" + method.getReturnType());
				System.out.println("---------------------------------");
				// 获取方法的参数对象
				Class<?>[] clazzes = method.getParameterTypes();
				for (Class<?> class1 : clazzes) {
					System.out.println("参数类型:" + class1);	
					System.out.println("参数名:"+class1.getName());
				}				
			}

			// 通过class对象创建实例
			Student student = (Student) clazz.newInstance();
			// Field studentName=clazz.getField("studentName");
			Field studentName = clazz.getDeclaredField("studentName");
			// 如果是获取单个私有方法,要用到setAccessible
			studentName.setAccessible(true);
			studentName.set(student, "张三");           
			// 通过class对象获取名为finishtask方法
			Method finishTask = clazz.getDeclaredMethod("finishTask", String.class);
			finishTask.setAccessible(true);
			// 调用finishTask方法
			finishTask.invoke(student, "数学");

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

5.java中有三种类加载器。

5.1 Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。

5.2 Extension ClassLoader 用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类

5.3 AppClassLoader 加载classpath指定的类,最常用的加载器,同时也是java中默认的加载器。

6.类的生命周期

  在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM;在程序执行中JVM通过装载,链接,初始化这3个步骤完成。

  类的装载是通过类加载器完成的,加载器将.class的二进制文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象,用来封装数据; 但是同一个类只会被类装载器装载以前链接就是把二进制数据组装为可以运行的状态。

链接分为校验,准备,解析这3个阶段:

校验 :一般用来确认此二进制文件是否适合当前的JVM(版本);

准备 :为静态成员分配内存空间,并设置默认值;

解析 :指的是转换常量池中的代码作为直接引用的过程,直到所有的符号引用都可以被运行程序使用(建立完整的对应关系);

  类型完成了初始化,初始化之后类的对象就可以正常使用了,直到一个对象不再使用之后,将被垃圾回收,释放空间。当没有任何引用指向Class对象时就会被卸载,结束类的生命周期。

### Java反射机制的概念及用法 #### 1. 反射机制的核心概念 Java反射机制是指在程序运行期间,能够动态获取类的相关信息并对其进行操作的能力。这种能力使得开发者可以在不知道具体实现的情况下,通过类的名称或其他方式加载类、创建实例、访问字段和调用方法[^1]。 #### 2. 获取 `Class` 对象的方式 要使用反射机制,首先需要获得目标类对应的 `Class` 对象。以下是几种常见的获取方式: - **通过 `.class` 属性**: 如果已知具体的类名,则可以直接使用该类的 `.class` 属性。 ```java Class<?> clazz = String.class; ``` - **通过对象的 `getClass()` 方法**: 已经有某个类的对象时,可以调用其 `getClass()` 方法。 ```java Object obj = new Integer(10); Class<?> clazz = obj.getClass(); ``` - **通过 `Class.forName(String className)` 静态方法**: 动态指定类的全限定名来获取 `Class` 对象。 ```java Class<?> clazz = Class.forName("java.lang.String"); ``` 上述三种方式均能成功返回一个表示对应类型的 `Class` 实例[^3]。 #### 3. 创建对象 一旦获得了 `Class` 对象,就可以利用它来创建新的实例。通常会调用无参构造器或者带参数的构造器完成这一过程。 ```java // 假设我们有一个名为 Person 的类 Class<?> personClass = Class.forName("com.example.Person"); // 调用无参构造函数创建新实例 Object instance = personClass.getDeclaredConstructor().newInstance(); System.out.println(instance); // 输出新建的实例 ``` #### 4. 访问字段 反射还支持读取或修改私有的成员变量值,即使这些字段被声明为 private。 ```java Field field = personClass.getDeclaredField("name"); // name 是假设的一个字段 field.setAccessible(true); String value = (String) field.get(instance); System.out.println(value); // 打印出字段的内容 ``` #### 5. 调用方法 同样地,也可以借助反射技术执行任意公开甚至隐藏的方法。 ```java Method method = personClass.getMethod("sayHello", null); method.invoke(instance, null); ``` 以上展示了如何运用反射去操控未知类型的数据结构与逻辑流程[^2]。 #### 性能考量与其他注意事项 尽管反射功能强大,但它也伴随着一定的代价——主要是因为它的灵活性是以牺牲效率为前提条件而达成的结果。因此,在设计软件架构的时候应该谨慎评估是否真的有必要引入反射机制。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值