前言

 在之前的面试经历中,对于String的考察还是挺频繁的,大致考察以下几个知识点:

  • String 常量池
  • new String()
  • == 和 equals 的区别
  • native 方法 String.intern()

    虽然面试中大体答对了,但是今天早上微信群里的一个问题我却答不上来,这个问题是这样的:
  1. String str3 = "what";
  2. String str4 = str3 + " a nice day";
  3. //运行时, + 相当于 new,所以堆中会有 "what a nice day"对象,常量池中会有"what"," a nice day"两个对象,而不会有 "what a nice day"对象。
  4. //这句话大佬们看看对不对啊,我怎么感觉不对啊
  5. //常量池不会有"what a nice day" 对象吗?

看完这个问题,说实话我也是有点懵的,我只是知道 "what a nice day"不会在常量池,但是不知道具体的原因,后来群里的同学说 + 号是调用了 StringBuffer 的append 方法。我去证实了,发现确实调用了 append 方法,但是当时没有 调用toString()方法,我很疑惑。(最后经过证实,是StringBuilder的append 方法,不是StringBuffer)。

代码验证

  1. public static void main(String[] args) {
  2. //#1
  3. String str1 = "what";
  4. //#2
  5. String str2 = str1 + " a nice day";
  6. //#3
  7. System.out.println("what a nice day".equals(str2));
  8. //#4
  9. System.out.println("what a nice day" == str2);
  10. }

现在有以下几个问题,小伙伴们看看是否能答出来,即使答出来了,你知道为什么吗?

  • #1 str1 存放位置?
  • #2 str2 存放位置?
  • #3 结果是 true 还是 false?
  • #4 结果是 true 还是 false?
  • #5 "what a nice day" 存放在哪个位置呢?

解答分析(基于JDK1.8)

