日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

String s=a+b+c,到底创建了几个对象?

發布時間:2025/3/20 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 String s=a+b+c,到底创建了几个对象? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先看一下這道常見的面試題,下面代碼中,會創建幾個字符串對象?

String?s="a"+"b"+"c";

如果你比較一下Java源代碼和反編譯后的字節碼文件,就可以直觀的看到答案,只創建了一個String對象。

估計大家會有疑問了,為什么源代碼中字符串拼接的操作,在編譯完成后會消失,直接呈現為一個拼接后的完整字符串呢?

這是因為在編譯期間,應用了編譯器優化中一種被稱為常量折疊(Constant Folding)的技術,會將編譯期常量的加減乘除的運算過程在編譯過程中折疊。編譯器通過語法分析,會將常量表達式計算求值,并用求出的值來替換表達式,而不必等到運行期間再進行運算處理,從而在運行期間節省處理器資源。

而上邊提到的編譯期常量的特點就是它的值在編譯期就可以確定,并且需要完整滿足下面的要求,才可能是一個編譯期常量:

  • 被聲明為final

  • 基本類型或者字符串類型

  • 聲明時就已經初始化

  • 使用常量表達式進行初始化

上面的前兩條比較容易理解,需要注意的是第三和第四條,通過下面的例子進行說明:

final?String?s1="hello?"+"Hydra"; final?String?s2=UUID.randomUUID().toString()+"Hydra";

編譯器能夠在編譯期就得到s1的值是hello Hydra,不需要等到程序的運行期間,因此s1屬于編譯期常量。而對s2來說,雖然也被聲明為final類型,并且在聲明時就已經初始化,但使用的不是常量表達式,因此不屬于編譯期常量,這一類型的常量被稱為運行時常量。再看一下編譯后的字節碼文件中的常量池區域:

可以看到常量池中只有一個String類型的常量hello Hydra,而s2對應的字符串常量則不在此區域。對編譯器來說,運行時常量在編譯期間無法進行折疊,編譯器只會對嘗試修改它的操作進行報錯處理。

另外值得一提的是,編譯期常量與運行時常量的另一個不同就是是否需要對類進行初始化,下面通過兩個例子進行對比:

public?class?IntTest1?{public?static?void?main(String[]?args)?{System.out.println(a1.a);} } class?a1{static?{System.out.println("init?class");}public?static?int?a=1; }

運行上面的代碼,輸出:

init?class 1

如果對上面進行修改,對變量a添加final進行修飾:

public?static?final?int?a=1;

再次執行上面的代碼,會輸出:

1

可以看到在添加了final修飾后,兩次運行的結果是不同的,這是因為在添加final后,變量a成為了編譯期常量,不會導致類的初始化。另外,在聲明編譯器常量時,final關鍵字是必要的,而static關鍵字是非必要的,上面加static修飾只是為了驗證類是否被初始化過。

我們再看幾個例子來加深對final關鍵字的理解,運行下面的代碼:

public?static?void?main(String[]?args)?{final?String?h1?=?"hello";String?h2?=?"hello";String?s1?=?h1?+?"Hydra";String?s2?=?h2?+?"Hydra";System.out.println((s1?==?"helloHydra"));System.out.println((s2?==?"helloHydra")); }

執行結果:

true false

代碼中字符串h1和h2都使用常量賦值,區別在于是否使用了final進行修飾,對比編譯后的代碼,s1進行了折疊而s2沒有,可以印證上面的理論,final修飾的字符串變量屬于編譯期常量。

再看一段代碼,執行下面的程序,結果會返回什么呢?

public?static?void?main(String[]?args)?{String?h?="hello";final?String?h2?=?h;String?s?=?h2?+?"Hydra";System.out.println(s=="helloHydra"); }

答案是false,因為雖然這里字符串h2被final修飾,但是初始化時沒有使用編譯期常量,因此它也不是編譯期常量。

