static 关键字在 Java 中有 5 种重要的用法,是理解Java类加载、内存模型以及编写高效代码的基础,也是技术面试中的高频考点。
- 静态变量:用于定义类变量,所有实例共享。
- 静态方法:属于类本身,可以通过类名调用,不能访问非静态成员。
- 静态代码块:用于静态变量的初始化,在类加载时执行一次。
- 静态内部类:独立于外部类实例创建的内部类,不能访问外部类的非静态成员。
- 静态导入:允许在使用静态成员时省略类名,简化代码。
1. 静态变量
静态变量用于定义类变量,所有实例共享,因此也叫类属性。它在类的加载阶段进行初始化。被 static 修饰的属性,在类加载后便可以使用 类名.属性名 的方式进行调用。
public class MyClass {
// 静态变量
static int staticVariable = 10;
public static void main(String[] args) {
// 通过类名访问静态变量
System.out.println(MyClass.staticVariable);
// 可以通过对象访问,但不推荐
MyClass obj = new MyClass();
System.out.println(obj.staticVariable);
}
}
2. 静态方法
静态方法属于类本身。被 static 修饰的方法,可以直接使用 类名.方法名() 来调用。
public class MyClass {
// 静态方法
static void staticMethod() {
System.out.println("This is a static method.");
}
public static void main(String[] args) {
// 通过类名调用静态方法
MyClass.staticMethod();
// 可以通过对象调用,但不推荐
MyClass obj = new MyClass();
obj.staticMethod();
}
}
需要特别注意一个关键限制:静态方法不能直接调用同类中的非静态方法,因为非静态方法依赖于具体的对象实例。而同类中的非静态方法则可以自由调用静态方法。
3. 静态代码块
静态代码块用于静态变量的初始化,它在类被加载到内存时执行,且只执行一次。其执行顺序在静态变量显式初始化之后。
public class InitializationOrder {
// 静态变量
static int var1 = initializeVar1();
static int var2 = 20;
// 静态代码块1
static {
System.out.println("Static Block 1");
System.out.println("var1 = " + var1);
System.out.println("var2 = " + var2);
var1 = 50;
}
// 静态代码块2
static {
System.out.println("Static Block 2");
System.out.println("var1 = " + var1);
System.out.println("var2 = " + var2);
var2 = 100;
}
static int initializeVar1() {
System.out.println("Initializing var1");
return 10;
}
public static void main(String[] args) {
System.out.println("Main method");
System.out.println("var1 = " + var1);
System.out.println("var2 = " + var2);
}
}
让我们分析一下这个类初始化的完整过程:
- 静态变量
var1 初始化:
- 调用
initializeVar1() 方法,输出:”Initializing var1”。
var1 被赋值为 10。
- 静态代码块1执行:
- 输出:”Static Block 1”。
- 输出:”var1 = 10″(此时
var1 已完成初始化)。
- 输出:”var2 = 20″(
var2 已完成显式赋值)。
var1 被重新赋值为 50。
- 静态代码块2执行:
- 输出:”Static Block 2”。
- 输出:”var1 = 50″(取静态代码块1修改后的值)。
- 输出:”var2 = 20″(此时
var2 还未在代码块中被修改)。
var2 被重新赋值为 100。
main 方法执行:
- 输出:”Main method”。
- 输出:”var1 = 50″(保持静态代码块2中的值)。
- 输出:”var2 = 100″(保持静态代码块2中的值)。
所以,运行结果为:
Initializing var1
Static Block 1
var1 = 10
var2 = 20
Static Block 2
var1 = 50
var2 = 20
Main method
var1 = 50
var2 = 100
4. 静态内部类
静态内部类是独立于外部类实例而存在的内部类。它不能直接访问外部类的非静态成员,但可以访问外部类的静态成员。创建它的实例时,无需先创建外部类的实例。
public class OuterClass {
static class StaticInnerClass {
void display() {
System.out.println("Inside static inner class.");
}
}
public static void main(String[] args) {
// 直接创建静态内部类的实例
OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();
inner.display();
}
}
5. 静态导入
静态导入允许我们在使用某个类的静态成员(变量或方法)时,省略类名,从而简化代码。这在频繁使用某个工具类的静态方法时(如 Math 或 Arrays)非常方便。
import static java.lang.Math.*;
public class MyClass {
public static void main(String[] args) {
// 可以直接使用Math类的静态成员,无需`Math.`前缀
System.out.println(sqrt(16)); // 相当于Math.sqrt(16)
System.out.println(PI); // 相当于Math.PI
}
}
掌握这五种用法,不仅能帮助你在面试中清晰回答相关问题,更是编写结构清晰、高效Java程序的基础。理解其背后的类加载、内存共享等机制,对于深入学习JVM也大有裨益。