亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java String 對象(你真的了解了嗎)

 更新時間:2019年10月11日 11:26:46   作者:平頭哥的技術(shù)博文  
這篇文章主要介紹了Java String 對象(你真的了解了嗎),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

String 對象的實現(xiàn)

String對象是 Java 中使用最頻繁的對象之一,所以 Java 公司也在不斷的對String對象的實現(xiàn)進行優(yōu)化,以便提升String對象的性能,看下面這張圖,一起了解一下String對象的優(yōu)化過程。

1. 在 Java6 以及之前的版本中

String對象是對 char 數(shù)組進行了封裝實現(xiàn)的對象,主要有四個成員變量: char 數(shù)組、偏移量 offset、字符數(shù)量 count、哈希值 hash。

String對象是通過 offset 和 count 兩個屬性來定位 char[] 數(shù)組,獲取字符串。這么做可以高效、快速地共享數(shù)組對象,同時節(jié)省內(nèi)存空間,但這種方式很有可能會導(dǎo)致內(nèi)存泄漏。

2. 從 Java7 版本開始到 Java8 版本

從 Java7 版本開始,Java 對String類做了一些改變。String類中不再有 offset 和 count 兩個變量了。這樣的好處是String對象占用的內(nèi)存稍微少了些,同時 String.substring 方法也不再共享 char[],從而解決了使用該方法可能導(dǎo)致的內(nèi)存泄漏問題。

3. 從 Java9 版本開始

將 char[] 數(shù)組改為了 byte[] 數(shù)組,為什么需要這樣做呢?我們知道 char 是兩個字節(jié),如果用來存一個字節(jié)的字符有點浪費,為了節(jié)約空間,Java 公司就改成了一個字節(jié)的byte來存儲字符串。這樣在存儲一個字節(jié)的字符是就避免了浪費。

在 Java9 維護了一個新的屬性 coder,它是編碼格式的標識,在計算字符串長度或者調(diào)用 indexOf() 函數(shù)時,需要根據(jù)這個字段,判斷如何計算字符串長度。coder 屬性默認有 0 和 1 兩個值, 0 代表Latin-1(單字節(jié)編碼),1 代表 UTF-16 編碼。如果 String判斷字符串只包含了 Latin-1,則 coder 屬性值為 0 ,反之則為 1。

String 對象的創(chuàng)建方式

1、通過字符串常量的方式

String str= "pingtouge"的形式,使用這種形式創(chuàng)建字符串時, JVM 會在字符串常量池中先檢查是否存在該對象,如果存在,返回該對象的引用地址,如果不存在,則在字符串常量池中創(chuàng)建該字符串對象并且返回引用。使用這種方式創(chuàng)建的好處是:避免了相同值的字符串重復(fù)創(chuàng)建,節(jié)約了內(nèi)存

2、String()構(gòu)造函數(shù)的方式

String str = new String("pingtouge")的形式,使用這種方式創(chuàng)建字符串對象過程就比較復(fù)雜,分成兩個階段,首先在編譯時,字符串pingtouge會被加入到常量結(jié)構(gòu)中,類加載時候就會在常量池中創(chuàng)建該字符串。然后就是在調(diào)用new()時,JVM 將會調(diào)用String的構(gòu)造函數(shù),同時引用常量池中的pingtouge字符串,
在堆內(nèi)存中創(chuàng)建一個String對象并且返回堆中的引用地址。

了解了String對象兩種創(chuàng)建方式,我們來分析一下下面這段代碼,加深我們對這兩種方式的理解,下面這段代碼片中,str是否等于str1呢?

 String str = "pingtouge";
 String str1 = new String("pingtouge");
 system.out.println(str==str1)

我們逐一來分析這幾行代碼,首先從String str = "pingtouge"開始,這里使用了字符串常量的方式創(chuàng)建字符串對象,在創(chuàng)建pingtouge字符串對象時,JVM會去常量池中查找是否存在該字符串,這里的答案肯定是沒有的,所以JVM將會在常量池中創(chuàng)建該字符串對象并且返回對象的地址引用,所以str指向的是pingtouge字符串對象在常量池中的地址引用。

然后是String str1 = new String("pingtouge")這行代碼,這里使用的是構(gòu)造函數(shù)的方式創(chuàng)建字符串對象,根據(jù)我們上面對構(gòu)造函數(shù)方式創(chuàng)建字符串對象的理解,str1得到的應(yīng)該是堆中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[]數(shù)組被private 和final修飾,我們知道對于一個被final的基本數(shù)據(jù)類型的變量,則其數(shù)值一旦在初始化之后便不能更改。這是String不可變的第二點。

