淺析String不可變性

来源:http://www.cnblogs.com/think-in-java/archive/2016/12/03/6127804.html
-Advertisement-
Play Games

在所有編程語言領域,我想字元串應該是地球上最常用的表達手段了吧。 在java的世界里,String是作為類出現的,核心的一個域就是一個char數組,內部就是通過維護一個不可變的char數組,來向外部輸出的。 這是jdk一段String類定義,首先類是final,表明類不可被繼承;核心域是privat ...


 

在所有編程語言領域,我想字元串應該是地球上最常用的表達手段了吧。

 


 

在java的世界里,String是作為類出現的,核心的一個域就是一個char數組,內部就是通過維護一個不可變的char數組,來向外部輸出的。

這是jdk一段String類定義,首先類是final,表明類不可被繼承;核心域是private final的,final表明這個引用所指向的記憶體地址不會改變,但這還不足說明value[]是不可變的;因為引用所指向的記憶體的值有可能發生變化,但是jdk是不會讓這樣的事情發生的。private 保證這個域對外部來說是不可見的,這還不夠,對value還要進行 保護性拷貝 。

舉一個簡單的例子:

這是一個String的構造函數,參數是一個char數組引用,它並沒有把這個數組引用直接賦值給實例對象的value成員變數,而是通過一個Arrays.copyOf的方式拷貝一個數組再給到對象的成員變數。為什麼呢?假設它這裡是直接一個賦值,那String的不可變性就徹底被破壞了,因為如此一來,存在一個外部引用與實例對象value引用指向相同的記憶體地址,通過外部引用就可以改變這個char數組對象,最終導致的結果就是String不再不可變。幸好JDK中所有的對value的操作都是保護性拷貝操作,不管是被賦值,還是賦值給其它外部引用。

說了這麼多,為什麼JAVA要String保持一個不可變的狀態呢?原因其實很簡單,因為String太太太太常用了,地球上沒有能比這個更常用的對象了,設計成不可變的,是為了減少大量的同步鎖的開銷。但是要註意 並不是聲明成final的類一定是不可變的 。

根據effective java一書中提到,類不變需遵循五條規則:

1.不提供任何機會修改對象狀態的方法

2.保證類不被擴展

3.所有域都是final

4.所有域都是私有的

5.確保對於任何可變組件的互斥訪問

 

有興趣的同學可以參考 effective 第十五條,這裡就不展開講了。作了那麼久的鋪墊,接下來可以談談avoid getfield opcode了,按翻譯來說就是防止"調用訪問域的操作碼",這段tip來自一段註釋。

十分常用的replace方法,內部演算法大概是這樣一個過程:先找到第一個oldChar的下標i,拷貝小標i之前的舊數組的內容到新的數組,新數組[i]='newChar',遍歷i之後的內容,若舊數組出現為oldChar則在新數組中替換為newChar,若沒有出現,則拷貝舊值到新數組。

起初我很奇怪,到底為什麼,一定要找到第一個出現oldChar的下標,為什麼不直接遍曆數組中每一個char 若為舊值,替換為新值。我從時間複雜度,空間複雜度去考慮這個演算法,始終沒有得到結果。我還是太年輕啊,後來才發覺其實還是為了維護一個String的設計原則:"對於擁有相同的字元字面量的情況下,String的構造還是優先返回原字元串對象"。這麼做應該是為瞭解決堆記憶體吧。

 

那麼,這一句註釋到底是什麼意思呢?要理解這句話,需要對JVM有一定的瞭解。

 

JVM在運行中的數據區,分為五個部分:方法區,堆區,虛擬機棧,本地方法棧,程式計數器。

首先類相關的信息肯定是放在方法區的,堆中放一些實例對象,程式計數器始終指向下一條將要執行的指令,虛擬機棧和本地方法棧分別是用來於普通方法和本地方法的。

