源碼淺析之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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...