下面也不靠猜,我们直接查看生成的字节码:

  1. localhost:test didi$ javap -verbose -p Main.class
  2. Classfile /develop/project/string-test/out/production/classes/com/fanpan26/string/test/Main.class
  3. Last modified 2019-11-29; size 972 bytes
  4. MD5 checksum 1d1f1a23bfe85c2f88d2f767e8aac314
  5. Compiled from "Main.java"
  6. public class com.fanpan26.string.test.Main
  7. minor version: 0
  8. major version: 52
  9. flags: ACC_PUBLIC, ACC_SUPER
  10. Constant pool:
  11. #1 = Methodref #13.#34 // java/lang/Object."<init>":()V
  12. #2 = String #35 // what
  13. #3 = Class #36 // java/lang/StringBuilder
  14. #4 = Methodref #3.#34 // java/lang/StringBuilder."<init>":()V
  15. #5 = Methodref #3.#37 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  16. #6 = String #38 // a nice day
  17. #7 = Methodref #3.#39 // java/lang/StringBuilder.toString:()Ljava/lang/String;
  18. #8 = Fieldref #40.#41 // java/lang/System.out:Ljava/io/PrintStream;
  19. #9 = String #42 // what a nice day
  20. #10 = Methodref #43.#44 // java/lang/String.equals:(Ljava/lang/Object;)Z
  21. #11 = Methodref #45.#46 // java/io/PrintStream.println:(Z)V
  22. #12 = Class #47 // com/fanpan26/string/test/Main
  23. #13 = Class #48 // java/lang/Object
  24. #14 = Utf8 <init>
  25. #15 = Utf8 ()V
  26. #16 = Utf8 Code
  27. #17 = Utf8 LineNumberTable
  28. #18 = Utf8 LocalVariableTable
  29. #19 = Utf8 this
  30. #20 = Utf8 Lcom/fanpan26/string/test/Main;
  31. #21 = Utf8 main
  32. #22 = Utf8 ([Ljava/lang/String;)V
  33. #23 = Utf8 args
  34. #24 = Utf8 [Ljava/lang/String;
  35. #25 = Utf8 str1
  36. #26 = Utf8 Ljava/lang/String;
  37. #27 = Utf8 str2
  38. #28 = Utf8 StackMapTable
  39. #29 = Class #24 // "[Ljava/lang/String;"
  40. #30 = Class #49 // java/lang/String
  41. #31 = Class #50 // java/io/PrintStream
  42. #32 = Utf8 SourceFile
  43. #33 = Utf8 Main.java
  44. #34 = NameAndType #14:#15 // "<init>":()V
  45. #35 = Utf8 what
  46. #36 = Utf8 java/lang/StringBuilder
  47. #37 = NameAndType #51:#52 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  48. #38 = Utf8 a nice day
  49. #39 = NameAndType #53:#54 // toString:()Ljava/lang/String;
  50. #40 = Class #55 // java/lang/System
  51. #41 = NameAndType #56:#57 // out:Ljava/io/PrintStream;
  52. #42 = Utf8 what a nice day
  53. #43 = Class #49 // java/lang/String
  54. #44 = NameAndType #58:#59 // equals:(Ljava/lang/Object;)Z
  55. #45 = Class #50 // java/io/PrintStream
  56. #46 = NameAndType #60:#61 // println:(Z)V
  57. #47 = Utf8 com/fanpan26/string/test/Main
  58. #48 = Utf8 java/lang/Object
  59. #49 = Utf8 java/lang/String
  60. #50 = Utf8 java/io/PrintStream
  61. #51 = Utf8 append
  62. #52 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
  63. #53 = Utf8 toString
  64. #54 = Utf8 ()Ljava/lang/String;
  65. #55 = Utf8 java/lang/System
  66. #56 = Utf8 out
  67. #57 = Utf8 Ljava/io/PrintStream;
  68. #58 = Utf8 equals
  69. #59 = Utf8 (Ljava/lang/Object;)Z
  70. #60 = Utf8 println
  71. #61 = Utf8 (Z)V
  72. {
  73. public com.fanpan26.string.test.Main();
  74. descriptor: ()V
  75. flags: ACC_PUBLIC
  76. Code:
  77. stack=1, locals=1, args_size=1
  78. 0: aload_0
  79. 1: invokespecial #1 // Method java/lang/Object."<init>":()V
  80. 4: return
  81. LineNumberTable:
  82. line 6: 0
  83. LocalVariableTable:
  84. Start Length Slot Name Signature
  85. 0 5 0 this Lcom/fanpan26/string/test/Main;
  86. public static void main(java.lang.String[]);
  87. descriptor: ([Ljava/lang/String;)V
  88. flags: ACC_PUBLIC, ACC_STATIC
  89. Code:
  90. stack=3, locals=3, args_size=1
  91. 0: ldc #2 // String what
  92. 2: astore_1
  93. 3: new #3 // class java/lang/StringBuilder
  94. 6: dup
  95. 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
  96. 10: aload_1
  97. 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  98. 14: ldc #6 // String a nice day
  99. 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  100. 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  101. 22: astore_2
  102. 23: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
  103. 26: ldc #9 // String what a nice day
  104. 28: aload_2
  105. 29: invokevirtual #10 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
  106. 32: invokevirtual #11 // Method java/io/PrintStream.println:(Z)V
  107. 35: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
  108. 38: ldc #9 // String what a nice day
  109. 40: aload_2
  110. 41: if_acmpne 48
  111. 44: iconst_1
  112. 45: goto 49
  113. 48: iconst_0
  114. 49: invokevirtual #11 // Method java/io/PrintStream.println:(Z)V
  115. 52: return
  116. LineNumberTable:
  117. line 9: 0
  118. line 11: 3
  119. line 13: 23
  120. line 15: 35
  121. line 16: 52
  122. LocalVariableTable:
  123. Start Length Slot Name Signature
  124. 0 53 0 args [Ljava/lang/String;
  125. 3 50 1 str1 Ljava/lang/String;
  126. 23 30 2 str2 Ljava/lang/String;
  127. StackMapTable: number_of_entries = 2
  128. frame_type = 255 /* full_frame */
  129. offset_delta = 48
  130. locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
  131. stack = [ class java/io/PrintStream ]
  132. frame_type = 255 /* full_frame */
  133. offset_delta = 0
  134. locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
  135. stack = [ class java/io/PrintStream, int ]
  136. }
  137. SourceFile: "Main.java"

Constant pool: 中的信息可以看到,#2 #6 #9 可以解答上文中的#1,#5两个问题。

  • str1 是存放在常量池的
  • "what a nice day" (非str2)也是存放在常量池的.

下面我们看一下 + 操作做了什么事情,可以在Code中看到,该操作调用了 StringBuilder.append 方法

  1. 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  2. 14: ldc #6 // String a nice day
  3. 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  4. 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

那么到这里一切都答案都出来了

  • str2 是存放在堆中。
  • equals 为 true
  • == 为 false

所以说其实 str1 + " a nice day" 就相当于 new StringBuilder().append(str1).append(" a nice day");

  1. //这两种写法生成的字节码是一样的。
  2. //String str2 = str1 + " a nice day";
  3. String str2 = new StringBuilder().append(str1).append(" a nice day").toString();

而StringBuilder 的toString 方法如下:

  1. @Override
  2. public String toString() {
  3. // 所以说 str2 其实是一个 new String,是不在常量池里面的。
  4. return new String(value, 0, count);
  5. }

总结

通过类的字节码可以查看底层具体用什么方式实现,所以说虽然看似一个简单的String问题,其实往深处挖掘还是考察了对生成的字节码的理解。还有,遇到一个问题,不能死记答案,有些人告诉你,+ 操作就是 new 对象,但是具体到底是不是或者为什么是有没有思考过呢?上文中如有错误,欢迎指出。

试一试

  1. /**
  2. * 以下程序输出的结果是什么?
  3. * */
  4. public static void main(String[] args) {
  5. String str1 = "what";
  6. String str2 = str1 + " a nice day";
  7. System.out.println("what a nice day".equals(str2));
  8. System.out.println("what a nice day" == str2);
  9. }
  1. /**
  2. * 以下程序输出的结果是什么?
  3. * */
  4. public static void main(String[] args) {
  5. String str1 = "what a nice day";
  6. String str2 = new String("what a nice day");
  7. System.out.println(str1.equals(str2));
  8. System.out.println(str1 == str2);
  9. }
  1. /**
  2. * 以下程序输出的结果是什么?
  3. * */
  4. public static void main(String[] args) {
  5. String str1 = "what";
  6. String str2 = str1.concat(" a nice day");
  7. System.out.println("what a nice day".equals(str2));
  8. System.out.println("what a nice day" == str2);
  9. System.out.println("what a nice day"==str2.intern());
  10. }

java.lang.String 的 + 号操作到底做了什么事情?的更多相关文章

  1. java.lang.String中的replace方法到底替换了一个还是全部替换了。

    你没有看错我说的就是那个最常用的java.lang.String,String可以说在Java中使用量最广泛的类了. 但是我却发现我弄错了他的一个API(也可以说是两个API),这个API是关于字符串 ...

  2. java.lang.String

    1.String 是一个类,广泛应用于 Java 程序中,相当于一系列的字符串.在 Java 语言中 strings are objects.创建一个 strings 最直接的方式是 String g ...

  3. Java源码学习 -- java.lang.String

    java.lang.String是使用频率非常高的类.要想更好的使用java.lang.String类,了解其源代码实现是非常有必要的.由java.lang.String,自然联想到java.lang ...

  4. 完美解决Invalid layout of java.lang.String at value问题的方法

    :-(昨天一天没有写东西了,今晚略显有愧啊.昨天整理了下自己的电脑和桌面,把一些没有用和杂乱的东西都收拾收拾,于是一天就没了.今天赶快来补文章.本篇主要讲的是解决Invalid layout of j ...

  5. mybatis问题: There is no getter for property named 'equipmentId' in 'class java.lang.String'

    本文来源于翁舒航的博客,点击即可跳转原文观看!!!(被转载或者拷贝走的内容可能缺失图片.视频等原文的内容) 若网站将链接屏蔽,可直接拷贝原文链接到地址栏跳转观看,原文链接:https://www.cn ...

  6. java:常用类(包装类,equals和==的比较,Date,java.lang.String中常用方法,枚举enum)

    *包装类: 将基本类型封装成类,其中包含属性和方法以方便对象操作. *byte---->Byte *short--->Short *long--->Long *float---> ...

  7. [Ljava.lang.String和java.lang.String区别

    在做项目时报了一个got class [Ljava.lang.String的提示,当时看到[Ljava.lang.String这个时,感觉有点怪怪的,第一次遇到这种情况.最后在网上查了下才明白.是数组 ...

  8. java.lang.ClassCastException: java.lang.String cannot be cast to com.jy.hfims.domain 映射实体类型错误

    今天在做 excel导出的时候,出现了一个问题"java.lang.ClassCastException: java.lang.String cannot be cast to com.do ...

  9. The type java.lang.String cannot be resolved. It is indirectly referenced from required .class files

    最近在做J2ME开发项目,配置环境一切OK,但是打开项目时某些文件提示: The type java.lang.String cannot be resolved. It is indirectly ...

随机推荐

  1. django-模板之comment标签(六)

    index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  2. R语言:绘制知识图谱

    知识图谱主要是通过将应用数学,图形学,信息可视化技术,信息科学等学科的理论与方法与计量学引文分析.共现分析等方法结合,利用可视化的图谱形象地展示学科的核心结构.发展历史.前沿领域以及整体知识架构达到多 ...

  3. 2018.8.10 python中的迭代器

    主要内容: 1.函数名的使用 2.闭包 3.迭代器 一.函数名的运用 函数名是一个变量,但他是一个特殊的变量,与括号配合可执行函数的变量. 1.函数名的内存地址 def func(): print(' ...

  4. [2018-01-12] python 当天学习笔记

    Python模块 Python欧快(Moudule),是一个Python文件,以.py结尾,包含了Python对象定义和Python语句. 模块让你能够有逻辑地组织你的Python代码段. 把相关的代 ...

  5. 如何将excel文件导入testlink

    Step 1 按照excel模板设计测试用例,其中优先级的定义为: 数值 定义 1 LOW 2 MEDIUM 3 HIGH Step 2 执行脚本,将excel转换成xml: 脚本 备注 包含:exc ...

  6. 水管局长数据加强版:lct,时光倒流,最小生成树,边化点

    Description: SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到 ...

  7. Python基础语法,他其实很贵!

    # -*- coding:utf-8 -*- # @Time :2019/7/19 11:26# @Author :ITester# @Email :1036881587@qq.com# @File ...

  8. The reference to entity "characterEncoding" must end with the ';'

    在配置数据库连接池数据源时,本来没有错误,结果加上编码转换格式后eclipse突然报错: 这是怎么回事? 经过查询,发现这个错误其实很好解决. 首先,原因是: .xml文件中 ‘ & ’字符需 ...

  9. 数据结构--树链剖分准备之LCA

    有关LCA的模板题    传送门 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和 ...

  10. 说一说JVM双亲委派机制与Tomcat

    双亲委派模型与JVM 类加载 讲个故事: 以前,爱捣鼓的小明突然灵机一动,写出了下面的代码 package java.lang; public class String { //...复制真正Stri ...