最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中String對(duì)象的深入理解

 更新時(shí)間:2023年05月23日 08:22:19   作者:Change666  
String對(duì)象作為Java語(yǔ)言中重要的數(shù)據(jù)類型之一,是我們平時(shí)編碼最常用的對(duì)象之一,因此也是內(nèi)存中占據(jù)空間最大的一個(gè)對(duì)象,然而很多人對(duì)它是一知半解,今天我們就來(lái)好好聊一聊這個(gè)既熟悉又陌生的String,需要的朋友可以參考下

一、 String認(rèn)識(shí)你,你認(rèn)識(shí)它么?

假如面試的時(shí)候問你,什么是String(或者談?wù)勀銓?duì)String的理解)?你會(huì)如何回答?“String是基礎(chǔ)對(duì)象類型之一,是Java語(yǔ)言中重要的數(shù)據(jù)類型之一”??峙逻@是大多數(shù)人的回答,能力強(qiáng)些的可能會(huì)說,String底層是用char[ ]數(shù)組來(lái)實(shí)現(xiàn)的;如果面試官讓你再繼續(xù)呢?估計(jì)很多人會(huì)一臉尷尬,腦海里極力搜索關(guān)于String的相關(guān)知識(shí),最后也只能恨自己平時(shí)對(duì)String關(guān)注的太少。下面就讓我們一步一步地去認(rèn)識(shí)String。
首先,來(lái)看一個(gè)面試經(jīng)常遇到,錯(cuò)誤率又很高的問題:

1 String str1 = “java”;
2 String str2 = new String(“java”);
3 String str3= str2.intern();
4 System.out.println(str1 == str2);
5 System.out.println(str2 == str3);
6 System.out.println(str1 == str3);

答案先不揭曉,各位先想一下,咱們繼續(xù)往下看:

二、String對(duì)象的實(shí)現(xiàn)

我們把String對(duì)象的實(shí)現(xiàn)分為三個(gè)階段來(lái)分析:java7之前的版本、java7/8版本、java8之后的版本。
1、 java7之前的版本中,String對(duì)象中主要由四個(gè)成員變量:char[]、偏移量offset、字符數(shù)量count、哈希值hash。String對(duì)象通過offset和count來(lái)定位char[],這么做可以高效、快速地共享數(shù)組對(duì)象,節(jié)省內(nèi)存空間,但這種方式很有可能會(huì)導(dǎo)致內(nèi)存泄漏。
2、 java7/8版本中,String 去除了offset 和 count 兩個(gè)變量。這樣的好處是String 對(duì)象占用的內(nèi)存稍微少了些,同時(shí),String.substring()方法也不再共享char[],從而解決了使用該方法可能導(dǎo)致的內(nèi)存泄漏問題。
3、 java8之后的版本中,char[] 屬性改為了 byte[] 屬性,增加了一個(gè)新的屬性coder,它是一個(gè)編碼格式的標(biāo)識(shí)。為什么這么做呢?我們知道一個(gè)char字符占16位,2 個(gè)字節(jié)。這種情況下,存儲(chǔ)單字節(jié)編碼內(nèi)的字符(占一個(gè)字節(jié)的字符)就顯得非常浪費(fèi)。JDK1.9 的String類為了節(jié)約內(nèi)存空間,于是使用了占8位,1個(gè)字節(jié)的 byte 數(shù)組來(lái)存放字符串。而新屬性coder的作用是,在計(jì)算字符串長(zhǎng)度或者使用 indexOf()函數(shù)時(shí),我們需要根據(jù)這個(gè)字段,判斷如何計(jì)算字符串長(zhǎng)度。coder屬性默認(rèn)有0和1兩個(gè)值,0代表Latin-1(單字節(jié)編碼),1代表UTF-16。如果 String判斷字符串只包含了Latin-1,則coder屬性值為0,反之則為1。

三、String是不可變對(duì)象

