日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

java string改变的影响_为什么Java的string类要设成immutable(不可变的)

發布時間:2023/11/27 生活经验 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java string改变的影响_为什么Java的string类要设成immutable(不可变的) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最流行的Java面試題之一就是:什么是不可變對象(immutable

object),不可變對象有什么好處,在什么情況下應該用,或者更具體一些,Java的String類為什么要設成immutable類型?

不可變對象,顧名思義就是創建后不可以改變的對象,典型的例子就是Java中的String類,

String s = "ABC";

s.toLowerCase();

.toLowerCase()并沒有改變“ABC”的值,而是創建了一個新的String類”abc”,然后將新的實例的指向變量s。

相對于可變對象,不可變對象有很多優勢。

1)不可變對象可以提高String

Pool的效率和安全性。如果你知道一個對象是不可變的,那么需要拷貝這個對象的內容時,就不用復制它的本身而只是復制它的地址,復制地址(通常一個指針的大小)需要很小的內存效率也很高。對于同時引用這個“ABC”的其他變量也不會造成影響。

2)不可變對象對于多線程是安全的,因為在多線程同時進行的情況下,一個可變對象的值很可能被其他進程改變,這樣會造成不可預期的結果,而使用不可變對象就可以避免這種情況。

當然也有其他方面原因,但是最Java把String設成immutable最大的原因應該就是效率和安全的。

String和StringBuffer都是final類,他們生成的對象在堆中都是不可變的,在他們內部都是靠屬性char數組實現的,

那為什么StringBuffer可以在對象中追加字符串呢?呵呵,因為String中的char數組是finall的,也就是常量,是不可改變

的,而StringBuffer繼承自抽象類AbstractStringBuilder,他的內部實現靠他的父類完

成,AbstractStringBuilder內的char數組是變量,可以用append追加

StringBuffer和StringBuilder都實現了AbstractStringBuilder抽象類,此抽象類實現Appendable接口因此可追加

(在JDK 6.0 API中StringBuilder和Stringbuff寫著都繼承自Object

而且在他的API中找不到AbstractStringBuilder類)

首先,必須強調一點:String

Pool不是在堆區,也不是在棧區,而是存在于方法區(Method Area)

解析:

String

Pool是常量池(Constant?Pool)中的一塊。

我們知道,常量就是不可以再改變的值,給它建一個池子很明顯是為了加快程序運行的速度;在一個程序中,常量和變量是相對存在的;變量因為可變性所以一般存在于棧中,而常量去作為一個特殊群體被存在在常量池中。

常量池(constant

pool)指的是在編譯期被確定,并被保存在已編譯的.class文件中的一些數據。--- (很明顯在方法區)

它包括了關于類、方法、接口等中的常量,也包括字符串常量(這個就是Sring

Pool啦)。

在編譯好的class文件中,有個區域稱為Constant

Pool,它是一個由數組組成的表,類型為cp_info

constant_pool[],用來存儲程序中使用的各種常量,包括Class/String/Integer等各種基本Java數據類型。

=======?================

上面這些,簡單理解:一個Class類,它里面有常量的存在,比如

int a=10;String

b="123450";它們在JVM看來就是常量(當然在方法中可能被修改啦),在Class被加載時,JVM特意都把它放在一個數組中維護起來,并且把該數組放在方法區中,起名叫常量池。

常量池存在于方法區,它包含各種類型的常量(8個基本數據類型,包裝類型等)

我們把常量池中的String

Pool中的常量作為對象來看待 --- 因為String就是對象,String類型的常量自然也是對象啦!

比如:

String str1

= new String("Hello");

它創建了2個對象,一個是堆中的String對象,一個是String

Pool中的String對象。

要理解Java中String的運作方式,必須明確一點:String是一個非可變類(immutable)。什么是非可變類呢?簡單說來,非可變類的實例是不能被修改的,每個實例中包含的信息都必須在該實例創建的時候就提供出來,并且在對象的整個生存周期內固定不變。Java為什么要把String設計為非可變類呢?你可以問問

james Gosling

:)。但是非可變類確實有著自身的優勢,如狀態單一,對象簡單,便于維護。其次,該類對象對象本質上是線程安全的,不要求同步。此外用戶可以共享非可變對象,甚至可以共享它們的內部信息。(詳見

《Effective java》item

13)。String類在java中被大量運用,甚至在class文件中都有其身影,因此將其設計為簡單輕便的非可變類是比較合適的。

一、創建。

好了,知道String是非可變類以后,我們可以進一步了解String的構造方式了。創建一個Stirng對象,主要就有以下兩種方式:

java 代碼

String?str1?=newString("abc");

Stirng?str2?="abc";

