【Java】static —— 静态变量

一、引入

static翻译过来就是静态的意思。

首先来看一个需求:写一个JavaBean类来描述这个班级的学生。

属性:姓名、年龄、性别

行为:学习

从今天起我们涉及到的类就有很多个了,因此从现在开始每一个练习创建的不是一个类,而是一个包。而在IDEA中,包名是按照字母从小到大进行排序的,因此为了让大家在课后复习的时候更方便,就在前面加了一个 a01 ,就表示是序号的意思,表示第一题,a02 表示第2题。但由于起名字不能以数字开头,因此在数字前面都加了一个 a

image-20240410150004348

Student.java

package com.itheima.a01staticdemo1;

public class Student {
    //属性:姓名 年龄 性别
    //新增:老师的姓名
    private String name;
    private int age;
    private String gender;
    public String teacherName;

    public Student() {
    }

    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    //行为
    public void study() {
        System.out.println(name + "正在学习");
    }

    public void show() {
        System.out.println(name + ", " + age + ", " + gender + ", " + teacherName);
    }
}

StudentTest.java

package com.itheima.a01staticdemo1;

import java.util.Random;

public class StudentTest {
    public static void main(String[] args) {
        //1.创建第一个学生对象
        Student s1 = new Student();
        s1.setName("张三");
        s1.setAge(23);
        s1.setGender("男");
        s1.teacherName = "阿玮老师";

        s1.study();
        s1.show();

        //2.创建第二个学生对象
        Student s2 = new Student();
        s2.setName("李四");
        s2.setAge(24);
        s2.setGender("女");
        s2.teacherName = "阿玮老师";

        s2.study();
        s2.show();
    }
}

由于一个班只有一个老师,teacherName 应该是共享的。既然是共享的,那每一次都需要用对象去调用 teacherName 去赋值,太麻烦了。能不能只赋值一次,就让这个类所有对象都共享一个值。这必定是可以的。

解决办法:在 teacherName 前面加一个修饰符 —— static,一旦加上之后,Strudent 这个类所有的对象都共享同一个老师的姓名了。

public String teacherName;

此时当第一个学生对 teacherName 赋值的时候,所有的对象再去获取 teacherName 的时候就已经有值了。

s1.teacherName = "阿玮老师";

并且一旦用 static 修饰后,它还多了一种调用方式:直接用类名去调用。

Student.teacherName = "阿玮老师";

二、概念

static:static表示静态,是Java中的一个修饰符,可以修饰成员方法、成员变量。

因此我们需要将 static 修饰成员方法、 static 修饰成员变量分开去学习。


三、static 修饰成员变量

成员变量一旦被 static 修饰之后,这个成员变量就叫做 静态变量

静态变量 的特点:

  • 被这个类所有对象共享

  • 不属于对象,属于类

  • 随着类的加载而加载,优先于对象存在

    而对象一定要等 new 关键字执行了,它才在内存中出现

调用方式:

  • 类名调用(推荐)
  • 对象名调用

至于推荐类名调用的原因:既然所有的对象都共享这个属性,这个属性就不属于某个特定的类,因此用一个特定的对象去调用它,在语法中是可以的,但是不合理,因此我们需要用 类名调用


四、static 内存图

为了内存图尽可能让大家理解,所以下面代码的成员变量前面都没有加 private

程序刚开始启动,肯定是main方法先进栈。

然后执行main方法中的第一行代码中:Student.teacherName = "阿玮老师",在这行代码中,使用类名调用了 Student 类中的静态变量 teacherName,并赋值为 "阿玮老师"。此时就用到了 Student 类。因此在内存中,就会将 Student 类的字节码文件,加载到方法区,并在内存中创建了一个 单独存放 静态变量的空间,我们可以把这个空间叫做 静态区

因此当 Student 的字节码文件加载到方法区后,静态区 就出现了。

在JDK8以前,静态区是在方法区里面的。JDK8以后,就挪到了堆空间当中。

在静态区中就存着这个内所有的静态变量,例如 teacherName。由于 teacherName 为引用数据类型,因此默认初始化值为 null

要注意的是,现在内存中并没有对象,因为我们代码还没有执行到 new 关键字,只有 new 关键字执行了,在内存当中才有对象!

由此可见:静态变量是随着类的加载而加载的,优先于对象出现的。

在以后我们还会用 static 去修饰其他的内容,其他的内容也会去遵守这个规则,只要使用 static 修饰的,都是随着类的加载而加载,加载的时候是优先于对象出现的。

image-20240410160029254

此时第一行还没完,等号的右边还需要赋值,因此它会将 静态区 中的 teacherNamer 赋值为 "阿玮老师",原来的 null 就会被覆盖。

image-20240410160625737

接下来再来看第二行代码:Student s1 = new Student();,这个时候就出现 new 关键字了,此时对象在内存当中才会出现。

等号的左边相当于在栈的main方法中定义了一个变量 s1,等号的右边有 new 关键字了,所以就在堆内存中开辟了一个空间。

假设这个空间的地址值是 0x0011 ,这个空间也是我们平时所说的对象。

在这个空间里面存储的是所有的非静态的变量:nameage,并进行默认初始化 null0

