Java基礎(chǔ)教程之String深度分析
前言
String可以說是Java中使用最多最頻繁、最特殊的類,因為同時也是字面常量,而字面常量包括基本類型、String類型、空類型。
一. String的使用
1. String的不可變性
/** * The {@code String} class represents character strings. All * string literals in Java programs, such as {@code "abc"}, are * implemented as instances of this class. * <p> * Strings are constant; their values cannot be changed after they * are created. String buffers support mutable strings. * Because String objects are immutable they can be shared. For example: * ... */ public final class String { private final char value[]; }
String對象一旦在堆中創(chuàng)建出來,就無法再修改。因為String對象放在char數(shù)組中,該數(shù)組由final關(guān)鍵字修飾,不可變。
2. 定義一個字符串
/** * 定義一個字符串 */ String str1 = "helloworld"; String str2 = "helloworld"; //也可以,但基本不這樣寫 String str3 = new String("helloworld"); System.out.println(str1 == str2); System.out.println(str1 == str3); //運行結(jié)果 true, false
上面三句代碼怎么理解呢?這里需要先引入一個概念,字符串常量池。
字符串常量池是一塊特殊的獨立內(nèi)存空間,放在Java Heap中 { 在Jdk7.0之前字符串常量池存放在PermGen中,Jdk7.0的時候移動到了Java Heap(在堆中仍然是獨立的),Jdk8.0的時候去掉了PermGen,用Metaspace進行取代 } ,Java內(nèi)存模型不是本章討論的重點。
str1和str2引用的字符串字面量就在字符串常量池中,而str3引用的對象在Java Heap中。
怎么,還不太好理解?舉個例子
工作一天,到下班時間了,準備看會兒金瓶.,算了,《三國演義》,打開小說網(wǎng)站,在線閱讀;過了半個小時,女票回家了,看《三國演義》也是她想做的事兒,我看網(wǎng)址發(fā)給她,好,她也開始看了,再過半個小時,我爸回來了,他也是三國迷,但是他不喜歡在線看,因此在書店買了一本看。
上面提到的小說網(wǎng)站就是一個字符串常量池,包含了很多字符串字面量,如《三國演義》、《西游記》、《紅樓夢》等,每個字符串字面量在常量池中保持獨一份,無論誰進網(wǎng)站看《三國演義》都是同樣的網(wǎng)址和同樣的內(nèi)容。
我和女票就是str1和str2,我們看的都是同一個網(wǎng)站的《三國演義》,不僅內(nèi)容一樣,引用的地址也一樣(字符串常量池中保留了唯一的“helloworld”),因此str1 == str2 運行結(jié)果為true
而我爸就是str3,與我和女票都不一樣,雖然看的內(nèi)容也是《三國演義》,但是通過實體書籍來看,引用地址不一樣,同時一本書籍不支持多個人同時看(字符串對象在java heap中,且每次new都會新建一個對象),因此str1 == str3 運行結(jié)果為false。
一個字符串字面量總是引用String類的同一個實例,因為被String.intern()方法限定了,同樣我們可以調(diào)用該方法將堆中的String對象放到字符串常量池中,這樣做可以提升內(nèi)存使用效率,同時可以讓所用使用者共享唯一的實例。
System.out.println(str1 == str3.intern()); //運行結(jié)果為true
那么該方法的實現(xiàn)邏輯是怎么樣的呢,我們看一下源碼
/** * Returns a canonical representation for the string object. * <p> * A pool of strings, initially empty, is maintained privately by the * class {@code String}. * <p> * When the intern method is invoked, if the pool already contains a * string equal to this {@code String} object as determined by * the {@link #equals(Object)} method, then the string from the pool is * returned. Otherwise, this {@code String} object is added to the * pool and a reference to this {@code String} object is returned. * <p> * It follows that for any two strings {@code s} and {@code t}, * {@code s.intern() == t.intern()} is {@code true} * if and only if {@code s.equals(t)} is {@code true}. * <p> * All literal strings and string-valued constant expressions are * interned. String literals are defined in section 3.10.5 of the * <cite>The Java™ Language Specification</cite>. * * @return a string that has the same contents as this string, but is * guaranteed to be from a pool of unique strings. */ public native String intern();
我們發(fā)現(xiàn)這是一個native方法,看一下注釋,發(fā)現(xiàn)str3.intern()方法大致流程是:
當(dāng)執(zhí)行intern()時,會先判斷字符串常量池中是否含有相同(通過equals方法)的字符串字面量,如果有直接返回字符串字面量;如果不含,則將該字符串對象添加到字符串常量池中,同時返回該對象在字符串常量池的引用。
返回的引用需要賦值才可,否則還是會指向堆中的地址,即:
String str4 = new String("helloChina"); System.out.println(str4.intern() == str4);//false str4 = str4.intern(); String str5 = "helloChina"; String str6 = "helloZhonghua" System.out.println(str4 == str5);//true
下面我們看一下內(nèi)存結(jié)構(gòu)
3. 再次賦值給已定義的字符串
str6 = "helloHuaxia";
我們開始已經(jīng)說了String是由final關(guān)鍵字修飾,不可變,那么此時在內(nèi)存中如何體現(xiàn)呢?
4. String 對 “+” 的處理
String str7 = "good good" + " study"; String str8 = "good good study"; system.out.println(str7 == str8);
通過編譯工具后得到
String str7 = "good good study"; String str8 = "good good study";
因此我們可以發(fā)現(xiàn)編譯器在編譯期間就是進行變量合并,而不會在常量池中創(chuàng)建三個對象 “good good”,“ study”,"good good study"。str7 == str8 運行結(jié)果 true。
但如果這樣
String str9 = "good good "; String str10 = str9 + "study"; system.out.println(str8 == str10);//false
這時運行結(jié)果為false,通過String變量 + 字符常量方式得到的結(jié)果會在堆中,不在常量池中,當(dāng)然可以通過intern()方法放進常量池中,同時不僅“+”如此,調(diào)用substring(),toUpperCase(),trim()等返回的都是String在堆中的地址。
5. String常用的方法
//str1 == "hello,world "; //獲取長度 str1.length()//12; //截取位置2到5之間的字符串(包括位置2,不包括位置5,從0開始) str1.substring(2,5);//"llo" //判斷是否含有字符串“ello” str1.contains("ello");//true,通過indexOf實現(xiàn) //獲取ello在str1中的開始位置 str1.indexOf("ello");//1 //將字符串轉(zhuǎn)化為字符串?dāng)?shù)據(jù) str1.split(",");//["hello","world"] //去掉字符串兩側(cè)空格 str1.trim();//"hello,world"
二. 總結(jié)
本文從String的不可變性,String創(chuàng)建時字面量和String對象的不同,字符串字面量常量池,字符串的內(nèi)存結(jié)構(gòu),常用的String相關(guān)方法的描述,若有不對之處,請批評指正,望共同進步,謝謝!
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。
- Java String字符串和Unicode字符相互轉(zhuǎn)換代碼
- java對象轉(zhuǎn)換String類型的三種方法
- Java List轉(zhuǎn)換成String數(shù)組幾種實現(xiàn)方式詳解
- java判斷字符串String是否為空問題淺析
- java string類的常用方法詳細介紹
- java String[]字符串?dāng)?shù)組自動排序的簡單實現(xiàn)
- Java的string類為什么是不可變的
- java中String與StringBuilder的區(qū)別
- Java中String類(字符串操作)的10個常見問題和解決方法
- JAVA中STRING的常用方法小結(jié)
相關(guān)文章
JAVA中HTTP基本認證(Basic Authentication)實現(xiàn)
HTTP 基本認證是一種簡單的認證方法,本文主要介紹了JAVA中HTTP基本認證(Basic Authentication),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07一篇文章帶你搞定SpringBoot不重啟項目實現(xiàn)修改靜態(tài)資源
這篇文章主要介紹了一篇文章帶你搞定SpringBoot不重啟項目實現(xiàn)修改靜態(tài)資源,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09SpringSecurity6自定義JSON登錄的實現(xiàn)
目前最新版的Spring Boot已經(jīng)到了3.0.5了,隨之而來Spring Security 目前的版本也到了6.0.2了,Spring Security寫法的變化特別多,本文就來介紹下,感興趣的可以了解一下2023-12-12Java語法基礎(chǔ)之循環(huán)結(jié)構(gòu)語句詳解
這篇文章主要為大家詳細介紹了Java語法基礎(chǔ)之循環(huán)結(jié)構(gòu)語句,感興趣的小伙伴們可以參考一下2016-09-09SpringBoot集成SwaggerUi以及啟動時遇到的錯誤
這篇文章主要介紹了SpringBoot集成SwaggerUi以及啟動時遇到的錯誤,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06