Java 公司為什么要將String設(shè)置成不可變的,主要從以下三方面考慮:

  • 1、保證 String 對象的安全性。假設(shè) String 對象是可變的,那么 String 對象將可能被惡意修改。
  • 2、保證 hash 屬性值不會頻繁變更,確保了唯一性,使得類似 HashMap 容器才能實現(xiàn)相應(yīng)的 key-value 緩存功能。
  • 3、可以實現(xiàn)字符串常量池

String 對象的優(yōu)化

字符串是我們常用的Java類型之一,所以對字符串的操作也是避免不了的,在對字符串的操作過程中,如果使用不當,性能會天差地別。那么在字符串的操作過程中,有哪些地方需要我們注意呢?

優(yōu)雅的拼接字符串

字符串的拼接是對字符串操作使用最頻繁的操作之一,由于我們知道String對象的不可變性,所以我們在做拼接時盡可能少的使用+進行字符串拼接或者說潛意識里認為不能使用+進行字符串拼接,認為使用+進行字符串拼接會產(chǎn)生許多無用的對象。事實真的是這樣嗎?我們來做一個實驗。我們使用+來拼接下面這段字符串。

String str8 = "ping" +"tou"+"ge";

一起來分析一下這段代碼會產(chǎn)生多少個對象?如果按照我們理解的意思來分析的話,首先會創(chuàng)建ping對象,然后創(chuàng)建pingtou對象,最后才會創(chuàng)建pingtouge對象,一共創(chuàng)建了三個對象。真的是這樣嗎?其實不是這樣的,Java 公司怕我們程序員手誤,所以對編譯器進行了優(yōu)化,上面的這段字符串拼接會被我們的編譯器優(yōu)化,優(yōu)化成一個String str8 = "pingtouge";對象。除了對常量字符串拼接做了優(yōu)化以外,對于使用+號動態(tài)拼接字符串,編譯器也做了相應(yīng)的優(yōu)化,以便提升String的性能,例如下面這段代碼:

String str = "pingtouge";

for(int i=0; i<1000; i++) {
   str = str + i;
}

編譯器會幫我們優(yōu)化成這樣

String str = "pingtouge";

for(int i=0; i<1000; i++) {
      str = (new StringBuilder(String.valueOf(str))).append(i).toString();
}

可以看出 Java 公司對這一塊進行了不少的優(yōu)化,防止由于程序員不小心導(dǎo)致String性能急速下降,盡管 Java 公司在編譯器這一塊做了相應(yīng)的優(yōu)化,但是我們還是能看出 Java 公司優(yōu)化的不足之處,在動態(tài)拼接字符串時,雖然使用了 StringBuilder 進行字符串拼接,但是每次循環(huán)都會生成一個新的 StringBuilder 實例,同樣也會降低系統(tǒng)的性能。

所以我們在做字符串拼接時,我們需要從代碼的層面進行優(yōu)化,在動態(tài)的拼接字符串時,如果不涉及到線程安全的情況下,我們顯示的使用 StringBuilder 進行拼接,提升系統(tǒng)性能,如果涉及到線程安全的話,我們使用 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() 函數(shù)的官方注釋說明,大概意思就是 intern 函數(shù)用來返回常量池中的某字符串,如果常量池中已經(jīng)存在該字符串,則直接返回常量池中該對象的引用。否則,在常量池中加入該對象,然后 返回引用。

有一位Twitter工程師在QCon全球軟件開發(fā)大會上分享了一個他們對 String對象優(yōu)化的案例,他們利用String.intern()方法將以前需要20G內(nèi)存存儲優(yōu)化到只需要幾百兆內(nèi)存。這足以體現(xiàn)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);

  }

從結(jié)果中可以看出,未使用String.intern()方法時,構(gòu)造相同值的字符串對象返回不同的對象引用地址,使用String.intern()方法后,構(gòu)造相同值的字符串對象時,返回相同的對象引用地址。這能幫我們節(jié)約不少空間

String.intern()方法雖然好,但是我們要結(jié)合場景使用,不能亂用,因為常量池的實現(xiàn)是類似于一個HashTable的實現(xiàn)方式,HashTable 存儲的數(shù)據(jù)越大,遍歷的時間復(fù)雜度就會增加。如果數(shù)據(jù)過大,會增加整個字符串常量池的負擔(dān)。

靈活的字符串的分割

