源碼淺析之String

来源:http://www.cnblogs.com/tang-h/archive/2017/11/30/7932086.html
-Advertisement-
Play Games

平常工作中String字元串對象用的那麼多,但是真的瞭解String嗎? 源碼淺析先從String開刀。 想要真正瞭解一個類,首先得從源碼入手。(本文JDK源碼版本1.7.0_75) 先看類信息: 由源碼可以看出: final修飾了String類,表明該類不能被其他類繼承。 String 實現了Se ...



平常工作中String字元串對象用的那麼多,但是真的瞭解String嗎?

源碼淺析先從String開刀。

想要真正瞭解一個類,首先得從源碼入手。(本文JDK源碼版本1.7.0_75)

 

先看類信息:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence

由源碼可以看出:

final修飾了String類,表明該類不能被其他類繼承。

String 實現了Serializable介面,支持java序列化操作。

實現了Comparable介面,就必須實現介面下的compareTo方法,說明String對象支持比較,可對其排序。

再一個CharSequence,是字元序列,包括length(), charAt(int index), subSequence(int start, int end)幾個介面。通過下麵對String類中屬性也可以知道,其實String的本質就是字元數組,所以實現了CharSequence介面。


繼續看類中的屬性:

private final char value[]; //存放字元串的字元數組
private final int offset; //字元數組第一個下標的偏移量
private final int count;//String中的字元數量
private int hash; // string的hashcode值

value、offset和count屬性都是final修飾的,說明瞭這幾個屬性都是不可變的,而value數組存儲的字元串對象的實際內容,由此說明String對象是不可變的。不可變的對象怎麼做修改操作呢?下麵會解答。
從value[]就能看出String的實質了,就是字元數組,數組的大小就是字元的數量。那int類型的hash值是幹嘛的?存放hashcode,而hashcode的存在是便於快捷查找。比如在HashMap、HashTable等集合容器中,字元串作為key時,就是通過字元串的hashcode在散列結構中定位存儲位置的。


String中方法眾多,不一一分析了,著重看看幾個重要的方法。
字元串比較方法(用的很頻繁,如果連源碼都沒看過,豈不是很low?)

 1     public boolean equals(Object anObject) {
 2         if (this == anObject) {
 3             return true;
 4         }
 5         if (anObject instanceof String) {
 6             String anotherString = (String) anObject;
 7             int n = count;
 8             if (n == anotherString.count) {
 9                 char v1[] = value;
10                 char v2[] = anotherString.value;
11                 int i = offset;
12                 int j = anotherString.offset;
13                 while (n-- != 0) {
14                     if (v1[i++] != v2[j++])
15                         return false;
16                 }
17                 return true;
18             }
19         }
20         return false;
21     }

傳入Object對象(Object是所有對象的父類),比較本類的引用地址和傳入的引用地址是否相等,引用地址相等說明就是一個對象,返回true。
如果不等,判斷傳入的對象是不是String類型(不是一類的就沒什麼可比性了),接著再比較字元串的長度,長度一樣則一一比較字元數組中的內容,數組中的內容全部一致則返回true。
比較順序是引用地址->類類型->字元長度->字元數組,而不是直接比較字元數組中的內容,最大化的提高比較效率。

 

下麵看計算hash值的方法

 1     public int hashCode() {
 2         int h = hash;
 3         if (h == 0 && count > 0) {
 4             int off = offset;
 5             char val[] = value;
 6             int len = count;
 7 
 8             for (int i = 0; i < len; i++) {
 9                 h = 31 * h + val[off++];
10             }
11             hash = h;
12         }
13         return h;
14     }

這個方法是計算hash值並傳給hash屬性。第一次調hashcode方法時,hash值是0,需要計算hash。根據源碼也看的很清楚,得出hash值的計算公式:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]。問題來了,為什麼取31為權重呢?主要是因為31是一個奇質數,並且31*i=32*i-i=(i<<5)-i,這種位移與減法結合的計算在電腦中相比一般的運算要快很多。
hash值的作用主要是定位查找對象,判斷迴文數也可以使用String類的hashcode是否相等進行判斷。

 

字元串操作方法