在上面的一些例子中,在執行常量折疊的過程中都遵循了使用常量表達式進行初始化這一原則,這里可能有的同學還會有疑問,到底什么樣才能算得上是常量表達式呢?在Oracle官網的文檔中,列舉了很多種情況,下面對常見的情況進行列舉(除了下面這些之外官方文檔上還列舉了不少情況,如果有興趣的話,可以自己查看):

  • 基本類型和String類型的字面量

  • 基本類型和String類型的強制類型轉換

  • 使用+或-或!等一元運算符(不包括++和--)進行計算

  • 使用加減運算符+、-,乘除運算符*、 / 、% 進行計算

  • 使用移位運算符 >>、 <<、 >>>進行位移操作

  • ……

字面量(literals)是用于表達源代碼中一個固定值的表示法,在Java中創建一個對象時需要使用new關鍵字,但是給一個基本類型變量賦值時不需要使用new關鍵字,這種方式就可以被稱為字面量。Java中字面量主要包括了以下類型的字面量:

//整數型字面量: long?l=1L; int?i=1;//浮點類型字面量: float?f=11.1f; double?d=11.1;//字符和字符串類型字面量: char?c='h'; String?s="Hydra";//布爾類型字面量: boolean?b=true;

當我們在代碼中定義并初始化一個字符串對象后,程序會在常量池(constant pool)中緩存該字符串的字面量,如果后面的代碼再次用到這個字符串的字面量,會直接使用常量池中的字符串字面量。

除此之外,還有一類比較特殊的null類型字面量,這個類型的字面量只有一個就是null,這個字面量可以賦值給任意引用類型的變量,表示這個引用類型變量中保存的地址為空,也就是還沒有指向任何有效的對象。

那么,如果不是使用的常量表達式進行初始化,在變量的初始化過程中引入了其他變量(且沒有被final修飾)的話,編譯器會怎樣進行處理呢?我們下面再看一個例子:

public?static?void?main(String[]?args)?{String?s1="a";String?s2=s1+"b";String?s3="a"+"b";System.out.println(s2=="ab");System.out.println(s3=="ab"); }

結果打印:

false true

為什么會出現不同的結果?在Java中,String類型在使用==進行比較時,是判斷的引用是否指向堆內存中的同一塊地址,出現上面的結果那么說明指向的不是內存中的同一塊地址。

通過之前的分析,我們知道s3會進行常量折疊,引用的是常量池中的ab,所以相等。而字符串s2在進行拼接時,表達式中引用了其他對象,不屬于編譯期常量,因此不能進行折疊。

那么,在沒有常量折疊的情況下,為什么最后返回的是false呢?我們看一下這種情況下,編譯器是如何實現,先執行下面的代碼:

public?static?void?main(String[]?args)?{String?s1="my?";String?s2="name?";String?s3="is?";String?s4="Hydra";String?s=s1+s2+s3+s4; }

然后使用javap對字節碼文件進行反編譯,可以看到在這一過程中,編譯器同樣會進行優化:

可以看到,雖然我們在代碼中沒有顯示的調用StringBuilder,但是在字符串拼接的場景下,Java編譯器會自動進行優化,新建一個StringBuilder對象,然后調用append方法進行字符串的拼接。而在最后,調用了StringBuilder的toString方法,生成了一個新的字符串對象,而不是引用的常量池中的常量。這樣,也就能解釋為什么在上面的例子中,s2=="ab"會返回false了。

本文代碼基于Java 1.8.0_261-b12 版本測試

公眾號后臺回復

"面試"---領取大廠面試資料

"導圖"---領取24張Java后端學習筆記導圖

"架構"---領取29本java架構師電子書籍

"實戰"---領取springboot實戰項目

關注公眾號

有趣、深入、直接

與你聊聊技術

覺得有用,一鍵四連吧~

總結

以上是生活随笔為你收集整理的String s=a+b+c,到底创建了几个对象?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。