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

Java中關于String的全面解析

 更新時間:2019年05月28日 11:03:21   作者:Java雜記  
這篇文章主要介紹了Java中關于String全面解析,下面我們來一起學習一下吧

前言

基于字符串String在java中的地位,關于String的常識性知識就不多做介紹了,我們先來看一段代碼

public class Test {
public static void main(String[] args) {
String a = "abc";
String b = "abc";
String c = new String("abc");
System.out.println(a==b);
System.out.println(a.equals(b));
System.out.println(a==c);
System.out.println(a.equals(c));
}
}

那么上段代碼的結果是什么呢?答案是:true true false true,有初學java的朋友肯定會納悶,a==c為什么會是false呢?equals判斷的為什么都是true呢?

根據(jù)這些問題,我們就通過對String的解讀來一步一步的了解。

為什么a==c的結果是false

明白這個問題需要對JVM的內(nèi)存結構有一定的了解,說是了解也不需要太多,能夠get到下圖的知識點就行了。

ps:本文中所有的圖示均是為了方便理解,畫出來的大致樣子,如果想要了解的更加清楚,請自行研究虛擬機原理。

java語法設計的時候針對String,提供了兩種創(chuàng)建方式和一種特殊的存儲機制(String intern pool )。

兩種創(chuàng)建字符串對象的方式:

1.字面值的方式賦值

2.new關鍵字新建一個字符串對象

這兩種方法在性能和內(nèi)存占用方面存在這差異

String Pool串池:是在內(nèi)存堆中專門劃分一塊空間,用來保存所有String對象數(shù)據(jù),當構造一個新字符串String對象時(通過字面量賦值的方法),Java編譯機制會優(yōu)先在這個池子里查找是否已經(jīng)存在能滿足需要的String對象,如果有的話就直接返回該對象的地址引用(沒有的話就正常的構造一個新對象,丟進去存起來),這樣下次再使用同一個String的時候,就可以直接從串池中取,不需要再次創(chuàng)建對象,也就避免了很多不必要的空間開銷。

根據(jù)以上的概念,我們再來看前言中的代碼,當JVM執(zhí)行到String a = "abc";的時候,會先看常量池里有沒有字符串剛好是“abc”這個對象,如果沒有,在常量池里創(chuàng)建初始化該對象,并把引用指向它,如下圖。

當執(zhí)行到String b = "abc";時,發(fā)現(xiàn)常量池已經(jīng)有了abc這個值,于是不再在常量池中創(chuàng)建這個對象,而是把引用直接指向了該對象,如下圖:

繼續(xù)執(zhí)行到 String c = new String("abc");這時候我們加了一個new關鍵字,這個關鍵字呢就是告訴JVM,你直接在堆內(nèi)存里給我開辟一塊新的內(nèi)存,如下圖所示:

這時候我們執(zhí)行四個打印語句,我們需要知道==比較的是地址,equals比較的是內(nèi)容(String中的重寫過了),abc三個變量的內(nèi)容完全一樣,因此equals的結果都是true,ab是一個同一個對象,因此地址一樣,a和c很顯然不是同一個對象,那么此時為false也是很好理解的。

String相關源碼

在本文中只有String的部分源碼,畢竟String的源碼有3000多行,全部來寫進來不那么現(xiàn)實,我們挑一些比較有意思的代碼來做一定的分析說明。

屬性

我們先來看一下String都有哪些成員變量,比較關鍵的屬性有兩個,如下:

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
char數(shù)組
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0

從源碼中我們能夠看到,在String類中聲明了一個char[]數(shù)組,變量名value,聲明了一個int類型的變量hash(該String對象的哈希值的緩存)。也就是說java中的String類其實就是對char數(shù)組的封裝。

構造方法

接下來我們通過一句代碼來了解一下字符串創(chuàng)建的過程,String c = new String("abc");我們知道使用new關鍵字就會使用到構造方法,所以如下。

