Java String 物件,你真的瞭解了嗎?
String 物件的實現
String
物件是 Java 中使用最頻繁的物件之一,所以 Java 公司也在不斷的對String
物件的實現進行優化,以便提升String
物件的效能,看下面這張圖,一起了解一下String
物件的優化過程。
1. 在 Java6 以及之前的版本中
String
物件是對 char 陣列進行了封裝實現的物件,主要有四個成員變數: char 陣列、偏移量 offset、字元數量 count、雜湊值 hash。
String
物件是通過 offset 和 count 兩個屬性來定位 char[] 陣列,獲取字串。這麼做可以高效、快速地共享陣列物件,同時節省記憶體空間,但這種方式很有可能會導致記憶體洩漏。
2. 從 Java7 版本開始到 Java8 版本
從 Java7 版本開始,Java 對String
類做了一些改變。String
類中不再有 offset 和 count 兩個變量了。這樣的好處是String
物件佔用的記憶體稍微少了些,同時 String.substring 方法也不再共享 char[],從而解決了使用該方法可能導致的記憶體洩漏問題。
3. 從 Java9 版本開始
將 char[] 陣列改為了 byte[] 陣列,為什麼需要這樣做呢?我們知道 char 是兩個位元組,如果用來存一個位元組的字元有點浪費,為了節約空間,Java 公司就改成了一個位元組的byte來儲存字串。這樣在儲存一個位元組的字元是就避免了浪費。
在 Java9 維護了一個新的屬性 coder,它是編碼格式的標識,在計算字串長度或者呼叫 indexOf() 函式時,需要根據這個欄位,判斷如何計算字串長度。coder 屬性預設有 0 和 1 兩個值, 0 代表Latin-1(單位元組編碼),1 代表 UTF-16 編碼。如果 String
判斷字串只包含了 Latin-1,則 coder 屬性值為 0 ,反之則為 1。
String 物件的建立方式
1、通過字串常量的方式
String str= "pingtouge"
的形式,使用這種形式建立字串時, JVM 會在字串常量池中先檢查是否存在該物件,如果存在,返回該物件的引用地址,如果不存在,則在字串常量池中建立該字串物件並且返回引用。使用這種方式建立的好處是:避免了相同值的字串重複建立,節約了記憶體
2、String()建構函式的方式
String str = new String("pingtouge")
的形式,使用這種方式建立字串物件過程就比較複雜,分成兩個階段,首先在編譯時,字串pingtouge
會被加入到常量結構中,類載入時候就會在常量池中建立該字串。然後就是在呼叫new()時,JVM 將會呼叫String
的建構函式,同時引用常量池中的pingtouge
字串,
在堆記憶體中建立一個String
物件並且返回堆中的引用地址。
瞭解了String
物件兩種建立方式,我們來分析一下下面這段程式碼,加深我們對這兩種方式的理解,下面這段程式碼片中,str
是否等於str1
呢?
String str = "pingtouge";
String str1 = new String("pingtouge");
system.out.println(str==str1)
我們逐一來分析這幾行程式碼,首先從String str = "pingtouge"
開始,這裡使用了字串常量的方式建立字串物件,在建立pingtouge
字串物件時,JVM會去常量池中查詢是否存在該字串,這裡的答案肯定是沒有的,所以JVM將會在常量池中建立該字串物件並且返回物件的地址引用,所以str
指向的是pingtouge
字串物件在常量池中的地址引用。
然後是String str1 = new String("pingtouge")
這行程式碼,這裡使用的是建構函式的方式建立字串物件,根據我們上面對建構函式方式建立字串物件的理解,str1
得到的應該是堆中pingtouge
字串的引用地址。由於str
指向的是pingtouge
字串物件在常量池中的地址引用而str1
指向的是堆中pingtouge
字串的引用地址,所以str
肯定不等於str1
。
String 物件的不可變性
從我們知道String
物件的那一刻起,我想大家都知道了String
物件是不可變的。那它不可變是怎麼做到的呢?Java
這麼做能帶來哪些好處?我們一起來簡單的探討一下,先來看看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;
}
從這段原始碼中可以看出,String
類用了 final 修飾符,我們知道當一個類被 final 修飾時,表明這個類不能被繼承,所以String
類不能被繼承。這是String
不可變的第一點
再往下看,用來儲存字串的char value[]
陣列被private
和final
修飾,我們知道對於一個被final
的基本資料型別的變數,則其數值一旦在初始化之後便不能更改。這是String
不可變的第二點。
Java 公司為什麼要將String
設定成不可變的,主要從以下三方面考慮:
- 1、保證 String 物件的安全性。假設 String 物件是可變的,那麼 String 物件將可能被惡意修改。
- 2、保證 hash 屬性值不會頻繁變更,確保了唯一性,使得類似 HashMap 容器才能實現相應的 key-value 快取功能。
- 3、可以實現字串常量池
String 物件的優化
字串是我們常用的Java
型別之一,所以對字串的操作也是避免不了的,在對字串的操作過程中,如果使用不當,效能會天差地別。那麼在字串的操作過程中,有哪些地方需要我們注意呢?
優雅的拼接字串
字串的拼接是對字串操作使用最頻繁的操作之一,由於我們知道String
物件的不可變性,所以我們在做拼接時儘可能少的使用+
進行字串拼接或者說潛意識裡認為不能使用+
進行字串拼接,認為使用+
進行字串拼接會產生許多無用的物件。事實真的是這樣嗎?我們來做一個實驗。我們使用+
來拼接下面這段字串。
String str8 = "ping" +"tou"+"ge";
一起來分析一下這段程式碼會產生多少個物件?如果按照我們理解的意思來分析的話,首先會建立ping
物件,然後建立pingtou
物件,最後才會建立pingtouge
物件,一共建立了三個物件。真的是這樣嗎?其實不是這樣的,Java 公司怕我們程式設計師手誤,所以對編譯器進行了優化,上面的這段字串拼接會被我們的編譯器優化,優化成一個String str8 = "pingtouge";
物件。除了對常量字串拼接做了優化以外,對於使用+
號動態拼接字串,編譯器也做了相應的優化,以便提升String
的效能,例如下面這段程式碼:
String str = "pingtouge";
for(int i=0; i<1000; i++) {
str = str + i;
}
編譯器會幫我們優化成這樣
String str = "pingtouge";
for(int i=0; i<1000; i++) {
str = (new StringBuilder(String.valueOf(str))).append(i).toString();
}
可以看出 Java 公司對這一塊進行了不少的優化,防止由於程式設計師不小心導致String
效能急速下降,儘管 Java 公司在編譯器這一塊做了相應的優化,但是我們還是能看出 Java 公司優化的不足之處,在動態拼接字串時,雖然使用了 StringBuilder 進行字串拼接,但是每次迴圈都會生成一個新的 StringBuilder 例項,同樣也會降低系統的效能。
所以我們在做字串拼接時,我們需要從程式碼的層面進行優化,在動態的拼接字串時,如果不涉及到執行緒安全的情況下,我們顯示的使用 StringBuilder 進行拼接,提升系統性能,如果涉及到執行緒安全的話,我們使用 StringBuffer 來進行字串拼接
巧妙的使用 intern() 方法
* <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>
public native String intern();
這是 intern() 函式的官方註釋說明,大概意思就是 intern 函式用來返回常量池中的某字串,如果常量池中已經存在該字串,則直接返回常量池中該物件的引用。否則,在常量池中加入該物件,然後 返回引用。
有一位Twitter
工程師在QCon
全球軟體開發大會上分享了一個他們對 String
物件優化的案例,他們利用String.intern()
方法將以前需要20G記憶體儲存優化到只需要幾百兆記憶體。這足以體現String.intern()
的威力,我們一起來看一個例子,簡單的瞭解一下String.intern()
的用法。
public static void main(String[] args) {
String str = new String("pingtouge");
String str1 = new String("pingtouge");
System.out.println("未使用intern()方法:"+(str==str1));
System.out.println("未使用intern()方法,str:"+str);
System.out.println("未使用intern()方法,str1:"+str1);
String str2= new String("pingtouge").intern();
String str3 = new String("pingtouge").intern();
System.out.println("使用intern()方法:"+(str2==str3));
System.out.println("使用intern()方法,str2:"+str2);
System.out.println("使用intern()方法,str3:"+str3);
}
從結果中可以看出,未使用String.intern()
方法時,構造相同值的字串物件返回不同的物件引用地址,使用String.intern()
方法後,構造相同值的字串物件時,返回相同的物件引用地址。這能幫我們節約不少空間
String.intern()
方法雖然好,但是我們要結合場景使用,不能亂用,因為常量池的實現是類似於一個HashTable
的實現方式,HashTable
儲存的資料越大,遍歷的時間複雜度就會增加。如果資料過大,會增加整個字串常量池的負擔。
靈活的字串的分割
字串的分割是字串操作的常用操作之一,對於字串的分割,大部分人使用的都是 Split() 方法,Split() 方法大多數情況下使用的是正則表示式,這種分割方式本身沒有什麼問題,但是由於正則表示式的效能是非常不穩定的,使用不恰當會引起回溯問題,很可能導致 CPU 居高不下。在以下兩種情況下 Split() 方法不會使用正則表示式:
- 傳入的引數長度為1,且不包含“.$|()[{^?*+\”regex元字元的情況下,不會使用正則表示式
- 傳入的引數長度為2,第一個字元是反斜槓,並且第二個字元不是ASCII數字或ASCII字母的情況下,不會使用正則表示式
所以我們在字串分割時,應該慎重使用 Split() 方法,首先考慮使用 String.indexOf() 方法進行字串分割,如果 String.indexOf() 無法滿足分割要求,再使用 Split() 方法,使用 Split() 方法分割字串時,需要注意回溯問題。
文章不足之處,望大家多多指點,共同學習,共同進步
參考資料
- Java效能調優實戰 劉超
最後
打個小廣告,歡迎掃碼關注微信公眾號:「平頭哥的技術博文」,一起進步吧。
相關推薦
Java String 物件,你真的瞭解了嗎?
String 物件的實現 String物件是 Java 中使用最頻繁的物件之一,所以 Java 公司也在不斷的對String物件的實現進行優化,以便提升String物件的效能,看下面這張圖,一起了解一下String物件的優化過程。 1. 在 Java6 以及之前的版本中 String物件是對 char 陣
#套路非常深的一道Java面試題 ,你中招了嗎?
在求職的過程中,技術測試是不可缺少的一部分,也是最關鍵的一部分,但是有些面試官喜歡去抽一些“套路”比較深的題目,看看面試者對於程式設計的理解是否深刻,這其中的題目中,也不乏有佼佼者! 如果有想學習java的程式設計師,可來我們的java學習扣qun:79979,2590免費送java的視訊教程噢
這些神經網路調參細節,你都瞭解了嗎
【磐創AI導讀】:本文主要介紹了神經網路調參並附有python程式碼介紹。今天在寫畢設的時候又回顧了一下神經網路調參的一些細節問題,特來總結下。主要從weight_decay,clip_norm,lr_decay說起。以前剛入門的時候調參只是從hidden_size,hidd
這些Java基礎面試知識點,你都掌握了嗎?
本文主要是我最近複習Java基礎原理過程中寫的Java基礎學習總結。Java的知識點其實非常多,並且有些知識點比較難以理解,有時候我們自以為理解了某些內容,其實可能只是停留在表面上,沒有理解其底層實現原理。 紙上得來終覺淺,絕知此事要躬行。筆者之前對每部分的內容 對做了比較深入的學習以及程式碼實
看完這篇文章,你就瞭解了Android Handler的一切
Message More ...next() { // Return here if the message loop has already quit and been disposed. // This can happen if the appl
10個用Java謀生非常有趣的方式,你全都掌握了嗎?
提升 ava ext 如果 cap suse 努力 混合 ges 令我驚訝的是,有些人覺得編程並不令人興奮——只將它當作是一份枯燥的工作。不過,雖然可能的確有很多無聊的編程工作,但這並不意味著你不得不接受這些工作中的一個。 程序員有各種各樣的機會,運用他們的技能去做一些有趣
開發中濫用面向物件,你是否違背了程式設計原則
Switch 宣告 Switch 宣告(Switch Statements) 你有一個複雜的 switch
Java多執行緒併發05——那麼多的鎖你都瞭解了嗎
> 在多執行緒或高併發情境中,經常會為了保證資料一致性,而引入鎖機制,本文將為各位帶來有關鎖的基本概念講解。關注我的公眾號「Java面典」瞭解更多 Java 相關知識點。 根據鎖的各種特性,可將鎖分為以下幾類: * 樂觀鎖/悲觀鎖 * 獨享鎖(互斥鎖)/共享鎖(讀寫鎖) * 可重入鎖 * 公平鎖/非公平鎖
VR禁毒體驗,你試過了嗎?
研發 img 成了 此舉 pict clas bsp 廣州 嘗試 VR禁毒體驗,你試過了嗎? 大家先來跟著小編的文字,嘗試冥想一下——當你正處於寧靜的情緒中,對於顏色感覺生動、豐富而深刻,感到周圍事物絢麗多彩,五光十色;對音樂的鑒賞能力增強,對其他聲音也很敏感。然後,感到時
【項目管理】經驗之談 | 資深項目經理都避免的5個坑,你中招了嗎?
尊重 最終 fail 同方 快速 這就是 tro 理解 動力 哈嘍!大家好! 那天看到最有趣的一句話就是 為了填坑,一位項目經理胖了20斤 。。。。。 今天就給大家介紹一下 項目經理要註意的那些“坑” 項目經理“誤踩雷區” 1 未告知成員工作目標 作為項目經理
C語言/原子/編譯,你真的明白了嗎?
clas done ati pre 內存 程序 導致 裏的 creat 說到原子,類似於以下的代碼可能人人都可以看出貓膩。 #include <stdio.h> #include <pthread.h> int cnt = 0;
身份證掃描識別/身份證OCR識別的正確姿勢,你get到了嗎?
視頻流 開發包 掃描識別 出錯 應用 左右 信息 設備 ucs 自從國家規定電信實名制之後,實名制已經推廣到各個領域:辦理通信業務需要實名制、銀行開戶需要實名制、移動支付需要實名制,就連註冊個自媒體賬戶都需要實名制。 而實名制的背後,就是身份證信息的采集和錄入驗證。 傳統的
IT輪子系列(六)——Excel上傳與解析,一套代碼解決所有Excel業務上傳,你Get到了嗎
tryparse mappath src 個推 列名 import ges bject tab 前言 在日常開發當中,excel的上傳與解析是很常見的。根據業務不同,解析的數據模型也都不一樣。不同的數據模型也就需要不同的校驗邏輯,這往往需要寫多套的代碼進行字段的檢驗,如必填
柯夢嬌:3.5非農強勢來襲,你準備好了嗎?
均線 黃金 有效 走勢分析 body 現象 線圖 盈利 交易 柯夢嬌:3.5非農強勢來襲,你準備好了嗎? 就像貝利的烏鴉嘴一樣,資本市場常有些令人訝異的“規律”,比如期指“逢9必跌”、原油市場“7月必跌”等等。如今,在黃金市場上,也出現了這一種詭異的現象——周五總是
python小白也可以分分鐘爬取微博數據,並生成有個性的詞雲,你get到了嗎?
python 爬蟲 web開發 編程Python(發音:英[?pa?θ?n],美[?pa?θɑ:n]),是一種面向對象、直譯式電腦編程語言,也是一種功能強大的通用型語言,已經具有近二十年的發展歷史,成熟且穩定。它包含了一組完善而且容易理解的標準庫,能夠輕松完成很多常見的任務。它的語法非常簡捷和清晰,與其它大多
共享人才時代已經到來,你做好準備了嗎?
共享共享經濟是一種全新的經濟模式,近幾年得到了突飛猛進的發展。其本質是整合線下的閑散物品或服務者,讓他們提供產品或服務。在共享過程中,供給方通過在特定時間內提供使用權或服務獲得酬勞,需求方雖無所有權但在特定使用權內滿足了需要。 人才是決定企業成敗的關鍵性因素之一。共享人才做為一種新生的力量,在互聯網大環境中扮
記一次安裝多版本php的四個雷區,你踩著了嗎
path start cgi 命令執行 mysq -c tool port 一鍵 記一次安裝多版本的php的四個雷區,你踩著了嗎 需求:公司需要在同一臺服務器上安裝不同版本的php,而這一臺的服務上已經安裝了php.7.1,現需要同
逸管家:共享人才時代已經到來,你做好準備了嗎?
特定 正常 之間 結束 銷售 相關 不可 人的 十分 ? 共享人才時代已經到來,你做好準備了嗎? 共享經濟是一種全新的經濟模式,近幾年得到了突飛猛進的發展。其本質是整合線下的閑散物品或服務者,讓他們提供產品或服務。 在共享過程中,供給方通過在特定時間內提供使用權或服務
初學C語言編程時最容易犯的錯誤,你踩坑了嗎?
不同的 類型 alt 結果 如何 wid text size can C編譯的程序對語法檢查並不像其它高級語言那麽嚴格,這就給編程人員留下“靈活的余地”,但還是由於這個靈活給程序的調試帶來了許多不便,尤其對初學C語言的人來說,經常會出一些連自己都不知道錯在哪裏的錯誤。看著有
HTTPS時代已來,你做好準備了嗎?
兩種方法 運營商 增長 images 同步 有效期 很多 取證 錯誤類型 摘要: 全站HTTPS時代的到來,這也是最近越來越多的網站上HTTPS和更換證書的原因。那麽究竟什麽是HTTPS?它為什麽會提升安全系數?CDN HTTPS又將如何做到安全性與性能同時提升?作為用戶,