java.lang.String 的 + 号操作到底做了什么?
2020-05-22 16:03:56来源:博客园 阅读 ()
java.lang.String 的 + 号操作到底做了什么?
作者:丶Pz
https://www.cnblogs.com/panzi/p/11956782.html
在之前的面试经历中,对于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);
}
现在有以下几个问题,小伙伴们看看是否能答出来,即使答出来了,你知道为什么吗?
-
str1 存放位置?
-
str2 存放位置?
-
结果是 true 还是 false?
-
结果是 true 还是 false?
-
"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();
而StringBuffer的toString 方法如下:
@Override
public?String?toString()?{
????//?所以说 str2 其实是一个 new String,是不在常量池里面的。
????return?new?String(value,?0,?count);
}
总结
通过类的字节码(3种骚操作,教你查看 Java 字节码)可以查看底层具体用什么方式实现,所以说虽然看似一个简单的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());
}
推荐去我的博客阅读更多:
1.Java JVM、集合、多线程、新特性系列教程
2.Spring MVC、Spring Boot、Spring Cloud 系列教程
3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程
4.Java、后端、架构、阿里巴巴等大厂最新面试题
觉得不错,别忘了点赞+转发哦!
原文链接:https://www.cnblogs.com/javastack/p/12935062.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- 学习Java 8 Stream Api (4) - Stream 终端操作之 collect 2020-06-11
- 5月到6月程序员到底经历了和什么,工资狂跌***元,你是否也 2020-06-10
- 计算机基础到底是哪些基础?为什么很重要! 2020-06-08
- JVM系列之.JVM内存模型如何正确运用操作?本文详解 2020-06-04
- 到底是程序员的世界有 10 种人,你是哪一种?看懂的都是老司 2020-06-02
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash