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

服務(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源碼和String常量池的全面解析

java String源碼和String常量池的全面解析

2021-03-06 13:35NiceCui Java教程

下面小編就為大家分享一篇java String源碼和String常量池的全面解析,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧

1. string 介紹,常用方法源碼分析

2. string 常量池分析

常用方法

equals

trim

replace

concat

split

startswith 和 endswith

substring

touppercase() 和 tolowercase()

compareto

string 介紹

string類被final所修飾,也就是說string對象是不可變量,并發(fā)程序最喜歡不可變量了。string類實(shí)現(xiàn)了serializable, comparable, charsequence接口。

從一段代碼說起:

java" id="highlighter_329325">
?
1
2
3
4
5
public void stringtest(){
 string a = "a"+"b"+1;
 string b = "ab1";
 system.out.println(a == b);
}

大家猜一猜結(jié)果如何?如果你的結(jié)論是true。好吧,再來一段代碼:

?
1
2
3
4
5
public void stringtest(){
 string a = new string("ab1");
 string b = "ab1";
 system.out.println(a == b);
}

結(jié)果如何呢?正確答案是false。

讓我們看看經(jīng)過編譯器編譯后的代碼如何

?
1
2
3
4
5
6
//第一段代碼
public void stringtest() {
 string a = "ab1";
 string b = "ab1";
 system.out.println(a == b);
}
?
1
2
3
4
5
6
//第二段代碼
public void stringtest() {
 string a1 = new string("ab1");
 string b = "ab1";
 system.out.println(a1 == b);
}

也就是說第一段代碼經(jīng)過了編譯期優(yōu)化,原因是編譯器發(fā)現(xiàn)"a"+"b"+1和"ab1"的效果是一樣的,都是不可變量組成。但是為什么他們的內(nèi)存地址會相同呢?如果你對此還有興趣,那就一起看看string類的一些重要源碼吧。

源碼

一、 string屬性

string類中包含一個(gè)不可變的char數(shù)組用來存放字符串,一個(gè)int型的變量hash用來存放計(jì)算后的哈希值。

?
1
2
3
4
5
6
/** the value is used for character storage. */
private final char value[];
/** cache the hash code for the string */
private int hash; // default to 0
/** use serialversionuid from jdk 1.0.2 for interoperability */
private static final long serialversionuid = -6849794470754667710l;

二、 string構(gòu)造函數(shù)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//不含參數(shù)的構(gòu)造函數(shù),一般沒什么用,因?yàn)関alue是不可變量
public string() {
 this.value = new char[0];
}
//參數(shù)為string類型
public string(string original) {
 this.value = original.value;
 this.hash = original.hash;
}
//參數(shù)為char數(shù)組,使用java.utils包中的arrays類復(fù)制
public string(char value[]) {
 this.value = arrays.copyof(value, value.length);
}
//從bytes數(shù)組中的offset位置開始,將長度為length的字節(jié),以charsetname格式編碼,拷貝到value
public string(byte bytes[], int offset, int length, string charsetname)
 throws unsupportedencodingexception {
 if (charsetname == null)
 throw new nullpointerexception("charsetname");
 checkbounds(bytes, offset, length);
 this.value = stringcoding.decode(charsetname, bytes, offset, length);
}
//調(diào)用public string(byte bytes[], int offset, int length, string charsetname)構(gòu)造函數(shù)
public string(byte bytes[], string charsetname)
 throws unsupportedencodingexception {
 this(bytes, 0, bytes.length, charsetname);
}

三、 string常用方法

1. equals

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
boolean equals(object anobject)
public boolean equals(object anobject) {
 //如果引用的是同一個(gè)對象,返回真
 if (this == anobject) {
 return true;
 }
 //如果不是string類型的數(shù)據(jù),返回假
 if (anobject instanceof string) {
 string anotherstring = (string) anobject;
 int n = value.length;
 //如果char數(shù)組長度不相等,返回假
 if (n == anotherstring.value.length) {
  char v1[] = value;
  char v2[] = anotherstring.value;
  int i = 0;
  //從后往前單個(gè)字符判斷,如果有不相等,返回假
  while (n-- != 0) {
  if (v1[i] != v2[i])
   return false;
  i++;
  }
  //每個(gè)字符都相等,返回真
  return true;
 }
 }
 return false;
}
?
1
2
3
string e1 = "good";
string e2 = "good everyday";
e1.equals(e2); // 返回 false

1 首先判斷是否 引用同一個(gè)對象 == 也就是判斷 這兩個(gè)引用的 內(nèi)存地址是否相同,如果相同 直接返回 true

2 會判斷是否類型 相同,是否是同一種數(shù)據(jù)類型

3 類型 相同 就會比較 轉(zhuǎn)換成的 字符 數(shù)組的長度 是否相同

4 從后往前 比較 每一個(gè)字符 是否 相同

判斷順序 =》 1.內(nèi)存地址 2.數(shù)據(jù)類型 3.字符數(shù)組長度 4.單個(gè)字符比較

2. compareto

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int compareto(string anotherstring)
public int compareto(string anotherstring) {
 //自身對象字符串長度len1
 int len1 = value.length;
 //被比較對象字符串長度len2
 int len2 = anotherstring.value.length;
 //取兩個(gè)字符串長度的最小值lim
 int lim = math.min(len1, len2);
 char v1[] = value;
 char v2[] = anotherstring.value;
 int k = 0;
 //從value的第一個(gè)字符開始到最小長度lim處為止,如果字符不相等,返回自身(對象不相等處字符-被比較對象不相等字符)
 while (k < lim) {
 char c1 = v1[k];
 char c2 = v2[k];
 if (c1 != c2) {
  return c1 - c2;
 }
 k++;
 }
 //如果前面都相等,則返回(自身長度-被比較對象長度)
 return len1 - len2;
}
?
1
2
3
4
5
6
string co1 = "hello" ;
string co2 = "hello";
string co3 = "hello you";
  
system.out.println(co1.compareto(co2)); // 0
system.out.println(co1.compareto(co3)); // -4

這個(gè)方法寫的很巧妙,先從0開始判斷字符大小。

如果兩個(gè)對象能比較字符的地方比較完了還相等,就直接返回自身長度減被比較對象長度,如果兩個(gè)字符串長度相等,則返回的是0,巧妙地判斷了三種情況。

3.hashcode

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int hashcode()
public int hashcode() {
 int h = hash;
 //如果hash沒有被計(jì)算過,并且字符串不為空,則進(jìn)行hashcode計(jì)算
 if (h == 0 && value.length > 0) {
  char val[] = value;
  //計(jì)算過程
  //s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
  for (int i = 0; i < value.length; i++) {
   h = 31 * h + val[i];
  }
  //hash賦值
  hash = h;
 }
 return h;
}
?
1
2
3
4
5
6
7
8
9
10
11
12
string a = "toyou";
char val[] = a.tochararray();
char c1 = 't';
char c2 = 'a';
int f = c1;
int e = c2;
   
system.out.println(e);   // 97 a
system.out.println(f);   // 116 t
system.out.println(31*val[0]); // 3596
system.out.println(31*c1);  // 3596
// hashcode 計(jì)算中 因?yàn)閏har 字符可以自動轉(zhuǎn)換成對應(yīng)的 int 整形

string類重寫了hashcode方法,object中的hashcode方法是一個(gè)native調(diào)用。

string類的hash采用多項(xiàng)式計(jì)算得來,我們完全可以通過不相同的字符串得出同樣的hash,所以兩個(gè)string對象的hashcode相同,并不代表兩個(gè)string是一樣的。

同一個(gè)string 對象 hashcode 一定相同, 但是 hashcode相同 ,不一定是同一個(gè)對象

4.startswith

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
boolean startswith(string prefix,int toffset)
public boolean startswith(string prefix, int toffset) {
 char ta[] = value;
 int to = toffset;
 char pa[] = prefix.value;
 int po = 0;
 int pc = prefix.value.length;
 // note: toffset might be near -1>>>1.
 //如果起始地址小于0或者(起始地址+所比較對象長度)大于自身對象長度,返回假
 if ((toffset < 0) || (toffset > value.length - pc)) {
  return false;
 }
 //從所比較對象的末尾開始比較
 while (--pc >= 0) {
  if (ta[to++] != pa[po++]) {
   return false;
  }
 }
 return true;
}
public boolean startswith(string prefix) {
 return startswith(prefix, 0);
}
public boolean endswith(string suffix) {
 return startswith(suffix, value.length - suffix.value.length);
}
?
1
2
3
4
string d = "www.58fxp.com";
   
 system.out.println(d.startswith("www")); // true
 system.out.println(d.endswith("com")); // true

起始比較和末尾比較都是比較經(jīng)常用得到的方法,例如在判斷一個(gè)字符串是不是http協(xié)議的,或者初步判斷一個(gè)文件是不是mp3文件,都可以采用這個(gè)方法進(jìn)行比較。

5.concat

?
1
2
3
4
5
6
7
8
9
10
11
12
string 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);
}
?
1
2
3
string cat = "much";
   
