更新時間:2019-09-04 10:51:24 來源:動力節點 瀏覽2610次
今天動力節點java培訓機構小編為大家分享“java中string的不可變性詳解”,希望通過此文能夠幫助大家,下面就隨小編一起看看java中string的不可變性詳解的內容吧。
一、java string 原理(為什么說String類是不可變的)
1、什么是不可變對象
如果一個對象在創建之后就不能再改變它的狀態,那么這個對象是不可變的(Immutable)。不能改變狀態的意思是,不能改變對象內的成員變量,包括基本數據類型變量的值不能改變,引用類型的變量不能指向其他的對象,引用類型指向的對象的狀態也不能改變。
2、final關鍵字的作用
如果要創建一個不可變對象,關鍵一步就是要將所有的成員變量聲明為final類型。所以下面簡單回顧一下final關鍵字的作用:
final修飾類,表示該類不能被繼承,俗稱斷子絕孫類,該類的所有方法自動地成為final方法
final修飾方法,表示子類不可重寫該方法
final修飾基本數據類型變量,表示該變量為常量,值不能再修改
final修飾引用類型變量,表示該引用在構造對象之后不能指向其他的對象,但該引用指向的對象的狀態可以改變
3、String類不可變性的分析
先看下面這段代碼:
String s = "abc"; //(1)
System.out.println("s = " + s);
s = "123"; //(2)
System.out.println("s = " + s);
打印結果為:
看到這里,你可能對String是不可變對象產生了疑惑,因為從打印結果可以看出,s的值的確改變了。其實不然,因為s只是一個String對象的引用,并不是String對象本身。
當執行(1)處這行代碼之后,會先在方法區的運行時常量池創建一個String對象"abc",然后在Java棧中創建一個String對象的引用s,并讓s指向"abc",如下圖所示:
圖1
當執行完(2)處這行代碼之后,會在方法區的運行時常量池創建一個新的String對象"123",然后讓引用s重新指向這個新的對象,而原來的對象"abc"還在內存中,并沒有改變,如下圖所示:
圖2
4、String類不可變性的原理
要理解String類的不可變性,首先看一下String類中都有哪些成員變量。在JDK1.8中,String的成員變量主要有以下幾個:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
/**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written into an ObjectOutputStream according to
* <a href="{@docRoot}/../platform/serialization/spec/output.html">
* Object Serialization Specification, Section 6.2, "Stream Elements"</a>
*/
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
首先可以看到,String類使用了final修飾符,表明String類是不可繼承的。
然后,我們主要關注String類的成員變量value,value是char[]類型,因此String對象實際上是用這個字符數組進行封裝的。再看value的修飾符,使用了private,也沒有提供setter方法,所以在String類的外部不能修改value,同時value也使用了final進行修飾,那么在String類的內部也不能修改value,但是上面final修飾引用類型變量的內容提到,這只能保證value不能指向其他的對象,但value指向的對象的狀態是可以改變的。通過查看String類源碼可以發現,String類不可變,關鍵是因為SUN公司的工程師,在后面所有String的方法里都很小心的沒有去動字符數組里的元素。所以String類不可變的關鍵都在底層的實現,而不僅僅是一個final。
5、String對象真的不可變嗎
上面提到,value雖然使用了final進行修飾,但是只能保證vaue不能指向其他的對象,但value指向的對象的狀態是可以改變的,也就是說,可以修改value指向的字符數組里面的元素。因為value是private類型的,所以只能使用反射來獲取String對象的value屬性,再去修改value指向的字符數組里面的元素。通過下面的代碼進行驗證:
String s = "Hello World";
System.out.println("s = " + s);
//獲取String類中的value屬性
Field valueField = String.class.getDeclaredField("value");
//改變value屬性的訪問權限
valueField.setAccessible(true);
//獲取s對象上的value屬性的值
char[] value = (char[]) valueField.get(s);
//改變value所引用的數組中的第6個字符
value[5] = '_';
System.out.println("s = " + s);
打印結果為:
s = Hello World
s = Hello_World
在上述代碼中,s始終指向同一個String對象,但是在反射操作之后,這個String對象的內容發生了變化。也就是說,通過反射是可以修改String這種不可變對象的。
二、設計目標(為什么String要設計成不可變的)
在Java中,將String設計成不可變的是綜合考慮到內存、同步、數據結構及安全等各種因素的結果,下文將為各種因素做一個小結。
1、運行時常量池的需要
String s = "abc";
執行上述代碼時,JVM首先在運行時常量池中查看是否存在String對象“abc”,如果已存在該對象,則不用創建新的String對象“abc”,而是將引用s直接指向運行時常量池中已存在的String對象“abc”;如果不存在該對象,則先在運行時常量池中創建一個新的String對象“abc”,然后將引用s指向運行時常量池中創建的新String對象。
String s1 = "abc";
String s2 = "abc";
執行上述代碼時,在運行時常量池中只會創建一個String對象"abc",這樣就節省了內存空間。示意圖如下所示:
圖3
2、同步
因為String對象是不可變的,所以是多線程安全的,同一個String實例可以被多個線程共享。這樣就不用因為線程安全問題而使用同步。
3、允許String對象緩存hashcode
查看上文JDK1.8中String類源碼,可以發現其中有一個字段hash,String類的不可變性保證了hashcode的唯一性,所以可以用hash字段對String對象的hashcode進行緩存,就不需要每次重新計算hashcode。所以Java中String對象經常被用來作為HashMap等容器的鍵。
4、安全性
如果String對象是可變的,那么會引起很嚴重的安全問題。比如,數據庫的用戶名、密碼都是以字符串的形式傳入來獲得數據庫的連接,或者在socket編程中,主機名和端口都是以字符串的形式傳入。因為String對象是不可變的,所以它的值是不可改變的,否則黑客們可以鉆到空子,改變String引用指向的對象的值,造成安全漏洞。
以上就是動力節點java培訓機構小編介紹的“java中string的不可變性詳解”的內容,希望對大家有幫助,更多精彩內容請繼續關注動力節點java培訓機構官網,每天會有精彩內容分享與你。
相關免費視頻教程推薦
java基礎視頻教程下載——String對象的不可變性:http://www.dabaquan.cn/xiazai/2514.html
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習