当你希望某个东西(变量、方法、代码块、内部类)不依赖于具体的对象,而是被所有对象共享,或者通过类名直接访问时,就应该使用 static。

下面我们从几个方面详细解释 static 的使用场景。
静态变量
用途:声明一个类级别的变量,该变量被所有该类的实例共享。
特点:
- 内存存储:静态变量存储在方法区的静态存储区,而不是堆中,整个类只有一份拷贝。
- 生命周期:随着类的加载而创建,随着类的卸载而销毁,生命周期比对象长。
- 访问方式:可以通过
类名.变量名或对象名.变量名访问,但推荐使用前者,因为它更清晰地表明这是一个静态成员。
使用场景: 当所有对象都需要共享同一个数据时。

示例:
public class Student {
// 每个学生都有自己的名字和年龄,这些是实例变量
private String name;
private int age;
// 学校名称是所有学生共享的,所以用 static
private static String schoolName = "清华大学";
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void study() {
System.out.println(this.name + " 正在 " + schoolName + " 学习。");
}
public static void main(String[] args) {
Student s1 = new Student("张三", 20);
Student s2 = new Student("李四", 21);
s1.study(); // 输出: 张三 正在 清华大学 学习。
s2.study(); // 输出: 李四 正在 清华大学 学习。
// 修改学校名称,会影响所有学生
Student.schoolName = "北京大学";
s1.study(); // 输出: 张三 正在 北京大学 学习。
s2.study(); // 输出: 李四 正在 北京大学 学习。
}
}
什么时候不用:如果每个对象都需要拥有自己独立的副本(比如每个学生的学号),那么就应该使用实例变量,而不是静态变量。
静态方法
用途:声明一个属于类的方法,而不是属于某个对象的方法。
特点:

- 访问限制:静态方法只能访问静态变量和静态方法,它不能直接访问实例变量和实例方法,因为它没有
this引指向任何具体对象。 - 调用方式:可以通过
类名.方法名()直接调用,无需创建对象。
使用场景:
- 工具类方法:当一个方法不依赖于任何对象的状态(实例变量)时,可以将其设为静态。
java.lang.Math中的Math.max(),java.util.Arrays中的Arrays.sort()。 - 工厂方法:用于创建和返回对象实例,如
java.util.Collections中的emptyList()。 - 入口方法:程序的入口点
public static void main(String[] args)必须是静态的,因为它需要在创建任何对象之前由 JVM 调用。
示例:
public class MathUtils {
// 这个方法不依赖于任何对象状态,所以是静态的
public static int add(int a, int b) {
return a + b;
}
// 这个方法依赖于实例变量 result,所以不能是静态的
private int result;
public void accumulate(int num) {
this.result += num;
}
}
public class Main {
public static void main(String[] args) {
// 直接通过类名调用,无需创建 MathUtils 对象
int sum = MathUtils.add(10, 20);
System.out.println("Sum is: " + sum); // 输出: Sum is: 30
}
}
静态初始化块
用途:在类加载时执行一次,用于初始化静态变量。
特点:
- 执行时机:当 JVM 加载类时,按顺序执行静态块,一个类可以有多个静态块,它们按在代码中出现的顺序执行。
- 执行次数:每个类只执行一次。
使用场景: 当初始化静态变量需要复杂的逻辑(如读取配置文件、建立数据库连接等)时,可以使用静态代码块。
示例:
public class DatabaseConfig {
private static String dbUrl;
private static String dbUser;
private static String dbPassword;
// 静态代码块,用于初始化静态变量
static {
System.out.println("正在加载数据库配置...");
// 模拟从配置文件读取
dbUrl = "jdbc:mysql://localhost:3306/mydb";
dbUser = "root";
dbPassword = "password";
System.out.println("数据库配置加载完成。");
}
public static void printConfig() {
System.out.println("URL: " + dbUrl);
System.out.println("User: " + dbUser);
}
public static void main(String[] args) {
// 第一次访问类时,静态块会执行
DatabaseConfig.printConfig();
// 第二次访问,静态块不会再次执行
DatabaseConfig.printConfig();
}
}
输出:
正在加载数据库配置...
数据库配置加载完成。
URL: jdbc:mysql://localhost:3306/mydb
User: root
URL: jdbc:mysql://localhost:3306/mydb
User: root
静态内部类
用途:将一个内部类声明为 static。
特点:
- 与外部类的关联:静态内部类不持有对外部类对象的隐式引用(即没有
外部类.this),它可以独立于外部类对象存在。 - 创建方式:可以直接通过
外部类名.内部类名来创建实例,无需先创建外部类对象。
使用场景: 当一个内部类不依赖于外部类的实例状态时,使用静态内部类可以避免不必要的内存开销(因为不会创建外部类的引用)。
示例:
public class OuterClass {
private String outerField = "Outer field";
// 非静态内部类(成员内部类)
class InnerClass {
public void display() {
// 可以访问外部类的成员
System.out.println(outerField);
}
}
// 静态内部类
static class StaticInnerClass {
// 不能直接访问外部类的非静态成员 outerField
// System.out.println(outerField); // 编译错误!
public static void staticDisplay() {
System.out.println("This is a static inner class method.");
}
}
}
public class Main {
public static void main(String[] args) {
// 创建静态内部类的实例,不需要 OuterClass 的实例
OuterClass.StaticInnerClass staticInner = new OuterClass.StaticInnerClass();
staticInner.staticDisplay(); // 输出: This is a static inner class method.
// 创建非静态内部类的实例,必须先有 OuterClass 的实例
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
inner.display(); // 输出: Outer field
}
}
总结与何时使用
| 成员类型 | 何时使用 | 关键特点 |
|---|---|---|
| 静态变量 | 当某个数据被所有实例共享时。 | 类级别,一份拷贝,生命周期长。 |
| 静态方法 | 当方法不依赖对象状态,或作为工具方法、工厂方法时。 | 只能访问静态成员,可通过类名直接调用。 |
| 静态代码块 | 在类加载时执行一次,用于复杂初始化。 | 按顺序执行,每个类只执行一次。 |
| 静态内部类 | 当内部类不依赖外部类实例时。 | 不持有外部类引用,可独立创建。 |
重要的注意事项
- 线程安全:静态变量是共享的,在多线程环境下对其进行修改需要注意线程安全问题。
- 内存泄漏:静态变量生命周期很长,如果它持有一个对大对象的引用(如集合、IO流等),可能会导致这些对象无法被垃圾回收,从而造成内存泄漏。
- 滥用
static:不要为了方便而滥用static,如果一个方法或变量明显是属于某个特定对象的(BankAccount的balance),就应该使用非静态的实例成员,过度使用static会使代码变得难以测试和维护,因为它增加了类之间的耦合性。