著重說一下虛擬機棧,它是線程私有的,描述的是java方法執行的記憶體模型:每個方法在執行的同時創建一個棧幀,用於存放局部變數表,操作數棧,方法入口,動態鏈接等。

 

 局部變數表用來存放一些基本數據類,和引用。操作數棧的話,是用來作運算用的,打個比方

int a=1;
int b =2;
int c =a+b;

JVM會先把a,b的值壓入到操作數棧保存起來,等到程式計數器執行加法指令的時候,再把a,b從棧中pop出來。重點就在於a,b值是從哪裡壓入到棧中的,如果沒有那麼接下來要遍歷的就是value數組了,value毫無疑問是堆中的數據,也就是每一次遍歷會經歷,數據由堆中取出,進入棧幀,再由棧幀壓入到操作數棧,最後pop出來進行運算。

如果執行了上述代碼,情況就大不相同了,val就是一個局部變數,它會被存放在局部變數表中,接來下的運算,就是局部變數表到操作數棧了,屬於一個棧幀內的數據轉移。JVM為了避免頻繁進行堆棧數據轉移,將值複製到本地變數一次,以避免在接下來的幾行中迴圈的每一次迭代,從堆中多次取下的欄位值。

 

除了我提到的這些,String源碼中還有許多值得學習的演算法,設計方式,代碼優化,比如,還有常量池的實現。

 

最後我想求教一個問題啊,在JDK7點版本中,String引入了一段靜態代碼塊,

我甚是不解啊,不知道有沒有大神幫我解讀一下這段代碼的含義?


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前邊我們說過了 Http協議 有兩個缺陷 一個是無狀態 、 一個是純文本 。 純文本也就是說Http請求中的內容都是以字元串的形式發送的。 但是Java又是一個強類型語言,所以將一個字元串轉換成Java中的數據類型這一重任就落在了Struts2的肩膀上。 類型轉換最常見的可能就是將將10/29/20 ...
  • 系統環境 ubuntu 14.04 LTS vsftpd安裝 配置文件 預設配置文件的位置為 /etc/vsftpd.conf 常見問題 500 OOPS: vsftpd: refusing to run with writable root inside chroot 解:所設置的目錄不能有寫許可權 ...
  • 2016-12-03 數組定義字元串: 每次定義數組的時候,系統都會在記憶體開闢你指定數組大小的空間,並且數組中的內容對於我們是可讀可寫的,看如下代碼: 1 #include<stdio.h> 2 int main() 3 { 4 char str[100] = "hello world"; 5 ch ...
  • 原文:http://www.cnblogs.com/imaker/p/6128049.html 所屬年份:2010.9;2012.3編寫函數fun,其功能是:根據以下公式求π的值(要求精度0.0005,即某項小於0.0005時停止迭代)。 程式運行後,若輸入精度0.0005,則程式應輸出為3.14… ...
  • 如下是作業,用python做一個ftp,主要利用socket。 server端在linux下運行,在client端可以執行shell命令(靜態的) 在client端輸入get xxx,即可下載。 在client端輸入put xxx,即可上傳。 server端: client端: ...
  • 1.簡化Java開發 Spring是一個開源框架,它的根本使命在於簡化java開發。為了降低java開發的複雜性,Spring採取了以下4種關鍵策略: 1.基於POJO的輕量級和最小侵入性編程; 有很多框架強迫應用繼承它們的類或實現它們的介面從而導致應用與框架綁死,而基於Spring構建的應用通常沒 ...
  • STL的pair,有兩個值,可以是不同的類型。 template struct pair; 註意,pair在頭文件utility中,不要include。(一個錯誤是 include ) 成員類型 first_type first的類型 second_type second的類型 成員變數 first... ...
  • 解釋如下: content 中需要被替換的就是{}中的參數,array數組中存放的是對應的要替換的參數;使用MessageFormat方法的時候,需要要將這些參數的個數匹配正確,並且數序要指定,否則匹配出錯。這樣就實現了參數的替換。很簡單,也很死板。 MessageFormat:出自java.tex ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...