【图灵干货】Java高级教程第十六节:JVM-字符串底层实现原理
2021-11-25 11:56:03
什么样的字符串将被输入到常量池中。
1.直接写字数。
2.字面量的拼接结果(注意:如果字符串拼接中有一个变量,那么结果将不进入字符串常量池)
3.调用string的intern方法可以将字符串存入一个字符串常量池。
文字拼贴的原则。
下面是一个列表代码。
packagecom.hgy;
importjava.util.Arrays;
importjava.util.列表;
publicclasshello{
publicstaticvoidmain(String[]args){
字符串="hello"+"world";
}
}
查看idea中的编译类文件。
//
//Sourcecoderecreatedfroma.classfilebyIntelliJIDEA。
//(poweredbyFernflowerdecompiler)
//
packagecom.hgy;
publicclasshello{
publichello(){
}
publicstaticvoidmain(String[]args){
Stringa="helloworld";
}
}
结论:
通过以上两个文件,我们可以看到,在编译过程中对字符串进行了优化,直接将其合并到一个字符串中;该字符串被存储在字符串常量池中。
线与变数拼接原则。
java源代码。
packagecom.hgy;
importjava.util.Arrays;
importjava.util.列表;
publicclasshello{
publicstaticvoidmain(String[]args){
Stringv="java";
Stringa=v+"hello"+"world";
}
}
使用jclasslib查看main方法的字节码命令。
若名词不理解请阅读请自己理解java虚拟机栈的知识。
只需两行,就能生成如此多的字节码命令;我在代码中简单地解释了它们各自的功能,
0ldc#2//将java从String常量池中装载。
2astore_1//将常数存储在一个索引为1的本地变量表中。
3new#3//为StringBuilder对象分配内存空间。
6dup
7invokespecial#4>//对StringBuilder执行构型。
造方法
10aload_1//获得一个本地变量表索引为1的引用地址,
11invokevirtual#5//将上面装载的内容作为参数。
传入append方法。
14ldc#6//将helloworld从string常量池中装载。
16invokevirtual#5//将上面装载的内容作为参数。
传入append方法。
19invokevirtual#7//调用toString方法。
22astore_2//将结果存储在一个局部变量表中。
23雷顿。
我们可以知道字符串拼接实际上就是创建一个StringBuilder对象,然后将append内容包含在内,最后调用toString方法以获取结果。
3.1为何结果未存储在常量池。
根据上面的字节码指令,字符串拼接结果是StringBuilder的toString方法的结果,那么toString中具体有什么作用,又是为什么结果不在常量池中?
这里有一个StringBuilder.toString和字节码指令。
@Override。
发布StringtoString(){
//Createacopy,don'tsharethearray
//此处value使用char数组【JDK8版】
returnnewString(value,0,count);
}
0new#80。
3dup
4aload_0。
5getfield#234。
8iconst_0。
9aload_0
10getfield#233。
13invokespecial#291>。
16areturn。
我们可以很好地解释到,事实上,我们最终调用了String的构造方法,传递一个char数组,这样,最终的结果肯定也会出现在我们身边。
为何串接效率不高。
4.1准备源代码。
先写两种方法,一种是使用字符串拼接,一种是StringBuilder;
publicclasshello{
publicvoidconcatStrByDefault(){
字符串="name";
{for(inti=0;i<100;i++)
basic+=i;
}
system.out.print(basic);
}
publicvoidconcatStrByBuilder{
StringBuilderbasic=newStringBuilder("name");
{for(inti=0;i<100;i++)
basic.append(i);
}
system.out.println(basic.toString);
}
}
4.2字节码的指令层次的分析。
一上代码执行时间一长我就不再重复测试了相信大家会这样,下一步我们一起来看看这两种方法字节码指令。
下面是concatStrByDefault方法的字节码指令。
简述如下:循环是将33行的goto指令调至第5行,因此连续循环;11行即循环中不停地通过new创建StringBuilder对象,即创建了多少次StringBuilder对象。
物体,如果看过我之前写字符串拼接的原理,那么StringBuilder的toString方法中也new了一个String对象;在这里,所有这些对象的创建必然要求垃圾回收效率低。
0ldc#2。
2astore_1。
3iconst_0。
4istore_2。
5iload_2。
6bipush100。
8if_icmpge36(+28)
11new#3。
图灵学院成立于2017年7月15日,现阶段提供 计算机基础原理、JavaSE核心、Java后端、 面试必备算法、python核心编程、数据分析、web 开发题、人工智能等专题课程,为想学习Python的学员提供优质的培训服务,帮助学员掌握更加全面的技能,是计算机人员职场中提职加薪的首选。
免费java架构师视频学习地址:免费视频