public String(String original) {
this.value = original.value;
this.hash = original.hash;
}

構造方法中的代碼非常簡單,把傳進來的字符串的value值,也就是char數(shù)組賦值給當前對象,hash同樣處理,那么問題來了WTF original?

在這里需要注意的是java中的一個機制,在Java中,當值被雙引號引起來(如本示例中的"abc"),JVM會去先檢查看一看常量池里有沒有abc這個對象,如果沒有,把abc初始化為對象放入常量池,如果有,直接返回常量池內(nèi)容。所以也就是說在沒有“abc”的基礎上,執(zhí)行代碼會在串池中創(chuàng)建一個abc,也會在堆內(nèi)存中再new出來一個。最終的結果如下圖:

那么這時候如果再有一個String c2 = new String("abc");呢?如圖

關于這一點我們通過IDEA的debug功能也能夠看到,你會發(fā)現(xiàn),c和c2其中的char數(shù)組的地址是相同的。足以說明在創(chuàng)建c和c2的時候使用的是同一個數(shù)組。

equals方法

public boolean equals(Object anObject) {
//如果兩個對象是同一個引用,那么直接返回true
if (this == anObject) {
return true;
}
/*
1.判斷傳入的對象是不是String類型
2.判斷兩個對象的char數(shù)組長度是否一致
3.循環(huán)判斷char數(shù)組中的每一個值是否相等
以上條件均滿足才會返回true
*/
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

為什么String不可變?

串池需要

為什么說是串池需要呢?在開篇的時候我們提到過,串池中的字符串會被多個變量引用,這樣的機制讓字符串對象得到了復用,避免了很多不必要的內(nèi)存消耗。

那么大家試想一下,如果String對象本身允許二次修改的話,我有一個字符串“abc”同時被100個變量引用,其中一個引用修改了String對象,那么將會影響到其他99個引用該對象的變量,這樣會對其他變量造成不可控的影響。

不可變性的優(yōu)點

安全性

字符串不可變安全性的考慮處于兩個方面,數(shù)據(jù)安全和線程安全。

數(shù)據(jù)安全,大家可以回憶一下,我們都在哪些地方大量的使用了字符串?網(wǎng)絡數(shù)據(jù)傳輸,文件IO等,也就是說當我們在傳參的時候,使用不可變類不需要去考慮誰可能會修改其內(nèi)部的值,如果使用可變類的話,可能需要每次記得重新拷貝出里面的值,性能會有一定的損失。

線程安全,因為字符串是不可變的,所以是多線程安全的,同一個字符串實例可以被多個線程共享,這樣便不用因為線程安全問題而使用同步。

性能效率

關于性能效率一方面是復用,另一方面呢需要從hash值的緩存方向來說起了。

String的Hash值在很多的地方都會被使用到,如果保證了String的不可變性,也就能夠保證Hash值始終也是不可變的,這樣就不需要在每次使用的時候重新計算hash值了。

String不可變性是如何實現(xiàn)的?

通過對屬性私有化,final修飾,同時沒有提供公開的get set方法以及其他的能夠修改屬性的方法,保證了在創(chuàng)建之后不會被從外部修改。

同時不能忘了,String也是被final修飾的,在之前的文章中我們提到過,final修飾類的結果是String類沒有子類。

那么String真的不能改變嗎?不是,通過反射我們可以,代碼如下:

String c = new String("abc");
System.out.println(c);
//獲取String類中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");
//改變value屬性的訪問權限
valueFieldOfString.setAccessible(true);
//獲取s對象上的value屬性的值
char[] value = (char[]) valueFieldOfString.get(c);
//改變value所引用的數(shù)組中的第5個字符
value[1] = '_';
System.out.println(c);


執(zhí)行的結果是

abc
a_c

也就是說我們改變了字符串對象的值,有什么意義呢?沒什么意義,我們從來不會這么做。

其他問題

不是特別需要請不要使用new關鍵字創(chuàng)建字符串
從前文我們知道使用new關鍵字創(chuàng)建String的時候,即便串池中存在相同String,仍然會再次在堆內(nèi)存中創(chuàng)建對象,會浪費內(nèi)存,另一方面對象的創(chuàng)建相較于從串池中取效率也更低下。

String StringBuffer StringBuilder的區(qū)別

關于三者的區(qū)別,在面試題中經(jīng)常的出現(xiàn),String對象不可變,因此在進行任何內(nèi)容上的修改時都會創(chuàng)建新的字符串對象,一旦修改操作太多就會造成大量的資源浪費。

StringBuffer和StringBuilder在進行字符串拼接的時候不會創(chuàng)建新的對象,而是在原對象上修改,不同之處在于StringBuffer線程安全,StringBuilder線程不安全。所以在進行字符串拼接的時候推薦使用StringBuffer或者StringBuilder。

相關文章

  • Mybatis源碼解析之mapper接口的代理模式詳解

    Mybatis源碼解析之mapper接口的代理模式詳解

    這篇文章主要介紹了Mybatis源碼解析之mapper接口的代理模式詳解,在mybatis中執(zhí)行sql時有兩種方式,一種是基于statementId,也就是直接調(diào)用SqlSession的方法,需要的朋友可以參考下
    2023-12-12
  • Java?ThreadPoolExecutor線程池有關介紹

    Java?ThreadPoolExecutor線程池有關介紹

    這篇文章主要介紹了Java?ThreadPoolExecutor線程池有關介紹,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-09-09
  • java以json格式向后臺服務器接口發(fā)送請求的實例

    java以json格式向后臺服務器接口發(fā)送請求的實例

    下面小編就為大家分享一篇java以json格式向后臺服務器接口發(fā)送請求的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • SpringMVC @RequestBody屬性名大寫字母注入失敗的解決

    SpringMVC @RequestBody屬性名大寫字母注入失敗的解決

    這篇文章主要介紹了SpringMVC @RequestBody屬性名大寫字母注入失敗的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • Spring?Boot之Validation自定義實現(xiàn)方式的總結

    Spring?Boot之Validation自定義實現(xiàn)方式的總結

    這篇文章主要介紹了Spring?Boot之Validation自定義實現(xiàn)方式的總結,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • Springboot Cucumber測試配置介紹詳解

    Springboot Cucumber測試配置介紹詳解

    這篇文章主要介紹了Springboot Cucumber測試配置介紹詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • Java實現(xiàn)拓撲排序算法的示例代碼

    Java實現(xiàn)拓撲排序算法的示例代碼

    在圖論中,拓撲排序(Topological Sorting)是一個有向無環(huán)圖(DAG, Directed Acyclic Graph)的所有頂點的線性序列。本文將為大家講講拓撲排序算法的原理及實現(xiàn),需要的可以參考一下
    2022-07-07
  • 淺談Thread.sleep(0)到底有什么用

    淺談Thread.sleep(0)到底有什么用

    為什么要用sleep,主要是為了暫停當前線程,把cpu片段讓出給其他線程,減緩當前線程的執(zhí)行,本文主要介紹了Thread.sleep(0)到底有什么用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • SpringBoot項目中連接SQL Server的三種方式

    SpringBoot項目中連接SQL Server的三種方式

    連接SQL Server是許多Spring Boot項目中常見的需求之一,本文主要介紹了SpringBoot項目中連接SQL Server的三種方式,具有一定的參考價值 ,感興趣的可以了解一下
    2023-09-09
  • 整理很詳細的Java正則表達式使用大全

    整理很詳細的Java正則表達式使用大全

    這篇文章為大家整理了很詳細的Java正則表達式使用大全,大家在使用Java正則表達式的時候可查閱這篇文章,認為不錯的朋友可以收藏起來
    2015-12-12

最新評論