运维开发网
广告位招商联系QQ:123077622
 
广告位招商联系QQ:123077622

面试官:你真的了解String的创建吗?

运维开发网 https://www.qedev.com 2020-07-31 08:19 出处:51CTO 作者:程序员麦冬
起因在文章的开始之前,有一个问题需要思考。Strings="MRyan";Strings=newString("MRyan")以上是String的两种赋值方式,它们有什么区别吗?它们在内存中有几个实例?存储在哪个区域里?实例存储在哪里?字面量存储在哪里?想要回答这些问题,需要对JVM有一定的了解狂补JVM基础知识都知道JVM的内存结构包括堆,虚拟机栈,方法区,程序计数器,本地方法栈。1.堆:作为整

起因

在文章的开始之前,有一个问题需要思考。

String s = "MRyan";
String s = new String("MRyan")

以上是String的两种赋值方式,它们有什么区别吗?它们在内存中有几个实例?存储在哪个区域里?实例存储在哪里?字面量存储在哪里?

想要回答这些问题,需要对JVM有一定的了解

狂补JVM基础知识

都知道JVM的内存结构包括堆,虚拟机栈,方法区,程序计数器,本地方法栈。

1. 堆:作为整个JVM内存结构中占用最大的一块空间,存放对象实例和数组,它是线程共享的。

2. 虚拟机栈:在线程的生命周期中,参与计算的数据会频繁的入栈和出栈,栈的生命周期和线程一样,栈内的每条数据都是栈帧。在每个java方法被调用的时候,就会创建一个栈帧并入栈,执行完调用在出栈。栈帧包含:局部变量表,操作数栈,动态链接,返回地址。它是线程独占的 。

3. 方法区:又称非堆区,用于存储JVM加载的类型信息,常量,静态变量,代码缓存等数据。在JDK1.7后用元空间替代永久代,随之字符串常量池和静态变量移动到了堆中。它是线程共享的。

其中在JDK1.7以后包括常量池,运行时常量池,字符串常量池都由方法区移动到了堆内存中。

  • 常量池:即class文件常量池,是class文件的一部分,用于保存编译时确定的数据。包括字面量和符号引用。

  • 运行时常量池:它是每个类私有的。每个class文件里的“常量池”在类被加载器加载之后,常量池中的数据就通过映射存放在运行时常量池,由于Java语言并不要求常量一定只能在编译期产生,运行期间也可能产生新的常量,这些常量被放在运行时常量池中,这里所说的常量包括:基本类型包装类(包装类不管理浮点型,整形只会管理-128到127)和String(也可以通过String.intern()方法可以强制将String放入常量池)。

  • 字符串常量池:HotSpot VM里,记录interned string的一个全局表叫做StringTable,它本质上就是个HashSet。注意它只存储对java.lang.String实例的引用,而不存储String对象的内容。

有了以上知识点,我们可以继续说回String了

回归正题

编写一段代码如下:

package com.mryan.csdn.string;

class Test{

   public static void main(String[] args){

       String s1= "MRyan";

   }

}

1234567

声明了一个字面量为“MRyan”的字符串,因为由于String的不可变性(被final修饰)所以字符串"MRyan"作为字面量存储在class文件常量池中。

类加载之后class文件里常量池里大部分数据会被加载到运行时常量池,但是String类型的数据的引用会被同时存在字符串常量池中,实例创建在堆中。而其实这只是Test类被类加载的时候,并未有启动程序线程,此时“MRyan”的引用已经存放在字符串常量池中,实例存放在堆中。

等主线程开始创建s1变量时,JVM就会到字符串常量池里找,看有没有能equals(“MRyan”)的String。如果找到了,就在虚拟机栈中当前栈帧的局部变量表里创建s1变量,然后把字符串常量池里对MRyan对象的引用复制给s1变量。找不到的话,才会在heap堆重新创建一个对象,然后把引用驻留到字符串常量区。然后再把引用复制栈帧的局部变量表。

画个简图:

面试官:你真的了解String的创建吗?


当定义了多个值为"MRyan"的String,情况是什么样的呢?

package com.mryan.csdn.string;

class Test{

   public static void main(String[] args){

       String s1= "MRyan";

       String s2= "MRyan";

       String s3= "MRyan";

       System.out.println(s1==s2);

       System.out.println(s2==s3);

   }

}

1234567891011

输出:

面试官:你真的了解String的创建吗?

和刚才一样,其实是局部变量表里三个变量统一指向同一个堆内存地址。画个简图:

面试官:你真的了解String的创建吗?


但如果是用new关键字来创建字符串,情况就不一样了

package com.mryan.csdn.string;

class Test{

   public static void main(String[] args){

       String s1= "MRyan";

       String s2= "MRyan";

       String s3= new String("MRyan");

       System.out.println(s1==s2);

       System.out.println(s2==s3);

   }

}

1234567891011

输出:

面试官:你真的了解String的创建吗?

这时候,s1和s2还是和之前一样。但s3不同因为new关键字会在Heap堆申请一块全新的内存,来创建新对象。虽然字面还是"MRyan",但是完全不同的对象,有不同的内存地址。

画个简图:

面试官:你真的了解String的创建吗?

扫码领视频副本.gif

0

精彩评论

暂无评论...
验证码 换一张
取 消