国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java雜記17—String全面解析

jeffrey_up / 3182人閱讀

摘要:所以也就是說在沒有的基礎(chǔ)上,執(zhí)行代碼會(huì)在串池中創(chuàng)建一個(gè),也會(huì)在堆內(nèi)存中再出來一個(gè)。不可變性的優(yōu)點(diǎn)安全性字符串不可變安全性的考慮處于兩個(gè)方面,數(shù)據(jù)安全和線程安全。

摘要: String基本特性,String源碼,為什么String不可變?

前言

基于字符串String在java中的地位,關(guān)于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));
    }
}

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

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

為什么a==c的結(jié)果是false

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

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

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

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

字面值的方式賦值

new關(guān)鍵字新建一個(gè)字符串對象

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

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

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

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

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

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

String相關(guān)源碼

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

屬性

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

public final class String
    implements java.io.Serializable, Comparable, 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類中聲明了一個(gè)char[]數(shù)組,變量名value,聲明了一個(gè)int類型的變量hash(該String對象的哈希值的緩存)。也就是說java中的String類其實(shí)就是對char數(shù)組的封裝。

構(gòu)造方法

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

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

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

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

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

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

equals方法

 public boolean equals(Object anObject) {
     //如果兩個(gè)對象是同一個(gè)引用,那么直接返回true
        if (this == anObject) {
            return true;
        }
     /*
     1.判斷傳入的對象是不是String類型
     2.判斷兩個(gè)對象的char數(shù)組長度是否一致
     3.循環(huán)判斷char數(shù)組中的每一個(gè)值是否相等
     以上條件均滿足才會(huì)返回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不可變? 串池需要

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

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

不可變性的優(yōu)點(diǎn)
安全性

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

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

線程安全,因?yàn)樽址遣豢勺兊模允嵌嗑€程安全的,同一個(gè)字符串實(shí)例可以被多個(gè)線程共享,這樣便不用因?yàn)榫€程安全問題而使用同步。

性能效率

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

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

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

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

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

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

String c = new String("abc");
System.out.println(c);
//獲取String類中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");

//改變value屬性的訪問權(quán)限
valueFieldOfString.setAccessible(true);

//獲取s對象上的value屬性的值
char[] value = (char[]) valueFieldOfString.get(c);

//改變value所引用的數(shù)組中的第5個(gè)字符
value[1] = "_";
System.out.println(c);

執(zhí)行的結(jié)果是

abc
a_c

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

其他問題 不是特別需要請不要使用new關(guān)鍵字創(chuàng)建字符串

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

String StringBuffer StringBuilder的區(qū)別

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

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

我不能保證每一個(gè)地方都是對的,但是可以保證每一句話,每一行代碼都是經(jīng)過推敲和斟酌的。希望每一篇文章背后都是自己追求純粹技術(shù)人生的態(tài)度。

永遠(yuǎn)相信美好的事情即將發(fā)生。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/69575.html

相關(guān)文章

  • Java雜記9—NIO

    摘要:前言非阻塞,也被稱之為新,它重新定義了一些概念。緩沖通道通道選擇器阻塞,幾乎所有的程序員都會(huì)的字節(jié)流,字符流,輸入流,輸出流等分類就是針對而言的。緩沖區(qū)基本屬性容量表示最大數(shù)據(jù)容量,緩沖區(qū)容量不能為負(fù),并且創(chuàng)建后不能更改。 前言 非阻塞IO,也被稱之為新IO,它重新定義了一些概念。 緩沖buffer 通道 channel 通道選擇器 BIO 阻塞IO,幾乎所有的java程序員都會(huì)的...

    tainzhi 評論0 收藏0
  • webpack系列-插件機(jī)制雜記

    摘要:系列文章系列第一篇基礎(chǔ)雜記系列第二篇插件機(jī)制雜記系列第三篇流程雜記前言本身并不難,他所完成的各種復(fù)雜炫酷的功能都依賴于他的插件機(jī)制。的插件機(jī)制依賴于一個(gè)核心的庫,。是什么是一個(gè)類似于的的庫主要是控制鉤子函數(shù)的發(fā)布與訂閱。 系列文章 Webpack系列-第一篇基礎(chǔ)雜記 Webpack系列-第二篇插件機(jī)制雜記 Webpack系列-第三篇流程雜記 前言 webpack本身并不難,他所完成...

    Neilyo 評論0 收藏0
  • 篤行雜記之Zookeeper SessionTimeOut分析

    摘要:前言本文為篤行日常工作記錄,爛筆頭系列。最終通過分析源碼了解到最終的確定是一個(gè)協(xié)商的過程,而不是簡單的配置生效。根據(jù)客戶端上報(bào)的和服務(wù)端自身的。如果上報(bào)的小于則設(shè)置為如果上報(bào)的大于則設(shè)置為如果介于兩則之間,則以上報(bào)的時(shí)間為準(zhǔn)。 0.前言 本文為篤行日常工作記錄,爛筆頭系列。 源碼前面,了無秘密 — by 侯杰 近期的一個(gè)C++項(xiàng)目里使用了Zookeeper做服務(wù)發(fā)現(xiàn),期間遇到了Sess...

    mcterry 評論0 收藏0
  • Java雜記3—流程控制之條件

    摘要:流程控制首先來介紹程序的流程控制。后面跟一個(gè)代碼塊邏輯關(guān)系是當(dāng)布爾表達(dá)式為真的時(shí)候執(zhí)行代碼塊,為假的時(shí)候不執(zhí)行。 流程控制 首先來介紹程序的流程控制。 所謂的流程控制,就是在我們前面的課程中我們已經(jīng)學(xué)過了變量,數(shù)據(jù)類型,運(yùn)算符,表達(dá)式,這些都是計(jì)算機(jī)編程的基本元素,但是我們程序的基本執(zhí)行單元應(yīng)該是語句,程序執(zhí)行,執(zhí)行的不是表達(dá)式,而是執(zhí)行語句。就好像我們小時(shí)候先學(xué)認(rèn)字兒,再學(xué)組詞,但最...

    jackzou 評論0 收藏0
  • JavaScript 雜記(一)

    摘要:基礎(chǔ)簡單基礎(chǔ)數(shù)據(jù)類型值得注意的是的值是,表示一個(gè)空對象指針,沒有指向任何對象。的值是,表示申明變量或?qū)ο蟮膶傩詤s未初始化。值是派生自的,所以對他們執(zhí)行相等測試會(huì)返回。字符串單引號和雙引號都可以用來表示字符串,只要前后一致即可。 目的 記憶總是會(huì)隨著時(shí)間而淡化,學(xué)習(xí)了點(diǎn)什么,就記錄下點(diǎn)什么。 做點(diǎn)什么,總比不做要好。 基礎(chǔ) 簡單(基礎(chǔ))數(shù)據(jù)類型 Number ...

    raise_yang 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<