java.lang.String 的 + 号操作到底做了什么事情?
前言
在之前的面试经历中,对于String
的考察还是挺频繁的,大致考察以下几个知识点:
- String 常量池
- new String()
- == 和 equals 的区别
- native 方法 String.intern()
虽然面试中大体答对了,但是今天早上微信群里的一个问题我却答不上来,这个问题是这样的:
String str3 = "what";
String str4 = str3 + " a nice day";
//运行时, + 相当于 new,所以堆中会有 "what a nice day"对象,常量池中会有"what"," a nice day"两个对象,而不会有 "what a nice day"对象。
//这句话大佬们看看对不对啊,我怎么感觉不对啊
//常量池不会有"what a nice day" 对象吗?
看完这个问题,说实话我也是有点懵的,我只是知道 "what a nice day"不会在常量池,但是不知道具体的原因,后来群里的同学说 + 号是调用了 StringBuffer 的append 方法。我去证实了,发现确实调用了 append 方法,但是当时没有 调用toString()方法,我很疑惑。(最后经过证实,是StringBuilder的append 方法,不是StringBuffer)。
代码验证
public static void main(String[] args) {
//#1
String str1 = "what";
//#2
String str2 = str1 + " a nice day";
//#3
System.out.println("what a nice day".equals(str2));
//#4
System.out.println("what a nice day" == str2);
}
现在有以下几个问题,小伙伴们看看是否能答出来,即使答出来了,你知道为什么吗?
#1
str1 存放位置?#2
str2 存放位置?#3
结果是 true 还是 false?#4
结果是 true 还是 false?#5
"what a nice day" 存放在哪个位置呢?
解答分析(基于JDK1.8)
下面也不靠猜,我们直接查看生成的字节码:
localhost:test didi$ javap -verbose -p Main.class
Classfile /develop/project/string-test/out/production/classes/com/fanpan26/string/test/Main.class
Last modified 2019-11-29; size 972 bytes
MD5 checksum 1d1f1a23bfe85c2f88d2f767e8aac314
Compiled from "Main.java"
public class com.fanpan26.string.test.Main
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #13.#34 // java/lang/Object."<init>":()V
#2 = String #35 // what
#3 = Class #36 // java/lang/StringBuilder
#4 = Methodref #3.#34 // java/lang/StringBuilder."<init>":()V
#5 = Methodref #3.#37 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#6 = String #38 // a nice day
#7 = Methodref #3.#39 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#8 = Fieldref #40.#41 // java/lang/System.out:Ljava/io/PrintStream;
#9 = String #42 // what a nice day
#10 = Methodref #43.#44 // java/lang/String.equals:(Ljava/lang/Object;)Z
#11 = Methodref #45.#46 // java/io/PrintStream.println:(Z)V
#12 = Class #47 // com/fanpan26/string/test/Main
#13 = Class #48 // java/lang/Object
#14 = Utf8 <init>
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 LineNumberTable
#18 = Utf8 LocalVariableTable
#19 = Utf8 this
#20 = Utf8 Lcom/fanpan26/string/test/Main;
#21 = Utf8 main
#22 = Utf8 ([Ljava/lang/String;)V
#23 = Utf8 args
#24 = Utf8 [Ljava/lang/String;
#25 = Utf8 str1
#26 = Utf8 Ljava/lang/String;
#27 = Utf8 str2
#28 = Utf8 StackMapTable
#29 = Class #24 // "[Ljava/lang/String;"
#30 = Class #49 // java/lang/String
#31 = Class #50 // java/io/PrintStream
#32 = Utf8 SourceFile
#33 = Utf8 Main.java
#34 = NameAndType #14:#15 // "<init>":()V
#35 = Utf8 what
#36 = Utf8 java/lang/StringBuilder
#37 = NameAndType #51:#52 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#38 = Utf8 a nice day
#39 = NameAndType #53:#54 // toString:()Ljava/lang/String;
#40 = Class #55 // java/lang/System
#41 = NameAndType #56:#57 // out:Ljava/io/PrintStream;
#42 = Utf8 what a nice day
#43 = Class #49 // java/lang/String
#44 = NameAndType #58:#59 // equals:(Ljava/lang/Object;)Z
#45 = Class #50 // java/io/PrintStream
#46 = NameAndType #60:#61 // println:(Z)V
#47 = Utf8 com/fanpan26/string/test/Main
#48 = Utf8 java/lang/Object
#49 = Utf8 java/lang/String
#50 = Utf8 java/io/PrintStream
#51 = Utf8 append
#52 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#53 = Utf8 toString
#54 = Utf8 ()Ljava/lang/String;
#55 = Utf8 java/lang/System
#56 = Utf8 out
#57 = Utf8 Ljava/io/PrintStream;
#58 = Utf8 equals
#59 = Utf8 (Ljava/lang/Object;)Z
#60 = Utf8 println
#61 = Utf8 (Z)V
{
public com.fanpan26.string.test.Main();
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 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/fanpan26/string/test/Main;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=3, args_size=1
0: ldc #2 // String what
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: aload_1
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6 // String a nice day
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_2
23: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
26: ldc #9 // String what a nice day
28: aload_2
29: invokevirtual #10 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
32: invokevirtual #11 // Method java/io/PrintStream.println:(Z)V
35: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
38: ldc #9 // String what a nice day
40: aload_2
41: if_acmpne 48
44: iconst_1
45: goto 49
48: iconst_0
49: invokevirtual #11 // Method java/io/PrintStream.println:(Z)V
52: return
LineNumberTable:
line 9: 0
line 11: 3
line 13: 23
line 15: 35
line 16: 52
LocalVariableTable:
Start Length Slot Name Signature
0 53 0 args [Ljava/lang/String;
3 50 1 str1 Ljava/lang/String;
23 30 2 str2 Ljava/lang/String;
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 48
locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream, int ]
}
SourceFile: "Main.java"
从 Constant pool:
中的信息可以看到,#2 #6 #9
可以解答上文中的#1,#5
两个问题。
- str1 是存放在常量池的
- "what a nice day" (非str2)也是存放在常量池的.
下面我们看一下 + 操作做了什么事情,可以在Code中看到,该操作调用了 StringBuilder.append 方法
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6 // String a nice day
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
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");
//这两种写法生成的字节码是一样的。
//String str2 = str1 + " a nice day";
String str2 = new StringBuilder().append(str1).append(" a nice day").toString();
而StringBuilder 的toString 方法如下:
@Override
public String toString() {
// 所以说 str2 其实是一个 new String,是不在常量池里面的。
return new String(value, 0, count);
}
总结
通过类的字节码可以查看底层具体用什么方式实现,所以说虽然看似一个简单的String问题,其实往深处挖掘还是考察了对生成的字节码的理解。还有,遇到一个问题,不能死记答案,有些人告诉你,+ 操作就是 new 对象,但是具体到底是不是或者为什么是有没有思考过呢?上文中如有错误,欢迎指出。
试一试
/**
* 以下程序输出的结果是什么?
* */
public static void main(String[] args) {
String str1 = "what";
String str2 = str1 + " a nice day";
System.out.println("what a nice day".equals(str2));
System.out.println("what a nice day" == str2);
}
/**
* 以下程序输出的结果是什么?
* */
public static void main(String[] args) {
String str1 = "what a nice day";
String str2 = new String("what a nice day");
System.out.println(str1.equals(str2));
System.out.println(str1 == str2);
}
/**
* 以下程序输出的结果是什么?
* */
public static void main(String[] args) {
String str1 = "what";
String str2 = str1.concat(" a nice day");
System.out.println("what a nice day".equals(str2));
System.out.println("what a nice day" == str2);
System.out.println("what a nice day"==str2.intern());
}
java.lang.String 的 + 号操作到底做了什么事情?的更多相关文章
- java.lang.String中的replace方法到底替换了一个还是全部替换了。
你没有看错我说的就是那个最常用的java.lang.String,String可以说在Java中使用量最广泛的类了. 但是我却发现我弄错了他的一个API(也可以说是两个API),这个API是关于字符串 ...
- java.lang.String
1.String 是一个类,广泛应用于 Java 程序中,相当于一系列的字符串.在 Java 语言中 strings are objects.创建一个 strings 最直接的方式是 String g ...
- Java源码学习 -- java.lang.String
java.lang.String是使用频率非常高的类.要想更好的使用java.lang.String类,了解其源代码实现是非常有必要的.由java.lang.String,自然联想到java.lang ...
- 完美解决Invalid layout of java.lang.String at value问题的方法
:-(昨天一天没有写东西了,今晚略显有愧啊.昨天整理了下自己的电脑和桌面,把一些没有用和杂乱的东西都收拾收拾,于是一天就没了.今天赶快来补文章.本篇主要讲的是解决Invalid layout of j ...
- mybatis问题: There is no getter for property named 'equipmentId' in 'class java.lang.String'
本文来源于翁舒航的博客,点击即可跳转原文观看!!!(被转载或者拷贝走的内容可能缺失图片.视频等原文的内容) 若网站将链接屏蔽,可直接拷贝原文链接到地址栏跳转观看,原文链接:https://www.cn ...
- java:常用类(包装类,equals和==的比较,Date,java.lang.String中常用方法,枚举enum)
*包装类: 将基本类型封装成类,其中包含属性和方法以方便对象操作. *byte---->Byte *short--->Short *long--->Long *float---> ...
- [Ljava.lang.String和java.lang.String区别
在做项目时报了一个got class [Ljava.lang.String的提示,当时看到[Ljava.lang.String这个时,感觉有点怪怪的,第一次遇到这种情况.最后在网上查了下才明白.是数组 ...
- 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 ...
- 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 ...
随机推荐
- Python标准库---random模块的使用
更新时间:2019.09.12(更新目录) 目录 1. 谈谈随机数 2. random模块 2.1 random.seed() 2.2 random.random() 2.3 random ...
- web.xml 配置文件 超详细说明!!!
一.web.xml是什么? 首先 web.xml 是java web 项目的一个重要的配置文件,但是web.xml文件并不是Java web工程必须的. web.xml文件是用来配置:欢迎页.serv ...
- 【51nod 1251】 Fox序列的数量(以及带限制插板法讲解)
为什么网上没有篇详细的题解[雾 可能各位聚聚觉得这道题太简单了吧 /kk 题意 首先题目是求满足条件的序列个数,条件为:出现次数最多的数仅有一个 分析 感谢 刚睡醒的 JZ姐姐在咱写题解忽然陷入自闭的 ...
- [Jupyter Notebook]Notebook添加Ancona虚拟环境
1.首先安装ipykernel:conda install ipykernel 解决安装ipykernel权限报错问题 wangbin@Skyell_Cloud:~$ sudo chown -R wa ...
- [考试反思]0725NOIP模拟测试8
看清你是个什么东西了么? 现在看清了么?rank#15?垃圾玩意? 你什么也不是.你没有骄傲,偷懒的资格! 节节败退,永无止境,你想掉到什么样子? 你还在为了成功拿到送分的T1而沾沾自喜?只不过是勉强 ...
- 如何在SqlServer中使用层级节点类型hierarchyid
Sql Server2008开始新增的 hierarchyid 数据类型使存储和查询层次结构数据变得更为简单. 为了使用这个类型,笔者在此进行简单记录,同时为需要的朋友提供一个简单的参考. --获取层 ...
- C语言程序设计100例之(8):尼科彻斯定理
例8 尼科彻斯定理 题目描述 尼科彻斯定理可以叙述为:任何一个整数的立方都可以表示成一串连续的奇数的和.需要注意的是,这些奇数一定是连续的,如:1,3,5,7,9,…. 例如,对于整数5,5*5 ...
- java-optional-快速使用-教程
前言: 在公司中开发项目时碰到一个从Java8引入的一个Optional类,以前jdk版本使用的比较低,没有使用过,于是我在网上浏览了一些文档写篇文章学习总结一下,希望没有用过的朋友们都能够快速学习到 ...
- Docker学习-VMware Workstation 本地多台虚拟机互通,主机网络互通,解决name or service not known
NAT网络通用配置 测试连接 主机到虚拟机 虚拟机到虚拟机 ,虚拟机到外网 CentOS,提示name or service not known 设置DNS服务器 vi /etc/resolv.co ...
- ASP.NET Core 1.0: Using Entity Framework Core
伴随着ASP.NET Core 1.0发布的还有Entity Framework Core 1.0; 官方文档链接:https://docs.efproject.net/en/latest/platf ...