CoreJava -- 01 -- Java静态变量、成员变量、局部变量之间的区别

原文链接:CoreJava – 01 – Java静态变量、成员变量、局部变量之间的区别


相关文章:


一、静态变量

  • 静态变量 (类变量) 使用 static 进行修饰,有默认值,定义在类里,方法体外,存储在方法区中

    • 方法区

      • 用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

      • Java 8 之前,方法区的具体实现为永久代;Java 8 开始,方法区的具体实现为元空间

      • 永久代使用的是虚拟机的内存;元空间使用的是本地内存

  • 静态变量在类被加载的时候创建,直到该类被销毁前一直存在,是类级别的变量

    • 类的生命周期

      • 加载 --> 验证 --> 准备 --> 解析 --> 初始化 --> 使用 --> 卸载
    • 类的加载过程

      • 加载 --> 验证 --> 准备 --> 解析 --> 初始化
    • 准备阶段

      • 在类生命周期的准备阶段中,会为静态变量分配内存并设置静态变量的初始值,这些变量所使用的内存都将在方法区中进行分配

        静态变量初始值
        booleanfalse
        char‘\u0000’
        byte、short、int0
        long0L
        float0.0F
        double0.0D
        Stringnull
      • 举例说明 1

        public staic int value = 123;
        
        • 如上所示,变量 value 在准备阶段过后的初始值为 0 而不是 123,因为这个时候尚未开始执行任何的 Java 方法

        • 而把 value 赋值为 123 的 putstatic 指令是在程序被编译后才执行的,该指令存放于类构造器 <clinit>() 方法之中,也就是说,把 value 赋值为 123 的动作将在初始化阶段才会执行

      • 举例说明 2

        public staic final int value = 123;
        
        • 如上所示,当 value 加了 final 进行修饰时,在编译时 javac 会为 value 生成一个 ConstantValue 属性

        • ConstantValue 的作用是通知虚拟机自动为静态变量赋值,只有被 static 修饰的变量才可以使用这项属性,且仅限于基本数据类型和字符串

        • 因此此处在准备阶段,虚拟机会根据 ConstantValue 属性直接将 value 赋值为 123;如果该变量没有被 final 进行修饰,或者并非基本数据类型或字符串,则会在类构造器中进行初始化

    • 初始化阶段

      • 初始化阶段是执行类构造器 <clinit>() 方法的过程
    • <clinit>() 方法

      • 类构造方法,由编译器自动收集类中所有静态变量的赋值动作和静态语句块中的语句合并产生

二、成员变量

  • 成员变量 (实例变量),有默认值,定义在类里,方法体外

  • 成员变量会在对象实例化时随着对象一起被分配到 Java 堆中

    • Java 堆

      • 是 Java 虚拟机所管理的内存中最大的一块

      • 被所有线程所共享,用于存放对象实例

      • 可以处理物理上不连续的内存空间,只要逻辑上连续的即可

  • 成员变量在对象被创建的时候创建,直到该对象被销毁前一直存在,是对象级别的变量

    • <init>() 方法

      • 实例构造方法,在对象实例化时执行

三、局部变量

  • 局部变量没有默认值,在使用前必须先赋值

  • 局部变量定义在代码块里、或方法体里、或方法签名里,存储在栈中 (虚拟机栈的局部变量表)

    • 虚拟机栈

      • 虚拟机栈是线程私有的,生命周期与线程相同,其描述的是 Java 方法执行时的内存模型
    • 栈帧 (Stack Frame)

      • 栈帧是方法运行时的基础数据结构,用于存储局部变量表、操作数栈、动态链接、方法出口等信息

      • 每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程

    • 局部变量表

      • 局部变量表存放了编译器可知的各种基本数据类型、对象引用以及 returnAddress 类型

        • 基本数据类型

          • char、byte、short、int、long、float、double、boolean
        • 对象引用

          • 可能是一个指向对象起始地址的引用指针

          • 也可能是指向一个代表对象的句柄或其他与此对象相关的位置

        • returnAdress 类型

          • 指向一条字节码指令的地址
      • 局部变量表所需的内存空间在编译期间完成分配,在方法运行期间不会改变局部变量表的大小

  • 局部变量在对象被创建的时候创建,或在对象方法被调用的时候创建,直到该对象被销毁前、或所在方法执行结束前一直存在,是对象级别的变量


四、注意点

  • 成员变量与局部变量可以重名,如果重名,在方法内直接访问该变量,默认访问局部变量;如果想访问成员变量,可以在变量前面加上 this

五、举例说明

public class VariableTest {

    // 静态变量
    static {
        a = 1;
        b = 1;
        c = 1;
    }
    static int a;
    static int b = 2;
    static int c = 2;
    static {
        c = 3;
    }
    
    // 构造方法
    public VariableTest() {
        f = 4;
    }

    // 成员变量
    {
        d = 1;
        e = 1;
        f = 1;
    }
    int d;
    int e = 2;
    int f = 2;
    {
        f = 3;
    }

    public static void main(String[] args) {
        System.out.println(VariableTest.a); // 1
        System.out.println(VariableTest.b); // 2
        System.out.println(VariableTest.c); // 3
        System.out.println(new VariableTest().d); // 1
        System.out.println(new VariableTest().e); // 2
        System.out.println(new VariableTest().f); // 4
    }
}
  • 如上所示,静态变量的赋值操作由定义顺序,从上到下依次执行

  • 实例变量的赋值操作也由定义顺序,从上到下依次执行,如果在构造方法中对实例变量进行赋值,则会覆盖之前的值,是实例变量的最终值


六、归纳总结

  • 静态变量

    • 使用 static 进行修饰,有默认值,定义在类里,方法体外,存储在方法区中

    • 在类被加载的时候创建,直到该类被销毁前一直存在,是类级别的变量

  • 成员变量

    • 有默认值,定义在类里,方法体外,会在对象实例化时随着对象一起被分配到 Java 堆中

    • 在对象被创建的时候创建,直到该对象被销毁前一直存在,是对象级别的变量

  • 局部变量

    • 没有默认值,在使用前必须先赋值,定义在代码块里、或方法体里、或方法签名里,存储在栈中 (虚拟机栈的局部变量表)

    • 在对象被创建的时候创建,或在对象方法被调用的时候创建,直到该对象被销毁前、或所在方法执行结束前一直存在,是对象级别的变量


七、参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值