1、為什么String是不可變對(duì)象很多人背面試題的時(shí)候想必都對(duì)此很熟悉,那為什么String對(duì)象是不可變的呢?你有想過這其中的原因么?通過源碼我們知道,String類被final關(guān)鍵字修飾了,而且變量char[]也被final修飾了。Java語(yǔ)法告訴我們:被final修飾的類不可被繼承,被final修飾的變量不可被改變,一旦賦值了初始值,該final變量的值就不能被重新賦值,即不可更改,而char[]被 final+private修飾,說明String對(duì)象不可被更改。即String對(duì)象一旦創(chuàng)建成功,就不能再對(duì)它進(jìn)行改變。
2、為什么String被設(shè)計(jì)成不可變對(duì)象首先,是為了保證String對(duì)象的安全性,避免被惡意篡改。比如將值為“abc”的引用賦值給str對(duì)象,即String str = “abc”,如果此時(shí)有人惡意將“abc”改為“abcd”或其他值就會(huì)造成意想不到的錯(cuò)誤。
其次,確保屬性值hash不頻繁變動(dòng),保證其唯一性。
3、為實(shí)現(xiàn)字符串常量池提供方便舉一個(gè)反例來(lái)證明String對(duì)象的不可變性
針對(duì)String對(duì)象不可變性,有人可能會(huì)說:對(duì)于一個(gè)String str =“hello”,然后改為String str =“world”,這個(gè)時(shí)候str的值變成了“world”,str值確實(shí)改變了,為什么還說String對(duì)象不可變呢?
首先,我們來(lái)解釋一下對(duì)象和引用。對(duì)象在內(nèi)存中是一塊內(nèi)存地址,str則是一個(gè)指向該內(nèi)存地址的引用,所以在這個(gè)例子中,第一次賦值的時(shí)候,創(chuàng)建了一個(gè)“hello”對(duì)象,str引用指向“hello”地址;第二次賦值的時(shí)候,又重新創(chuàng)建了一個(gè)對(duì)象“world”,str引用指向了“world”,但“hello”對(duì)象依然存在于內(nèi)存中。也就是說str并不是對(duì)象,而只是一個(gè)對(duì)象引用。真正的對(duì)象依然還在內(nèi)存中,沒有被改變。所以在Java中要比較兩個(gè)對(duì)象是否相等,通常是用“==”,而要判斷兩個(gè)對(duì)象的值是否相等,則需要用equals方法來(lái)判斷。

四、String常量池

在java中,創(chuàng)建字符串通常有兩種方式:一種是通過字符串常量池的形式,比如String str = “abcd”;另一種是直接通過new的形式,如String string = new String(“abcd”);
針對(duì)第一種方式創(chuàng)建字符串時(shí),JVM首先會(huì)檢查該對(duì)象是否存在于字符串常量池中,如果存在,就返回該引用,否則在常量池中創(chuàng)建新的字符串對(duì)象,然后將引用返回。這種方式可以減少同一個(gè)值的字符串對(duì)象的重復(fù)創(chuàng)建,節(jié)約內(nèi)存。
采用new形式創(chuàng)建字符串時(shí),首先在編譯類文件時(shí),"abcd"常量字符串將會(huì)放入到常量結(jié)構(gòu)中,在類加載時(shí),“abcd"將會(huì)在常量池中創(chuàng)建;其次,在調(diào)用new時(shí),JVM命令將會(huì)調(diào)用String的構(gòu)造函數(shù),同時(shí)引用常量池中的"abcd”字符串,在堆內(nèi)存中創(chuàng)建一個(gè) String對(duì)象;最后,string將引用String對(duì)象。

五、String.intern()方法詳解

先來(lái)看一個(gè)示例:

  String a =new String("abc").intern();
  String b = new String("abc").intern();
  System.out.print(a==b);

