杰瑞科技汇

static变量为何属于类而非对象?

什么是静态变量?

在 Java 中,使用 static 关键字修饰的成员变量被称为静态变量类变量

static变量为何属于类而非对象?-图1
(图片来源网络,侵删)

与它相对的是实例变量(没有使用 static 修饰的变量)。

核心区别在于:它们的生命周期和所有者不同

  • 实例变量:属于对象,每创建一个对象,就会为这个对象分配一套独立的实例变量,它们存储在堆内存中,其生命周期与对象的生命周期相同。
  • 静态变量:属于,它不依赖于任何具体的对象,当类被加载到内存时,静态变量就会被分配内存空间,它的生命周期与类的生命周期相同,直到程序结束才会被销毁,无论你创建了多少个对象,静态变量在内存中只有一份拷贝,被所有对象共享。

静态变量的核心特性

1 内存分配与生命周期

  • 分配时机:静态变量在类加载时分配内存,而不是在创建对象(new)时。
  • 生命周期:从类加载开始,到整个应用程序结束,它的生命周期比任何对象都长。
  • 存储位置:通常存在于方法区(在 JDK 7 及之前)或元空间(在 JDK 8 及之后)的运行时常量池中。

2 共享性

这是静态变量最重要的特性,所有该类的实例都共享同一个静态变量,一个对象对静态变量的修改,会影响到所有其他对象。

3 访问方式

由于静态变量不依赖于对象,所以有两种访问方式:

static变量为何属于类而非对象?-图2
(图片来源网络,侵删)
  1. 通过类名访问(推荐)

    ClassName.staticVariableName;

    Student.studentCount

  2. 通过对象名访问(不推荐,但语法允许)

    objectName.staticVariableName;

    s1.studentCount

    static变量为何属于类而非对象?-图3
    (图片来源网络,侵删)

为什么不推荐通过对象名访问? 因为静态变量属于类,而不是对象,通过对象名访问容易造成混淆,让其他开发者误以为这个变量是对象独有的,最佳实践是始终使用类名来访问静态成员。


静态变量 vs. 实例变量

为了更清晰地理解,我们用一个表格来对比:

特性 静态变量 (类变量) 实例变量 (对象变量)
所属 属于 属于对象
内存拷贝 整个类只有一份拷贝,所有对象共享 每创建一个对象,就会产生一份独立的拷贝
内存分配 类加载时分配 创建对象 (new) 时分配
生命周期 与类相同,随类的加载而创建,随类的卸载而销毁 与对象相同,随对象的创建而创建,随对象的回收而销毁
访问方式 类名.变量名 (推荐) 或 对象名.变量名 (不推荐) 对象名.变量名
典型用途 存储所有对象共享的数据,如:计数器、配置信息、常量等 存储对象特有的数据,如:姓名、年龄、ID等

代码示例

这个例子将完美地展示静态变量的“共享”特性。

