static 关键字在 Java 中是一个非常核心且常用的关键字,它被用来表示“静态”的,当一个成员(变量、方法、代码块或内部类)被声明为 static 时,它就不再属于类的某个具体实例,而是属于类本身。

这意味着,无论你创建了这个类的多少个对象,static 成员都只在内存中拥有一份副本,并且所有对象共享这份副本。
下面我们从 static 的四个主要作用域来详细说明:
静态变量 (Static Variables / Class Variables)
静态变量,也叫类变量,是使用 static 关键字修饰的成员变量。
核心特点:
- 属于类,不属于对象:静态变量存储在方法区的静态域中,而不是堆内存中,它不依赖于任何对象实例。
- 共享性:所有该类的对象共享同一个静态变量,任何一个对象修改了静态变量的值,这个修改对所有其他对象都是可见的。
- 生命周期:静态变量的生命周期与类相同,从类被加载到 JVM 内存中开始,直到类被卸载,静态变量才会被销毁。
- 访问方式:可以通过
类名.静态变量名或对象名.静态变量名的方式访问。推荐使用类名.的方式,因为它更能清晰地表明这是一个静态成员,且无需创建对象。
示例代码:
public class Student {
// 实例变量,每个学生对象都有自己的名字
private String name;
// 静态变量,所有学生共享学校名称
private static String schoolName = "清华大学";
public Student(String name) {
this.name = name;
}
public void study() {
System.out.println(this.name + "正在" + schoolName + "学习。");
}
public static void main(String[] args) {
// 创建两个学生对象
Student s1 = new Student("张三");
Student s2 = new Student("李四");
s1.study(); // 输出: 张三正在清华大学学习。
s2.study(); // 输出: 李四正在清华大学学习。
// 通过对象修改静态变量 (不推荐,但语法上允许)
s1.schoolName = "北京大学";
System.out.println(s2.schoolName); // 输出: 北京大学 (证明共享性)
// 推荐使用类名访问和修改
Student.schoolName = "复旦大学";
System.out.println("学校的名字现在是: " + Student.schoolName); // 输出: 学校的名字现在是: 复旦大学
}
}
典型应用场景:
- 计数器:记录创建了多少个对象。
- 共享配置:如数据库连接池的配置信息、API 的基础 URL 等。
- 常量:虽然常量更推荐使用
public static final来定义。
静态方法 (Static Methods / Class Methods)
静态方法是使用 static 关键字修饰的成员方法。