雖然兩個語句都是返回一個String對象的引用,但是jvm對兩者的處理方式是不一樣的。對于第一種,jvm會馬上在heap中創建一個String對象,然后將該對象的引用返回給用戶。對于第二種,jvm首先會在內部維護的String

Pool中通過String的 equals

方法查找是對象池中是否存放有該String對象,如果有,則返回已有的String對象給用戶,而不會在heap中重新創建一個新的String對象;如果對象池中沒有該String對象,jvm則在heap中創建新的String對象,將其引用返回給用戶,同時將該引用添加至String

Pool中。注意:使用第一種方法創建對象時,jvm是不會主動把該對象放到String

Pool

里面的,除非程序調用

String的intern方法??聪旅娴睦?#xff1a;

java 代碼

String?str1?=newString("abc");//jvm?在堆上創建一個String對象

//jvm?在strings?pool中找不到值為“abc”的字符串,因此

//在堆上創建一個String對象,并將該對象的引用加入至strings?pool中

//此時堆上有兩個String對象

Stirng?str2?="abc";

if(str1?==?str2){

System.out.println("str1?==?str2");

}else{

System.out.println("str1?!=?str2");

}

//打印結果是?str1?!=?str2,因為它們是堆上兩個不同的對象

String?str3?="abc";

//此時,jvm發現String

Pool中已有“abc”對象了,因為“abc”equals?“abc”

//因此直接返回str2指向的對象給str3,也就是說str2和str3是指向同一個對象的引用

if(str2?==?str3){

System.out.println("str2?==?str3");

}else{

System.out.println("str2?!=?str3");

}

//打印結果為?str2?==?str3

再看下面的例子:

java 代碼

String?str1?=newString("abc");//jvm?在堆上創建一個String對象

str1?=?str1.intern();

//程序顯式將str1放到String Pool中,intern運行過程是這樣的:首先查看String

Pool

//有沒“abc”對象的引用,沒有,則在堆中新建一個對象,然后將新對象的引用加入至

//String

Pool中。執行完該語句后,str1原來指向的String對象已經成為垃圾對象了,隨時會

//被GC收集。

//此時,jvm發現String

Pool中已有“abc”對象了,因為“abc”equals?“abc”

//因此直接返回str1指向的對象給str2,也就是說str2和str1引用著同一個對象,

//此時,堆上的有效對象只有一個。

Stirng?str2?="abc";

if(str1?==?str2){

System.out.println("str1?==?str2");

}else{

System.out.println("str1?!=?str2");

}

//打印結果是?str1?==?str2

為什么jvm可以這樣處理String對象呢?就是因為String的非可變性。既然所引用的對象一旦創建就永不更改,那么多個引用共用一個對象時互不影響。

二、串接(Concatenation)。

java程序員應該都知道濫用String的串接操作符是會影響程序的性能的。性能問題從何而來呢?歸根結底就是String類的非可變性。既然String對象都是非可變的,也就是對象一旦創建了就不能夠改變其內在狀態了,但是串接操作明顯是要增長字符串的,也就是要改變String的內部狀態,兩者出現了矛盾。怎么辦呢?要維護String的非可變性,只好在串接完成后新建一個String

對象來表示新產生的字符串了。也就是說,每一次執行串接操作都會導致新對象的產生,如果串接操作執行很頻繁,就會導致大量對象的創建,性能問題也就隨之而來了。

為了解決這個問題,jdk為String類提供了一個可變的配套類,StringBuffer。使用StringBuffer對象,由于該類是可變的,串接時僅僅時改變了內部數據結構,而不會創建新的對象,因此性能上有很大的提高。針對單線程,jdk

5.0還提供了StringBuilder類,在單線程環境下,由于不用考慮同步問題,使用該類使性能得到進一步的提高。

三、String的長度

我們可以使用串接操作符得到一個長度更長的字符串,那么,String對象最多能容納多少字符呢?查看String的源代碼我們可以得知類String中是使用域

count 來記錄對象字符的數量,而count?的類型為 int,因此,我們可以推測最長的長度為

2^32,也就是4G。

不過,我們在編寫源代碼的時候,如果使用 Sting str =

"aaaa";的形式定義一個字符串,那么雙引號里面的ASCII字符最多只能有 65534 個。為什么呢?因為在class文件的規范中,

CONSTANT_Utf8_info表中使用一個16位的無符號整數來記錄字符串的長度的,最多能表示 65536個字節,而java

class 文件是使用一種變體UTF-8格式來存放字符的,null值使用兩個字節來表示,因此只剩下 65536- 2 =

65534個字節。也正是變體UTF-8的原因,如果字符串中含有中文等非ASCII字符,那么雙引號中字符的數量會更少(一個中文字符占用三個字節)。如果超出這個數量,在編譯的時候編譯器會報錯。

總結

以上是生活随笔為你收集整理的java string改变的影响_为什么Java的string类要设成immutable(不可变的)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。