string newcat = cat.concat(" yes"); // much yes

concat方法也是經(jīng)常用的方法之一,它先判斷被添加字符串是否為空來決定要不要創(chuàng)建新的對象。

1 如果 拼接的字符 長度為0 直接返回 原字符對象

2 拼接的字符 不為空 返回 新的 字符對象

判斷字符長度 生成新對象

6.replace

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
string replace(char oldchar,char newchar)
public string replace(char oldchar, char newchar) {
 //新舊值先對比
 if (oldchar != newchar) {
  int len = value.length;
  int i = -1;
  char[] val = value; /* avoid getfield opcode */
  //找到舊值最開始出現(xiàn)的位置
  while (++i < len) {
   if (val[i] == oldchar) {
    break;
   }
  }
  //從那個(gè)位置開始,直到末尾,用新值代替出現(xiàn)的舊值
  if (i < len) {
   char buf[] = new char[len];
   for (int j = 0; j < i; j++) {
    buf[j] = val[j];
   }
   while (i < len) {
    char c = val[i];
    buf[i] = (c == oldchar) ? newchar : c;
    i++;
   }
   return new string(buf, true);
  }
 }
 return this;
}
?
1
2
3
4
string r1 = "how do you do";
   
string r2 = r1.replace("do","is");
system.out.println(r2); // how is you is

