当前位置: 首页 > news >正文

Java-Spring入门指南(九)反射与反射对象

Java-Spring入门指南(九)反射与反射对象

  • 前言
  • 一、反射与反射对象
    • 1.1 什么是反射?
    • 1.2 反射的核心
  • 二、获得反射对象
    • 2.1 核心方式:Class.forName(类的全路径名)
  • 三、得到Class类的几种方式
    • 3.1 四种获取方式全解析
      • 方式1:类名.class
      • 方式2:对象.getClass()
      • 方式3:Class.forName(全路径名)
      • 方式4:基本数据类型的包装类.TYPE
    • 3.2 四种方式对比与选择
  • 四、所有类型的Class对象
    • 4.1 各类类型的Class对象实战
    • 4.2 关键结论


前言

在上一篇博客中,我们留下了一个关键伏笔:注解的核心能力(被程序读取)必须依赖反射实现

  • 学完之后还有没有一些问题?

@Log注解贴在方法上,程序是怎么‘看到’这个注解并自动打印日志的?”
“Spring的@Autowired为什么能‘凭空’找到并注入对象?”

答案正是反射。如果说注解是“贴在代码上的标签”,那反射就是“能读取标签、甚至修改标签对应内容的万能扫描仪”。

  • 本文将从“反射的本质”切入,手把手带你掌握反射的核心:理解反射对象、吃透Class对象的获取方式、覆盖所有类型的反射场景。

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的Java-Spring入门指南知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_13040333.html?spm=1001.2014.3001.5482

在这里插入图片描述


一、反射与反射对象

1.1 什么是反射?

反射(Reflection)是Java的核心特性之一,指程序在运行时可以动态获取类的结构信息(属性、方法、构造器等),并可以动态操作对象的属性和方法的能力。

  • 简单来说,正常的编程逻辑是“先有类,再创建对象”(编译期确定),而反射是“先有对象或类名,再反推类的结构”(运行期动态获取)。

可以用一个生活化的类比理解:

  • 正常逻辑:你拿到一本《Java入门书》(类),直接翻阅目录(结构)、阅读内容(调用方法);
  • 反射逻辑:你捡到一张写着“《Java入门书》”的书签(对象/类名),通过这张书签找到这本书,再拆解它的目录、修改里面的内容(动态操作)。

1.2 反射的核心

反射的所有操作都围绕一个核心——Class类对象(简称“Class对象”)。

JVM在加载一个类时,会自动为这个类创建一个唯一的java.lang.Class类的实例(即Class对象),这个对象会完整封装该类的所有结构信息,包括:

  • 类的基本信息(类名、包名、父类、实现的接口);
  • 类的属性(Field对象数组);
  • 类的方法(Method对象数组);
  • 类的构造器(Constructor对象数组);
  • 类上的注解(Annotation对象数组)。

一句话总结:Class对象是“类的说明书”,反射就是“阅读说明书并操作类”的过程

二、获得反射对象

获得Class对象是反射操作的第一步。JVM保证:一个类在内存中只会有一个Class对象,无论通过哪种方式获取,其本质都是同一个实例。

2.1 核心方式:Class.forName(类的全路径名)

这是最常用的方式,尤其适用于编译期不知道类名,运行时动态指定类的场景(如框架加载配置文件中的类)。

实战代码

// 实体类:pojo/entity
class User {private String name;private int age;// 无参构造器(反射创建对象时常用)public User() {}// 有参构造器public User(String name, int age) {this.name = name;this.age = age;}// getter/setterpublic String getName() { return name; }public void setName(String name) { this.name = name; }public int getAge() { return age; }public void setAge(int age) { this.age = age; }@Overridepublic String toString() {return "User{name='" + name + "', age=" + age + "}";}
}// 测试反射获取Class对象
public class Test02 {public static void main(String[] args) {try {// 1. 通过Class.forName(类的全路径名)获取Class对象// 全路径名:包名 + 类名Class c1 = Class.forName("com.kuang.reflection.User");System.out.println("c1: " + c1); // 输出:class com.kuang.reflection.User// 2. 多次获取同一类的Class对象,验证唯一性Class c2 = Class.forName("com.kuang.reflection.User");Class c3 = Class.forName("com.kuang.reflection.User");Class c4 = Class.forName("com.kuang.reflection.User");System.out.println("c2.hashCode(): " + c2.hashCode());System.out.println("c3.hashCode(): " + c3.hashCode());System.out.println("c4.hashCode(): " + c4.hashCode());// 输出结果:四个hashCode完全相同,证明是同一个对象} catch (ClassNotFoundException e) {// 注意:必须处理ClassNotFoundException异常// 异常原因:类的全路径名写错、类未被加载到类路径e.printStackTrace();}}
}

关键说明

