一文弄懂String的所有小秘密
文章目錄
- 簡介
- String是不可變的
- 傳值還是傳引用
- substring() 導致的內存泄露
- 總結
一文弄懂String的所有小秘密
簡介
String是java中非常常用的一個對象類型。可以說java中使用最多的就是String了。那么String到底有哪些秘密呢?接下來本文將會一一講解。
String是不可變的
String是不可變的,官方的說法叫做immutable或者constant。
String的底層其實是一個Char的數組。
private final char value[];所有的String字面量比如"abc"都是String的實現。
考慮下面的賦值操作:
String a="abc"; String b="abc";對于java虛擬機來說,"abc"是字符串字面量,在JDK 7之后,這個字符串字面量是存儲在java heap中的。而在JDK 7之前是有個專門的方法區來存儲的。
有了“abc”,然后我們將“abc” 賦值給a和b。
可以看到這里a和b只是java heap中字符串的引用。
再看看下面的代碼發生了什么:
String c= new String("abc");首先在java heap中創建了“abc”,然后調用String的構造函數:
public String(String original) {this.value = original.value;this.hash = original.hash;}在構造函數中,String將底層的字符串數組賦值給value。
因為Array的賦值只是引用的賦值,所以上述new操作并不會產生新的字符串字面值。
但是new操作新創建了一個String對象,并將其賦值給了c。
String的不可變性還在于,String的所有操作都會產生新的字符串字面量。原來的字符串是永遠不會變化的。
字符串不變的好處就在于,它是線程安全的。任何線程都可以很安全的讀取字符串。
傳值還是傳引用
一直以來,java開發者都有這樣的問題,java到底是傳值還是傳引用呢?
我想,這個問題可以從兩方面來考慮。
首先對于基礎類型int,long,double來說,對他們的賦值是值的拷貝。而對于對象來說,賦值操作是引用。
另一方面,在方法調用的參數中,全部都是傳值操作。
public static void main(String[] args) {String x = new String("ab");change(x);System.out.println(x); }public static void change(String x) {x = "cd"; }我們看上面的例子,上面的例子輸出ab。因為x是對“ab”的引用,但是在change方法中,因為是傳值調用,所以會創建一個新的x,其值是“ab”的引用地址。當x被重新賦值之后,改變的只是拷貝之后的x值。而本身的x值是不變的。
substring() 導致的內存泄露
第一次看到這個話題,大家可能會很驚訝,substring方法居然會導致內存泄露?這個話題要從JDK 6開始講起。
我們先看下JDK 6的實現:
String(int offset, int count, char value[]) {this.value = value;this.offset = offset;this.count = count; }public String substring(int beginIndex, int endIndex) {//check boundaryreturn new String(offset + beginIndex, endIndex - beginIndex, value); }可以看到,JDK 6的substring方法底層還是引用的原始的字符串數組。唯一的區別就是offset和count不同。
我們考慮一下下面的應用:
String string = "abcdef"; String subString = string.substring(1, 3); string = null;雖然最后我們將String賦值為null,但是subString仍然引用了最初的string。將不會被垃圾回收。
在JDK 7之后,String的實現發送了變化:
public String(char value[], int offset, int count) {//check boundarythis.value = Arrays.copyOfRange(value, offset, offset + count); }public String substring(int beginIndex, int endIndex) {//check boundaryint subLen = endIndex - beginIndex;return new String(value, beginIndex, subLen); }Arrays.copyOfRange將會拷貝一份新的數組,而不是使用之前的數組。從而不會發生上面的內存泄露的問題。
總結
雖然String是我們經常使用的對象,但是里面的原理還是值得我們了解的。
更多精彩內容且看:
- 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
- Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
- java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程
本文作者:flydean程序那些事
本文鏈接:http://www.flydean.com/string-all-in-one/
本文來源:flydean的博客
歡迎關注我的公眾號:程序那些事,更多精彩等著您!
總結
以上是生活随笔為你收集整理的一文弄懂String的所有小秘密的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入理解java的泛型
- 下一篇: JDK 14的新特性:文本块Text B