這個(gè)方法也有討巧的地方,例如最開始先找出舊值出現(xiàn)的位置,這樣節(jié)省了一部分對比的時(shí)間。

replace(string oldstr,string newstr)方法通過正則表達(dá)式來判斷。

7.trim

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
string trim()
public string trim() {
 int len = value.length;
 int st = 0;
 char[] val = value; /* avoid getfield opcode */
 //找到字符串前段沒有空格的位置
 while ((st < len) && (val[st] <= ' ')) {
  st++;
 }
 //找到字符串末尾沒有空格的位置
 while ((st < len) && (val[len - 1] <= ' ')) {
  len--;
 }
 //如果前后都沒有出現(xiàn)空格,返回字符串本身
 return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
?
1
2
3
4
5
6
7
8
9
string t1 = " public void "; // 前后各一個(gè)空格
   
system.out.println("t1:"+t1.length()); // 13 帶空格長度
   
string t2 = t1.trim();
   
system.out.println("t2:"+t2.length()); // 11 去掉空格
   
system.out.println(t2);

8.intern

?
1
2
string intern()
public native string intern();
?
1
string dd = new string("bb").intern();

ntern方法是native調(diào)用,它的作用是在方法區(qū)中的常量池里通過equals方法尋找等值的對象,

如果沒有找到則在常量池中開辟一片空間存放字符串并返回該對應(yīng)string的引用,否則直接返回常量池中已存在string對象的引用。

可以為new方法創(chuàng)建的 字符對象 也去強(qiáng)制查看常量池 是否已存在

將引言中第二段代碼

?
1
2
3
//string a = new string("ab1");
//改為
string a = new string("ab1").intern();

則結(jié)果為為真,原因在于a所指向的地址來自于常量池,而b所指向的字符串常量默認(rèn)會調(diào)用這個(gè)方法,所以a和b都指向了同一個(gè)地址空間。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
int hash32()
private transient int hash32 = 0;
int hash32() {
 int h = hash32;
 if (0 == h) {
  // harmless data race on hash32 here.
  h = sun.misc.hashing.murmur3_32(hashing_seed, value, 0, value.length);
  // ensure result is not zero to avoid recalcing
  h = (0 != h) ? h : 1;
  hash32 = h;
 }
 return h;
}

在jdk1.7中,hash相關(guān)集合類在string類作key的情況下,不再使用hashcode方式離散數(shù)據(jù),而是采用hash32方法。

這個(gè)方法默認(rèn)使用系統(tǒng)當(dāng)前時(shí)間,string類地址,system類地址等作為因子計(jì)算得到hash種子,通過hash種子在經(jīng)過hash得到32位的int型數(shù)值。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public int length() {
 return value.length;
}
public string tostring() {
 return this;
}
public boolean isempty() {
 return value.length == 0;
}
public char charat(int index) {
 if ((index < 0) || (index >= value.length)) {
  throw new stringindexoutofboundsexception(index);
 }
 return value[index];
}

以上是一些簡單的常用方法。

總結(jié)

string對象是不可變類型,返回類型為string的string方法每次返回的都是新的string對象,除了某些方法的某些特定條件返回自身。

string對象的三種比較方式:

==內(nèi)存比較:直接對比兩個(gè)引用所指向的內(nèi)存值,精確簡潔直接明了。

equals字符串值比較:比較兩個(gè)引用所指對象字面值是否相等。

hashcode字符串?dāng)?shù)值化比較:將字符串?dāng)?shù)值化。兩個(gè)引用的hashcode相同,不保證內(nèi)存一定相同,不保證字面值一定相同。

