文章目录
- 455. Java 反射 - 核心 Class 类
- 1. 什么是 Class 类?
- 2. 获取 Class 对象的三种标准方式
- 代码示例:
- 3. 不同类型的 Class 表现
- 4. ClassLoader:类的加载器
- 加载器层级:
- 示例代码:
- 5. 总结
455. Java 反射 - 核心 Class 类
1. 什么是 Class 类?
在 Java 中,java.lang.Class是反射机制的入口。
- 类型的镜子:每个类加载到 JVM 后,都会产生一个唯一的
Class对象。它描述了该类的结构(成员变量、方法、构造器、父类等)。 - 万物皆有 Class:不仅是普通的类,接口、枚举、数组、基本类型(如
int.class)、甚至 Lambda 表达式和匿名类,在 JVM 中都有对应的Class实例。 - 单例性:对于同一个类,JVM 只会创建一个
Class实例。
2. 获取 Class 对象的三种标准方式
| 方式 | 语法 | 适用场景 |
|---|---|---|
| 通过对象实例 | obj.getClass() | 已有对象实例,想知道它的运行时实际类型。 |
| 通过类字面量 | ClassName.class | 编译期已知类名,最安全、性能最高的方式。 |
| 通过类名字符串 | Class.forName("全类名") | 编译期不知道类名,运行时动态加载(如从配置文件读取)。 |
代码示例:
// 1. getClass():总是返回实际运行时的类型List<String>list=newArrayList<>();System.out.println(list.getClass());// class java.util.ArrayList// 2. 类字面量Class<?>c1=String.class;Class<?>c2=int.class;// 基本类型也可以// 3. 动态加载 (需要处理异常)Class<?>c3=Class.forName("java.util.Scanner");3. 不同类型的 Class 表现
反射不仅限于普通类,看看其他特殊类型在“镜子”里的样子:
数组 (Arrays):内部名由
[和类型缩写组成。byte[]bytes=newbyte[10];System.out.println(bytes.getClass());// class [B (B代表byte)枚举 (Enums):返回的是整个枚举类型的定义。
enumColor{RED,BLUE}System.out.println(Color.RED.getClass());// class Color匿名内部类:由编译器自动生成,命名格式通常为
外部类$数字。Runnabler=newRunnable(){@Overridepublicvoidrun(){}};System.out.println(r.getClass());// class Main$1Lambda 表达式:由 JVM 在运行时动态生成的匿名类,通常不建议依赖其类名。
4. ClassLoader:类的加载器
每一个Class对象内部都持有一个对ClassLoader的引用,负责将字节码文件加载到内存中。
加载器层级:
- Bootstrap ClassLoader (启动类加载器):加载 JDK 核心库(如
java.lang.*)。在代码中通常返回null。 - Platform/Extension ClassLoader (扩展类加载器):加载标准扩展库。
- App/System ClassLoader (应用类加载器):加载我们自己编写的类和第三方 Jar 包。
示例代码:
// JDK 核心类由 Bootstrap 加载,返回 nullSystem.out.println(String.class.getClassLoader());// 自定义类由 AppClassLoader 加载System.out.println(MyClass.class.getClassLoader());5. 总结
- 入口点:所有反射操作(获取字段、调用方法)都必须先拿到
Class对象。 - 动态性:
Class.forName()是实现插件化和框架解耦的关键。 - 唯一性:无论你用哪种方式获取,同一个类的
Class对象在内存中只有一个。 - 泛型擦除:注意,
List<String>.class是不存在的,反射只能拿到原始类型List.class。