基于本文回答
0
评论

Java Static关键字详解

知识点图片

本文讲解Java的static关键字。它修饰的成员属于类,被所有对象共享。这些静态成员不依赖于对象实例,可直接通过类名进行访问。

我们来详细、系统地讲解一下Java中的static关键字。

static是Java中一个非常重要的关键字,它用于修饰类的成员,包括变量、方法、代码块和内部类。static的核心思想是:被static修饰的成员属于类本身,而不是类的某个具体实例(对象)。

为了更好地理解,我们可以把一个类(Class)想象成一张“蓝图”,而对象(Object/Instance)是根据这张蓝图建造出来的“房子”。

  • 非静态成员(实例成员):像是每栋房子里各自的家具、电器。每栋房子都有自己的电视、冰箱,它们是独立的。
  • 静态成员(类成员):像是这个小区(由同一张蓝图建造的所有房子)共享的设施,比如小区的游泳池或大门。它不属于任何一栋具体的房子,而是所有房子共享的。

static可以修饰什么?

static主要用于修-饰以下四种结构:

  1. 静态变量 (Static Variables / Class Variables)
  2. 静态方法 (Static Methods / Class Methods)
  3. 静态代码块 (Static Initialization Blocks)
  4. 静态内部类 (Static Nested Classes)

下面我们逐一详细解释。

1. 静态变量 (Static Variables)

当一个变量被声明为static时,它就不再是对象的属性,而是类的属性。这意味着:

  • 内存中只有一份:无论这个类创建了多少个对象,静态变量在内存中都只有一个副本。
  • 所有对象共享:所有该类的对象共享这一个静态变量。任何一个对象修改了它的值,其他所有对象看到的值都会改变。
  • 生命周期长:它随着类的加载而创建,随着类的卸载而销毁。它的生命周期比任何对象的生命周期都长。
  • 访问方式:推荐使用 类名.静态变量名 的方式访问,虽然也可以通过 对象.静态变量名 访问,但前者更能体现其“类级别”的特性,也更清晰。

示例: 统计创建了多少个学生对象。

java
class Student {
    String name; // 实例变量,每个学生都有自己的名字
    int age;     // 实例变量,每个学生都有自己的年龄
    static int studentCount = 0; // 静态变量,所有学生对象共享

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
        studentCount++; // 每创建一个学生对象,计数器加1
    }

    public void showInfo() {
        System.out.println("Name: " + name + ", Age: " + age);
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("Initial student count: " + Student.studentCount); // 推荐访问方式

        Student s1 = new Student("Alice", 18);
        System.out.println("After creating s1, student count: " + Student.studentCount);

        Student s2 = new Student("Bob", 19);
        System.out.println("After creating s2, student count: " + Student.studentCount);

        // 不推荐,但也可以通过对象访问
        System.out.println("Accessing via s1: " + s1.studentCount); 
    }
}

输出:

plaintext
Initial student count: 0
After creating s1, student count: 1
After creating s2, student count: 2
Accessing via s1: 2

2. 静态方法 (Static Methods)

当一个方法被声明为static时,它成为一个类方法。

  • 不依赖于对象:调用静态方法不需要创建类的实例,可以直接通过 类名.方法名() 来调用。
  • 无法访问实例成员:由于静态方法不与任何特定对象关联,它不能直接访问类中的非静态(实例)变量和非静态(实例)方法。因为它不知道你想访问哪个对象的实例成员。
  • 不能使用thissuperthis关键字代表当前对象,super代表父类对象。既然静态方法不属于任何对象,自然也就不能使用thissuper
  • 可以访问静态成员:静态方法可以直接访问本类的其他静态变量和调用其他静态方法。

示例: 创建一个数学工具类。

java
class MathUtils {
    // 这是一个静态方法,因为它执行的操作不依赖于任何对象的状态
    public static int add(int a, int b) {
        return a + b;
    }

    // 这是一个实例方法,假设它需要访问某个对象的状态
    public double PI = 3.14;
    public double circleArea(double radius) {
        return PI * radius * radius; // 访问了实例变量 PI
    }

    public static void tryAccessInstanceMember() {
        // 下面这行会编译错误!
        // System.out.println(PI); // 错误: 无法从静态上下文中引用非静态 变量 PI
        // circleArea(1.0);       // 错误: 无法从静态上下文中引用非静态 方法 circleArea(double)
    }
}