  • 类的全路径名:必须包含包名(如com.kuang.reflection.User而非User),否则会抛出ClassNotFoundException
  • 唯一性验证c2~c4hashCode相同,因为JVM对一个类只加载一次,对应唯一的Class对象;
  • 异常处理Class.forName()是受检异常,必须显式处理(try-catchthrows)。

三、得到Class类的几种方式

除了Class.forName(),Java还提供了另外3种获取Class对象的方式,不同方式适用于不同场景。我们先完整列出所有方式,再对比分析。

3.1 四种获取方式全解析

方式1:类名.class

直接通过“类名 + .class”获取,适用于编译期已知具体类的场景(如手动获取StringUser的Class对象)。

// 方式1:类名.class
Class c1 = User.class;
Class c2 = String.class;
System.out.println("User的Class对象:" + c1); // class com.kuang.reflection.User
System.out.println("String的Class对象:" + c2); // class java.lang.String

方式2:对象.getClass()

通过类的实例对象调用getClass()方法获取,适用于已有对象,需要反推其类结构的场景(如方法参数为Object类型,需动态获取其具体类型)。

// 方式2:对象.getClass()
User user = new User("小明", 20);
Class c3 = user.getClass();
System.out.println("通过user对象获取:" + c3); // class com.kuang.reflection.User

方式3:Class.forName(全路径名)

前文已详细讲解,适用于编译期未知类,运行时通过字符串指定的场景(框架核心用法,如Spring读取配置文件中的className)。

方式4:基本数据类型的包装类.TYPE

基本数据类型(intchar等)没有getClass()方法,也不能用Class.forName()(因为基本类型不是类),需通过其包装类的.TYPE属性获取。

// 方式4:包装类.TYPE(针对基本数据类型)
Class c4 = int.class;          // 基本类型int的Class对象
Class c5 = Integer.TYPE;       // 包装类Integer的TYPE属性,等价于int.class
Class c6 = Integer.class;      // 包装类Integer本身的Class对象(≠ int.class)System.out.println(c4 == c5);  // true(都是基本类型int的Class对象)
System.out.println(c4 == c6);  // false(c4是int,c6是Integer类)

补充:数组的Class对象
数组也是一种特殊的类型,其Class对象可通过“数组对象.getClass()”或“数组类型.class”获取,且数组的Class对象与元素类型、维度相关

// 数组的Class对象
int[] arr1 = new int[10];
int[][] arr2 = new int[2][3];
String[] arr3 = new String[5];Class c7 = arr1.getClass();
Class c8 = arr2.getClass();
Class c9 = arr3.getClass();System.out.println("int[]的Class:" + c7);    // class [I([表示数组,I表示int)
System.out.println("int[][]的Class:" + c8);  // class [[I(二维数组)
System.out.println("String[]的Class:" + c9); // class [Ljava.lang.String;(L表示引用类型)

3.2 四种方式对比与选择

获取方式适用场景编译期是否已知类能否获取基本类型Class
类名.class已知具体类,手动获取能(如int.class)
对象.getClass()已有对象实例,反推类结构是(已知对象类型)否(基本类型无对象)
Class.forName(全路径名)运行时动态加载(框架常用)否(基本类型无全路径)
包装类.TYPE获取基本数据类型的Class对象能(仅基本类型)

四、所有类型的Class对象

除了普通类,Java中的接口、枚举、注解、基本类型、void、数组等,都有对应的Class对象。这也是Class类被称为“反射根源”的原因——它能封装所有Java类型的结构信息。

4.1 各类类型的Class对象实战

我们用代码覆盖所有类型的Class对象获取,直观感受其差异:

package com.kuang.reflection;import java.io.Serializable;
import java.lang.annotation.*;// 1. 定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {}// 2. 定义枚举
enum Season { SPRING, SUMMER }// 3. 定义实现接口的类
@MyAnnotation
class Student implements Serializable {public String id;
}// 测试所有类型的Class对象
public class TestAllClass {public static void main(String[] args) {// 1. 普通类Class c1 = Student.class;// 2. 接口Class c2 = Serializable.class;// 3. 枚举Class c3 = Season.class;// 4. 注解Class c4 = MyAnnotation.class;// 5. 基本数据类型Class c5 = int.class;// 6. void类型Class c6 = void.class;// 7. 数组Class c7 = String[].class;// 打印所有Class对象System.out.println("1. 普通类:" + c1);       // class com.kuang.reflection.StudentSystem.out.println("2. 接口:" + c2);         // interface java.io.SerializableSystem.out.println("3. 枚举:" + c3);         // class com.kuang.reflection.SeasonSystem.out.println("4. 注解:" + c4);         // interface com.kuang.reflection.MyAnnotationSystem.out.println("5. 基本类型:" + c5);     // intSystem.out.println("6. void类型:" + c6);     // voidSystem.out.println("7. 数组:" + c7);         // class [Ljava.lang.String;}
}

4.2 关键结论