你覺得輸出的是false還是true?
答案是:true
在字符串常量中,默認(rèn)會(huì)將對(duì)象放入常量池中;在字符串變量中,對(duì)象是會(huì)創(chuàng)建在堆內(nèi)存中,同時(shí)也會(huì)在常量池中創(chuàng)建一個(gè)字符串對(duì)象,復(fù)制到堆內(nèi)存對(duì)象中,并返回堆內(nèi)存對(duì)象引用。如果調(diào)用intern()方法,會(huì)去查看字符串常量池中是否有等于該對(duì)象的字符串,如果沒有,就在常量池中新增該對(duì)象,并返回該對(duì)象引用;如果有,就返回常量池中的字符串引用。堆內(nèi)存中原有的對(duì)象由于沒有引用指向它,將會(huì)通過垃圾回收器回收。
所以針對(duì)上面的例子中,在一開始創(chuàng)建a變量時(shí),會(huì)在堆內(nèi)存中創(chuàng)建一個(gè)對(duì)象,同時(shí)會(huì)在加載類時(shí),在常量池中創(chuàng)建一個(gè)字符串對(duì)象,在調(diào)用intern()方法之后,會(huì)去常量池中查找是否有等于該字符串的對(duì)象,有就返回引用。在創(chuàng)建b字符串變量時(shí),也會(huì)在堆中創(chuàng)建一個(gè)對(duì)象,此時(shí)常量池中有該字符串對(duì)象,就不再創(chuàng)建。調(diào)用 intern 方法則會(huì)去常量池中判斷是否有等于該字符串的對(duì)象,發(fā)現(xiàn)有等于"abc"字符串的對(duì)象,就直接返回引用。而在堆內(nèi)存中的對(duì)象,由于沒有引用指向它,將會(huì)被垃圾回收。所以a和b引用的是同一個(gè)對(duì)象。
看完這些內(nèi)容后,文章開頭的問題,相比你也有了答案了。分別是:false、false、true。

六、String、StringBuffer和StringBuilder的區(qū)別

1.對(duì)象的可變與不可變String是不可變對(duì)象,原因上面的內(nèi)容已經(jīng)解釋過了,這里不再贅述。
StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在AbstractStringBuilder中也是使用字符數(shù)組保存數(shù)據(jù),這兩種對(duì)象都是可變的。如下:
char[ ] value;
2.是否是線程安全String中的對(duì)象是不可變的,也就可以理解為常量,所以是線程安全。
AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
StringBuffer對(duì)方法加了同步鎖或者對(duì)調(diào)用的方法加了同步鎖,所以是線程安全的。看如下源碼:

1  public synchronized StringBuffer reverse() {
2      super.reverse();
3      return this;
4  }
5
6  public int indexOf(String str) {
7      return indexOf(str, 0);        //存在 public synchronized int indexOf(String str, int fromIndex) 方法
8  }

StringBuilder并沒有對(duì)方法進(jìn)行加同步鎖,所以是非線程安全的。
3.StringBuilder與StringBuffer共同點(diǎn)StringBuilder與StringBuffer有公共的抽象父類AbstractStringBuilder。
抽象類與接口的一個(gè)區(qū)別是:抽象類中可以定義一些子類的公共方法,子類只需要增加新的功能,不需要重復(fù)寫已經(jīng)存在的方法;而接口中只是對(duì)方法的申明和常量的定義。
StringBuilder、StringBuffer的方法都會(huì)調(diào)用AbstractStringBuilder中的公共方法,如super.append(…)。只是StringBuffer會(huì)在方法上加synchronized關(guān)鍵字,進(jìn)行同步。
如果程序不是多線程的,那么使用StringBuilder效率高于StringBuffer。

下面來(lái)幾道測(cè)試題,看看自己對(duì)String究竟掌握了多少

七、測(cè)試題

test1、如下代碼中創(chuàng)建了幾個(gè)對(duì)象

1 String str1 = "abc";
2 String str2 = new String("abc");

