static 平时天天用——工具类的方法、常量、单例,但"静态和实例到底啥区别、静态内部类为什么不持有外部引用"这些点一直没系统梳理过。这篇把 static 从头讲透:静态变量、静态方法、静态代码块、静态内部类,以及它们和"实例成员"的根本区别。
一、核心思想:static = 属于类,不属于实例
Java 的成员(变量、方法)分两类:
- 实例成员:属于每个对象(new 一个就有自己的一份)
- 静态成员(static):属于类本身,所有实例共享一份,甚至不需要实例就能访问
java
public class Counter {
public static int total = 0; // 静态变量:属于类,所有实例共享
public int count = 0; // 实例变量:属于每个对象,各一份
public void increment() {
total++; // 静态的,共享
count++; // 实例的,自己的
}
}
Counter a = new Counter();
Counter b = new Counter();
a.increment();
System.out.println(a.total); // 1(共享,b 也能看到)
System.out.println(b.total); // 1
System.out.println(a.count); // 1(自己的)
System.out.println(b.count); // 0(b 自己的,还是 0)一句话:static 让成员"属于类",所有实例共享,不用 new 就能用。
二、静态变量:共享的"类变量"
静态变量在类加载时初始化一次,之后所有实例共享这一个变量:
java
public class Config {
public static final String APP_NAME = "BinManager"; // 静态常量:最常用
public static int visitCount = 0; // 静态变量:全局共享
}
// 用:Config.APP_NAME(不用 new)最常见的静态变量是静态常量(static final)——配置、枚举值都这么写,通过类名直接访问,全局只有一份。
三、静态方法:不用 new 就能调
静态方法属于类,通过类名调用,不用创建实例:
java
public class MathUtils {
public static int add(int a, int b) { return a + b; }
}
int sum = MathUtils.add(1, 2); // 类名调用,不用 new MathUtils()关键限制:静态方法不能访问实例成员(实例变量、实例方法)——因为它不依赖任何实例,自然也就没有"某个对象的成员"可用:
java
public class Demo {
private String name = "BinMaker"; // 实例变量
public static void staticMethod() {
// System.out.println(name); // ❌ 编译报错:静态方法不能访问实例变量
}
public void instanceMethod() {
System.out.println(name); // ✅ 实例方法能访问
}
}记忆:静态方法里只有静态成员和参数可用,碰不到"对象"。
四、静态代码块:类加载时执行一次
静态代码块在类第一次被加载时执行一次,适合做一次性的初始化:
java
public class DBConfig {
static {
System.out.println("类加载时执行一次,加载数据库配置");
// 初始化静态变量
}
}类加载一次,静态代码块就执行一次——之后不管 new 多少个对象都不会再执行。
五、静态内部类:不持有外部类引用
这是最容易混的:静态内部类和普通内部类的根本区别是"持有不持有外部类实例":
java
public class Outer {
private String name = "外部";
// 普通内部类:持有外部类实例引用,能访问外部成员
class Inner {
public void hello() {
System.out.println(name); // 能访问外部类成员
}
}
// 静态内部类:不持有外部类引用,不能访问外部实例成员
static class StaticInner {
public void hello() {
// System.out.println(name); // ❌ 没有外部类引用,访问不了
}
}
}
// 使用区别:静态内部类可以独立 new,普通内部类必须先有外部类实例
Outer.StaticInner s = new Outer.StaticInner(); // ✅ 独立创建
Outer.Inner i = new Outer().new Inner(); // 要先 new 外部类为什么有静态内部类:很多内部类其实不需要访问外部类成员(比如只是为了分组、或作为辅助结构),用 static 声明后:
- 不持有外部类引用 → 省内存(不隐式带上外部实例)
- 可以独立创建 → 不需要外部实例
经典例子:Map.Entry、Integer.IntegerCache 都是静态内部类。
六、常见场景
| 场景 | 例子 |
|---|---|
| 工具类 | Math、Collections、Arrays——全是静态方法,不用 new |
| 常量 | static final 常量,类名直接访问 |
| 单例 | 静态变量 + 私有构造(见《单例模式》) |
| 静态工厂 | Integer.valueOf() 返回静态缓存的实例(见《享元模式》) |
小结
static= 成员属于类,所有实例共享,不用 new 就能用- 静态变量:类加载时初始化一次,全局共享
- 静态方法:类名调用,不能访问实例成员
- 静态代码块:类加载时执行一次
- 静态内部类:不持有外部类引用,可独立创建,省内存
想了解 static 在面向对象里的位置,看《Java 面向对象》;想了解静态变量和实例变量的内存区别(都在 JVM 的哪块区域),看《JVM 内存模型与垃圾回收》。
