9.1 拿到的是说明书的封面(Class 对象),这一节把说明书翻开:先搞清 Class 类本身也是个类,再用它的一堆 get 方法把类名、字段、方法、构造器统统拆出来看——这就是反射的弹药库。
一、先解开一个绕脑的问题:Class 类也是个类
Java 里万物皆对象。那"类"这个概念本身呢?它也是对象——谁来描述它?答案就是 JDK 自带的 java.lang.Class 类。
| 名字 | 它是什么 | 它的对象描述谁 |
|---|---|---|
Student 类 |
我们自己写的类 | 描述"学生" |
Class 类 |
JDK 自带的类(java.lang 包) | 描述"别的类" |
Class 对象 |
Class 类的实例 | 就是一份类的说明书 |
绕脑点(也是名考点):Class 类的构造器是私有的,JVM 加载类时自动帮你创建,你永远 new 不出来。这也正好呼应 9.1——三把钥匙拿到的都是同一份,因为压根只能有一份。
类加载时机回顾(1.4 运行机制的账):JVM 第一次用到某个类(new 它、访问它的 static 成员、Class.forName(...)……)时,把 .class 文件读进内存,顺便生成唯一一份 Class 对象,往后就一直用这一份。
二、说明书里到底写了什么
Class 对象就是一座弹药库——反射的全部能力都从这里取货。你写的每一个类,加载后它的类名、字段、方法、构造器、注解、父类、接口……全都被装进这一份对象里。
三、Class 常用方法:四大族
大规律(记这一句就够):
- 带
Declared:全都要——private 也拿,但不含父类的 - 不带
Declared:只要 public,含继承来的(所以getMethods()会把 Object 的toString()/equals()也带出来)
返回值拿到的 Field / Method / Constructor 就是反射三兄弟(都在 java.lang.reflect 包里)——这节先混脸熟,下节开始真刀真枪用它们。
| 你想要 | 方法 | 返回 |
|---|---|---|
全类名 com.it.Student |
getName() |
String |
简名 Student |
getSimpleName() |
String |
| 父类 | getSuperclass() |
Class |
| 实现的接口 | getInterfaces() |
Class[] |
| 所有公有字段 | getFields() |
Field[] |
| 所有字段(含私有) | getDeclaredFields() |
Field[] |
| 指定名字的字段 | getDeclaredField("name") |
Field |
| 所有公有方法 | getMethods() |
Method[] |
| 所有方法(含私有) | getDeclaredMethods() |
Method[] |
| 所有公有构造器 | getConstructors() |
Constructor[] |
| 指定构造器 | getDeclaredConstructor(参数类型...) |
Constructor |
四、收网旧账
- 8.1 泛型基础:类型擦除的另一半真相。泛型只在编译期站岗,运行时所有
List都是同一个List的 Class 对象——因为运行时根本只有一份"最原始的类"的说明书,跟你泛型套了几层没关系。 - 4.5 Object类:
getClass()是 Object 的方法——万物之祖早就给反射留了后门,今天正式启用。 - 5.2 异常:
Class.forName()抛的ClassNotFoundException是受检异常,必须throws或try-catch。 - 7.8 Stream:
Field[]、Method[]拿到手,用Arrays.stream(...)打印最顺手。
五、动手:给 Student 类做个体检
写个 Student(带私有字段、公有/私有方法),然后用 Class 对象把它拆开看:
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Student {
private String name;
public int age;
public void study() { }
private void cheat() { }
}
import java.lang.reflect.Class;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Demo02 {
public static void main(String[] args) throws ClassNotFoundException {
Class<Student> c = Student.class; // 三把钥匙随便用一把
System.out.println("全类名:" + c.getName());
System.out.println("简名:" + c.getSimpleName());
System.out.println("父类:" + c.getSuperclass().getSimpleName());
System.out.println("--- 公有字段 getFields ---");
for (Field f : c.getFields()) {
System.out.println(f);
}
System.out.println("--- 全部字段 getDeclaredFields ---");
for (Field f : c.getDeclaredFields()) {
System.out.println(f);
}
System.out.println("--- 全部方法 getDeclaredMethods ---");
for (Method m : c.getDeclaredMethods()) {
System.out.println(m);
}
}
}
运行后你会发现:getFields() 只数出 age(public),getDeclaredFields() 把 name(private)也揪出来了——Declared 规律当场验证。
三个 30 秒实验
- 把
c.getMethods()打印出来,数一数——怎么多了wait()、notify()、toString()?那是从 Object 继承来的(4.5 的账)。 - 打印
int.class.getName()和String[].class.getName()——基本类型和数组也有 Class 对象(名字长得怪:int、[Ljava.lang.String;)。 - 打印
c.getInterfaces()——如果让 Student 实现Comparable,接口也会出现在说明书里。
六、常见误区
- ❌ "Class 类这么神奇,我
new Class()一个玩玩" —— 构造器私有,只有 JVM 能造。你只能领,不能造。 - ❌ "getDeclaredFields() 会把父类的字段也拿全" —— 恰恰相反:Declared = 全要但只管自己家;父类的得先
getSuperclass()再查。 - ❌ "拿到的 Field 就是字段的值" —— Field 是字段的说明书(名字、类型、修饰符),值要另外取(后面章节)。
- ❌ "getMethods() 数出来比我写的方法多,是不是错了" —— 没错,它把 Object 继承来的也算上了,数数别慌。
- ❌ "只有引用类型才有 Class 对象" ——
int、double、数组甚至void都有(int.class、void.class),万物皆可说明书。
七、自测一下
private(私有)。说明书由 JVM 在类加载时统一创建,保证每个类全 JVM 只有一份——这正是 9.1 三把钥匙 == 成立的根本原因。
带 Declared = 全都要(含私有)但不含父类;不带 = 只要 public 但含继承来的。两个"全"字方向相反,别背反了。
简名:getSimpleName();全类名:getName()。
因为不带 Declared 的方法含继承来的——这俩是 Object 类的方法,Student 继承了它们,自然被算进来。
目标:打印类名、全部字段(含私有)、全部方法(含私有),并各数出个数。
Class<Student> c = Student.class;
System.out.println("体检对象:" + c.getName());
Field[] fs = c.getDeclaredFields();
Method[] ms = c.getDeclaredMethods();
System.out.println("字段 " + fs.length + " 个:");
for (Field f : fs) System.out.println(" " + f);
System.out.println("方法 " + ms.length + " 个:");
for (Method m : ms) System.out.println(" " + m);
能独立写出这段,下节"反射造对象"你就能看懂 Spring 的影子了。
下一步学什么
- ☑ 9.3 反射的常见操作 ✅ —— 点此复习:反射三件套(造对象/拆字段/调方法)、setAccessible 后门、"操作谁就把谁传进去"。
相关笔记
- 9.1 反射概述:三把钥匙拿到的就是本节的 Class 对象,没读先补
- 8.1 泛型基础:类型擦除的另一半真相
- 4.5 Object类:getClass() 的出处
- 5.2 运行时异常与编译时异常:ClassNotFoundException 的受检待遇