一句话总结
static 属于类而非实例,在类加载时初始化,所有实例共享。可修饰变量/方法/代码块/内部类。静态内部类不持有外部类引用(避免内存泄漏),静态方法不能重写只能隐藏(静态分派看引用类型)。初始化顺序:父类静态 → 子类静态,同级别按代码顺序。
初级理解
static 表示"静态的",被 static 修饰的成员属于类而非实例,在类加载时初始化,所有实例共享。
static 可以修饰:
1. 静态变量(类变量):所有实例共享,通过类名直接访问
2. 静态方法:属于类,不能访问非静态成员,不能使用 this/super
3. 静态代码块:类加载时执行一次,用于初始化静态变量
4. 静态内部类:不依赖外部类实例,可以直接创建
public class Counter {
private static int count = 0; // 静态变量,所有实例共享
private String name;
static { // 静态代码块,类加载时执行一次
System.out.println("类初始化");
}
public Counter(String name) {
this.name = name;
count++; // 每创建一个实例,count 加 1
}
public static int getCount() { // 静态方法
return count;
// return name; // 编译错误!不能访问非静态变量
}
static class Builder { // 静态内部类
public void build() { System.out.println("building..."); }
}
}
中级深入
静态内部类 vs 非静态内部类:
| 对比 | 静态内部类 | 非静态内部类 |
| 依赖 | 不依赖外部类实例 | 必须通过外部类实例创建 |
| 访问权限 | 只能访问外部类静态成员 | 可以访问外部类所有成员 |
| 内存 | 不持有外部类引用 | 持有外部类引用(可能内存泄漏) |
| 典型应用 | Builder 模式、工具类 | 迭代器、事件监听器 |
静态导入(JDK 5):通过 import static 可以直接使用类的静态成员,无需类名前缀。
import static java.lang.Math.*;
import static java.lang.System.out;
double r = sqrt(pow(x, 2) + pow(y, 2)); // 无需 Math.
out.println(r); // 无需 System.
高级拓展
static 与类加载时机:静态变量和静态代码块在类的初始化阶段执行。类加载分为:加载 → 验证 → 准备 → 解析 → 初始化。静态变量在准备阶段赋默认值(0/null),在初始化阶段赋代码中指定的值。
静态变量的初始化顺序:按照代码中的出现顺序依次执行。父类的静态变量先于子类初始化。
class Parent {
static { System.out.println("1. Parent 静态块"); }
}
class Child extends Parent {
static { System.out.println("2. Child 静态块"); }
public static void main(String[] args) {
System.out.println("3. main 方法");
}
}
// 输出:1 → 2 → 3
静态方法不能被重写:子类可以定义与父类同名的静态方法,但这不是重写(Override),而是隐藏(Hide)。调用哪个方法取决于引用类型而非实际类型。
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"(静态分派,看引用类型)
面试加分项:能说出静态内部类不持有外部类引用(避免内存泄漏)和静态分派机制,说明你对 JVM 有深入理解。
实战场景
场景:单例模式中的静态内部类
// 静态内部类实现单例(推荐,线程安全 + 懒加载)
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE; // 首次访问 Holder 时才加载
}
}
// 优点:利用类加载机制保证线程安全,同时实现懒加载
场景:静态变量导致的内存问题
// 反例:静态集合持有对象引用,导致无法 GC
public class DataCache {
private static Map<String, Object> cache = new HashMap<>();
public static void put(String key, Object val) {
cache.put(key, val); // 永远不会被回收!
}
}
// 正例:使用 WeakHashMap 或定期清理
private static Map<String, WeakReference<Object>> cache = new HashMap<>();
面试模拟
Q:静态方法和实例方法的区别?
A:1) 静态方法属于类,通过类名调用;实例方法属于对象,通过实例调用;2) 静态方法不能访问实例变量和实例方法,不能使用 this/super;3) 静态方法不能被子类重写,只能隐藏;4) 静态方法在类加载时分配,实例方法在创建对象时分配。
Q:静态内部类和非静态内部类的区别?
A:1) 静态内部类不依赖外部类实例,可直接 new;非静态内部类必须先有外部类实例;2) 静态内部类只能访问外部类静态成员;非静态内部类可访问所有成员;3) 静态内部类不持有外部类引用,不会导致内存泄漏;4) 静态内部类常用于 Builder 模式,非静态内部类常用于迭代器。