背景与动机
在 Java 里,不是所有字段和方法都属于“某个对象”。有些成员属于类本身,而不是类创建出来的某个实例,这就是 static 的作用。
学 static 最关键的是分清一件事:
- 实例成员:跟着对象走,每个对象各有一份
- 静态成员:跟着类走,所有对象共享一份
核心原理拆解
1. 静态字段属于类,所有对象共享
class User { static int count = 0;
User() { count++; }}这里的 count 不是每个 User 对象各有一份,而是整个 User 类共享一份。只要创建一个新对象,count 就会累加。
这类静态字段常用于:
- 统计实例数量
- 保存全局共享配置
- 定义类级别状态
1.1 静态字段和实例字段在内存上的区别
这里很容易混淆一个点:类里声明了字段,不代表所有字段的具体值都放在同一个地方。
可以先这样理解:
- 类的信息本身,比如有哪些字段、有哪些方法,属于类级信息
- 实例字段的具体值,跟着对象走,通常存在堆中的对象实例里
static字段不跟某个对象绑定,可以先简化理解为放在类级区域中,整个类共享一份
所以要分清两个层面:
- “字段定义”属于类信息
- “字段具体值”要看它到底是实例字段还是静态字段
例如:
class User { static int count = 2; String name; int age;}在这个例子里:
User这个类有哪些字段,属于类信息- 某个对象里的
name = "Tom"、age = 18,是某个实例自己的状态 count = 2则是整个User类共享的一份类级状态

- 为什么
static只有一份 - 为什么普通字段每个对象各有一份
- 为什么不能把实例字段值和静态字段值都笼统理解成“都在方法区里”
2. 静态方法属于类,不依赖具体对象
class MathUtil { static int add(int a, int b) { return a + b; }}调用时不需要先 new 对象:
int sum = MathUtil.add(1, 2);这类方法适合放“只做计算或工具处理,不依赖对象状态”的逻辑。
2.1 从内存角度理解静态字段和静态方法
如果把 static 字段和 static 方法放到一张图里理解,重点要看清楚它们和对象的关系:
static字段属于类,所有对象共享static方法也属于类,调用时不依赖某个具体对象- 对象实例仍然在堆中创建,栈里拿到的通常只是对象引用
例如下面这段代码:
class User { static int count = 0;
static void printCount() { System.out.println(count); }}理解时可以抓住这几点:
count不是某个对象私有的字段,而是类级字段。printCount()不需要new User()后才能调用,直接User.printCount()就行。static方法里可以直接访问static字段,因为它们都属于同一个类级上下文。- 但
static方法如果要处理某个对象的实例状态,还是得显式拿到对象。

- 栈里的引用
- 堆里的对象
- 类级区域中的静态成员和类信息
3. 为什么静态方法里不能直接访问实例字段
class User { String name;
static void printName() { // System.out.println(name); // 编译失败 }}原因很直接:静态方法属于类,但 name 属于具体对象。类级方法执行时,并不知道你要访问的是哪个对象的 name。
如果静态方法确实要处理对象状态,就必须显式接收对象:
static void printName(User user) { System.out.println(user.name);}4. 静态方法不是多态重写
很多人学到继承后,会自然把静态方法也当成“可以像实例方法那样重写”。实际上,静态方法更接近“类自己的方法”,子类写了同名静态方法时,发生的是隐藏,不是运行时多态。
class Parent { static void hello() { System.out.println("parent"); }}
class Child extends Parent { static void hello() { System.out.println("child"); }}
Parent p = new Child();p.hello(); // 输出 parent上面这段代码按引用类型 Parent 来决定调用哪个静态方法,而不是按真实对象类型 Child 决定。
5. static final 常量
class Config { static final String APP_NAME = "demo";}这是 Java 里非常常见的写法:
static:说明它属于类final:说明它不会再被重新赋值
所以 static final 很适合定义常量。
6. 静态代码块
class Config { static { System.out.println("class init"); }}静态代码块会在类加载时执行一次,通常用于初始化复杂静态资源。
入门阶段先记住两点:
- 它不是每次创建对象都执行
- 它通常只执行一次
7. 什么时候适合用 static
- 工具类方法:比如
Math、Objects、Collections - 常量:比如
static final int MAX_SIZE = 100 - 计数器:比如统计对象创建数量
- 工厂方法:比如
User.of(...)
如果某段逻辑明显依赖对象自己的状态,就不应该轻易写成 static。
最小可运行代码示例
public class StaticDemo { public static void main(String[] args) { User u1 = new User("Tom"); User u2 = new User("Jerry");
System.out.println(User.count); System.out.println(MathUtil.add(3, 5)); System.out.println(Config.APP_NAME); User.printName(u1); }}
class User { static int count = 0; private final String name;
User(String name) { this.name = name; count++; }
static void printName(User user) { System.out.println(user.name); }}
class MathUtil { static int add(int a, int b) { return a + b; }}
class Config { static final String APP_NAME = "demo";}常见陷阱与错误示例
1. 把静态字段误当成对象独有状态
静态字段是所有对象共享的。改动一次,所有实例看到的都是同一份值。
2. 在静态方法里直接访问实例字段
静态方法没有默认的“当前对象”,所以不能直接使用实例字段和实例方法。
3. 把依赖对象状态的逻辑写成静态方法
如果一个方法需要频繁读写对象字段,通常说明它更适合作为实例方法,而不是静态方法。
4. 误以为静态方法也会参与运行时多态
子类可以写同名静态方法,但这不是实例方法那种重写,而是类级别的隐藏。调用结果主要看引用类型,不看实际对象类型。
面试高频问题
1. static 修饰字段和方法分别表示什么
修饰字段表示该字段属于类,所有实例共享;修饰方法表示该方法属于类,不依赖某个对象即可调用。
2. 为什么静态方法不能直接访问实例字段
因为静态方法属于类,执行时不绑定具体对象,自然无法知道要访问哪一个实例的字段。
3. static final 常用于什么场景
最常见的是定义类级常量,比如配置项、固定字符串、固定数值等。
4. 静态方法能不能被重写
严格说不是“重写”,而是“隐藏”。实例方法有运行时多态,静态方法没有。
一句话总结
static 的本质是“把成员提升到类级别”,理解它之后,类成员和实例成员的边界就会清楚很多。