3.7 static关键字
static 就是"属于类,不属于对象"——加了 static 的成员全类只有一份,所有对象共享;不加的成员每个对象各一份。前面埋的三处伏笔(静态代码块、static 方法里的 this、main 的秘密)今天全部收网。
1. 三处伏笔,今天收网
| 伏笔 | 在哪埋的 | 答案预告 |
|---|---|---|
| 静态代码块为什么只跑一次 | 3.6 | 它属于类,类只加载一次 |
| static 方法里为什么不能用 this | 3.5 误区 3 | 没有"当前对象",this 无从指起 |
main 那串词到底是什么 |
1.3 起天天写 | JVM 不 new 对象就要喊它,所以它必须是 static |
static 的完整读法:跟着类走、全类一份、不 new 也能用。下面逐个拆。
2. 静态属性——全类一份的"公共记事板"
回顾 3.2:new 一次,堆里多一辆车,每辆车各有自己的 brand 格子——这叫实例属性(跟对象走)。
现在给 Car 类加一个需求:"统计一共造了多少辆车"。这个数不属于任何一辆车,属于整个 Car 家族——加 static:
public class Car {
private String brand; // 实例属性:每车一份
public static int count = 0; // 静态属性:全类一份
public Car(String brand) {
this.brand = brand;
count++; // 每造一辆,公共的 count 加 1
}
}
public class TestCar {
public static void main(String[] args) {
Car c1 = new Car("宝马");
Car c2 = new Car("奔驰");
Car c3 = new Car("奥迪");
System.out.println(Car.count); // 3 —— 用类名访问(推荐)
System.out.println(c1.count); // 也是 3(对象只是转手,还是那一份)
}
}
注意最后一行:c1.count 也能访问,但改的还是全类那一份——不管从哪个对象手里碰它,碰到的都是同一块公共记事板(上面那张图右边)。所以规范写法是 类名.静态成员,一眼看出"这是全类的,不是某辆车的"。
实例属性住在堆里的对象身上(new 一次多一份);静态属性不属于任何对象,只有类层一份,第一次用到类时就存在——所以还没 new 也能访问。这就是"跟类走"的含义。
3. 静态方法——不用 new 就能喊
方法加 static,就成了静态方法:通过类名直接调用,不需要对象。
public class MathUtil {
public static int max(int a, int b) {
return a > b ? a : b;
}
}
// 调用:
int m = MathUtil.max(3, 5); // 不 new,直接类名喊
你其实早就用过静态方法:
Math.random(); // 0~1 的随机数
Math.abs(-5); // 绝对值
Integer.parseInt("123"); // 字符串转数字
全是类名直接喊——这些工具类不需要对象(谁用 max 都一样,方法又不依赖某辆车的数据),所以做成 static 最省事。工具类的标配就是:private 构造(不让 new)+ 全静态方法。
4. main 的秘密——为什么它是 static
回看那行你抄了两章的咒语:
public static void main(String[] args)
public:JVM 在类外面,得让它够得着static:程序启动时一个对象都还没有,JVM 要喊 main,不可能先 new 一个类再喊——所以 main 必须不依赖对象,即必须是 staticvoid:main 跑完程序自然退出,不用交货String[] args:命令行参数(现在不用管)
一句话:main 是程序的入口,入口必须"不依赖对象",所以必须是 static。 1.3 埋到今天的谜底就是这个。
5. 访问规则——谁能动谁(本节考试中心)
static 把成员分成两个圈子,规则就三句(上面那张图):
| 场景 | 能碰实例成员吗 | 能碰静态成员吗 | 能用 this 吗 |
|---|---|---|---|
| 静态方法里 | ❌ 不能 | ✅ 能 | ❌ 不能 |
| 实例方法里 | ✅ 能 | ✅ 能 | ✅ 能 |
为什么静态方法碰不了实例成员? 时机问题:静态方法可以"不 new 就喊"(MathUtil.max(3,5)),喊的时候堆里可能一个对象都没有——那 brand 是哪辆车的 brand?无从指起。实例成员必须"对着某个对象"才有意义,静态方法没有这个对象,所以编译器直接禁止。
3.5 误区 3(this 不能用)、3.6 静态块(连实例属性都碰不了)、本节静态方法禁碰实例成员——三条规则是同一个原因:static 的世界里对象可能还不存在。记住这一句,三条全通。
反过来,实例方法里什么都能用——实例方法一定是"某个对象在执行"(this 存在),而静态成员全类共享,顺手就能碰。
6. 动手部分:造车计数器 + 静态工具方法
第 1 步:在 IDEA02 里新建类 Car(或新文件 Car3,避免和旧的冲突):
public class Car {
private String brand;
public static int count = 0; // 公共记事板
public Car(String brand) {
this.brand = brand;
count++;
}
public void showInfo() { // 实例方法:两圈都能碰
System.out.println(brand + "(全家族已造 " + count + " 辆)");
}
public static int getCount() { // 静态方法:不 new 也能查
return count;
// System.out.println(brand); // ← 解开注释试试:直接编译报错
}
}
第 2 步:测试类 TestCar:
public class TestCar {
public static void main(String[] args) {
System.out.println("还没造车:" + Car.count); // 0 —— 没 new 也能查
Car c1 = new Car("宝马");
Car c2 = new Car("奔驰");
System.out.println("现在:" + Car.getCount()); // 2
c1.showInfo(); // 宝马(全家族已造 2 辆)—— 实例方法里能碰静态成员
}
}
看到什么算成功:三行——"还没造车:0"、"现在:2"、"宝马(全家族已造 2 辆)"。第一行是关键证据:没 new 任何对象,Car.count 已经存在且是 0。
三个 30 秒实验:
- 解开
getCount里那行注释——编译报错"无法从静态上下文中引用非静态变量 brand",亲眼确认规则表第一格的 ❌ - 写
c1.count = 99;再打印Car.count——输出 99!通过对象改静态属性,改的就是全类那份(所以规范才推荐类名访问) - 在
showInfo里写System.out.println(this.brand);——正常运行,实例方法里 this 好好的
7. 常见误区
恰恰相反——静态成员不跟着任何对象走(所以 new 三次它也只有一份)。这也是 GC 的一个推论:静态属性持有对象引用时,那个对象永远不会被回收(全类共用着呢),乱用 static 存大对象是内存泄漏的经典来源。
能碰!规则禁的是"直接写实例属性名"(没有对象可依托),但方法参数传进来的对象是实打实的:public static void check(Car c) { c.brand } 完全合法——static 禁止的是"凭空找对象",不禁止"有人把对象递给你"。
反了——实例方法是"全都要":this 有(对象在执行)、静态成员全类共享顺手就能碰。受限的只有静态方法那一格。
静态成员全类共享、改一处全变——只适合"全体共用的东西"(配置常量、工具方法、计数器)。每辆车自己的 brand 加了 static,所有车瞬间变成同一个牌子。判断标准一句话:这个数据是"每对象一份"还是"全类一份"?
有问题,同一个道理:静态方法执行时对象可能不存在,实例方法必须有对象才能跑。要调就先 new 一个,或者把对方也改成 static(工具类通常两者皆无状态,全 static 没毛病)。
8. 自测题
从"属于对象"变成"属于类":全类只有一份,不依赖对象存在(类第一次被使用时就有),所有对象共享。访问规范用 类名.成员。对照:实例成员每个对象一份,new 一次多一份。
因为静态方法可以"不 new 就喊"(如 MathUtil.max(3,5)),执行时堆里可能一个对象都没有——实例属性没有依托对象、this 没有指认对象,无从谈起。核心:static 的世界里对象可能还没出生。
main 是程序入口,由 JVM 在没有任何对象时直接喊起——它必须不依赖对象,所以必须是 static。没有 static,JVM 就得先 new 你的类才能喊 main,而"用哪个构造、参数给谁"在启动时刻无解。
合法,但改的是全类那一份——对象访问静态成员只是转手,不存在"c1 自己的 count"。这也是为什么规范推荐 Car.count 写法:一眼表明"动的是公共记事板",避免误以为是 c1 的私产。
静态方法:只能直接访问 count(brand 没有依托对象);实例方法:两者都能。判断标准就一条——访问时"对象"存不存在:实例成员必须对着某个对象才有意义,静态成员跟着类走永远存在。参数传进来的对象另说(误区 2)。
9. 下一步学什么
- ☑
3.7 static关键字✅ 三处伏笔全部收网:静态块、this 限制、main 的秘密 - ☑
3.8 本章小结✅ 第三章完结:七节串线、速查 14 条、七大名坑、毕业大题(独立写 Book 类)
相关笔记
- 3.6 代码块 ← 静态代码块的"仅一次"之谜,本节给出答案
- 3.5 this关键字 ← "static 里没有 this"的反面教材
- 3.2 类与对象 ← 实例属性"每对象一份"的本源
- 1.3 第一个Java程序 ← main 那串词,今天每个字都有解释了
- 📇 Java基础 · 索引
- 🏠 返回主页