字符串常量池的設(shè)計(jì)思想

一.字符串常量池設(shè)計(jì)初衷

每個(gè)字符串都是一個(gè)string對象,系統(tǒng)開發(fā)中將會頻繁使用字符串,如果像其他對像那樣創(chuàng)建銷毀將極大影響程序的性能。

jvm為了提高性能和減少內(nèi)存開銷,在實(shí)例化字符串的時(shí)候進(jìn)行了優(yōu)化

為字符串開辟了一個(gè)字符串常量池,類似于緩存區(qū)

創(chuàng)建字符串常量時(shí),首先判斷字符串常量池是否存在該字符串

存在該字符串返回引用實(shí)例,不存在,實(shí)例化字符串,放入池中

實(shí)現(xiàn)基礎(chǔ)

實(shí)現(xiàn)該優(yōu)化的基礎(chǔ)是每個(gè)字符串常量都是final修飾的常量,不用擔(dān)心常量池存在數(shù)據(jù)沖突

運(yùn)行時(shí)實(shí)例創(chuàng)建的全局字符串常量池中有一個(gè)表,總是為池中每個(gè)唯一的字符串對象維護(hù)一個(gè)引用,這就意味著它們一直引用著字符串常量池中的對象,所以,在常量池中的這些字符串不會被垃圾收集器回收

堆、棧、方法區(qū)

了解字符串常量池,首先看一下 堆棧方法區(qū)

java String源碼和String常量池的全面解析

存儲的是對象,每個(gè)對象都包含一個(gè)與之對應(yīng)的class

