欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中String對象的深入理解

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

一、 String認識你,你認識它么?

假如面試的時候問你,什么是String(或者談?wù)勀銓tring的理解)?你會如何回答?“String是基礎(chǔ)對象類型之一,是Java語言中重要的數(shù)據(jù)類型之一”。恐怕這是大多數(shù)人的回答,能力強些的可能會說,String底層是用char[ ]數(shù)組來實現(xiàn)的;如果面試官讓你再繼續(xù)呢?估計很多人會一臉尷尬,腦海里極力搜索關(guān)于String的相關(guān)知識,最后也只能恨自己平時對String關(guā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,它是一個編碼格式的標識。為什么這么做呢?我們知道一個char字符占16位,2 個字節(jié)。這種情況下,存儲單字節(jié)編碼內(nèi)的字符(占一個字節(jié)的字符)就顯得非常浪費。JDK1.9 的String類為了節(jié)約內(nèi)存空間,于是使用了占8位,1個字節(jié)的 byte 數(shù)組來存放字符串。而新屬性coder的作用是,在計算字符串長度或者使用 indexOf()函數(shù)時,我們需要根據(jù)這個字段,判斷如何計算字符串長度。coder屬性默認有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
在字符串常量中,默認會將對象放入常量池中;在字符串變量中,對象是會創(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)用的方法加了同步鎖,所以是線程安全的??慈缦略创a:

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對象的深入理解的詳細內(nèi)容,更多關(guān)于Java String對象的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java使用BIO和NIO進行文件操作對比代碼示例

    Java使用BIO和NIO進行文件操作對比代碼示例

    這篇文章主要介紹了Java使用BIO和NIO進行文件操作對比代碼示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-05-05
  • Java內(nèi)省之Introspector解讀

    Java內(nèi)省之Introspector解讀

    這篇文章主要介紹了Java內(nèi)省之Introspector解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Java并發(fā)編程ThreadLocalRandom類詳解

    Java并發(fā)編程ThreadLocalRandom類詳解

    這篇文章主要介紹了Java并發(fā)編程ThreadLocalRandom類詳解,通過提出問題為什么需要ThreadLocalRandom展開詳情,感興趣的朋友可以參考一下
    2022-06-06
  • Java使用注解和反射簡化編程的方法示例

    Java使用注解和反射簡化編程的方法示例

    這篇文章主要介紹了Java使用注解和反射簡化編程的方法,結(jié)合實例形式分析了java使用注解和反射調(diào)用大量函數(shù)簡化編程的相關(guān)操作技巧,需要的朋友可以參考下
    2019-10-10
  • java swing實現(xiàn)電影購票系統(tǒng)

    java swing實現(xiàn)電影購票系統(tǒng)

    這篇文章主要為大家詳細介紹了java swing實現(xiàn)電影購票系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • Java?集合框架?Queue?和?Stack?體系

    Java?集合框架?Queue?和?Stack?體系

    這篇文章主要介紹了Java?集合框架Queue和Stack體系,Stack?繼承自Vector,并拓展了五個允許將容器視為棧結(jié)構(gòu)的操作,Queue接口定義了隊列的能力,它繼承自Collection,更多相關(guān)內(nèi)容需要得小伙伴可以參考一下
    2022-06-06
  • Java中?springcloud.openfeign應(yīng)用案例解析

    Java中?springcloud.openfeign應(yīng)用案例解析

    使用OpenFeign能讓編寫Web?Service客戶端更加簡單,使用時只需定義服務(wù)接口,然后在上面添加注解,OpenFeign也支持可拔插式的編碼和解碼器,這篇文章主要介紹了Java中?springcloud.openfeign應(yīng)用案例解析,需要的朋友可以參考下
    2024-06-06
  • Java操作XML工具類XmlUtil詳解

    Java操作XML工具類XmlUtil詳解

    這篇文章主要為大家詳細介紹了Java操作XML工具類XmlUtil的使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • 5種Java中數(shù)組的拷貝方法總結(jié)分享

    5種Java中數(shù)組的拷貝方法總結(jié)分享

    這篇文章主要介紹了5種Java中數(shù)組的拷貝方法總結(jié)分享,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下
    2022-07-07
  • Java并發(fā)工具類之CountDownLatch詳解

    Java并發(fā)工具類之CountDownLatch詳解

    這篇文章主要介紹了Java并發(fā)工具類之CountDownLatch詳解,CountDownLatch可以使一個獲多個線程等待其他線程各自執(zhí)行完畢后再執(zhí)行,CountDownLatch可以解決那些一個或者多個線程在執(zhí)行之前必須依賴于某些必要的前提業(yè)務(wù)先執(zhí)行的場景,需要的朋友可以參考下
    2023-12-12

最新評論