public class Main {
    public static void main(String[] args) {
        // 直接通过类名调用静态方法
        int sum = MathUtils.add(5, 10);
        System.out.println("Sum: " + sum);

        // 要调用实例方法,必须先创建对象
        MathUtils myUtil = new MathUtils();
        double area = myUtil.circleArea(2.0);
        System.out.println("Area: " + area);
    }
}

最经典的静态方法例子就是 java.lang.Math 类,里面所有的方法如 Math.sqrt(), Math.max() 都是静态的。

3. 静态代码块 (Static Initialization Blocks)

静态代码块是一段用static { ... }包裹起来的代码。

  • 执行时机:它在类被加载到JVM时执行,并且只执行一次
  • 执行顺序:它的执行时机早于任何对象的创建,也早于任何静态方法的调用。如果一个类有多个静态代码块,它们会按照在代码中出现的顺序依次执行。
  • 主要用途:通常用于对静态变量进行复杂的初始化。比如,从配置文件读取信息来初始化静态变量。

示例:

java
class Config {
    public static String DB_URL;
    public static int MAX_CONNECTIONS;

    // 静态代码块
    static {
        System.out.println("Static block is executing...");
        // 模拟从配置文件加载配置
        DB_URL = "jdbc:mysql://localhost:3306/mydb";
        MAX_CONNECTIONS = 10;
    }

    // 构造函数
    public Config() {
        System.out.println("Constructor is called.");
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("Main method started.");
        System.out.println("DB URL: " + Config.DB_URL); // 第一次访问静态成员,会触发类的加载
        System.out.println("Max Connections: " + Config.MAX_CONNECTIONS);
        
        System.out.println("--------------------");
        new Config(); // 创建对象
    }
}

输出:

plaintext
Main method started.
Static block is executing...
DB URL: jdbc:mysql://localhost:3306/mydb
Max Connections: 10
--------------------
Constructor is called.

注意,静态代码块在main方法中第一次访问Config.DB_URL时执行,且只执行了一次。

4. 静态内部类 (Static Nested Classes)

一个类可以定义在另一个类的内部,这被称为内部类。如果用static修饰,它就变成了静态内部类。

  • 与外部类的关系:静态内部类不持有对外部类实例的引用。它就像一个普通的顶级类,只是恰好被“包装”在另一个类的命名空间里。
  • 实例化:创建静态内部类的实例时,不需要先创建外部类的实例。
  • 访问限制:它只能访问外部类的静态成员,不能访问外部类的实例成员。

示例: 经典的Builder设计模式就常用静态内部类实现。

java
class Computer {
    // 实例变量
    private final String CPU;
    private final String RAM;
    private final String storage;

    private Computer(Builder builder) {
        this.CPU = builder.CPU;
        this.RAM = builder.RAM;
        this.storage = builder.storage;
    }

    @Override
    public String toString() {
        return "Computer [CPU=" + CPU + ", RAM=" + RAM + ", storage=" + storage + "]";
    }

    // 静态内部类 Builder
    public static class Builder {
        private String CPU;
        private String RAM;
        private String storage;

        public Builder setCPU(String CPU) {
            this.CPU = CPU;
            return this;
        }

        public Builder setRAM(String RAM) {
            this.RAM = RAM;
            return this;
        }

        public Builder setStorage(String storage) {
            this.storage = storage;
            return this;
        }

        public Computer build() {
            return new Computer(this);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建静态内部类的实例,并用它来构建外部类对象
        Computer gamingPC = new Computer.Builder()
                .setCPU("Intel i9")
                .setRAM("32GB")
                .setStorage("1TB SSD")
                .build();
        
        System.out.println(gamingPC);
    }
}

为什么 main 方法是 public static void

  • public:因为它需要被JVM从任何地方调用。
  • static:因为程序启动时,还没有创建任何对象。JVM需要一个入口点来执行代码,而不需要先创建一个类的实例。static方法正好满足这个要求。
  • void:因为它执行完毕后,不需要返回任何值给JVM。

总结 (Key Takeaways)

特性 静态成员 (static) 实例成员 (非static)
归属 属于 属于对象 (实例)
内存 在方法区/元空间中,只有一份 在堆内存中,每个对象都有一份
生命周期 随类的加载而生,随类的卸载而灭 随对象的创建而生,随对象的销毁而灭
访问方式 类名.成员 (推荐) 对象引用.成员
方法内访问 static方法只能访问static成员 实例方法既可以访问static成员,也可以访问实例成员
this关键字 static方法中不能使用this 实例方法中可以使用this
常见用途 工具类方法、常量(static final)、共享计数器、单例模式 描述对象自身状态和行为的属性和方法
右滑查看面试常问