static 是 Java 中一个非常核心和重要的关键字,它被用来表示“静态的”或“属于类的”,当一个成员(变量、方法、代码块或内部类)被声明为 static 时,它就不再属于类的某个具体实例(对象),而是直接属于类本身。
你可以把 static 成员想象成一个“共享的资源”,这个资源被该类的所有对象所共享,而不是每个对象都拥有一个独立的副本。
下面我们从几个方面来深入理解 static。
静态变量 (Static Variables / Class Variables)
静态变量是与类相关联的变量,而不是与对象实例相关联。
特点:
- 唯一性:一个类的静态变量在内存中只有一份副本,无论你创建了多少个该类的对象,它们都共享这一个静态变量。
- 生命周期:静态变量的生命周期与类相同,当类被加载到 JVM 中时,它就被创建;当类被卸载时,它才被销毁,它的生命周期比对象的创建和销毁要长。
- 访问方式:可以通过
类名.变量名或对象名.变量名的方式来访问,但推荐使用类名.变量名,这样更能清晰地表明它是一个静态成员。
示例代码:
public class Counter {
// 这是一个静态变量,属于Counter类,而不是Counter的某个对象
static int count = 0;
public Counter() {
// 每次创建对象时,count都会自增
count++;
}
}
public class Main {
public static void main(String[] args) {
// 创建第一个对象
Counter c1 = new Counter();
System.out.println("c1.count: " + c1.count); // 输出 1
System.out.println("Counter.count: " + Counter.count); // 推荐这样访问,输出 1
// 创建第二个对象
Counter c2 = new Counter();
System.out.println("c2.count: " + c2.count); // 输出 2 (因为c1和c2共享同一个count)
System.out.println("Counter.count: " + Counter.count); // 输出 2
// 创建第三个对象
Counter c3 = new Counter();
System.out.println("Counter.count: " + Counter.count); // 输出 3
}
}
分析:
Counter.count在内存中只有一份。- 每次调用
new Counter()构造函数时,所有Counter对象共享的count都会加 1。
静态方法 (Static Methods / Class Methods)
静态方法是属于类的方法,而不是属于对象的方法。
特点:
- 无隐式
this参数:静态方法不能直接访问类的实例变量(非静态变量)和实例方法,因为它不依赖于任何具体的对象实例,没有this引用来指向当前对象。 - 访问限制:静态方法只能直接访问:
- 同一个类中的其他静态成员(静态变量和静态方法)。
- 它自己的局部变量。
- 调用方式:同样可以通过
类名.方法名()或对象名.方法名()来调用,推荐使用类名.方法名()。
常见用途:
- 工具类方法,如
Math.sqrt()、Collections.sort()。 - 工厂方法,用于创建和返回对象。
- 程序的入口点
public static void main(String[] args)。
示例代码:
public class Calculator {
// 静态变量
static double PI = 3.14159;
// 静态方法:计算圆的面积
// 注意:这个方法没有使用任何实例变量
public static double calculateArea(double radius) {
return PI * radius * radius;
}
// 实例方法:需要一个Calculator对象才能调用
public void printInfo() {
System.out.println("This is a Calculator instance.");
}
}
public class Main {
public static void main(String[] args) {
// 通过类名直接调用静态方法
double area = Calculator.calculateArea(10.0);
System.out.println("Area: " + area); // 输出 Area: 314.159
// 也可以通过对象名调用,但不推荐
Calculator calc = new Calculator();
double area2 = calc.calculateArea(5.0);
System.out.println("Area2: " + area2); // 输出 Area2: 78.53975
// 调用静态变量
System.out.println("PI: " + Calculator.PI);
// calc.printInfo(); // 这是可以的,因为printInfo是实例方法
// Calculator.printInfo(); // 这是错误的!编译器会报错
}
}
分析:
calculateArea是一个纯计算功能,不依赖于任何 Calculator 对象的状态,所以非常适合定义为静态方法。printInfo需要一个对象才能执行,所以是实例方法,不能通过Calculator.printInfo()调用。
静态代码块 (Static Initialization Block)
静态代码块是一段用 包裹的代码,前面加上 static 关键字。
特点:
- 执行时机:静态代码块在类被加载到 JVM 时执行,并且只执行一次,通常用于初始化静态变量。
- 执行顺序:如果一个类中有多个静态代码块,它们会按照在代码中出现的顺序依次执行。
- 执行时机在构造函数之前:静态代码块的执行优先于任何构造函数。
示例代码:
public class StaticBlockDemo {
static int staticVar;
// 静态代码块
static {
System.out.println("静态代码块1被执行。");
staticVar = 100; // 初始化静态变量
}
// 第二个静态代码块
static {
System.out.println("静态代码块2被执行。");
}
public StaticBlockDemo() {
System.out.println("构造函数被执行。");
}
public static void main(String[] args) {
System.out.println("main方法开始。");
System.out.println("创建第一个对象...");
new StaticBlockDemo();
System.out.println("创建第二个对象...");
new StaticBlockDemo();
}
}
输出结果:
静态代码块1被执行。
静态代码块2被执行。
main方法开始。
创建第一个对象...
构造函数被执行。
创建第二个对象...
构造函数被执行。
分析:
- 静态代码块只在类第一次被加载时执行了一次,之后创建对象时不会再执行。
- 构造函数在每次创建对象时都会执行。
静态内部类 (Static Nested Class)
内部类(嵌套类)被声明为 static 时,就变成了静态内部类。
特点:
- 与外部类实例解耦:静态内部类不持有对外部类对象的隐式引用(即没有
this指向外部类),创建静态内部类的对象时,不需要先创建外部类的对象。 - 访问限制:它可以访问外部类的所有静态成员(包括私有静态成员),但不能直接访问外部类的实例成员。
- 创建方式:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
示例代码:
public class OuterClass {
private static int outerStaticVar = 10;
private int outerInstanceVar = 20;
// 静态内部类
static class StaticInnerClass {
public void display() {
// 可以访问外部类的静态成员
System.out.println("外部类的静态变量: " + outerStaticVar);
// 不能访问外部类的实例成员,下面这行代码会编译报错
// System.out.println("外部类的实例变量: " + outerInstanceVar);
}
}
}
public class Main {
public static void main(String[] args) {
// 创建静态内部类的对象,不需要先创建OuterClass的对象
OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();
inner.display(); // 输出: 外部类的静态变量: 10
}
}
static 的关键点总结
| 特性 | 非静态成员 (实例成员) | 静态成员 (static) |
|---|---|---|
| 所属 | 属于对象 (Object) | 属于类 (Class) |
| 内存分配 | 每创建一个对象,就会分配一份 | 在类加载时只分配一份,所有对象共享 |
| 访问方式 | 只能通过 对象名.成员名 访问 |
可通过 类名.成员名 或 对象名.成员名 访问 |
| 生命周期 | 随对象的创建而创建,随对象的销毁而销毁 | 随类的加载而创建,随类的卸载而销毁 |
| 访问权限 | 可以访问类的所有成员(静态和非静态) | 只能访问类的静态成员,不能直接访问实例成员 |
| 用途 | 描述对象特有的属性和行为 | 描述类级别的共享数据、工具方法、常量等 |
常见误区
- “
static方法里不能有this”:这是正确的,因为static方法不与任何对象绑定,所以没有this。 - “
static方法里不能调用非static方法”:这也是正确的,因为static方法不知道要调用的是哪个对象的非static方法。 - 过度使用
static:不要仅仅为了不创建对象而滥用static,如果一个方法或变量依赖于对象的状态,就应该定义为非静态的。static应该用于那些真正属于类级别的、共享的概念。
希望这个详细的解释能帮助你完全理解 Java 中的 static 关键字!