public class Student {
    // 实例变量:每个学生有自己的名字和年龄
    private String name;
    private int age;
    // 静态变量:所有学生共享一个班级,记录学生总数
    private static String className = "计算机科学1班";
    private static int studentCount = 0; // 静态变量,用于计数
    // 构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
        // 每当创建一个新学生时,学生总数加1
        studentCount++;
        System.out.println(name + " 同学加入了班级 " + className);
        System.out.println("当前班级总人数: " + studentCount);
    }
    // 静态方法:可以访问静态变量,但不能直接访问实例变量
    public static void printClassName() {
        System.out.println("班级名称是: " + className);
        // 下面这行代码会编译错误,因为静态方法不知道具体是哪个对象的name
        // System.out.println("学生名字是: " + this.name); 
    }
    public static void main(String[] args) {
        System.out.println("--- 开始招生 ---");
        // 创建第一个学生对象
        Student s1 = new Student("张三", 20);
        System.out.println("s1 的内存地址: " + s1);
        System.out.println("s1 访问的 studentCount: " + s1.studentCount); // 通过对象名访问
        System.out.println("通过类名访问的 studentCount: " + Student.studentCount); // 通过类名访问 (推荐)
        System.out.println("--------------------");
        // 创建第二个学生对象
        Student s2 = new Student("李四", 21);
        System.out.println("s2 的内存地址: " + s2);
        System.out.println("s2 访问的 studentCount: " + s2.studentCount);
        System.out.println("--------------------");
        // 创建第三个学生对象
        Student s3 = new Student("王五", 22);
        System.out.println("s3 的内存地址: " + s3);
        System.out.println("s3 访问的 studentCount: " + s3.studentCount);
        System.out.println("--------------------");
        // 验证共享性:通过 s1 修改静态变量
        System.out.println("现在通过 s1 修改班级名称...");
        s1.className = "软件工程2班"; // 虽然语法允许,但不推荐!
        // 推荐写法: Student.className = "软件工程2班";
        // 检查 s2 和 s3 的班级名称是否也改变了
        System.out.println("s2 的班级名称: " + s2.className); // 输出: 软件工程2班
        System.out.println("s3 的班级名称: " + s3.className); // 输出: 软件工程2班
        // 调用静态方法
        Student.printClassName();
    }
}

运行结果分析:

--- 开始招生 ---
张三 同学加入了班级 计算机科学1班
当前班级总人数: 1
s1 的内存地址: com.example.Student@15db9742
s1 访问的 studentCount: 1
通过类名访问的 studentCount: 1
--------------------
李四 同学加入了班级 计算机科学1班
当前班级总人数: 2
s2 的内存地址: com.example.Student@6d06d69c
s2 访问的 studentCount: 2
--------------------
王五 同学加入了班级 计算机科学1班
当前班级总人数: 3
s3 的内存地址: com.example.Student@7852e922
s3 访问的 studentCount: 3
--------------------
现在通过 s1 修改班级名称...
s2 的班级名称: 软件工程2班
s3 的班级名称: 软件工程2班
班级名称是: 软件工程2班

从结果可以看出:

  1. studentCount 从 0 开始,每创建一个对象就自增 1,证明所有对象共享同一个 studentCount
  2. 通过 s1 修改 className 后,s2s3 访问到的 className 也变成了新值,证明所有对象共享同一个 className

静态变量的典型用途

  1. 计数器:如上面的例子,用来统计创建了多少个对象。
  2. 共享的配置或常量:数据库连接池的配置、API 的基础 URL 等,所有对象都使用同一个配置。
  3. 工具类中的共享状态:虽然工具类通常是无状态的,但有时也需要一个共享的缓存或计数器。
  4. 定义常量:在 Java 中,通常使用 public static final 来定义一个常量。static 表示它属于类,一份拷贝;final 表示它的值不能被修改。
    public class MathUtils {
        public static final double PI = 3.14159;
    }
    // 使用方式
    double radius = 5;
    double circumference = 2 * MathUtils.PI * radius;

静态变量的限制

  1. 不能在静态方法中访问非静态成员:静态方法在类加载时就可能存在,此时对象可能还未创建,静态方法无法知道具体是哪个对象的实例变量,所以不能直接访问实例变量或实例方法,反之,实例方法可以访问静态成员,因为它已经依附于一个对象,而这个对象所属的类是已知的。
  2. 不能使用 thissuper 关键字this 代表当前对象的引用,而静态成员不属于任何特定对象,this 在静态上下文中没有意义。

特性
本质 静态变量是类变量,属于类本身,而不是类的实例。
内存 在类加载时分配,整个 JVM 中只有一份拷贝,被所有实例共享。
生命周期 从类加载开始,到类卸载结束,比对象的生命周期长。
访问 推荐使用 类名.变量名 的方式访问。
用途 适用于所有对象共享的数据,如计数器、共享配置、常量等。
限制 不能在静态方法中直接访问实例变量,也不能使用 this

理解 static 变量是掌握 Java 面向对象编程的关键一步,它能帮助你更好地管理内存和设计高效的程序结构。

分享:
扫描分享到社交APP
上一篇
下一篇