字节:成员变量,局部变量,存放在哪里,为什么局部变量需要初始化

最近一个小伙伴反馈了他在面试过中遇到的问题。

  • 为什么成员变量不需要手动初始化,而局部变量需要手动初始化?
  • 成员变量、静态变量、局部变量存放在哪里?

这个问题考察了我们很多知识点,涉及了 Jvm 内存结构、类加载机制、 Java 对象模型等等知识点。

Jvm 内存结构和 Java 虚拟机的运行时区域有关 Java 对象模型和 Java 对象在虚拟机中的表现形式有关

如果对这些知识点,不是很了解的话,是很难全部回答上来,今天这篇文章我们围绕这些问题,一起来分析一下。

在 Java 中我们可以将变量分为两大类成员变量和局部变量,而成员变量又分为两大类静态成员变量和非静态成员变量。

在 Java 语言中,没有赋值的变量是不能使用的,无论是成员变量还是局部变量,在 “使用之前” 都需要初始化,而我们经常在代码中看到成员变量没有手动初始化,也可以正常使用,这是因为成员变量在类的加载过程已经被初始化了。

类的加载过程被分为 5 个阶段:“加载” -> “验证” -> “准备” -> “解析” -> “初始化”,而不同的变量,在不同的阶段会被赋予不同的值。

静态成员变量

静态成员变量在类加载过程中会被赋值 2 次。

public static int code = 3;

  • 第一次赋值是在 “准备” 阶段,赋上 Jvm 给的默认值,这个阶段 code = 0
  • 第二次赋值是在 “初始化” 阶段,赋上在代码中声明的默认值,这个阶段 code = 3

静态成员变量存放在方法区。方法区在 JVM 内存结构中是一个非常重要的区域,存储了每个类的信息( 包括类的名称、方法信息、字段信息)、静态变量、常量等等。

方法区中有一个常量池,用来存储编译期间生成的字面量和符号引用。

  • 字面量:指由字母、数字等构成的字符串或者数字常量,包含文本字符串,final 的常量值等
  • 符号引用:类和接口的全限定名,字段的名称和描述符,方法的名称和描述符

当程序运行起来,被加载到内存后,才会为这些常量分配内存地址,这时原来的常量池转变成了运行时常量池,在运行期间也可以将新的常量放入运行时常量池,比如 Stringintern 方法。

非静态成员变量

非静态成员变量在类加载过程中会被赋值 1 次。

public int age = 18;

在 “初始化” 阶段赋上在代码中声明的默认值,这个阶段 age = 18,对象实例化之后,该对象存放在 Java 堆中,没有赋值的话,默认就为 0。

局部变量

Java 方法以栈帧的形式,运行在虚拟机栈(Java 栈)中,栈是线程私有的,程序启动的时候,会创建一个 main 线程,操作系统会为每一个线程分配一段内存,线程创建的时候会创建一个虚拟机栈,虚拟机栈的生命周期和线程一样,线程结束了,虚拟机栈也销毁了。

每个 Java 方法,对应一个个栈帧,所以方法开始和结束,都是一个个栈帧入栈和出栈的过程,效果如下图所示。更多内容可以前往查看我另外一篇文章 内存如何记录方法调用和返回过程

每个 Java 方法,都是一个个栈帧,每个栈帧包括了:局部变量表、操作数栈、方法返回地址、动态链接、附加信息。

方法中的局部变量会存放在栈帧的局部变量表中,局部变量表主要用于存放方法参数和方法内部定义的局部变量。例如下面的代码。

public class Main5 {
    public static void main(String... args) {
        Main5 main = new Main5();
    }
}

方法 main 的对应的局部变量表,如下图所示。

正如你所见,局部变量表存放了方法参数和方法中的局部变量。

局部变量和成员变量不一样,Jvm 不会给局部变量一个默认值,所以我们在使用局部变量的时候需要手动初始化,否则是无法使用的。

为什么局部变量必须手动初始化呢?

原因之一为了防止引用到一个错误的值。

为了达到节省资源的目的,局部变量表的空间是可以被复用的,如果一个变量超过了它的作用域,在作用域之后声明的变量,可以复用之前的变量的位置,如果我们不手动初始化,很可能访问到之前变量的内容。

public void byteCode(){
    boolean devEnv = true;
    if(devEnv){
        int a = 1;
        int b = 2;
    }
    // 为了节省资源 c 可能复用 a 或者 b 的位置,通过变量 c 可能会访问到 a 或者 b 的内容
    int c;
}

为了达到节省资源的目的 c 可能复用 a 或者 b 的位置,通过变量 c 可能会访问到 a 或者 b 的内容,​并不是一个正确的值,如果我们主动初始化可以避免这类问题的出现。

为什么 Jvm 不给局部变量一个默认值呢?

这里借用《Thinking in Java》作者 Bruce Eckel 的一句话来回答这个问题。

编译器可以为局部变量赋上一个默认值,但是未初始化的局部变量更有可能是程序员的疏忽,所以采用默认值范围会掩盖这种失误。因此强制程序员提供一个初始值,往往能够帮助找出程序里的缺陷。

全文到这里就结束了,感谢你的阅读,坚持原创不易,欢迎在看、点赞、分享给身边的小伙伴,我会持续分享原创干货!!!

关于Android技术储备

最近几年国内的初级Android程序员已经很多了,但是中高级的Android技术人才仍然稀缺“;这的确不假,今年已经是2023年了,距离Android巅峰时期那会已经过去七八年了,现在想找一个适合的中高级Android工程师的确不容易,一般需要进行大量的面试才能挑选出一个比较满意的。

所以 要学好Android 还是很有必要的,但必须得有一个学习规划。最后大家分享一份全套的Android学习资料,给那些想学习 Android 的小伙伴们一点帮助!

本文适用于:

  • 任何想学习Android开发但不知道从哪里开始的人
  • 也适用于已经开始进行Android开发但想要变得更好的任何人

一、Android所有方向的学习路线

为了成为更好的 Android 开发者,这里为大家提供了总的路线图。它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。如果下面这个学习路线能帮助大家成为一个更好的 Android 开发者,那么我的使命也就完成了:

包括:Android应用开发、系统开发、音视频开发、Flutter开发、小程序开发、UI界面、车载系统开发等等

在这里插入图片描述

二、学习软件

工欲善其事必先利其器。学习Android常用的Android Studio视频教程和Android Studio最新的安装包都在这里了,给大家节省了很多时间。


三、进阶学习视频

我们在学习的时候,往往书籍源码难以理解,阅读困难,这时候视频教程教程是就很适合了,生动形象加上案例实战,科学有趣才能更方便的学习下去。

在这里插入图片描述

四、实战案例

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

在这里插入图片描述

五、经典书籍阅读

阅读Android经典书籍可以帮助读者提高技术水平,开拓视野,掌握核心技术,提高解决问题的能力,同时也可以借鉴他人的经验。对于想要深入学习Android开发的读者来说,阅读Android经典书籍是非常有必要的。

在这里插入图片描述

六、面试资料

我们学习Android必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

图片

请添加图片描述

这份完整版的Android全套学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

猜你喜欢

转载自blog.csdn.net/Eqiqi/article/details/132988027