jvm只存在一個(gè)堆區(qū),被所有線程共享,堆中不存在基本類型和對象引用,只存在對象本身

對象由垃圾回收器負(fù)責(zé)回收,因此大小和生命周期不需要確定

每個(gè)線程都包含一個(gè)棧區(qū),棧區(qū)只存放基礎(chǔ)數(shù)據(jù)類型對象和自定義對象引用

每個(gè)棧中的數(shù)據(jù)(原始類型和對象引用)都是私有的

棧分為三個(gè)部分,基本類型變量區(qū)、執(zhí)行環(huán)境上下文、操作指令區(qū)(存放操作指令)

數(shù)據(jù)大小和生命周期是可以確定的,當(dāng)沒有引用指向這個(gè)數(shù)據(jù)時(shí),這個(gè)數(shù)據(jù)就會消失

方法區(qū)

靜態(tài)區(qū),跟堆一樣,被所有的線程共享

方法區(qū)包含的都是在整個(gè)程序中永遠(yuǎn)唯一的元素,如class、static變量;

字符串常量池

字符串常量池存在于方法區(qū)

代碼:堆棧方法區(qū)存儲字符串

?
1
2
3
4
5
string str1 = “abc”;
string str2 = “abc”;
string str3 = “abc”;
string str4 = new string(“abc”);
string str5 = new string(“abc”);

java String源碼和String常量池的全面解析

面試題

string str4 = new string(“abc”) 創(chuàng)建多少個(gè)對象?

拆分: str4 = 、 new string()、"abc"

通過new 可以創(chuàng)建一個(gè)新的對象,new 方法創(chuàng)建實(shí)例化對象不會去常量池尋找是否已存在,只要new 都會實(shí)例化一個(gè)新的對象出來

"abc"每個(gè)字符串 都是一個(gè)string 對象,如果常量池中沒有則會創(chuàng)建一個(gè)新對象放入常量池,否則返回對象引用

將對象地址賦值給str4,創(chuàng)建一個(gè)引用

所以,常量池中沒有“abc”字面量則創(chuàng)建兩個(gè)對象,否則創(chuàng)建一個(gè)對象,以及創(chuàng)建一個(gè)引用

string str1 = new string("a"+"b") ; 會創(chuàng)建多少個(gè)對象? string str2 = new string("abc") + "abc" ; 會創(chuàng)建多少個(gè)對象?

以上這篇java string源碼和string常量池的全面解析就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。

原文鏈接:http://www.cnblogs.com/NiceCui/p/8046564.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日韩久久影院 | 日产中文乱码卡一卡二 | 婚色阿花在线全文免费笔 | 国产精品日韩在线观看 | 免费一级片在线观看 | java hd国产高清 | 精品视频在线观看 | 精品综合在线 | 欧美成人aa久久狼窝动画 | 九九影院午夜理论片无码 | 男人躁女人p | 亚洲午夜精品久久久久久人妖 | 国产亚洲女人久久久久久 | 午夜在线观看免费完整直播网 | 大陆国语自产精品视频在 | 国产一区二区三区高清 | 非洲黑人又大粗gay 非洲黑人bbwbbwbbw | 秋霞午夜 | 石原莉奈被店长侵犯免费 | 色综合亚洲精品激情狠狠 | 亚洲国产精品网站久久 | 免费国产在线视频 | 东北美女野外bbwbbw免费 | 15一16japanese破| 国产免费美女视频 | 午夜国产精品影院在线观看 | 国产成人精品在线 | a男人的天堂久久a毛片 | heyzo1754北岛玲在线视频 | 福利一区在线观看 | 经典欧美gifxxoo动态图暗网 | 超h 超重口 高h 污肉1v1 | 精品视频免费在线观看 | 美女草b| 和老外3p爽粗大免费视频 | 99久久免费国内精品 | 成人免费毛片一区二区三区 | 国产绿帽| 免费一级毛片在线播放 | 久久精品热在线观看85 | 日本人泡妞18xxⅹ |