字符串的分割是字符串操作的常用操作之一,對于字符串的分割,大部分人使用的都是 Split() 方法,Split() 方法大多數(shù)情況下使用的是正則表達式,這種分割方式本身沒有什么問題,但是由于正則表達式的性能是非常不穩(wěn)定的,使用不恰當會引起回溯問題,很可能導(dǎo)致 CPU 居高不下。在以下兩種情況下 Split() 方法不會使用正則表達式:

  1. 傳入的參數(shù)長度為1,且不包含“.$|()[{^?*+\”regex元字符的情況下,不會使用正則表達式
  2. 傳入的參數(shù)長度為2,第一個字符是反斜杠,并且第二個字符不是ASCII數(shù)字或ASCII字母的情況下,不會使用正則表達式

所以我們在字符串分割時,應(yīng)該慎重使用 Split() 方法,首先考慮使用 String.indexOf() 方法進行字符串分割,如果 String.indexOf() 無法滿足分割要求,再使用 Split() 方法,使用 Split() 方法分割字符串時,需要注意回溯問題。

文章不足之處,望大家多多指點,共同學(xué)習(xí),共同進步

參考資料

Java性能調(diào)優(yōu)實戰(zhàn) 劉超

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • java之swing單選框用法實例分析

    java之swing單選框用法實例分析

    這篇文章主要介紹了java之swing單選框用法,以實例形式分析了swing圖形界面單選框的實現(xiàn)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-09-09
  • 詳解Java中字符流與字節(jié)流的區(qū)別

    詳解Java中字符流與字節(jié)流的區(qū)別

    這篇文章主要為大家詳細介紹了Java中字符流與字節(jié)流的區(qū)別,這兩個的概念易混淆,今天就為大家進行詳細區(qū)分,感興趣的小伙伴們可以參考一下
    2016-04-04
  • java從控制臺接收一個數(shù)字的實例詳解

    java從控制臺接收一個數(shù)字的實例詳解

    這篇文章主要介紹了java從控制臺接收一個數(shù)字的實例詳解的相關(guān)資料,這里提供實例代碼,注釋說明清晰,需要的朋友可以參考下
    2017-07-07
  • Java8中Optional類的使用說明

    Java8中Optional類的使用說明

    Optional類主要解決的問題是臭名昭著的空指針異常(NullPointerException),每個Java程序員都非常了解的異常,這篇文章主要給大家介紹了關(guān)于Java8中Optional類使用的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • springboot單元測試兩種方法實例詳解

    springboot單元測試兩種方法實例詳解

    這篇文章主要介紹了springboot單元測試兩種方法實例詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • Spring實戰(zhàn)之使用XML方式管理聲明式事務(wù)操作示例

    Spring實戰(zhàn)之使用XML方式管理聲明式事務(wù)操作示例

    這篇文章主要介紹了Spring實戰(zhàn)之使用XML方式管理聲明式事務(wù)操作,結(jié)合實例形式詳細分析了Spring XML方式管理聲明式事務(wù)具體步驟、配置、接口及使用技巧,需要的朋友可以參考下
    2020-01-01
  • MyBatis Mapper.xml中的命名空間及命名方式

    MyBatis Mapper.xml中的命名空間及命名方式

    這篇文章主要介紹了MyBatis Mapper.xml中的命名空間及命名方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 詳解Spring Boot 項目啟動時執(zhí)行特定方法

    詳解Spring Boot 項目啟動時執(zhí)行特定方法

    這篇文章主要介紹了詳解Spring Boot 項目啟動時執(zhí)行特定方法,Springboot給我們提供了兩種“開機啟動”某些方法的方式:ApplicationRunner和CommandLineRunner。感興趣的小伙伴們可以參考一下
    2018-06-06
  • SpringBoot如何基于POI-tl和word模板導(dǎo)出龐大的Word文件

    SpringBoot如何基于POI-tl和word模板導(dǎo)出龐大的Word文件

    這篇文章主要介紹了SpringBoot如何基于POI-tl和word模板導(dǎo)出龐大的Word文件,poi-tl是一個基于Apache?POI的Word模板引擎,也是一個免費開源的Java類庫
    2022-08-08
  • 如何解決Mybatis--java.lang.IllegalArgumentException: Result Maps collection already contains value for X

    如何解決Mybatis--java.lang.IllegalArgumentException: Result Maps

    這兩天因為項目需要整合spring、struts2、mybatis三大框架,但啟動的時候總出現(xiàn)這個錯誤,困擾我好久,折騰了好久終于找到問題根源,下面小編給大家分享下問題所在及解決辦法,一起看看吧
    2016-12-12

最新評論