一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Java String源碼分析并介紹Sting 為什么不可變

Java String源碼分析并介紹Sting 為什么不可變

2020-08-10 17:33張紀(jì)剛 Java教程

這篇文章主要介紹了Java String源碼分析并介紹Sting 為什么不可變的相關(guān)資料,需要的朋友可以參考下

Java String源碼分析

什么是不可變對象?

眾所周知, 在Java中, String類是不可變的。那么到底什么是不可變的對象呢? 可以這樣認(rèn)為:如果一個(gè)對象,在它創(chuàng)建完成之后,不能再改變它的狀態(tài),那么這個(gè)對象就是不可變的。不能改變狀態(tài)的意思是,不能改變對象內(nèi)的成員變量,包括基本數(shù)據(jù)類型的值不能改變,引用類型的變量不能指向其他的對象,引用類型指向的對象的狀態(tài)也不能改變。

區(qū)分對象和對象的引用

對于Java初學(xué)者, 對于String是不可變對象總是存有疑惑。看下面代碼:

?
1
2
3
4
5
String s = "ABCabc";
System.out.println("s = " + s);
 
s = "123456";
System.out.println("s = " + s);

打印結(jié)果為:

?
1
2
s = ABCabc
s = 123456

首先創(chuàng)建一個(gè)String對象s,然后讓s的值為“ABCabc”, 然后又讓s的值為“123456”。 從打印結(jié)果可以看出,s的值確實(shí)改變了。那么怎么還說String對象是不可變的呢? 其實(shí)這里存在一個(gè)誤區(qū): s只是一個(gè)String對象的引用,并不是對象本身。對象在內(nèi)存中是一塊內(nèi)存區(qū),成員變量越多,這塊內(nèi)存區(qū)占的空間越大。引用只是一個(gè)4字節(jié)的數(shù)據(jù),里面存放了它所指向的對象的地址,通過這個(gè)地址可以訪問對象。

也就是說,s只是一個(gè)引用,它指向了一個(gè)具體的對象,當(dāng)s=“123456”; 這句代碼執(zhí)行過之后,又創(chuàng)建了一個(gè)新的對象“123456”, 而引用s重新指向了這個(gè)心的對象,原來的對象“ABCabc”還在內(nèi)存中存在,并沒有改變。內(nèi)存結(jié)構(gòu)如下圖所示:

Java String源碼分析并介紹Sting 為什么不可變

Java和C++的一個(gè)不同點(diǎn)是, 在Java中不可能直接操作對象本身,所有的對象都由一個(gè)引用指向,必須通過這個(gè)引用才能訪問對象本身,包括獲取成員變量的值,改變對象的成員變量,調(diào)用對象的方法等。而在C++中存在引用,對象和指針三個(gè)東西,這三個(gè)東西都可以訪問對象。其實(shí),Java中的引用和C++中的指針在概念上是相似的,他們都是存放的對象在內(nèi)存中的地址值,只是在Java中,引用喪失了部分靈活性,比如Java中的引用不能像C++中的指針那樣進(jìn)行加減運(yùn)算。

為什么String對象是不可變的?

要理解String的不可變性,首先看一下String類中都有哪些成員變量。 在JDK1.6中,String的成員變量有以下幾個(gè):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class String
  implements java.io.Serializable, Comparable<String>, CharSequence
{
  /** The value is used for character storage. */
  private final char value[];
 
  /** The offset is the first index of the storage that is used. */
  private final int offset;
 
  /** The count is the number of characters in the String. */
  private final int count;
 
  /** Cache the hash code for the string */
  private int hash; // Default to 0

在JDK1.7中,String類做了一些改動(dòng),主要是改變了substring方法執(zhí)行時(shí)的行為,這和本文的主題不相關(guān)。JDK1.7中String類的主要成員變量就剩下了兩個(gè):

?
1
2
3
4
5
6
7
public final class String
  implements java.io.Serializable, Comparable<String>, CharSequence {
  /** The value is used for character storage. */
  private final char value[];
 
  /** Cache the hash code for the string */
  private int hash; // Default to 0

由以上的代碼可以看出, 在Java中String類其實(shí)就是對字符數(shù)組的封裝。JDK6中, value是String封裝的數(shù)組,offset是String在這個(gè)value數(shù)組中的起始位置,count是String所占的字符的個(gè)數(shù)。在JDK7中,只有一個(gè)value變量,也就是value中的所有字符都是屬于String這個(gè)對象的。這個(gè)改變不影響本文的討論。 除此之外還有一個(gè)hash成員變量,是該String對象的哈希值的緩存,這個(gè)成員變量也和本文的討論無關(guān)。在Java中,數(shù)組也是對象(可以參考我之前的文章java中數(shù)組的特性)。 所以value也只是一個(gè)引用,它指向一個(gè)真正的數(shù)組對象。其實(shí)執(zhí)行了String s = “ABCabc”; 這句代碼之后,真正的內(nèi)存布局應(yīng)該是這樣的:

 

Java String源碼分析并介紹Sting 為什么不可變

value,offset和count這三個(gè)變量都是private的,并且沒有提供setValue, setOffset和setCount等公共方法來修改這些值,所以在String類的外部無法修改String。也就是說一旦初始化就不能修改, 并且在String類的外部不能訪問這三個(gè)成員。此外,value,offset和count這三個(gè)變量都是final的, 也就是說在String類內(nèi)部,一旦這三個(gè)值初始化了, 也不能被改變。所以可以認(rèn)為String對象是不可變的了。

那么在String中,明明存在一些方法,調(diào)用他們可以得到改變后的值。這些方法包括substring, replace, replaceAll, toLowerCase等。例如如下代碼:

?
1
2
3
4
String a = "ABCabc";
System.out.println("a = " + a);
a = a.replace('A', 'a');
System.out.println("a = " + a);

打印結(jié)果為:

?
1
2
a = ABCabc
a = aBCabc

那么a的值看似改變了,其實(shí)也是同樣的誤區(qū)。再次說明, a只是一個(gè)引用, 不是真正的字符串對象,在調(diào)用a.replace('A', 'a')時(shí), 方法內(nèi)部創(chuàng)建了一個(gè)新的String對象,并把這個(gè)心的對象重新賦給了引用a。String中replace方法的源碼可以說明問題:

Java String源碼分析并介紹Sting 為什么不可變

讀者可以自己查看其他方法,都是在方法內(nèi)部重新創(chuàng)建新的String對象,并且返回這個(gè)新的對象,原來的對象是不會被改變的。這也是為什么像replace, substring,toLowerCase等方法都存在返回值的原因。也是為什么像下面這樣調(diào)用不會改變對象的值:

?
1
2
3
4
5
6
7
String ss = "123456";
 
System.out.println("ss = " + ss);
 
ss.replace('1', '0');
 
System.out.println("ss = " + ss);

打印結(jié)果:

?
1
2
ss = 123456
ss = 123456

String對象真的不可變嗎?

從上文可知String的成員變量是private final 的,也就是初始化之后不可改變。那么在這幾個(gè)成員中, value比較特殊,因?yàn)樗且粋€(gè)引用變量,而不是真正的對象。value是final修飾的,也就是說final不能再指向其他數(shù)組對象,那么我能改變value指向的數(shù)組嗎? 比如將數(shù)組中的某個(gè)位置上的字符變?yōu)橄聞澗€“_”。 至少在我們自己寫的普通代碼中不能夠做到,因?yàn)槲覀兏静荒軌蛟L問到這個(gè)value引用,更不能通過這個(gè)引用去修改數(shù)組。

