--- title: "02-static关键字" created: 2025-11-26 tags: - Java --- # static关键字 ## 核心概念 `static`关键字用于定义类级别的成员和方法,它改变了变量和方法的生命周期和作用域。深入理解`static`的使用,是掌握Java面向对象编程的关键。 --- ## 一、静态变量(类变量) ### 1.1 基本用法 ```java public class StaticVariableDemo { // 类变量:所有实例共享 public static int count = 0; // 实例变量:每个对象独立 private int instanceCount = 0; public StaticVariableDemo() { count++; // 所有实例共享 instanceCount++; // 每个实例独立 } } // 使用 StaticVariableDemo obj1 = new StaticVariableDemo(); StaticVariableDemo obj2 = new StaticVariableDemo(); System.out.println(StaticVariableDemo.count); // 2 System.out.println(obj1.count); // 2 ``` ### 1.2 内存模型 ```java public class StaticMemoryDemo { // 静态变量存储在方法区 private static final int[] STATIC_ARRAY = new int[1000]; // 实例变量存储在堆中 private int[] instanceArray = new int[100]; } ``` --- ## 二、静态方法 ### 2.1 基本特征 ```java public class StaticMethodDemo { // 静态方法:不依赖实例 public static int add(int a, int b) { return a + b; } // 不能直接访问非静态成员 public static void staticMethod() { // instanceMethod(); // ❌ 编译错误 // name = "test"; // ❌ 编译错误 } // 实例方法 public void instanceMethod() { staticMethod(); // ✅ 可以调用静态方法 } } // 使用 int result = StaticMethodDemo.add(1, 2); ``` ### 2.2 工具类设计 ```java // 典型的工具类设计 public final class MathUtils { private MathUtils() { // 防止实例化 throw new AssertionError("Cannot be instantiated"); } public static int max(int a, int b) { return a > b ? a : b; } public static double sqrt(double x) { return Math.sqrt(x); } } ``` --- ## 三、静态代码块 ### 3.1 初始化顺序 ```java public class StaticBlockDemo { // 静态变量 private static int staticVar = initStaticVar(); // 静态代码块 static { System.out.println("静态代码块1"); staticVar = 100; } // 实例变量 private int instanceVar = initInstanceVar(); // 实例代码块 { System.out.println("实例代码块"); } // 构造函数 public StaticBlockDemo() { System.out.println("构造函数"); } private static int initStaticVar() { System.out.println("静态变量初始化"); return 0; } private int initInstanceVar() { System.out.println("实例变量初始化"); return 0; } } // 初始化顺序 new StaticBlockDemo(); ``` **初始化顺序:** 1. 静态变量 2. 静态代码块 3. 实例变量 4. 实例代码块 5. 构造函数 ### 3.2 单例模式示例 ```java public class Singleton { // 私有静态实例 private static Singleton instance; // 静态代码块初始化 static { try { instance = new Singleton(); } catch (Exception e) { throw new RuntimeException("Singleton init error"); } } // 私有构造函数 private Singleton() {} // 获取实例 public static Singleton getInstance() { return instance; } } ``` 这段代码实现了 **单例模式**(Singleton Pattern),是用于确保类只有一个实例,并提供一个全局访问点的设计模式。 下面是对代码逐行的解释: 1. `private static Singleton instance;` - 这行声明了一个 `private` 的静态变量 `instance`,用于存储单例的唯一实例。`static` 关键字确保该变量属于类,而不是类的实例,因此它在所有对象中共享。 2. `static { ... }` - 这是一个 **静态代码块**(Static Block)。静态代码块在类加载时执行一次,通常用于初始化静态变量。 - 在静态代码块中,实例 `instance` 被初始化为 `new Singleton()`,即创建了 `Singleton` 类的唯一实例。 3. `private Singleton() {}` - 这是 **私有构造函数**,确保外部无法直接通过 `new Singleton()` 来创建 `Singleton` 类的对象。 - 通过将构造函数设为 `private`,可以避免外部创建多个实例,从而保证只有一个实例。 4. `public static Singleton getInstance() { ... }` - 这是 **公共静态方法**,用于返回唯一的实例。通过 `getInstance()` 方法,用户可以访问 `instance`,而不能直接访问 `instance` 变量。 - 由于 `instance` 是静态变量,`getInstance()` 方法也是静态的。这样,用户无需创建 `Singleton` 对象,就可以直接通过 `Singleton.getInstance()` 获取实例。 5. 错误处理 (`try-catch`): - 静态代码块中有一个 `try-catch` 语句,捕获实例化时可能发生的异常,并抛出一个 `RuntimeException`。 - 这种做法是为了在实例化过程中出现异常时,能够提供明确的错误信息。 总结: - **单例模式**确保 `Singleton` 类在整个应用中只有一个实例。该实例是通过静态代码块在类加载时初始化的。 - 通过私有构造函数,外部无法直接创建多个实例,确保了类的唯一性。 - 通过公共静态方法 `getInstance()`,用户可以访问这个唯一实例。 这种实现方式是 **懒汉式**(Lazy Initialization),即只有在第一次调用 `getInstance()` 方法时才会创建实例。 --- ## 四、内存泄漏风险 ### 4.1 静态集合导致的内存泄漏 ```java public class MemoryLeakDemo { // 静态集合可能导致内存泄漏 private static final List staticList = new ArrayList<>(); public void addToList(Object obj) { staticList.add(obj); // 对象永远不会被回收 } } ``` ### 4.2 解决方案 ```java public class SafeMemoryDemo { // 使用软引用或弱引用 private static final List> safeList = new ArrayList<>(); public void addToList(Object obj) { safeList.add(new WeakReference<>(obj)); } } ``` --- ## 五、高频面试题 ### Q1: static方法能否访问非static方法? **答:** - 不能直接访问 - 需要先创建对象,通过对象调用 ### Q2: static代码块的执行时机? **答:** - 类加载时执行 - 仅执行一次 - 早于实例初始化 ### Q3: 静态变量和实例变量有什么区别? **答:** - 静态变量:类级别,所有实例共享 - 实例变量:对象级别,每个实例独立 ### Q4: 为什么main方法是static的? **答:** - 程序入口无需创建对象 - JVM直接通过类名调用 - 提高程序启动效率 ### Q5: 静态内部类和非静态内部类有什么区别? **答:** - 静态内部类:不持有外部类引用 - 非静态内部类:持有外部类引用 - 静态内部类可以有静态成员 --- ## 六、最佳实践 ### ✅ 推荐做法 ```java // 1. 工具类设计 public final class StringUtils { private StringUtils() {} public static boolean isEmpty(String str) { return str == null || str.trim().isEmpty(); } } // 2. 单例模式 public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() { return INSTANCE; } } ``` ### ❌ 避免做法 ```java // 1. 避免过度使用静态变量 public class BadDesign { private static Map globalCache; // 不推荐 } // 2. 滥用静态方法 public class MathHelper { public static void complexCalculation() { // 过于复杂的静态方法 } } ``` --- ## 相关链接 - [[01-final关键字|01-final关键字]] - 单例模式(见 [[04-设计模式体系|设计模式体系]]) - [[02-类加载机制|类加载机制]] --- ## 记忆口诀 ``` static修饰类级别,变量方法皆可栖 所有实例共享用,无需创建对象时 初始化有先后,静态块最先执 工具类单例伴,内存泄漏要当心 ``` --- ⬅️ [[01-final关键字|final关键字]] 🏠 [[00-Java|00-Java]] ➡️ [[03-super与this关键字|super与this关键字]]