截取字元串

 1     public String substring(int beginIndex, int endIndex) {
 2         if (beginIndex < 0) {
 3             throw new StringIndexOutOfBoundsException(beginIndex);
 4         }
 5         if (endIndex > count) {
 6             throw new StringIndexOutOfBoundsException(endIndex);
 7         }
 8         if (beginIndex > endIndex) {
 9             throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
10         }
11         return ((beginIndex == 0) && (endIndex == count)) ? this
12                 : new String(offset + beginIndex, endIndex - beginIndex, value);
13     }

 字元串拼接

 1     public String concat(String str) {
 2         int otherLen = str.length();
 3         if (otherLen == 0) {
 4             return this;
 5         }
 6         int len = value.length;
 7         char buf[] = Arrays.copyOf(value, len + otherLen);
 8         str.getChars(buf, len);
 9         return new String(buf, true);
10     }

 替換字元串中的內容

 1     public String replace(char oldChar, char newChar) {
 2         if (oldChar != newChar) {
 3             int len = value.length;
 4             int i = -1;
 5             char[] val = value; /* avoid getfield opcode */
 6 
 7             while (++i < len) {
 8                 if (val[i] == oldChar) {
 9                     break;
10                 }
11             }
12             if (i < len) {
13                 char buf[] = new char[len];
14                 for (int j = 0; j < i; j++) {
15                     buf[j] = val[j];
16                 }
17                 while (i < len) {
18                     char c = val[i];
19                     buf[i] = (c == oldChar) ? newChar : c;
20                     i++;
21                 }
22                 return new String(buf, true);
23             }
24         }
25         return this;
26     }

字元串截取方法,先判斷截取位置是否越界,再返回新的字元串對象。再看其他對字元串操作的方法,replace、concat等其他方法,都有個共同點,就是對字元串的操作時原字元串並不會改變,都是在新生成的字元串對象上操作的。
String對象一旦被創建就是固定不可改變的,對String對象的任何操作都不影響到原對象,相關的更改操作都會生成新的字元串對象。


總結
1、String類初始化的對象是不可變的
因為String類是final修飾的,字元串一旦創建就不可改變。對字元串對象的修改操作都是新建字元串對象,不會對原對象更改。
2、String的hashcode並不能直接用於判斷字元串是否相等,但能用於字元串在容器存儲定位查找。
3、本篇涉及的內容有限,String創建對象在java記憶體中的引用並未涉及,以後再補充。


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

-Advertisement-
Play Games
更多相關文章
  • 一、回顧jQuery實現的ajax 首先說一下ajax的優缺點 jquery 實現的ajax 1 <!DOCTYPE html> 2 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <title>Title</title> 7 <scri ...
  • PyQt5 的 signal 與 slot 有所改變,例如,先定義一個 ZeroSignal 類: 使用時,一是綁定 slot 如下: 然後是找個機會發動之: 大約如此,完整代碼如下: OK! ...
  • java只所以被推廣,實際上很大原因是因為本身是跨平臺的,很大作用是因為虛擬機的關係。 一般情況下開發人員不需要關註虛擬機內部實現就可以日常開發了,但是有時候涉及到性能的時候就需要瞭解虛擬機的實現機制了。 那麼今天寫的內容更多的是關於編譯一套自己的虛擬機,為日後瞭解虛擬機底層原理鋪鋪路。 編譯虛擬機 ...
  • 歡迎訪問我的個人博客: "原文鏈接" 前言 人生苦短,我用python。學習python怎麼能不搞一下詞雲呢是不是(ง •̀_•́)ง 於是便有了這篇邊實踐邊記錄的筆記。 環境:VMware 12pro + CentOS7 + Python 2.7.5 安裝系統 之前一直用的是win10子系統,現在 ...
  • 首先上原文, 現在,假設我們要增強now()函數的功能,比如,在函數調用前後自動列印日誌,但又不希望修改now()函數的定義,這種在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator)。本質上,decorator就是一個返回函數的高階函數。 Decorator本質是高階函數? 不信 ...
  • 前言 一位小妹去面試前端,前端leader問了"什麼是ajax?",答:“接收後臺的數據,然後然後自己填充和渲染樣式”;一位小哥去面試後臺,技術經理問了“什麼是ajax?”,答:“在不需重新載入整個網頁的情況下,發送非同步請求,返回json數據給前端”。準確答案到底是什麼?Ajax到底屬於前端還是屬於 ...
  • 在實際項目開發中,經常存在一對一的關係,如一個人對應一張身份證信息,這就是一對一的關係。下麵是一個簡單的實例: 1、建表過程我就省略了,主要是一張Person表,一張IDCard表,其相關屬性見步驟2Pojo類屬性所示; 2、建立一個Person對象和一個IDCard對象: mybatis/pri/ ...
  • 查看原文 ...
一周排行
    -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 ...