字符串连接符 “+”及字符串常量池实验、字符串final属性

结果预览

public class StrTest{
public static void main(String[] args){
String str1="hello";
String str2="hello"+"wiaoong"; //常量 编译器直接优化为 hellowiaoong
String str3=str1+"wiaoong";
System.out.println(str3==str2); //false final String str4="hello";
String strFinal=str4+"wiaoong";
System.out.println(strFinal==str2); //true String str6="hello";
String str7=new String("hello");
System.out.println(str7==str6); //false
String str8=str7.intern();
System.out.println(str6==str8); //true String str11="123";
String str12=str11;
str11+="456";
System.out.println(str12); //123
System.out.println(str11); //456
}
}

场景一:编译器优化

public class StrTest{
public static void main(String[] args){
String str1="hello";
String str2="hello"+"wiaoong";
String str3=str1+"wiaoong";
}
}

Jvm编译指令:javap -c StrTest.class

这些指令我也是一知半解,但是不妨碍我们分析发生了什么,如果有更好的指令参考文章请留言分享。

附上一份JVM指令博文:JVM指令博客

结果分析:

编译后的JVM指令集

  public static void main(java.lang.String[]);
Code:
0: ldc #2 // String hello
2: astore_1
3: ldc #3 // String hellowiaoong
5: astore_2
6: new #4 // class java/lang/StringBuilder
9: dup
10: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
13: aload_1
14: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: ldc #7 // String wiaoong
19: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
25: astore_3
26: return

观察指令可以得知:

  • str2在编译时已经被编译器给优化了,也就是说连接符“+”连接的是两个常量时 如 "a"+"b", 和 "ab"是等价的
  • str3在编译的时候Jvm新建了一个StirngBuilder对象,并且分布把常量"wiaoong"和变量str1进行了append操作,最后返回了StirngBuilder.toString()给str3,

    也就是说连接符“+”连接中存在变量时,会通过StringBuilder构建新的对象。

    由上可知,str3==str2 为false.

场景二:编译器优化

 public class StrTest{
public static void main(String[] args){
final String str4="wiaoong";
String str5="hello"+"wiaoong";
String str3="hello"+str4;
System.out.println(str5==str3); //true
}
}

编译后的JVM指令集

public static void main(java.lang.String[]);
Code:
0: ldc #2 // String hellowiaoong
2: astore_2
3: ldc #2 // String hellowiaoong
5: astore_3
6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
9: aload_2
10: aload_3
11: if_acmpne 18
14: iconst_1
15: goto 19
18: iconst_0
19: invokevirtual #4 // Method java/io/PrintStream.println:(Z)V
22: return

由上可知,这是常量的一种.

场景三:字符串常量池

public class StrTest{
public static void main(String[] args){
String str6="hello";
String str7=new String("hello");
System.out.println(str7==str6); //false
String str8=str7.intern();
System.out.println(str6==str8); //true
}
}

编译后的JVM指令集

 public static void main(java.lang.String[]);
Code:
0: ldc #2 // String hello
2: astore_1
3: new #3 // class java/lang/String
6: dup
7: ldc #2 // String hello
9: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
12: astore_2
13: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
16: aload_2
17: aload_1
18: if_acmpne 25
21: iconst_1
22: goto 26
25: iconst_0
26: invokevirtual #6 // Method java/io/PrintStream.println:(Z)V
29: aload_2
30: invokevirtual #7 // Method java/lang/String.intern:()Ljava/lang/String;
33: astore_3
34: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
37: aload_1
38: aload_3
39: if_acmpne 46
42: iconst_1
43: goto 47
46: iconst_0
47: invokevirtual #6 // Method java/io/PrintStream.println:(Z)V
50: return

由上可知:

  • new 创建字符串时,检测到常量池中已经存在字面量“hello”,直接返回常量池中的应用给new构建新的对象

  • intern()能完成字符串主动入池操作,如果字符串已经存在字符串常量池中,就返回常量池中字符串的引用地址

    str6初始化后,常量池中已经存在hello了,执行intern()就直接返回引用. 即JVM指令集中的步骤7 ldc 操作,所以str6==str8为true

场景4:字符串final分析

public class StrTest{
public static void main(String[] args){
String str11="123";
String str12=str11;
str11+="456";
System.out.println(str12); //123
System.out.println(str11); //123456
}
}

分析:

str11+="456"这行代码创建了一个新的对象,并且把str11指向了新的匿名String对象的堆内存地址,而str11的堆内存内的内容是没有变化的。

上图:

      


这里又衍生出一个String str=a+b+c+d;创建了几个对象的面试常见问题?

分析:

public StringBuilder() {
super(16);
} public StringBuffer() {
super(16);
}

初始化char[]大小是16,如果超过这个长度则会进行char[]扩容,new一个新的char[]

执行toString()是又new 了一个String

算上JVM自动创建的StringBuilder(),结果是2-3个

如果字符长常量池中没有这个字符串,那么又会新建一个对象

