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