一、什么是 static
关键字?
static
是编程语言中的关键字,用于修饰变量、方法、代码块或类成员。
核心作用:
-
延长生命周期:使变量或对象的生命周期与程序运行周期一致(而非函数或对象的生命周期)。
-
限制作用域:隐藏变量或函数,限制其可见性范围(如仅当前文件可见)。
-
共享数据:让多个对象或实例共享同一份数据(如类级别的变量或方法)。
二、static
的三种典型用法
1. 静态变量(Static Variable)
- 作用:在类或函数内部定义时,变量的生命周期与程序运行周期一致,且所有实例共享同一份数据。
- 适用场景:统计类的实例数量、缓存数据等。
-
特点:
静态变量通过类名直接访问(无需创建对象)。 - 所有对象共享同一份数据,修改会影响所有实例。
class Counter {
static int count = 0; // 静态变量,所有实例共享
Counter() {
count++; // 每次创建对象时递增
}
}
public class Main {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
System.out.println("总实例数: " + Counter.count); // 输出 2
}
}
2. 静态方法(Static Method)
- 作用:直接通过类名调用,无需创建对象。静态方法只能访问静态成员(变量或方法)。
- 适用场景:工具方法(如数学计算、字符串处理)、工厂方法等。
-
特点:
静态方法没有this
指针,不能访问非静态成员。 - 适合与类本身相关但不依赖具体实例的功能。
class MathUtils {
static int add(int a, int b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
int result = MathUtils.add(3, 5); // 直接通过类名调用
System.out.println("结果: " + result); // 输出 8
}
}
3.static关键字修饰类
在 Java 中,
static
关键字 不能直接修饰顶级类(即直接定义的类),但可以用于修饰 内部类(嵌套类),这种内部类被称为 静态内部类(Static Nested Class)。以下是详细说明:
1. 静态内部类的定义
静态内部类是定义在另一个类内部的类,并且被 static
修饰。它的语法如下:
public class OuterClass {
// 静态内部类
public static class StaticNestedClass {
// 内部类的成员和方法
}
}
2. 静态内部类的特点
1.独立于外部类实例
静态内部类的实例不需要依赖外部类的实例即可创建。
OuterClass.StaticNestedClass nested = new OuterClass.StaticNestedClass();
2.不能直接访问外部类的非静态成员
静态内部类只能访问外部类的 静态成员(变量或方法)
public class OuterClass {
private static int staticVar = 10;
private int instanceVar = 20;
public static class StaticNestedClass {
void print() {
System.out.println(staticVar); // ✅ 允许访问静态变量
// System.out.println(instanceVar); // ❌ 编译错误:无法访问非静态变量
}
}
}
-
3.可以直接通过外部类名访问
静态内部类的调用方式为:
外部类名.静态内部类名
。
4.可以包含静态和非静态成员
静态内部类可以定义自己的静态变量、方法,也可以定义实例成员。
3. 使用场景
工具类或辅助类
当某个类仅用于辅助外部类的功能,且不需要访问外部类的实例数据时,可以将其定义为静态内部类。
public class MathUtils {
public static class Calculator {
public static int add(int a, int b) {
return a + b;
}
}
}
// 使用
int result = MathUtils.Calculator.add(3, 5);
单例模式中的内部类实现
静态内部类是实现线程安全单例模式的常用方法之一。
public class Singleton {
// 私有构造方法
private Singleton() {}
// 静态内部类
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
// 获取实例
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
4. 静态内部类 vs 普通内部类
特性 | 静态内部类 | 普通内部类 |
---|---|---|
是否需要外部类实例 | 否 | 是 |
是否可以访问外部类成员 | 只能访问静态成员 | 可以访问所有成员(静态和非静态) |
实例化方式 | Outer.StaticNested outer = new Outer.StaticNested(); | Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); |
内存占用 | 不持有外部类的引用,更轻量 | 持有外部类的引用 |
5. 注意事项
-
不能直接访问外部类的非静态成员
如果需要访问外部类的实例数据,应使用普通内部类(非静态内部类)。
-
静态内部类可以定义静态成员
与普通内部类不同,静态内部类可以包含静态变量和静态方法。
-
静态内部类可以被继承
静态内部类可以被其他类继承,但继承时仍需通过外部类名访问。
6. 示例代码
public class OuterClass {
private static String staticField = "Static Field";
private String instanceField = "Instance Field";
// 静态内部类
public static class StaticNestedClass {
void print() {
System.out.println(staticField); // ✅ 可以访问静态字段
// System.out.println(instanceField); // ❌ 无法访问实例字段
}
}
// 普通内部类
public class InnerClass {
void print() {
System.out.println(staticField); // ✅ 可以访问静态字段
System.out.println(instanceField); // ✅ 可以访问实例字段
}
}
public static void main(String[] args) {
// 创建静态内部类实例
OuterClass.StaticNestedClass nested = new OuterClass.StaticNestedClass();
nested.print(); // 输出: Static Field
// 创建普通内部类实例(需要外部类实例)
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
inner.print(); // 输出: Static Field 和 Instance Field
}
}
三、静态代码块
在 Java 中,静态代码块(Static Block)是使用 static
关键字修饰的代码块,用于在类加载时执行一次性的初始化操作。以下是关于静态代码块的详细解析:
1. 静态代码块的定义
静态代码块通过 static
关键字定义,语法如下:
public class MyClass {
// 静态变量
static int staticVar;
// 静态代码块
static {
// 初始化代码
staticVar = 10;
System.out.println("静态代码块被执行");
}
}
2. 静态代码块的特点
特性 | 说明 |
---|---|
类加载时执行 | 静态代码块在类被加载到 JVM 时执行,且 只执行一次。 |
优先于主函数和构造方法 | 静态代码块的执行顺序早于主函数(main 方法)和构造方法。 |
初始化静态成员 | 主要用于初始化静态变量或执行与类相关的初始化逻辑(如加载配置文件、数据库连接池等)。 |
无法直接访问实例成员 | 不能直接访问类的实例变量或实例方法,因为实例成员依赖于对象,而静态代码块在类加载时执行(对象尚未创建)。 |
异常处理限制 | 静态代码块不能抛出受检异常(Checked Exceptions),除非捕获或包装为 RuntimeException 。 |
3. 静态代码块的执行顺序
-
类首次加载时:
- 父类的静态代码块 → 子类的静态代码块
- 静态变量初始化 → 静态代码块(按代码中出现的顺序执行)
-
创建对象时:
- 实例代码块 → 构造方法
class Parent {
static { System.out.println("父类静态代码块"); }
}
public class Child extends Parent {
static { System.out.println("子类静态代码块"); }
public static void main(String[] args) {
System.out.println("主方法开始");
new Child();
}
}
结果:
父类静态代码块
子类静态代码块
主方法开始
4. 静态代码块的使用场景
1. 初始化静态变量
当静态变量的初始化需要复杂逻辑时,使用静态代码块封装初始化逻辑:
2. 加载静态资源
加载配置文件
注册 JDBC 驱动
3. 初始化静态集合
4. 类加载时的验证或检查
5. 静态代码块 vs 实例代码块
特性 | 静态代码块 | 实例代码块 |
---|---|---|
执行时机 | 类加载时执行,只执行一次 | 每次创建对象时执行 |
作用 | 初始化静态变量或类级别的资源 | 初始化实例变量或对象级别的资源 |
访问权限 | 只能访问静态成员 | 可以访问静态和非静态成员 |
语法 | static { ... } | { ... } |
public class Demo {
// 静态代码块
static {
System.out.println("静态代码块");
}
// 实例代码块
{
System.out.println("实例代码块");
}
public Demo() {
System.out.println("构造方法");
}
public static void main(String[] args) {
new Demo(); // 输出:静态代码块 -> 实例代码块 -> 构造方法
new Demo(); // 输出:实例代码块 -> 构造方法
}
}