String+、intern()、字符串常量池的更多相关文章

  1. String:字符串常量池

    String:字符串常量池 作为最基础的引用数据类型,Java 设计者为 String 提供了字符串常量池以提高其性能,那么字符串常量池的具体原理是什么,我们带着以下三个问题,去理解字符串常量池: 字 ...

  2. Java String:字符串常量池(转)

    作为最基础的引用数据类型,Java 设计者为 String 提供了字符串常量池以提高其性能,那么字符串常量池的具体原理是什么? 字符串常量池的设计思想是什么? 字符串常量池在哪里? 如何操作字符串常量 ...

  3. Java中,那些关于String和字符串常量池你不得不知道的东西

    老套的笔试题 在一些老套的笔试题中,会要你判断s1==s2为false还是true,s1.equals(s2)为false还是true. String s1 = new String("xy ...

  4. 字符串常量池和String.intern()方法在jdk1.6、1.7、1.8中的变化

    字符串常量池也是运行时常量池 jdk1.6中,它是在方法区中,属于“永久代” jdk1.7中,它被移除方法区,放在java堆中 jdk1.8中,取消了“永久代”,将常量池放在元空间,与堆独立了 pub ...

  5. 常量池之字符串常量池String.intern()

    运行时常量池是方法区(PermGen)的一部分. 需要提前了解: 1. JVM内存模型. 2. JAVA对象在JVM中内存分配 常量池的好处 常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现 ...

  6. 对于JVM中方法区,永久代,元空间以及字符串常量池的迁移和string.intern方法

    在Java虚拟机(以下简称JVM)中,类包含其对应的元数据,比如类的层级信息,方法数据和方法信息(如字节码,栈和变量大小),运行时常量池,已确定的符号引用和虚方法表. 在过去(当自定义类加载器使用不普 ...

  7. Knowledge Point 20180309 字符串常量池与String,intern()

    引言 什么都先不说,先看下面这个引入的例子: public static void test4(){ String str1 = new String("SEU") + new S ...

  8. 结合字符串常量池/String.intern()/String Table来谈一下你对java中String的理解

    1.字符串常量池 每创建一个字符串常量,JVM会首先检查字符串常量池,如果字符串已经在常量池中存在,那么就返回常量池中的实例引用.如果字符串不在池中,就会实例化一个字符串放到字符串池中.常量池提高了J ...

  9. Java中String字符串常量池总结

    最近到广州某建站互联网公司面试,当时面试官问假设有两个字符串String a="abc",String b = "abc";问输出a==b是true还是fals ...

随机推荐

  1. JS闭包(3)

    在将内部函数作为函数的返回值的时候,由于闭包的存在会携带上内部函数所使用的外部函数的变量,如果这些变量很多或者很大,那么在使用完返回的内部函数后最好将其置为null以便释放闭包中的携带变量,一面造成内 ...

  2. git github 对代码的管理

    参考:https://www.cnblogs.com/feynman61/p/9005252.html 一.Git 对远程仓库版本回退 场景: 同事 a.b 同时修改了代码,提交到仓库 同时 c 不熟 ...

  3. k短路算法

    k短路算法 求解k短路用到了A* 算法,A* ( A star )算法,又称启发式搜索算法,与之相对的,dfs与bfs都成为盲目型搜索:即为带有估价函数的优先队列BFS称为A*算法. 该算法的核心思想 ...

  4. [HEOI 2013]SAO

    Description 题库连接 给你一个 \(n\) 个节点的有向树,问你这棵树的拓扑序个数,对大质数取模.多测,测试组数 \(T\). \(1\leq n\leq 1000, 1\leq T\le ...

  5. Linux CentOS7 VMware 安装PHP5 、安装PHP7

    一.安装PHP5 PHP官网www.php.net 当前主流版本为5.6/7.1 cd /usr/local/src/ wget http://cn2.php.net/distributions/ph ...

  6. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 表格:边框表格

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  7. JavaScript引用类型与对象

    1.引用类型 引用类型的值(对象)是引用类型的一个实例.引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法. 对象是某个特定引用类型的实例.新对象是使用new操作符后跟一个构造 ...

  8. 【剑指Offer面试编程题】题目1517:链表中倒数第k个结点--九度OJ

    题目描述: 输入一个链表,输出该链表中倒数第k个结点. (hint: 请务必使用链表.) 输入: 输入可能包含多个测试样例,输入以EOF结束. 对于每个测试案例,输入的第一行为两个整数n和k(0< ...

  9. MAC Matlab 中文乱码

    环境:macOS High Sierra 10.13.4 问题:文件中文注释乱码(再次打开文件时) / 控制台输出中文乱码 解决方法: 官网下载补丁(https://ww2.mathworks.cn/ ...

  10. 如何更改placeholder属性中文字颜色

    如何更改placeholder属性中文字颜色 placeholder这个属性是HTML5中新增的属性,该属性的作用是规定可描述输入字段预期值的简短的提示信息,该提示会在用户输入之前显示在输入字段中,会 ...