對(duì)于1中的 String str1 = “abc”,首先會(huì)檢查字符串常量池中是否含有字符串a(chǎn)bc,如果有則直接指向,如果沒有則在字符串常量池中添加abc字符串并指向它.所以這種方法最多創(chuàng)建一個(gè)對(duì)象,有可能不創(chuàng)建對(duì)象。
對(duì)于2中的String str2 = new String(“abc”),首先會(huì)在堆內(nèi)存中申請(qǐng)一塊內(nèi)存存儲(chǔ)字符串a(chǎn)bc,str2指向其內(nèi)存塊對(duì)象。同時(shí)還會(huì)檢查字符串常量池中是否含有abc字符串,若沒有則添加abc到字符串常量池中。所以 new String()可能會(huì)創(chuàng)建兩個(gè)對(duì)象。
所以如果以上兩行代碼在同一個(gè)程序中,則1中創(chuàng)建了1個(gè)對(duì)象,2中創(chuàng)建了1個(gè)對(duì)象。如果將這兩行代碼的順序調(diào)換一下,則String str2 = new String(“abc”)創(chuàng)建了兩個(gè)對(duì)象,而 String str1 = "abc"沒有創(chuàng)建對(duì)象。

test2、看看下面的代碼創(chuàng)建了多少個(gè)對(duì)象:

1     String temp="apple";  
2     for(int i=0;i<1000;i++) {  
3           temp=temp+i;  
4     }

答案:1001個(gè)對(duì)象。

test3、下面的代碼創(chuàng)建了多少個(gè)對(duì)象:

1     String temp = new String("apple")  
2     for(int i=0;i<1000;i++) {  
3            temp = temp+i;  
4     }

答案:1002個(gè)對(duì)象。

test4:

1 String ok = "ok";  
2 String ok1 = new String("ok");  
3 System.out.println(ok == ok1);//fasle 

ok指向字符串常量池,ok1指向new出來(lái)的堆內(nèi)存塊,new的字符串在編譯期是無(wú)法確定的。所以輸出false。

test5:

1 String ok = "apple1";  
2 String ok1 = "apple"+1;  
3 System.out.println(ok==ok1);//true 

編譯期ok和ok1都是確定的,字符串都為apple1,所以ok和ok1都指向字符串常量池里的字符串a(chǎn)pple1。指向同一個(gè)對(duì)象,所以為true。

test6:

1 String ok = "apple1";  
2 int temp = 1;  
3 String ok1 = "apple"+temp;  
4 System.out.println(ok==ok1);//false

主要看ok和ok1能否在編譯期確定,ok是確定的,放進(jìn)并指向常量池,而ok1含有變量導(dǎo)致不確定,所以不是同一個(gè)對(duì)象.輸出false。

test7:

1 String ok = "apple1";  
2 final int temp = 1;  
3 String ok1 = "apple"+temp;  
4 System.out.println(ok==ok1);//true 

ok確定,加上final后使得ok1也在編譯期能確定,所以輸出true。

test8:

 1 public static void main(String[] args) {    
 2     String ok = "apple1";  
 3     final int temp = getTemp();  
 4     String ok1 = "apple"+temp;  
 5     System.out.println(ok==ok1);//false       
 6 }  
 7   
 8 public static int getTemp(){  
 9     return 1;  
10 }

ok一樣是確定的。而ok1不能確定,需要運(yùn)行代碼獲得temp,所以不是同一個(gè)對(duì)象,輸出false。

以上內(nèi)容如有不對(duì)的地方,還請(qǐng)各位指正!多謝!