再将这个小空间的地址赋值给 s1,因此 s1 记录的地址就是 0x0011

image-20240410161326967

现在如果我想通过 s1 去访问静态变量 teacherName,此时就回去静态区中去找对应的变量。

s1.name = "张三",这里的 s1 记录的是 0x0011,这行代码就相当于将 "张三" 赋值给 0x0011name。右上角的 null 就会被 "张三" 给覆盖。

再往下,s1.age = 23s1 记录的是 0x0011,因此这行我们就可以这么理解:把 23 赋值给 0x0011age。右上角的 0 就会被 23 给覆盖。

image-20240410161815444

再往下:s1.show(),用 s1 去调用 show() 方法。show() 方法就会被加载进栈,因为此时 show() 方法的调用者是 s1,就会通过 s1 去找里面的 nameage,然后再去找到静态区里面的 teacherName

因此在控制台中打印的就是 张三...23...阿玮老师

image-20240410162603733

此时方法执行完毕,show() 方法出栈。

然后执行:Student s2 = new Student();,创建了第二个对象 s2,等号的右边还是有 new 关键字,因此在堆中又开辟了一个新的小空间,假设地址为 0x0022。在这个小空间里面,它就会存储所有非静态的成员变量 nameage,并默认初始化为 null0

如果我想通过 s2 去获取静态变量,它也可以到下面静态区找到 teacherName

最后再将 0x0011 的地址赋值给左边的变量 s2

image-20240410164456555

继续,s2.show(),通过 s2 再去调用 show() 方法,此时 show() 方法就会被加载进栈。

又因为此时 show() 方法的调用者是 s2,就会通过 s2 去找里面的 nameage,然后再去找到静态区里面的 teacherName

因此在控制台中打印的就是 null...0...阿玮老师

image-20240410164918482

然后 show() 方法执行完毕,出栈。

通过上面的内存图接,我们可以看到里面的核心点。静态区里面的变量,是对象共享的,在内存中只有一份,谁要用谁去拿。

而非静态的东西,例如 nameage,都是每个对象独有的,每个对象都会单独存放一份,这就是它们的区别。


五、练习:请说出以下属性是否可以被定义为静态

在以后当中,以后有哪些属性可以用 static 来修饰呢?

其实很简单,我们只需要抓住两个字:共享。只要是所有对象都共享的,就必须要用 static 去修饰。

案例:在 Student 中有以下五个属性。有哪些属性可以被 static 修饰呢?

image-20240410165646113

是否能被 static 修饰,就看两个字:共享。但是这个共享不是绝对的,我们要看具体的业务场景。

1、name

如果说 namestatic 去修饰了,就表示说有的学生都共享同一个姓名,这种情况可能性非常少,因此这个 name 我们不会用 static 去修饰。

2、age

如果说 agestatic 去修饰了,就表示说有的学生都共享同一个年龄,但每个学生的年龄也是不一样的,因此 age 也不会用 static 去修饰。

3、teacherName

如果现在这个 Student 表示一个班的学生,一个班的学生老师肯定是共享的,因此在这种情况下,teacherName 就必须用 static 去修饰。

但是还有种情况,就是你放学之后,你自己给自己请的私人家教,它就不是共享的了,因此在这种情况下,老师的姓名就不能加 static 修饰了。

因此在不同情况下是否用 static 修饰,你要自己想,抓住一个核心点:共享

### Java Static 变量与全局变量的区别和用法 #### 静态变量 (Static Variables) 静态变量属于类而非对象实例,在整个程序运行期间只存在一份副本。声明静态变量的方式是在其定义前加上`static`关键字。 ```java public class Example { public static int count = 0; // 定义了一个名为count的静态整型变量 } ``` 当创建多个该类的对象时,这些对象共享同一个静态成员变量[^1]。这意味着如果其中一个对象修改了这个静态字段,则其他所有对该字段访问都会看到更新后的值。 #### 所谓“全局变量” 需要注意的是,“全局变量”的概念并不适用于Java语言;相反,某些编程环境允许在整个应用程序范围内可见并可访问的数据项被称为全局变量。然而,在Java中并没有真正意义上的全局变量——所有的变量都必须被封装在一个特定的作用域内,比如方法内部、构造函数里或者是作为某个类的一部分而存在的属性。 对于那些希望模拟全局行为的需求来说,可以考虑使用公共静态(final)常量或单例模式来实现相似的功能: - **公共静态常量**: 使用`public static final`修饰符定义不可变的数值。 ```java public class Constants { public static final double PI = 3.14; } ``` - **单例模式(Singleton Pattern)**: 创建一个仅能拥有唯一实例并且提供全局访问点的类。 ```java public class SingletonExample { private static SingletonExample instance; private SingletonExample() {} public static synchronized SingletonExample getInstance() { if (instance == null) { instance = new SingletonExample(); } return instance; } // Other methods... } ``` 因此,在讨论Java中的静态变量与所谓的“全局变量”之间的差异时,实际上是指如何通过不同方式达到跨作用域数据共享的目的。真正的区别在于前者是面向对象设计原则下的特性之一,后者则是利用各种机制模仿传统意义上跨越文件或其他模块边界的变量效果[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值