讲一讲Java的字符串常量池,看完你的思路就清晰了
前言
很多朋友Java的字符串常量池的概念困扰了很长一段时间,最近研究了一下jvm指令码,终于对它有了大概的了解。 在展示案例前,我们需要先搞清楚一个概念,众所周知,jvm的内存模型由程序计数器、虚拟机栈、本地方法栈、堆、元空间(方法区)、直接内存组成。 今天我们谈到的概念只和虚拟机栈、堆、元空间(方法区)有关。 先举个例子说明两种关于字符串最基本的使用情况:
String s =“abc”;在编译期间,会将等号右边的“abc”常量放在常量池中,在程序运行时,会将s变量压栈,栈中s变量直接指向元空间的字符串常量池abc项,没有经过堆内存。
String s = new String(“abc”);在编译期间,会将等号右边的“abc”常量放在常量池中,在程序运行时,先在堆中创建一个String对象,该对象的内容指向常量池的“abc”项。然后将s变量压栈,栈中s变量指向堆中的String对象。
下面通过javap -v xxx.class命令来查看class文件的指令码,通过分析这些指令码更确切的了解我们想知道的问题。笔者的jdk是1.8版本的,版本不同可能效果也不同。
第一个例子:String a = “abc”
首先展示我们的源代码
public class StringCodeTest {
public static void main(String[] args) {
String a = "abc";
System.out.println(a);
}
}
我们执行 javap -v StringCodeTest .class>StringCodeTest.txt命令,将指令码放到StringCodeTest.txt中。
Classfile /C:/Users/zhiyi/IdeaProjects/springboottest/target/classes/cn/lizy/service/StringCodeTest.class
Last modified 2020-11-7; size 610 bytes
MD5 checksum 2adeba87a0f1b315019efe540bb058cd
Compiled from "StringCodeTest.java"
public class cn.lizy.service.StringCodeTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#22 // java/lang/Object."<init>":()V
#2 = String #23 // abc
#3 = Fieldref #24.#25 // java/lang/System.out:Ljava/io/PrintStream;
#4 = Methodref #26.#27 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #28 // cn/lizy/service/StringCodeTest
#6 = Class #29 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcn/lizy/service/StringCodeTest;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 a
#19 = Utf8 Ljava/lang/String;
#20 = Utf8 SourceFile
#21 = Utf8 StringCodeTest.java
#22 = NameAndType #7:#8 // "<init>":()V
#23 = Utf8 abc
#24 = Class #30 // java/lang/System
#25 = NameAndType #31:#32 // out:Ljava/io/PrintStream;
#26 = Class #33 // java/io/PrintStream
#27 = NameAndType #34:#35 // println:(Ljava/lang/String;)V
#28 = Utf8 cn/lizy/service/StringCodeTest
#29 = Utf8 java/lang/Object
#30 = Utf8 java/lang/System
#31 = Utf8 out
#32 = Utf8 Ljava/io/PrintStream;
#33 = Utf8 java/io/PrintStream
#34 = Utf8 println
#35 = Utf8 (Ljava/lang/String;)V
{
public cn.lizy.service.StringCodeTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/lizy/service/StringCodeTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: ldc #2 // String abc
2: astore_1
3: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_1
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
LineNumberTable:
line 5: 0
line 6: 3
line 24: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 args [Ljava/lang/String;
3 8 1 a Ljava/lang/String;
}
SourceFile: "StringCodeTest.java"
关键要看Constant pool:(这个就是常量池)和main方法。


main方法中ldc #2 的意思是把常量池中的#2项压入到栈,而#2关联的#23就是“abc”常量。这就说明栈中的对象是直接指向了常量池的。String s = “abc”只生成了一个字符串对象(常量池中的对象)和一个栈中的引用
第二个例子 String s = new String(“abc”);
源代码
public class StringCodeTest {
public static void main(String[] args) {
String b = new String("abc");
System.out.println(b)
}
}
为了节省篇幅,第二个例子和第三个例子的jvm指令码就不再展示了,只截图关键点,有需要可以自己通过javap命令生成
main方法中先new #2, 而#2关联的#25就是String对象, 然后再ldc #3(关联的#26就是“abc”常量)也就是说,在执行时,先在堆中创建String对象,再把常量池中的#3项压入到栈(这里的栈应该是操作数栈,参考最上面的内存模型图)供String对象使用。所以,栈中的引用指向了堆中的String对象,String对象指向了常量池中的“abc”项,String s = new String(“abc”)生成了一个字符串对象(常量池中的对象)、一个堆中的String对象和一个栈中的引用。
第三个例子:String c = new String(“abc”)+“abc”;
源代码
public class StringCodeTest {
public static void main(String[] args) {
String c = new String("abc")+"abc";
System.out.println(c);
}
}
main方法中先创建了Stringbuiler对象,然后创建了String对象(就是new String("abc")), 再执行ldc #3将常量池中的字符串压入到栈。最后做计算,过程和第二个例子差不多,只不过多了个Stringbuiler的append操作。
总结:理解了大体的原理后,再遇到像 ==判断字符串相等,或者计算String s = new String(“abc”)再内存中创建了几个对象这样的问题时思路就清晰了
最后
感谢你看到这里,文章有什么不足还请指正,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!
讲一讲Java的字符串常量池,看完你的思路就清晰了的更多相关文章
- Java中,那些关于String和字符串常量池你不得不知道的东西
老套的笔试题 在一些老套的笔试题中,会要你判断s1==s2为false还是true,s1.equals(s2)为false还是true. String s1 = new String("xy ...
- Java字符串常量池
JVM为了减少字符串对象的重复创建,维护了一个特殊的内存,这段内存被称为字符串常量池. Java中字符串对象的创建有两种形式:一种是字面量形式,String str = "a":一 ...
- Java中的字符串常量池
ava中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";,另一种就是使用new这种标准的构造对象的方法,如String str = new ...
- Java中String字符串常量池总结
最近到广州某建站互联网公司面试,当时面试官问假设有两个字符串String a="abc",String b = "abc";问输出a==b是true还是fals ...
- java中字符串“不可变性”的破坏,使用反射破坏final属性。以及涉及到字符串常量池的问题。
大家都清楚java中String类是不可变的,它的定义中包含final关键字.一旦被创建,值就不能被改变(引用是可以改变的). 但这种“不可变性”不是完全可靠的,可以通过反射机制破坏.参考一下代码: ...
- Java中String字符串常量池
首先看一个例子,通过这个例子更能快速理解String常量池 public static void main(String[] args) { String a = "ab"; St ...
- Java SE之字符串常量池
Reference Document: 什么是字符串常量池? http://www.importnew.com/10756.html[Recommend] Java常量池理解与总结 http: ...
- 转载:Java中的字符串常量池详细介绍
引用自:http://blog.csdn.net/langhong8/article/details/50938041 这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重 ...
- java字符串常量池——字符串==比较的一个误区
转自:https://blog.csdn.net/wxz980927155/article/details/81712342 起因 再一次js的json对象的比较中,发现相同内容的json对象使用 ...
随机推荐
- python numpy输出排名
python numpy排序后输出排名 问题: 假设某班的成绩为: 姓名 成绩 名次 小红 95 小黑 67 小白 58 小绿 82 小蓝 76 小橙 79 小可爱 99 请根据表格,输出对应的名次 ...
- airtest操作夜神模拟器adb冲突解决办法
首先看错误日志明显是adb之间版本不对应互相干掉对方 第一步 去aritestIde所在目录\AirtestIDE\airtest\core\android\static\adb\windows 第二 ...
- C. k-Amazing Numbers 解析(思維)
Codeforce 1417 C. k-Amazing Numbers 解析(思維) 今天我們來看看CF1417C 題目連結 題目 略,請直接看原題. 前言 我實作好慢... @copyright p ...
- 【tensorflow】VMware Ubuntu+Tensorflow配置和使用
本文主要是记录配置tf环境和虚拟机时遇到的问题和方法,方便日后再查找(补前三年欠下的技术债) 宿主机环境:win10 64位 宿主机python: anaconda+python3.6 宿主机tens ...
- Yum 命令出现[Errno 256] No more mirrors to try错误的解决方式
今天我在虚拟机上安装 NetCore 的 SDK 的时候,出现错误,执行命令:"yum install dotnet-sdk-3.1",最后安装失败,很多安装包没有找到镜像.解决方 ...
- 走在深夜的小码农 Third Day
Css3 Third Day writer:late at night codepeasant css简介 CSS 是层叠样式表 ( Cascading Style Sheets ) 的简称. ...
- debian 安裝SSH 增加新用戶 并使用sudo
1 新建新用戶user 2 3 adduser user 4 5 passwd 123654 6 7 exit 刚安装好的Debian默认还没有sudo功能. 1.安装sudo # apt-get i ...
- Linux 系统编程 学习:09-线程:线程的创建、回收与取消
Linux 系统编程 学习:09-线程:线程的创建.回收与取消 背景 我们在此之前完成了 有关进程的学习.从这一讲开始我们学习线程. 完全的开发可以参考:<多线程编程指南> 在Linux ...
- python机器学习实现逻辑斯蒂回归
逻辑斯蒂回归 关注公众号"轻松学编程"了解更多. [关键词]Logistics函数,最大似然估计,梯度下降法 1.Logistics回归的原理 利用Logistics回归进行分类的 ...
- Django之简易用户系统(3)
目录 1. 总体设计思路 2. 搭建简易用户系统 2.1 配置模型Model 2. 2 写入数据库: 2.3 数据库验证表: 2.4 路由URL配置: 2.5 用户增加 后台执行脚本配置: 视图模板配 ...