以上就是Java中String對(duì)象的深入理解的詳細(xì)內(nèi)容,更多關(guān)于Java String對(duì)象的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 親身體驗(yàn)Intellij?Idea從卡頓到順暢全過程

    親身體驗(yàn)Intellij?Idea從卡頓到順暢全過程

    這篇文章主要介紹了親身體驗(yàn)Intellij?Idea從卡頓到順暢全過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • 詳解IntelliJ IDEA中TortoiseSVN修改服務(wù)器地址的方法

    詳解IntelliJ IDEA中TortoiseSVN修改服務(wù)器地址的方法

    這篇文章主要介紹了詳解IntelliJ IDEA中TortoiseSVN修改服務(wù)器地址的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2017-12-12
  • java騰訊AI人臉對(duì)比對(duì)接代碼實(shí)例

    java騰訊AI人臉對(duì)比對(duì)接代碼實(shí)例

    這篇文章主要介紹了java騰訊AI人臉對(duì)比對(duì)接,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • 解決SpringBoot搭建MyBatisPlus中selectList遇到LambdaQueryWrapper報(bào)錯(cuò)問題

    解決SpringBoot搭建MyBatisPlus中selectList遇到LambdaQueryWrapper報(bào)錯(cuò)問題

    這篇文章主要介紹了解決SpringBoot搭建MyBatisPlus中selectList遇到LambdaQueryWrapper報(bào)錯(cuò)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Java的Stream入門級(jí)教程

    Java的Stream入門級(jí)教程

    Java的Stream(流)是從Java?8?引入的一套面向集合數(shù)據(jù)的聲明式處理API,它讓你像寫“數(shù)據(jù)流水線”一樣,對(duì)一組元素做篩選、變換、聚合等操作,代碼更簡(jiǎn)潔、更可讀,本文給大家介紹Java的Stream入門級(jí)教程,感興趣的朋友跟隨小編一起看看吧
    2025-10-10
  • java如何讀取resources目錄和同級(jí)目錄文件

    java如何讀取resources目錄和同級(jí)目錄文件

    介紹了Java中讀取resources目錄和同級(jí)目錄文件的方法,并討論了在IDE和發(fā)布環(huán)境中可能遇到的問題,通過測(cè)試發(fā)現(xiàn),執(zhí)行目錄可能會(huì)影響文件讀取,建議在使用`user.dir`時(shí)注意jar包的運(yùn)行目錄問題
    2024-12-12
  • IDEA如何修改項(xiàng)目名稱出現(xiàn)中括號(hào)

    IDEA如何修改項(xiàng)目名稱出現(xiàn)中括號(hào)

    當(dāng)項(xiàng)目文件夾名稱與model名稱不一致時(shí),IDEA會(huì)在項(xiàng)目名旁顯示中括號(hào)以區(qū)分,修改項(xiàng)目名稱時(shí)出現(xiàn)中括號(hào)問題,通常是因?yàn)镮DE中model名與文件夾名不同步,解決方法是統(tǒng)一model名稱和文件夾名稱,可通過重構(gòu)功能或項(xiàng)目結(jié)構(gòu)設(shè)置完成,重命名操作應(yīng)謹(jǐn)慎,以避免路徑等引用錯(cuò)誤
    2024-10-10
  • Java 多線程實(shí)例講解(一)

    Java 多線程實(shí)例講解(一)

    本文主要介紹Java 多線程的知識(shí),這里整理了詳細(xì)的資料及簡(jiǎn)單示例代碼有需要的小伙伴可以參考下
    2016-09-09
  • spring boot 集成 swagger3及配置方法

    spring boot 集成 swagger3及配置方法

    Swagger 3是一種開源的API描述工具,它可以幫助開發(fā)人員設(shè)計(jì)、構(gòu)建、文檔化和測(cè)試API,這篇文章主要介紹了spring boot 集成 swagger3,需要的朋友可以參考下
    2023-05-05
  • SpringBoot如何實(shí)現(xiàn)并發(fā)任務(wù)并返回結(jié)果

    SpringBoot如何實(shí)現(xiàn)并發(fā)任務(wù)并返回結(jié)果

    這篇文章主要介紹了SpringBoot如何實(shí)現(xiàn)并發(fā)任務(wù)并返回結(jié)果問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07

最新評(píng)論

翁源县| 松阳县| 湛江市| 东兰县| 萍乡市| 富平县| 道孚县| 二连浩特市| 舞钢市| 饶平县| 汾西县| 新营市| 若羌县| 宁陕县| 视频| 宁都县| 寿阳县| 太白县| 晴隆县| 鹤峰县| 错那县| 当涂县| 岑巩县| 潞城市| 睢宁县| 沾化县| 滕州市| 新竹县| 中山市| 濮阳县| 新密市| 翼城县| 金阳县| 宝鸡市| 白朗县| 陕西省| 黄石市| 宽甸| 北安市| 靖安县| 玉山县|