  • 无论哪种类型(类、接口、枚举等),其Class对象都是Class类的实例;
  • 不同类型的Class对象打印结果有差异(如接口前缀为interface,基本类型直接显示名称);
  • 所有类型的Class对象都可以通过反射API操作(如获取注解、方法等),这是框架实现“通用逻辑”的基础(如Spring可以扫描所有类型的组件)。

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的Java-Spring入门指南知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_13040333.html?spm=1001.2014.3001.5482

非常感谢您的阅读,喜欢的话记得三连哦

在这里插入图片描述


文章转载自:

http://2UHIrsba.jpydf.cn
http://Sgk698H4.jpydf.cn
http://0LjV1efR.jpydf.cn
http://aWHXteq4.jpydf.cn
http://2kFPjqMx.jpydf.cn
http://oQlQhVoW.jpydf.cn
http://cMJdwTqu.jpydf.cn
http://Wrz4xg0h.jpydf.cn
http://MtQM6XAU.jpydf.cn
http://JARTLLG6.jpydf.cn
http://gXOIpTL9.jpydf.cn
http://OLuhRdxA.jpydf.cn
http://nerl9W30.jpydf.cn
http://DzyXBpUC.jpydf.cn
http://ugT646s2.jpydf.cn
http://zFQAJXw8.jpydf.cn
http://H34V0jzH.jpydf.cn
http://EDfXlJjX.jpydf.cn
http://hx17NxmP.jpydf.cn
http://clrxC9U8.jpydf.cn
http://4VjIeOOR.jpydf.cn
http://S9xRPOrh.jpydf.cn
http://3tT6G3TN.jpydf.cn
http://agMvv64p.jpydf.cn
http://vZjfCK8Z.jpydf.cn
http://JoPp4s57.jpydf.cn
http://TCBeSN5Q.jpydf.cn
http://WGb7TaZD.jpydf.cn
http://8Ggz2KS6.jpydf.cn
http://0hrGQof3.jpydf.cn
http://www.dtcms.com/a/384297.html

相关文章:

  • 从 Vue 到 Java:前后端分离项目后端迁移完整教程
  • 在 IDEA 2024 创建 Vue 项目(保姆级)
  • Electron 常见问题排查:调试与错误处理
  • 学生管理系统
  • 软件测试的艺术——黑白盒测试学习笔记
  • Electron开源库入门教程:跨平台桌面应用框架
  • 基于Springboot企业车辆管理系统
  • MySQL面试(1)
  • ArcGIS定向影像(1)——非传统影像轻量级解决方案
  • 【Linux指南】Makefile进阶:通用化语法与实战技巧
  • 移相全桥模拟控制电路
  • ES6 面试题及详细答案 80题 (62-80)-- 模块化与其他特性
  • D005 vue+django音乐推荐大数据|推荐算法|多权限| 可视化|完整源码
  • 工厂自动化正从 “人工堆叠” 向 “设备替代” 快速转变
  • 栈-227.基本计算器II-力扣(LeetCode)
  • python 自动化从入门到实战-做一个超实用的二维码生成工具(5)
  • 今天开始学习新内容“服务集群与自动化”--crond服务、--syslog服务以及DHCP协议
  • LeetCode 379 - 电话目录管理系统(Design Phone Directory)
  • 《Python 自动化实战:从零构建一个文件同步工具》
  • 风险规则引擎-RPA 作为自动化依赖业务决策流程的强大工具
  • Vue: 模板引用 (Template Refs)
  • Web2 vs Web3
  • 上海交大3D体素赋能具身导航!BeliefMapNav:基于3D体素信念图的零样本目标导航
  • SAP-ABAP:SAP ABAP中的JSON序列化利器:/UI2/CL_JSON=>SERIALIZE完全指南实例详解
  • stm32 can错误处理问题
  • python 自动化从入门到实战-开发一个随机点名系统(6)
  • 如何用 GitHub Actions 为 FastAPI 项目打造自动化测试流水线?
  • godot+visual studio配置c#环境
  • 文件查找失败:‘module‘ at node_modules\sass\sass.node.js:7
  • (一)Vue.js 框架简介