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

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í)候問(wèn)你,什么是String(或者談?wù)勀銓?duì)String的理解)?你會(huì)如何回答?“String是基礎(chǔ)對(duì)象類型之一,是Java語(yǔ)言中重要的數(shù)據(jù)類型之一”??峙逻@是大多數(shù)人的回答,能力強(qiáng)些的可能會(huì)說(shuō),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ò)誤率又很高的問(wèn)題:

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ì)象通過(guò)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)存泄漏問(wèn)題。
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ì)象是不可變的呢?你有想過(guò)這其中的原因么?通過(guò)源碼我們知道,String類被final關(guān)鍵字修飾了,而且變量char[]也被final修飾了。Java語(yǔ)法告訴我們:被final修飾的類不可被繼承,被final修飾的變量不可被改變,一旦賦值了初始值,該final變量的值就不能被重新賦值,即不可更改,而char[]被 final+private修飾,說(shuō)明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ì)說(shuō):對(duì)于一個(gè)String str =“hello”,然后改為String str =“world”,這個(gè)時(shí)候str的值變成了“world”,str值確實(shí)改變了,為什么還說(shuō)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)存中。也就是說(shuō)str并不是對(duì)象,而只是一個(gè)對(duì)象引用。真正的對(duì)象依然還在內(nèi)存中,沒有被改變。所以在Java中要比較兩個(gè)對(duì)象是否相等,通常是用“==”,而要判斷兩個(gè)對(duì)象的值是否相等,則需要用equals方法來(lái)判斷。

四、String常量池

在java中,創(chuàng)建字符串通常有兩種方式:一種是通過(guò)字符串常量池的形式,比如String str = “abcd”;另一種是直接通過(guò)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ì)通過(guò)垃圾回收器回收。
所以針對(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)容后,文章開頭的問(wèn)題,相比你也有了答案了。分別是:false、false、true。

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

1.對(duì)象的可變與不可變String是不可變對(duì)象,原因上面的內(nèi)容已經(jīng)解釋過(guò)了,這里不再贅述。
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)文章

  • Java使用BIO和NIO進(jìn)行文件操作對(duì)比代碼示例

    Java使用BIO和NIO進(jìn)行文件操作對(duì)比代碼示例

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

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

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

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

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

    Java使用注解和反射簡(jiǎn)化編程的方法示例

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

    java swing實(shí)現(xiàn)電影購(gòu)票系統(tǒng)

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

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

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

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

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

    Java操作XML工具類XmlUtil詳解

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

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

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

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

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

最新評(píng)論