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

Java中String對象的深入理解

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

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

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

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對象的實現(xiàn)

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

三、String是不可變對象

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

四、String常量池

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

五、String.intern()方法詳解

先來看一個示例:

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

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

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

1.對象的可變與不可變String是不可變對象,原因上面的內(nèi)容已經(jīng)解釋過了,這里不再贅述。
StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在AbstractStringBuilder中也是使用字符數(shù)組保存數(shù)據(jù),這兩種對象都是可變的。如下:
char[ ] value;
2.是否是線程安全String中的對象是不可變的,也就可以理解為常量,所以是線程安全。
AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
StringBuffer對方法加了同步鎖或者對調(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并沒有對方法進行加同步鎖,所以是非線程安全的。
3.StringBuilder與StringBuffer共同點StringBuilder與StringBuffer有公共的抽象父類AbstractStringBuilder。
抽象類與接口的一個區(qū)別是:抽象類中可以定義一些子類的公共方法,子類只需要增加新的功能,不需要重復(fù)寫已經(jīng)存在的方法;而接口中只是對方法的申明和常量的定義。
StringBuilder、StringBuffer的方法都會調(diào)用AbstractStringBuilder中的公共方法,如super.append(…)。只是StringBuffer會在方法上加synchronized關(guān)鍵字,進行同步。
如果程序不是多線程的,那么使用StringBuilder效率高于StringBuffer。

下面來幾道測試題,看看自己對String究竟掌握了多少

七、測試題

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

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

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

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

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

答案:1001個對象。

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

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

答案:1002個對象。

test4:

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

ok指向字符串常量池,ok1指向new出來的堆內(nèi)存塊,new的字符串在編譯期是無法確定的。所以輸出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。指向同一個對象,所以為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是確定的,放進并指向常量池,而ok1含有變量導(dǎo)致不確定,所以不是同一個對象.輸出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不能確定,需要運行代碼獲得temp,所以不是同一個對象,輸出false。

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

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

相關(guān)文章

  • 親身體驗Intellij?Idea從卡頓到順暢全過程

    親身體驗Intellij?Idea從卡頓到順暢全過程

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

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

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

    java騰訊AI人臉對比對接代碼實例

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

    解決SpringBoot搭建MyBatisPlus中selectList遇到LambdaQueryWrapper報錯問題

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

    Java的Stream入門級教程

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

    java如何讀取resources目錄和同級目錄文件

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

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

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

    Java 多線程實例講解(一)

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

    spring boot 集成 swagger3及配置方法

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

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

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

最新評論

威宁| 井研县| 白河县| 称多县| 隆化县| 盐亭县| 阿图什市| 沾益县| 洪洞县| 甘南县| 嘉祥县| 丹江口市| 当雄县| 安多县| 金湖县| 广平县| 河池市| 贵阳市| 陕西省| 安溪县| 乃东县| 临泉县| 瑞安市| 佳木斯市| 阿克苏市| 德兴市| 萝北县| 永城市| 彰武县| 石嘴山市| 罗平县| 沿河| 那曲县| 郎溪县| 红安县| 舟山市| 固阳县| 凤阳县| 马鞍山市| 钟山县| 满洲里市|