解讀String字符串拼接的原理
前言
明白什么是引用,什么是該引用指向的真正對象。
==對于基本數(shù)據(jù)類型比較的是值,對于引用數(shù)據(jù)類型比較的是指向的對象的地址,即兩者指向的是否是同一個對象。
String s = "gzc";
上述代碼中s為變量引用,它存在于棧中,而“gzc”則是該變量引用所指向的真正數(shù)據(jù),它存在于字符串常量池中。
言歸正傳
字符串拼接主要有2種情況:
1、常量與常量拼接
String s1 = "g"+"zc";//常量“g”與常量“zc”拼接
常量與常量拼接的原理:
字符串常量與常量之間的拼接操作其實在未加載到JVM內(nèi)存之前就已經(jīng)完成了,即在編譯期間就會對字符串常量之間的拼接操作進行優(yōu)化如下圖,
進行反編譯后,我們不難發(fā)現(xiàn)在編譯完之后,s4已經(jīng)被直接拼接好了。而且此時s3和s4指向的是字符串常量池中的同一個對象,即兩者存儲的對象地址是相同的。
所以s3==s4其結(jié)果為true。
2、涉及到變量的字符串拼接
2.1 變量與常量拼接
String s1 = "g"; String s2 = s1+"zc";//變量s1與常量“zc”拼接
2.2 變量與變量拼接
String s1 = "g"; String s2 = "zc"; String s3 = s1+s2;//變量s1與變量s2拼接
涉及到變量的字符串拼接原理:
只要字符串拼接其中涉及到變量,不管是幾個變量,那么其拼接原理都如下:
當(dāng)涉及到變量時,字符串用+進行字符串拼接的本質(zhì),其實就是利用StringBuilder類里的append()方法,將每一個字符串都一一添加進去,然后返回一個StringBuilder對象,所以可以不用新創(chuàng)建一個對象去接收返回值,直接鏈式編程得到最終添加的結(jié)果,最后再調(diào)用toString()方法將其轉(zhuǎn)換為我們想要的字符串String類型。
如下圖:
特別注意:
StringBuilder的toString()方法調(diào)用的是String重載的構(gòu)造器方法,是以字符數(shù)組為字符串實際內(nèi)容進行創(chuàng)建的,并未直接以字面量方式創(chuàng)建String對象,即:
所以如果我們上述代碼沒有定義s3和s4兩個變量,只定義了String s5 = s1+s2;
的話,那么其實字符串常量池中是不存在“gzc”這個字符串的,而是只有“g”和“zc”。
因為只有通過字面量定義一個字符串以及調(diào)用String的intern()方法,這兩種方式才會在字符串常量池中生成對應(yīng)的對象。
而StringBuilder調(diào)用toString()方法創(chuàng)建的String對象則會直接在堆中為其分配內(nèi)存,常量池中不會存在對應(yīng)的對象。
所以如果判斷s3==s5
,則結(jié)果為false
,因為s3指向的是字符串常量池中的“gzc”,而s5指向的是堆中的“gzc”對象,二者指向的對象地址不同,則比較結(jié)果自然為false。
特殊情況:
若變量被聲明為final類型,即為常量,則就遵循字符串常量拼接的規(guī)則了。
如下圖:
jdk 1.8 對String字符串拼接并沒有優(yōu)化
String s = new String("1") + new String("1"); String s2 = s + "1" + "1" + "1"; //String s = "1" + "1"; String s1 = "11"; System.out.println(s.intern() == s1);
public static void main(java.lang.String[] args); 0 new java.lang.StringBuilder [16] 3 dup 4 new java.lang.String [18] 7 dup 8 ldc <String "1"> [20] 10 invokespecial java.lang.String(java.lang.String) [22] 13 invokestatic java.lang.String.valueOf(java.lang.Object) : java.lang.String [25] 16 invokespecial java.lang.StringBuilder(java.lang.String) [29] 19 new java.lang.String [18] 22 dup 23 ldc <String "1"> [20] 25 invokespecial java.lang.String(java.lang.String) [22] 28 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [30] 31 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [34] 34 astore_1 [s] 35 new java.lang.StringBuilder [16] 38 dup 39 aload_1 [s] 40 invokestatic java.lang.String.valueOf(java.lang.Object) : java.lang.String [25] 43 invokespecial java.lang.StringBuilder(java.lang.String) [29] 46 ldc <String "1"> [20] 48 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [30] 51 ldc <String "1"> [20] 53 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [30] 56 ldc <String "1"> [20] 58 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [30] 61 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [34] 64 astore_2 [s2] 65 ldc <String "11"> [38] 67 astore_3 [s1] 68 getstatic java.lang.System.out : java.io.PrintStream [40] 71 aload_1 [s] 72 invokevirtual java.lang.String.intern() : java.lang.String [46] 75 aload_3 [s1] 76 if_acmpne 83 79 iconst_1 80 goto 84 83 iconst_0 84 invokevirtual java.io.PrintStream.println(boolean) : void [49] 87 return Line numbers: [pc: 0, line: 18] [pc: 35, line: 19] [pc: 65, line: 21] [pc: 68, line: 22] [pc: 87, line: 23] Local variable table: [pc: 0, pc: 88] local: args index: 0 type: java.lang.String[] [pc: 35, pc: 88] local: s index: 1 type: java.lang.String [pc: 65, pc: 88] local: s2 index: 2 type: java.lang.String [pc: 68, pc: 88] local: s1 index: 3 type: java.lang.String Stack map table: number of frames 2 [pc: 83, full, stack: {java.io.PrintStream}, locals: {java.lang.String[], java.lang.String, java.lang.String, java.lang.String}] [pc: 84, full, stack: {java.io.PrintStream, int}, locals: {java.lang.String[], java.lang.String, java.lang.String, java.lang.String}] }
從class文件中可以看出,依然new了兩個stringBuilder對象
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring事務(wù)注解@Transactional失效的八種場景分析
最近在開發(fā)采用Spring框架的項目中,使用了@Transactional注解,但發(fā)現(xiàn)事務(wù)注解失效了,所以這篇文章主要給大家介紹了關(guān)于Spring事務(wù)注解@Transactional失效的八種場景,需要的朋友可以參考下2021-05-05Mybatis實現(xiàn)Mapper動態(tài)代理方式詳解
這篇文章主要為大家詳細介紹了Mybatis實現(xiàn)Mapper動態(tài)代理方式,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08Java Dubbo協(xié)議下的服務(wù)端線程使用詳解
Dubbo是阿里開源項目,國內(nèi)很多互聯(lián)網(wǎng)公司都在用,已經(jīng)經(jīng)過很多線上考驗。Dubbo內(nèi)部使用了Netty、Zookeeper,保證了高性能高可用性,使用Dubbo可以將核心業(yè)務(wù)抽取出來,作為獨立的服務(wù),逐漸形成穩(wěn)定的服務(wù)中心2023-03-03基于Spring Boot的Environment源碼理解實現(xiàn)分散配置詳解
這篇文章主要給大家介紹了基于Spring Boot的Environment源碼理解實現(xiàn)分散配置的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08Springboot項目的Mapper中增加一個新的sql語句
本文主要介紹了Springboot項目的Mapper中增加一個新的sql語句,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05