核心特点:
- 属于类,不属于对象:静态方法可以通过
类名.静态方法名()直接调用,无需创建类的实例。 - 访问限制:由于静态方法不依赖于任何对象实例,因此它只能访问类的静态成员(静态变量和静态方法),不能直接访问类的实例成员(实例变量和实例方法)。
- 原因很简单:实例成员需要通过
this关键字来指向当前对象,而静态方法调用时可能根本没有对象存在(例如通过类名.调用时)。
- 原因很简单:实例成员需要通过
- 工具方法:通常用于定义一些通用的、不依赖于对象状态的工具函数,
Math.sqrt(),Collections.sort()等。
示例代码:
public class Calculator {
// 静态变量
private static double pi = 3.14159;
// 实例变量
private String brand;
public Calculator(String brand) {
this.brand = brand;
}
// 静态方法:计算圆的面积
public static double calculateCircleArea(double radius) {
// 可以访问静态变量 pi
return pi * radius * radius;
// 下面这行代码是错误的,不能访问实例变量 brand
// return brand; // Compile Error!
}
// 实例方法:打印计算器品牌
public void printBrand() {
System.out.println("计算器品牌: " + this.brand);
// 实例方法可以访问静态方法
double area = calculateCircleArea(10);
System.out.println("计算器品牌: " + this.brand + " 计算的圆面积是: " + area);
}
public static void main(String[] args) {
// 直接通过类名调用静态方法,无需创建对象
double area = Calculator.calculateCircleArea(5.0);
System.out.println("半径为5的圆面积是: " + area); // 输出: 半径为5的圆面积是: 78.53975
// 创建对象
Calculator myCalc = new Calculator("Casio");
myCalc.printBrand();
}
}
典型应用场景:
- 工具类:如
java.lang.Math,java.util.Collections中的方法。 - 工厂方法:用于创建对象,如
java.util.Collections.emptyList()。 main方法:程序的入口点,必须是public static void main(String[] args),因为它需要在没有创建任何对象的情况下被 JVM 调用。
静态代码块 (Static Initialization Block)
静态代码块是使用 static 关键字修饰的一段代码块,用于初始化类的静态变量。
核心特点:
- 执行时机:静态代码块在类被加载到 JVM 时执行,并且只执行一次。
- 执行顺序:如果一个类中有多个静态代码块,它们会按照在代码中出现的顺序依次执行。
- 执行顺序(类加载时):静态变量声明 -> 静态代码块 -> 实例变量声明 -> 实例代码块 -> 构造器。
- 主要用途:通常用于执行那些只需要执行一次的复杂初始化操作,比如加载配置文件、初始化数据库连接池等。
示例代码:
public class DatabaseConnector {
private static String connectionString;
private static boolean isInitialized = false;
// 静态代码块
static {
System.out.println("正在加载驱动程序...");
// 模拟从配置文件中读取连接信息
connectionString = "jdbc:mysql://localhost:3306/mydb";
System.out.println("数据库连接字符串已初始化: " + connectionString);
isInitialized = true;
}
public static void connect() {
if (isInitialized) {
System.out.println("正在连接到数据库: " + connectionString);
} else {
System.out.println("数据库连接器尚未初始化。");
}
}
public static void main(String[] args) {
// 当类第一次被使用时,静态代码块会自动执行
DatabaseConnector.connect();
// 再次调用,静态代码块不会再次执行
DatabaseConnector.connect();
}
}
输出结果:
正在加载驱动程序...
数据库连接字符串已初始化: jdbc:mysql://localhost:3306/mydb
正在连接到数据库: jdbc:mysql://localhost:3306/mydb
正在连接到数据库: jdbc:mysql://localhost:3306/mydb
静态内部类 (Static Nested Class)
内部类是定义在另一个类内部的类,当内部类被 static 修饰时,它就变成了静态内部类。
核心特点:
- 无需外部类实例:创建静态内部类的对象时,不需要先创建外部类的对象。
- 访问限制:静态内部类不能直接访问外部类的非静态(实例)成员,它只能访问外部类的静态成员。
- 与普通内部类的区别:
- 普通内部类:持有外部类的引用,可以访问外部类的所有成员(包括私有的),创建时必须依赖外部类对象:
Outer.Inner inner = new Outer().new Inner(); - 静态内部类:不持有外部类的引用,只能访问外部类的静态成员,创建时不需要外部类对象:
Outer.Inner inner = new Outer.Inner();
- 普通内部类:持有外部类的引用,可以访问外部类的所有成员(包括私有的),创建时必须依赖外部类对象:
示例代码:
public class OuterClass {
private static int staticOuterVar = 10;
private int instanceOuterVar = 20;
// 静态内部类
static class StaticNestedClass {
public void display() {
// 可以访问外部类的静态成员
System.out.println("访问外部类的静态变量: " + staticOuterVar);
// 下面这行代码是错误的,不能访问外部类的实例成员
// System.out.println(instanceOuterVar); // Compile Error!
}
}
public static void main(String[] args) {
// 创建静态内部类的对象,不需要外部类的实例
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
nestedObject.display(); // 输出: 访问外部类的静态变量: 10
}
}
总结表格
| 作用域 | 关键字 | 所属 | 内存位置 | 访问限制 | 典型用途 |
|---|---|---|---|---|---|
| 静态变量 | static |
类本身 | 方法区(静态域) | 所有对象共享 | 计数器、共享配置 |
| 静态方法 | static |
类本身 | 方法区(代码区) | 只能访问静态成员 | 工具方法、main方法 |
| 静态代码块 | static |
类本身 | - | - | 类的复杂初始化 |
| 静态内部类 | static |
类本身 | - | 不能访问外部类实例成员 | 当内部类不依赖外部类实例时使用 |
static 的关键点回顾
- 生命周期:
static成员的生命周期与类相同,比对象的生命周期更长。 - 共享性:
static成员是所有对象共享的,修改会影响所有对象。 - 访问方式:优先使用
类名.来访问static成员,以代码的清晰性。 - 访问限制:
static成员(方法、代码块、内部类)不能直接访问非static成员,因为它们可能没有this引用。 - 与内存的关系:
static变量和方法存储在方法区,而不是和对象一起存储在堆中。
理解 static 的核心在于“属于类本身,而非类的某个实例”,抓住这一点,就能很好地掌握它的各种用法和限制。