那么用什么方式可以訪問私有成員呢? 沒錯(cuò),用反射, 可以反射出String對象中的value屬性, 進(jìn)而改變通過獲得的value引用改變數(shù)組的結(jié)構(gòu)。下面是實(shí)例代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void testReflection() throws Exception {
   
  //創(chuàng)建字符串"Hello World", 并賦給引用s
  String s = "Hello World"
   
  System.out.println("s = " + s); //Hello World
   
  //獲取String類中的value字段
  Field valueFieldOfString = String.class.getDeclaredField("value");
   
  //改變value屬性的訪問權(quán)限
  valueFieldOfString.setAccessible(true);
   
  //獲取s對象上的value屬性的值
  char[] value = (char[]) valueFieldOfString.get(s);
   
  //改變value所引用的數(shù)組中的第5個(gè)字符
  value[5] = '_';
   
  System.out.println("s = " + s); //Hello_World
}

打印結(jié)果為:

?
1
2
s = Hello World
s = Hello_World

在這個(gè)過程中,s始終引用的同一個(gè)String對象,但是再反射前后,這個(gè)String對象發(fā)生了變化, 也就是說,通過反射是可以修改所謂的“不可變”對象的。但是一般我們不這么做。這個(gè)反射的實(shí)例還可以說明一個(gè)問題:如果一個(gè)對象,他組合的其他對象的狀態(tài)是可以改變的,那么這個(gè)對象很可能不是不可變對象。例如一個(gè)Car對象,它組合了一個(gè)Wheel對象,雖然這個(gè)Wheel對象聲明成了private final 的,但是這個(gè)Wheel對象內(nèi)部的狀態(tài)可以改變, 那么就不能很好的保證Car對象不可變。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

原文鏈接:http://blog.csdn.net/zhangjg_blog/article/details/18319521

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产毛片在线高清视频 | 免费国产网站 | 黑人干亚洲人 | a级动漫| 亚洲欧美久久婷婷爱综合一区天堂 | 久久久久久久伊人电影 | 精品在线免费播放 | 能免费观看的韩剧 | 日韩精品福利视频一区二区三区 | 国产精品中文字幕 | 脱了白丝校花的内裤猛烈进入 | 无遮挡h肉动漫在线观看电车 | 美女张开下身让男人桶 | 国产愉拍| 国产精品1024永久免费视频 | 国产精品特黄毛片 | 欧美在线观看一区二区三 | 69re在线观看 | 99自拍视频在线观看 | 99这里都是精品 | 女女同性做爰xxoo亲吻 | 亚洲一区二区三区久久精品 | 韩国女主播一区二区视频 | 国产精品手机视频一区二区 | 亚洲色图丝袜 | 国产欧美曰韩一区二区三区 | 日本视频在线免费看 | 美女视频在线观看视频 | 免费国产网站 | 美女校花被调教出奶水 | 99久久精品免费看国产一区二区 | 亚洲高清无码在线 视频 | 亚洲国产精久久久久久久 | 性bbbbwwbbbb| 色综合视频在线观看 | 蜜桃视频一区二区三区四区 | 国产成人在线免费视频 | 国产免费色视频 | 亚洲色图色 | 午夜伦伦电影理论片费看 | 美国女网址www呦女 美国复古性经典xxxxx |