1、String 類的定義
public final class String
implements java.io.Serializable, Comparable, CharSequence {}
和上一篇博客所講的 Integer 類一樣,這也是一個(gè)用 final 聲明的常量類,不能被任何類所繼承,而且一旦一個(gè)String對(duì)象被創(chuàng)建, 包含在這個(gè)對(duì)象中的字符序列是不可改變的, 包括該類后續(xù)的所有方法都是不能修改該對(duì)象的,直至該對(duì)象被銷毀,這是我們需要特別注意的(該類的一些方法看似改變了字符串,其實(shí)內(nèi)部都是創(chuàng)建一個(gè)新的字符串,下面講解方法時(shí)會(huì)介紹)。接著實(shí)現(xiàn)了 Serializable接口,這是一個(gè)序列化標(biāo)志接口,還實(shí)現(xiàn)了 Comparable 接口,用于比較兩個(gè)字符串的大小(按順序比較單個(gè)字符的ASCII碼),后面會(huì)有具體方法實(shí)現(xiàn);最后實(shí)現(xiàn)了 CharSequence 接口,表示是一個(gè)有序字符的集合,相應(yīng)的方法后面也會(huì)介紹。
2、字段屬性
/**用來(lái)存儲(chǔ)字符串 */
private final char value[];
/** 緩存字符串的哈希碼 */
private int hash; // Default to 0
/** 實(shí)現(xiàn)序列化的標(biāo)識(shí) */
private static final long serialVersionUID = -6849794470754667710L;
一個(gè) String 字符串實(shí)際上是一個(gè) char 數(shù)組。
3、構(gòu)造方法
String 類的構(gòu)造方法很多。可以通過(guò)初始化一個(gè)字符串,或者字符數(shù)組,或者字節(jié)數(shù)組等等來(lái)創(chuàng)建一個(gè) String 對(duì)象。
String str1 = "abc";//注意這種字面量聲明的區(qū)別,文末會(huì)詳細(xì)介紹
String str2 = new String("abc");
String str3 = new String(new char[]{'a','b','c'});
4、equals(Object anObject) 方法
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
String 類重寫(xiě)了 equals 方法,比較的是組成字符串的每一個(gè)字符是否相同,如果都相同則返回true,否則返回false。
5、hashCode() 方法
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
String 類的 hashCode 算法很簡(jiǎn)單,主要就是中間的 for 循環(huán),計(jì)算公式如下:
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
s 數(shù)組即源碼中的 val 數(shù)組,也就是構(gòu)成字符串的字符數(shù)組。這里有個(gè)數(shù)字 31 ,為什么選擇31作為乘積因子,而且沒(méi)有用一個(gè)常量來(lái)聲明?主要原因有兩個(gè):
①、31是一個(gè)不大不小的質(zhì)數(shù),是作為 hashCode 乘子的優(yōu)選質(zhì)數(shù)之一。
②、31可以被 JVM 優(yōu)化,31 * i = (i << 5) - i。因?yàn)橐莆贿\(yùn)算比乘法運(yùn)行更快更省性能。
6、charAt(int index) 方法
public char charAt(int index) {
//如果傳入的索引大于字符串的長(zhǎng)度或者小于0,直接拋出索引越界異常
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];//返回指定索引的單個(gè)字符
}
我們知道一個(gè)字符串是由一個(gè)字符數(shù)組組成,這個(gè)方法是通過(guò)傳入的索引(數(shù)組下標(biāo)),返回指定索引的單個(gè)字符。
7、compareTo(String anotherString) 和 compareToIgnoreCase(String str) 方法
我們先看看 compareTo 方法:
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
源碼也很好理解,該方法是按字母順序比較兩個(gè)字符串,是基于字符串中每個(gè)字符的 Unicode 值。當(dāng)兩個(gè)字符串某個(gè)位置的字符不同時(shí),返回的是這一位置的字符 Unicode 值之差,當(dāng)兩個(gè)字符串都相同時(shí),返回兩個(gè)字符串長(zhǎng)度之差。
compareToIgnoreCase() 方法在 compareTo 方法的基礎(chǔ)上忽略大小寫(xiě),我們知道大寫(xiě)字母是比小寫(xiě)字母的Unicode值小32的,底層實(shí)現(xiàn)是先都轉(zhuǎn)換成大寫(xiě)比較,然后都轉(zhuǎn)換成小寫(xiě)進(jìn)行比較。
8、concat(String str) 方法
該方法是將指定的字符串連接到此字符串的末尾。
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
首先判斷要拼接的字符串長(zhǎng)度是否為0,如果為0,則直接返回原字符串。如果不為0,則通過(guò) Arrays 工具類(后面會(huì)詳細(xì)介紹這個(gè)工具類)的copyOf方法創(chuàng)建一個(gè)新的字符數(shù)組,長(zhǎng)度為原字符串和要拼接的字符串之和,前面填充原字符串,后面為空。接著在通過(guò) getChars 方法將要拼接的字符串放入新字符串后面為空的位置。
注意:返回值是 new String(buf, true),也就是重新通過(guò) new 關(guān)鍵字創(chuàng)建了一個(gè)新的字符串,原字符串是不變的。這也是前面我們說(shuō)的一旦一個(gè)String對(duì)象被創(chuàng)建, 包含在這個(gè)對(duì)象中的字符序列是不可改變的。
9、indexOf(int ch) 和 indexOf(int ch, int fromIndex) 方法
indexOf(int ch),參數(shù) ch 其實(shí)是字符的 Unicode 值,這里也可以放單個(gè)字符(默認(rèn)轉(zhuǎn)成int),作用是返回指定字符第一次出現(xiàn)的此字符串中的索引。其內(nèi)部是調(diào)用 indexOf(int ch, int fromIndex),只不過(guò)這里的 fromIndex =0 ,因?yàn)槭菑?0 開(kāi)始搜索;而 indexOf(int ch, int fromIndex) 作用也是返回首次出現(xiàn)的此字符串內(nèi)的索引,但是從指定索引處開(kāi)始搜索。
public int indexOf(int ch) {
return indexOf(ch, 0);//從第一個(gè)字符開(kāi)始搜索
}
public int indexOf(int ch, int fromIndex) {
final int max = value.length;//max等于字符的長(zhǎng)度
if (fromIndex < 0) {//指定索引的位置如果小于0,默認(rèn)從 0 開(kāi)始搜索
fromIndex = 0;
} else if (fromIndex >= max) {
//如果指定索引值大于等于字符的長(zhǎng)度(因?yàn)槭菙?shù)組,下標(biāo)最多只能是max-1),直接返回-1
return -1;
}
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {//一個(gè)char占用兩個(gè)字節(jié),如果ch小于2的16次方(65536),絕大多數(shù)字符都在此范圍內(nèi)
final char[] value = this.value;
for (int i = fromIndex; i < max; i++) {//for循環(huán)依次判斷字符串每個(gè)字符是否和指定字符相等
if (value[i] == ch) {
return i;//存在相等的字符,返回第一次出現(xiàn)該字符的索引位置,并終止循環(huán)
}
}
return -1;//不存在相等的字符,則返回 -1
} else {//當(dāng)字符大于 65536時(shí),處理的少數(shù)情況,該方法會(huì)首先判斷是否是有效字符,然后依次進(jìn)行比較
return indexOfSupplementary(ch, fromIndex);
}
}
10、split(String regex) 和 split(String regex, int limit) 方法
split(String regex) 將該字符串拆分為給定正則表達(dá)式的匹配。split(String regex , int limit) 也是一樣,不過(guò)對(duì)于 limit 的取值有三種情況:
①、limit > 0 ,則pattern(模式)應(yīng)用n - 1 次
String str = "a,b,c";
String[] c1 = str.split(",", 2);
System.out.println(c1.length);//2
System.out.println(Arrays.toString(c1));//{"a","b,c"}
②、limit = 0 ,則pattern(模式)應(yīng)用無(wú)限次并且省略末尾的空字串
String str2 = "a,b,c,,";
String[] c2 = str2.split(",", 0);
System.out.println(c2.length);//3
System.out.println(Arrays.toString(c2));//{"a","b","c"}
③、limit < 0 ,則pattern(模式)應(yīng)用無(wú)限次
String str2 = "a,b,c,,";
String[] c2 = str2.split(",", -1);
System.out.println(c2.length);//5
System.out.println(Arrays.toString(c2));//{"a","b","c","",""}
下面我們看看底層的源碼實(shí)現(xiàn)。對(duì)于 split(String regex) 沒(méi)什么好說(shuō)的,內(nèi)部調(diào)用 split(regex, 0) 方法:
public String[] split(String regex) {
return split(regex, 0);
}
重點(diǎn)看 split(String regex, int limit) 的方法實(shí)現(xiàn):
public String[] split(String regex, int limit) {
/* 1、單個(gè)字符,且不是".$|()[{^?*+"其中一個(gè)
* 2、兩個(gè)字符,第一個(gè)是"",第二個(gè)大小寫(xiě)字母或者數(shù)字
*/
char ch = 0;
if (((regex.value.length == 1 &&
".$|()[{^?*+".indexOf(ch = regex.charAt(0)) == -1) ||
(regex.length() == 2 &&
regex.charAt(0) == '' &&
(((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
((ch-'a')|('z'-ch)) < 0 &&
((ch-'A')|('Z'-ch)) < 0)) &&
(ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE))
{
int off = 0;
int next = 0;
boolean limited = limit > 0;//大于0,limited==true,反之limited==false
ArrayList< String > list = new ArrayList< >();
while ((next = indexOf(ch, off)) != -1) {
//當(dāng)參數(shù)limit<=0 或者 集合list的長(zhǎng)度小于 limit-1
if (!limited || list.size() < limit - 1) {
list.add(substring(off, next));
off = next + 1;
} else {//判斷最后一個(gè)list.size() == limit - 1
list.add(substring(off, value.length));
off = value.length;
break;
}
}
//如果沒(méi)有一個(gè)能匹配的,返回一個(gè)新的字符串,內(nèi)容和原來(lái)的一樣
if (off == 0)
return new String[]{this};
// 當(dāng) limit<=0 時(shí),limited==false,或者集合的長(zhǎng)度 小于 limit是,截取添加剩下的字符串
if (!limited || list.size() < limit)
list.add(substring(off, value.length));
// 當(dāng) limit == 0 時(shí),如果末尾添加的元素為空(長(zhǎng)度為0),則集合長(zhǎng)度不斷減1,直到末尾不為空
int resultSize = list.size();
if (limit == 0) {
while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
resultSize--;
}
}
String[] result = new String[resultSize];
return list.subList(0, resultSize).toArray(result);
}
return Pattern.compile(regex).split(this, limit);
}
11、replace(char oldChar, char newChar) 和 String replaceAll(String regex, String replacement) 方法
①、replace(char oldChar, char newChar) :將原字符串中所有的oldChar字符都替換成newChar字符,返回一個(gè)新的字符串。
②、String replaceAll(String regex, String replacement):將匹配正則表達(dá)式regex的匹配項(xiàng)都替換成replacement字符串,返回一個(gè)新的字符串。
12、substring(int beginIndex) 和 substring(int beginIndex, int endIndex) 方法
①、substring(int beginIndex):返回一個(gè)從索引 beginIndex 開(kāi)始一直到結(jié)尾的子字符串。
public String substring(int beginIndex) {
if (beginIndex < 0) {//如果索引小于0,直接拋出異常
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;//subLen等于字符串長(zhǎng)度減去索引
if (subLen < 0) {//如果subLen小于0,也是直接拋出異常
throw new StringIndexOutOfBoundsException(subLen);
}
//1、如果索引值beginIdex == 0,直接返回原字符串
//2、如果不等于0,則返回從beginIndex開(kāi)始,一直到結(jié)尾
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
②、 substring(int beginIndex, int endIndex):返回一個(gè)從索引 beginIndex 開(kāi)始,到 endIndex 結(jié)尾的子字符串。
13、常量池
在前面講解構(gòu)造函數(shù)的時(shí)候,我們知道最常見(jiàn)的兩種聲明一個(gè)字符串對(duì)象的形式有兩種:
①、通過(guò)“字面量”的形式直接賦值
String str = "hello";
②、通過(guò) new 關(guān)鍵字調(diào)用構(gòu)造函數(shù)創(chuàng)建對(duì)象
String str = new String("hello");
那么這兩種聲明方式有什么區(qū)別呢?在講解之前,我們先介紹 JDK1.7(不包括1.7)以前的 JVM 的內(nèi)存分布:
①、程序計(jì)數(shù)器:也稱為 PC 寄存器,保存的是程序當(dāng)前執(zhí)行的指令的地址(也可以說(shuō)保存下一條指令的所在存儲(chǔ)單元的地址),當(dāng)CPU需要執(zhí)行指令時(shí),需要從程序計(jì)數(shù)器中得到當(dāng)前需要執(zhí)行的指令所在存儲(chǔ)單元的地址,然后根據(jù)得到的地址獲取到指令,在得到指令之后,程序計(jì)數(shù)器便自動(dòng)加1或者根據(jù)轉(zhuǎn)移指針得到下一條指令的地址,如此循環(huán),直至執(zhí)行完所有的指令。線程私有。
②、虛擬機(jī)棧:基本數(shù)據(jù)類型、對(duì)象的引用都存放在這。線程私有。
③、本地方法棧:虛擬機(jī)棧是為執(zhí)行Java方法服務(wù)的,而本地方法棧則是為執(zhí)行本地方法(Native Method)服務(wù)的。在JVM規(guī)范中,并沒(méi)有對(duì)本地方法棧的具體實(shí)現(xiàn)方法以及數(shù)據(jù)結(jié)構(gòu)作強(qiáng)制規(guī)定,虛擬機(jī)可以自由實(shí)現(xiàn)它。在HotSopt虛擬機(jī)中直接就把本地方法棧和虛擬機(jī)棧合二為一。
④、方法區(qū):存儲(chǔ)了每個(gè)類的信息(包括類的名稱、方法信息、字段信息)、靜態(tài)變量、常量以及編譯器編譯后的代碼等。注意:在Class文件中除了類的字段、方法、接口等描述信息外,還有一項(xiàng)信息是常量池,用來(lái)存儲(chǔ)編譯期間生成的字面量和符號(hào)引用。
⑤、堆:用來(lái)存儲(chǔ)對(duì)象本身的以及數(shù)組(當(dāng)然,數(shù)組引用是存放在Java棧中的)。
在 JDK1.7 以后,方法區(qū)的常量池被移除放到堆中了,如下:
常量池:Java運(yùn)行時(shí)會(huì)維護(hù)一個(gè)String Pool(String池), 也叫“字符串緩沖區(qū)”。String池用來(lái)存放運(yùn)行時(shí)中產(chǎn)生的各種字符串,并且池中的字符串的內(nèi)容不重復(fù)。
①、字面量創(chuàng)建字符串或者純字符串(常量)拼接字符串會(huì)先在字符串池中找,看是否有相等的對(duì)象,沒(méi)有的話就在字符串池創(chuàng)建該對(duì)象;有的話則直接用池中的引用,避免重復(fù)創(chuàng)建對(duì)象。
②、new關(guān)鍵字創(chuàng)建時(shí),直接在堆中創(chuàng)建一個(gè)新對(duì)象,變量所引用的都是這個(gè)新對(duì)象的地址,但是如果通過(guò)new關(guān)鍵字創(chuàng)建的字符串內(nèi)容在常量池中存在了,那么會(huì)由堆在指向常量池的對(duì)應(yīng)字符;但是反過(guò)來(lái),如果通過(guò)new關(guān)鍵字創(chuàng)建的字符串對(duì)象在常量池中沒(méi)有,那么通過(guò)new關(guān)鍵詞創(chuàng)建的字符串對(duì)象是不會(huì)額外在常量池中維護(hù)的。
③、使用包含變量表達(dá)式來(lái)創(chuàng)建String對(duì)象,則不僅會(huì)檢查維護(hù)字符串池,還會(huì)在堆區(qū)創(chuàng)建這個(gè)對(duì)象,最后是指向堆內(nèi)存的對(duì)象。
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
System.out.println(str1==str2);//true
System.out.println(str1==str3);//fasle
System.out.println(str2==str3);//fasle
System.out.println(str1.equals(str2));//true
System.out.println(str1.equals(str3));//true
System.out.println(str2.equals(str3));//true
對(duì)于上面的情況,首先 String str1 = "hello",會(huì)先到常量池中檢查是否有“hello”的存在,發(fā)現(xiàn)是沒(méi)有的,于是在常量池中創(chuàng)建“hello”對(duì)象,并將常量池中的引用賦值給str1;第二個(gè)字面量 String str2 = "hello",在常量池中檢測(cè)到該對(duì)象了,直接將引用賦值給str2;第三個(gè)是通過(guò)new關(guān)鍵字創(chuàng)建的對(duì)象,常量池中有了該對(duì)象了,不用在常量池中創(chuàng)建,然后在堆中創(chuàng)建該對(duì)象后,將堆中對(duì)象的引用賦值給str3,再將該對(duì)象指向常量池。如下圖所示:
注意:看上圖紅色的箭頭,通過(guò) new 關(guān)鍵字創(chuàng)建的字符串對(duì)象,如果常量池中存在了,會(huì)將堆中創(chuàng)建的對(duì)象指向常量池的引用。我們可以通過(guò)文章末尾介紹的intern()方法來(lái)驗(yàn)證。
使用包含變量表達(dá)式創(chuàng)建對(duì)象:
String str1 = "hello";
String str2 = "helloworld";
String str3 = str1+"world";//編譯器不能確定為常量(會(huì)在堆區(qū)創(chuàng)建一個(gè)String對(duì)象)
String str4 = "hello"+"world";//編譯器確定為常量,直接到常量池中引用
System.out.println(str2==str3);//fasle
System.out.println(str2==str4);//true
System.out.println(str3==str4);//fasle
str3 由于含有變量str1,編譯器不能確定是常量,會(huì)在堆區(qū)中創(chuàng)建一個(gè)String對(duì)象。而str4是兩個(gè)常量相加,直接引用常量池中的對(duì)象即可。
14、intern() 方法
這是一個(gè)本地方法:
public native String intern();
當(dāng)調(diào)用intern方法時(shí),如果池中已經(jīng)包含一個(gè)與該String確定的字符串相同equals(Object)的字符串,則返回該字符串。否則,將此String對(duì)象添加到池中,并返回此對(duì)象的引用。
這句話什么意思呢?就是說(shuō)調(diào)用一個(gè)String對(duì)象的intern()方法,如果常量池中有該對(duì)象了,直接返回該字符串的引用(存在堆中就返回堆中,存在池中就返回池中),如果沒(méi)有,則將該對(duì)象添加到池中,并返回池中的引用。
String str1 = "hello";//字面量 只會(huì)在常量池中創(chuàng)建對(duì)象
String str2 = str1.intern();
System.out.println(str1==str2);//true
String str3 = new String("world");//new 關(guān)鍵字只會(huì)在堆中創(chuàng)建對(duì)象
String str4 = str3.intern();
System.out.println(str3 == str4);//false
String str5 = str1 + str2;//變量拼接的字符串,會(huì)在常量池中和堆中都創(chuàng)建對(duì)象
String str6 = str5.intern();//這里由于池中已經(jīng)有對(duì)象了,直接返回的是對(duì)象本身,也就是堆中的對(duì)象
System.out.println(str5 == str6);//true
String str7 = "hello1" + "world1";//常量拼接的字符串,只會(huì)在常量池中創(chuàng)建對(duì)象
String str8 = str7.intern();
System.out.println(str7 == str8);//true
15、String 真的不可變嗎?
前面我們介紹了,String 類是用 final 關(guān)鍵字修飾的,所以我們認(rèn)為其是不可變對(duì)象。但是真的不可變嗎?
每個(gè)字符串都是由許多單個(gè)字符組成的,我們知道其源碼是由 char[] value 字符數(shù)組構(gòu)成。
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
value 被 final 修飾,只能保證引用不被改變,但是 value 所指向的堆中的數(shù)組,才是真實(shí)的數(shù)據(jù),只要能夠操作堆中的數(shù)組,依舊能改變數(shù)據(jù)。而且 value 是基本類型構(gòu)成,那么一定是可變的,即使被聲明為 private,我們也可以通過(guò)反射來(lái)改變。
String str = "vae";
//打印原字符串
System.out.println(str);//vae
//獲取String類中的value字段
Field fieldStr = String.class.getDeclaredField("value");
//因?yàn)関alue是private聲明的,這里修改其訪問(wèn)權(quán)限
fieldStr.setAccessible(true);
//獲取str對(duì)象上的value屬性的值
char[] value = (char[]) fieldStr.get(str);
//將第一個(gè)字符修改為 V(小寫(xiě)改大寫(xiě))
value[0] = 'V';
//打印修改之后的字符串
System.out.println(str);//Vae
通過(guò)前后兩次打印的結(jié)果,我們可以看到 String 被改變了,但是在代碼里,幾乎不會(huì)使用反射的機(jī)制去操作 String 字符串,所以,我們會(huì)認(rèn)為 String 類型是不可變的。
那么,String 類為什么要這樣設(shè)計(jì)成不可變呢?我們可以從性能以及安全方面來(lái)考慮:
安全
引發(fā)安全問(wèn)題,譬如,數(shù)據(jù)庫(kù)的用戶名、密碼都是以字符串的形式傳入來(lái)獲得數(shù)據(jù)庫(kù)的連接,或者在socket編程中,主機(jī)名和端口都是以字符串的形式傳入。因?yàn)樽址遣豢勺兊?,所以它的值是不可改變的,否則黑客們可以鉆到空子,改變字符串指向的對(duì)象的值,造成安全漏洞。
保證線程安全,在并發(fā)場(chǎng)景下,多個(gè)線程同時(shí)讀寫(xiě)資源時(shí),會(huì)引競(jìng)態(tài)條件,由于 String 是不可變的,不會(huì)引發(fā)線程的問(wèn)題而保證了線程。
HashCode,當(dāng) String 被創(chuàng)建出來(lái)的時(shí)候,hashcode也會(huì)隨之被緩存,hashcode的計(jì)算與value有關(guān),若 String 可變,那么 hashcode 也會(huì)隨之變化,針對(duì)于 Map、Set 等容器,他們的鍵值需要保證唯一性和一致性,因此,String 的不可變性使其比其他對(duì)象更適合當(dāng)容器的鍵值。
性能
當(dāng)字符串是不可變時(shí),字符串常量池才有意義。字符串常量池的出現(xiàn),可以減少創(chuàng)建相同字面量的字符串,讓不同的引用指向池中同一個(gè)字符串,為運(yùn)行時(shí)節(jié)約很多的堆內(nèi)存。若字符串可變,字符串常量池失去意義,基于常量池的String.intern()方法也失效,每次創(chuàng)建新的 String 將在堆內(nèi)開(kāi)辟出新的空間,占據(jù)更多的內(nèi)存
16、小結(jié)
好了,這就是JDK中java.lang.String 類的源碼解析。
-
JAVA
+關(guān)注
關(guān)注
19文章
2943瀏覽量
104096 -
源碼
+關(guān)注
關(guān)注
8文章
626瀏覽量
28966 -
JDK
+關(guān)注
關(guān)注
0文章
80瀏覽量
16548 -
string
+關(guān)注
關(guān)注
0文章
40瀏覽量
4696
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論