中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

網(wǎng)站建設(shè)中素材臺(tái)州seo排名優(yōu)化

網(wǎng)站建設(shè)中素材,臺(tái)州seo排名優(yōu)化,增城網(wǎng)站建設(shè)價(jià)格,隱私空間1.🥗String類簡(jiǎn)介 在我們寫(xiě)代碼的時(shí)候,String總是充斥著前前后后。 但你會(huì)不會(huì)經(jīng)常力不從心, “這個(gè)*** 字符串怎么** 轉(zhuǎn)換不成功啊” “*** 這個(gè)字符串到底是常量還是對(duì)象啊” “這*** 字符串內(nèi)存結(jié)構(gòu)到底* * * 是什么啊” “為啥我的字符串…

1.🥗String類簡(jiǎn)介

在我們寫(xiě)代碼的時(shí)候,String總是充斥著前前后后。

但你會(huì)不會(huì)經(jīng)常力不從心,

“這個(gè)*** 字符串怎么** 轉(zhuǎn)換不成功啊”

“*** 這個(gè)字符串到底是常量還是對(duì)象啊”

“這*** 字符串內(nèi)存結(jié)構(gòu)到底* * * 是什么啊”

“為啥我的字符串?dāng)?shù)組莫名其妙顯示空指針異常啊”

“字符串常量和字符串對(duì)象應(yīng)該怎么比較啊”

“這字符串?dāng)?shù)據(jù)不都是good嘛,怎么比一比又是false了***

“ 字符串怎么轉(zhuǎn)化成int啊啊“

”String類為啥是final修飾的捏?“

”String?StringBuffer??StringBuilder???“

A:”什么?!String不是基本數(shù)據(jù)類型??它還有什么是我不知道的?“

B:”樓上的,你猜一猜String能被繼承嘛?“

A:".....****"

哈哈,知識(shí)點(diǎn)看起來(lái)又多又雜,但我們好好縷一縷,它還是很好理解的。
在C語(yǔ)言中我們涉及到過(guò)字符串,但是在C語(yǔ)言中要表示字符串只能用字符數(shù)組或者字符指針,可以使用標(biāo)準(zhǔn)庫(kù)提供的字符串系列函數(shù)完成大部分操作,但是這種將數(shù)據(jù)和操作數(shù)據(jù)方法分離開(kāi)的方法不符合面向?qū)ο蟮乃枷?#xff0c;而字符串的應(yīng)用又非常廣泛,從上面的對(duì)話中就可以發(fā)現(xiàn),哈哈哈。
因此Java語(yǔ)言專門(mén)提供了String類
我們不論是在寫(xiě)代碼的時(shí)候,還是在校招的筆試當(dāng)中,字符串都是常客。
面試也會(huì)經(jīng)常問(wèn)到關(guān)于String的問(wèn)題

面試官:來(lái)來(lái)小伙子,簡(jiǎn)單背一下String類的源碼呢??小伙子:...我* 你****,(直接丟offer而去)

那么我們就帶著對(duì)它的一些問(wèn)題繼續(xù)往下看

2.🍗 String常用方法和性質(zhì)

2.1 🥩 字符串的構(gòu)造

String類提供給我們的構(gòu)造方式很多,最常見(jiàn)的有以下三種

//使用常量串來(lái)構(gòu)造
String s1 = "hello Gremmie";
System.out.println(s1);
//直接new出String對(duì)象
String s2 = new String("hello 葛玉禮");
System.out.println(s2);
//使用字符數(shù)組進(jìn)行構(gòu)造
char[] array = {'h','e','l','l','o',' ','玉','米'};
String s3 = new String(array);
System.out.println(s3);

🍲?[注意]:

  1. String是引用類型,內(nèi)部其實(shí)并不存儲(chǔ)字符串本身,在String類的實(shí)現(xiàn)源碼中,String類實(shí)例變量是這樣的👇
public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence,Constable, ConstantDesc {@Stableprivate final byte[] value;
//字符串實(shí)際上就存儲(chǔ)在這個(gè)用final修飾的byte數(shù)組中private final byte coder;/** Cache the hash code for the string */private int hash; // Default to 0
/*** @author Gremmie102* @date 2022/4/21 15:49* @purpose : 比較字符串引用*/
public class StringTestDemo1 {public static void main(String[] args) {//s1和s2引用的是不同的對(duì)象,s1和s3引用的是同一個(gè)對(duì)象String s1 = new String("hello");String s2 = new String("world");String s3 = s1;System.out.println(s1.length());//獲取字符串長(zhǎng)度--輸出5System.out.println(s1.isEmpty());//判斷字符串長(zhǎng)度是否為0,即是否為空}
}
  1. 在Java中用雙引號(hào)""引起來(lái)的也是String類型對(duì)象

//打印"hello"字符串(String對(duì)象)的長(zhǎng)度 System.out.println("hello".length);

2.2 🍙String對(duì)象的比較

字符串的比較是我們常用的操作,比如:字符串排序

  1. ==比較是否引用同一個(gè)對(duì)象

這里要注意的是,對(duì)于內(nèi)置類型(基礎(chǔ)數(shù)據(jù)),==比較的是變量中的值,對(duì)于引用類型==比較的是引用中的地址

public static void main(String[] args) {int a = 10;int b = 20;int c = 10;//對(duì)于基本類型變量,==比較兩個(gè)變量中存儲(chǔ)的值是否相同System.out.println(a==b);//falseSystem.out.println(a==c);//true//對(duì)于引用型變量,==比較兩個(gè)引用變量引用的是否為同一個(gè)對(duì)象String s1 = new String("hello");String s2 = new String("hello");String s3 = new String("world");String s4 = s1;System.out.println(s1==s2);//falseSystem.out.println(s2==s3);//falseSystem.out.println(s1==s4);//true}
  1. boolean equals(Object object)

按照字符大小的順序比較
String重寫(xiě)了父類Object中的equals方法,Object中equals默認(rèn)會(huì)按照==來(lái)比較,String重寫(xiě)equals方法之后,語(yǔ)法規(guī)則為:s1.equals(s2)
我們看看源碼

用代碼來(lái)給大家演示解釋一下👇

 public boolean equals(Object anObject){//1.先檢測(cè)this和anObject是否為同一個(gè)對(duì)象比較,如果是的化則返回trueif(this == anObject){return true;}//2.檢測(cè)anObject是否為String類型的對(duì)象,如果是的話那就繼續(xù)比較//不是的話那就返回falseif (anObject instanceof String){String anotherString = (String)anObject;int n = value.length;//3.判斷調(diào)用對(duì)象的this和anObject兩個(gè)字符串的長(zhǎng)度是否相同if (n == anotherString.value.length){char v1[] = value;char v2[] = anotherString.value;int i = 0;//4.按照字典順序,從前往后逐個(gè)字符進(jìn)行比較while(n-- != 0){if (v1[i] != v2[i])return false;i++;}return true;}}return false;}

然后我們?cè)賹?xiě)一個(gè)main方法來(lái)實(shí)驗(yàn)一下

    public static void main(String[] args) {String s1 = new String("hello");String s2 = new String("hello");String s3 = new String("HELLO");//s1,s2,s3引用的是三個(gè)不同的對(duì)象,因此==的比較結(jié)果都是falseSystem.out.println(s1 == s2);//falseSystem.out.println(s1 == s3);//false//equals比較:String對(duì)象中的逐個(gè)字符//雖然s1和s2引用的不是同一個(gè)對(duì)象,但是兩個(gè)對(duì)象中放置的內(nèi)容,所以輸出true//s1與s3引用的不是同一個(gè)對(duì)象,而且兩個(gè)對(duì)象中內(nèi)容也不同,因此輸出falseSystem.out.println(s1.equals(s2));//trueSystem.out.println(s1.equals(s3));//false}
  1. int compareTo(String s)方法:按照字典序進(jìn)行比較

與equals不同的是,equals返回的是boolean類型,而compareTo返回的是int類型。
具體比較方法為:

  1. 先按照字典順序大小比較,如果出現(xiàn)了不等的字符,則直接返回這兩個(gè)字符的大小差值
  2. 如果前k個(gè)字符相等(k為兩個(gè)字符長(zhǎng)度最小值)返回值為兩個(gè)字符串的長(zhǎng)度差值

源碼如下👇

測(cè)試代碼如下👇

    public static void main(String[] args) {String s1 = new String("abc");String s2 = new String("ac");String s3 = new String("abc");String s4 = new String("abcdef");System.out.println(s1.compareTo(s2));//不同,輸出字符差值-1System.out.println(s1.compareTo(s3));//相同輸出0System.out.println(s1.compareTo(s4));//前k個(gè)字符完全相同,輸出長(zhǎng)度差值-3}
  1. int compareToIgnoreCase(String str)方法:與compareTo方式相同,但是忽略大小寫(xiě)比較

源碼👇

public static void main(String[] args) {String s1 = new String("abc");String s2 = new String("ac");String s3 = new String("Abc");String s4 = new String("abcdef");System.out.println(s1.compareToIgnoreCase(s2));//不同,輸出字符差值-1System.out.println(s1.compareToIgnoreCase(s3));//相同,輸出0System.out.println(s1.compareToIgnoreCase(s4));//前k個(gè)字符完全相同,輸出長(zhǎng)度差-3}

2.3 🍱 字符串查找

這是一個(gè)超級(jí)實(shí)用的功能,String類中提供了一些常用的查找方法:

**方法 **** 功能 **
char charAt(int index)返回index位置上字符,如果index為負(fù)數(shù)或者越界,拋出 IndexOutOfBoundsException異常
int indexOf(int ch)返回ch第一次出現(xiàn)的位置,沒(méi)有返回-1
int indexOf(int ch, int fromIndex)從fromIndex位置開(kāi)始找ch第一次出現(xiàn)的位置,沒(méi)有返回-1
int indexOf(String str)返回str第一次出現(xiàn)的位置,沒(méi)有返回-1
int indexOf(String str, int fromIndex)從fromIndex位置開(kāi)始找str第一次出現(xiàn)的位置,沒(méi)有返回-1
int lastIndexOf(int ch)從后往前找,返回ch第一次出現(xiàn)的位置,沒(méi)有返回-1
int lastIndexOf(int ch, int fromIndex)從fromIndex位置開(kāi)始找,從后往前找ch第一次出現(xiàn)的位置,沒(méi)有返 回-1
int lastIndexOf(String str)從后往前找,返回str第一次出現(xiàn)的位置,沒(méi)有返回-1
int lastIndexOf(String str, int fromIndex)從fromIndex位置開(kāi)始找,從后往前找str第一次出現(xiàn)的位置,沒(méi)有返回-1

這里大家要注意的是,上面表格中有 int ch 的參數(shù),是char類型傳進(jìn)來(lái)強(qiáng)轉(zhuǎn)換成int值,以此來(lái)方便程序進(jìn)行,并不是說(shuō)這個(gè)字符就是一個(gè)數(shù)哦

public static void main(String[] args) {
String s = "aaabbbcccaaabbbccc";
System.out.println(s.charAt(3)); // 'b'
System.out.println(s.indexOf('c')); // 6
System.out.println(s.indexOf('c', 10)); // 15
System.out.println(s.indexOf("bbb")); // 3
System.out.println(s.indexOf("bbb", 10)); // 12
System.out.println(s.lastIndexOf('c')); // 17
System.out.println(s.lastIndexOf('c', 10)); // 8
System.out.println(s.lastIndexOf("bbb")); // 12
System.out.println(s.lastIndexOf("bbb", 10)); // 3

2.4 🥡 字符串的轉(zhuǎn)化

我們?cè)谧鲱}的時(shí)候,字符串的轉(zhuǎn)化也是非常重要的,主要有以下幾個(gè)

  1. 數(shù)值和字符串轉(zhuǎn)化
public static void main(String[] args) {// 數(shù)字轉(zhuǎn)字符串String s1 = String.valueOf(1234);String s2 = String.valueOf(12.34);String s3 = String.valueOf(true);String s4 = String.valueOf(new Student("Hanmeimei", 18));System.out.println(s1);System.out.println(s2);System.out.println(s3);System.out.println(s4);System.out.println("=================================");// 字符串轉(zhuǎn)數(shù)字// 注意:Integer、Double等是Java中的包裝類型,這個(gè)后面會(huì)講到int data1 = Integer.parseInt("1234");double data2 = Double.parseDouble("12.34");System.out.println(data1);System.out.println(data2);
}
  1. 大小寫(xiě)轉(zhuǎn)換
public static void main(String[] args) {String s1 = "hello";String s2 = "HELLO";// 小寫(xiě)轉(zhuǎn)大寫(xiě)System.out.println(s1.toUpperCase());// 大寫(xiě)轉(zhuǎn)小寫(xiě)System.out.println(s2.toLowerCase());
}
  1. 字符串轉(zhuǎn)數(shù)組
public static void main(String[] args) {
String s = "hello";
// 字符串轉(zhuǎn)數(shù)組
char[] ch = s.toCharArray();
for (int i = 0; i < ch.length; i++) {
System.out.print(ch[i]);
}
System.out.println();
// 數(shù)組轉(zhuǎn)字符串
String s2 = new String(ch);
System.out.println(s2);
}

個(gè)人覺(jué)得這個(gè)方法是非常實(shí)用的!!

  1. String格式化輸入
public static void main(String[] args) {
String s = String.format("%d-%d-%d", 2019, 9,14);
System.out.println(s);
}

2.5 🥟 字符串替換

那么在做一些算法題目時(shí),我們需要替換字符串中的內(nèi)容。
好家伙,直接上手利用toCharArray然后幾個(gè)循環(huán)
沒(méi)必要!
我們使用一個(gè)指定的新的字符串替換掉已有的字符串?dāng)?shù)據(jù),可用的方法如下:

方法功能
String replaceAll(String regex, String replacement)替換所有的指定內(nèi)容
String replaceFirst(String regex, String replacement)替換首個(gè)內(nèi)容
String str = "helloworld" ;
System.out.println(str.replaceAll("l", "_"));
System.out.println(str.replaceFirst("l", "_"));
//輸出:
//he__owor_d
//he_loworld

🌭?【注意】:由于字符串是不可變的對(duì)象,替換不修改當(dāng)前字符串,而是產(chǎn)生一個(gè)新的字符串

2.6 🫕 字符串的拆分

我們可以利用split將一個(gè)完整的字符串按照指定的分隔符劃分為若干個(gè)子字符串

方法功能
String[] split(String regex)將字符串全部拆分
String[] split(String regex,int limit)將字符串以指定的格式,拆分為limit組

代碼1👇實(shí)現(xiàn)字符串的拆分

String str = "hello world hello Gremmie" ;
String[] result = str.split(" ") ; // 按照空格拆分
for(String s: result){System.out.println(s);
}

代碼2 👇字符串的部分拆分

public static void main(String[] args) {String str = "hello world hello Gremmie" ;String[] result = str.split(" ",2) ;for(String s: result) {System.out.println(s);}}

運(yùn)行結(jié)果👇

拆分是特別常用的操作,一定要牢牢記住。 有一些特殊字符作為分隔符的話可能無(wú)法正確區(qū)分,需要加上轉(zhuǎn)移符號(hào)

代碼3👇拆分IP地址

    public static void main(String[] args) {String str = "106.14.57.10" ;String[] result = str.split("\\.") ;for(String s: result) {System.out.println(s);}}

運(yùn)行結(jié)果👇

🍫?【注意事項(xiàng)】:

  1. 字符"|","","+"*都得加上轉(zhuǎn)義字符,前面加上?""?.
  2. 而如果是?""?,那么就得寫(xiě)成?"\"?.
  3. 如果一個(gè)字符串中有多個(gè)分隔符,可以用"|"作為連字符.

代碼4👇多次拆分

public static void main(String[] args) {String str = "name=Gremmie&age=19" ;String[] result = str.split("&") ;for (int i = 0; i < result.length; i++) {String[] temp = result[i].split("=") ;System.out.println(temp[0]+" = "+temp[1]);}}

運(yùn)行結(jié)果👇

2.7 🥣 字符串的截取

現(xiàn)在我們遇到一種情況,你已經(jīng)知道了索引,想要截取一段字符串 這個(gè)時(shí)候,你是不是又想上手循環(huán)了? 慢著!substring可以滿足你!😋

方法功能
String substring(int beginIndex)從指定索引截取到結(jié)尾
String substring(int beginIndex, int endIndex)截取部分的內(nèi)容

代碼👇字符串的截取

public static void main(String[] args) {String str = "helloGremmie" ;System.out.println(str.substring(5));//是從下標(biāo)為5截到最后的System.out.println(str.substring(0, 5));//下標(biāo)索引范圍左閉右開(kāi),[0,5)}

🍟?注意事項(xiàng):

  1. 索引要從0開(kāi)始
  2. 要注意前閉后開(kāi)的規(guī)定,substring(0,5)表示包含0號(hào)下標(biāo)的字符,不包含5號(hào)下標(biāo)

2.8 🍰 字符串前后的空白刪除

String中的trim()方法用來(lái)去掉字符串中的左右兩個(gè)空格,保留中間的空格

代碼示例👇觀察trim()方法的使用

public static void main(String[] args) {String str = "   hello Gremmie  " ;System.out.println("["+str+"]");System.out.println("["+str.trim()+"]");}

運(yùn)行結(jié)果👇

trim 會(huì)去掉字符串開(kāi)頭和結(jié)尾的空白字符(空格, 換行, 制表符等).

2.9🥫字符串常量池

字符串常量池實(shí)現(xiàn)的前提條件就是Java中String對(duì)象是不可變的,這樣可以安全保證多個(gè)變量共享同一個(gè)對(duì)象。 如果Java中的String對(duì)象可變的話,一個(gè)引用操作改變了對(duì)象的值,那么其他的變量也會(huì)受到影響,顯然這樣是不合理的。 在JDK6.0及之前版本,字符串常量池存放在方法區(qū)中在JDK7.0版本以后,字符串常量池被移到了堆中了。至于為什么移到堆內(nèi),大概是由于方法區(qū)的內(nèi)存空間太小了

2.9.1 🥗 創(chuàng)建對(duì)象(一)

我們先來(lái)看看下面這段代碼,創(chuàng)建String對(duì)象的方式是相同的嘛?

public static void main(String[] args) {String s1 = "hello";String s2 = "hello";String s3 = new String("hello");String s4 = new String("hello");System.out.println(s1 == s2); // trueSystem.out.println(s1 == s3); // falseSystem.out.println(s3 == s4); // false
}

上述程序創(chuàng)建方式類似,為什么s1和s2引用的是同一個(gè)對(duì)象,而s3和s4不是呢? 在Java程序中,類似于:1, 2, 3,3.14,“hello”等字面類型的常量經(jīng)常頻繁使用,為了使程序的運(yùn)行速度更快、 更節(jié)省內(nèi)存,Java為8種基本數(shù)據(jù)類型和String類都提供了常量池。

在系統(tǒng)設(shè)計(jì)中,我們嘗嘗會(huì)使用到”池”的概念。Eg:數(shù)據(jù)庫(kù)連接池,socket連接池,線程池,組件隊(duì)列。”池”可以節(jié)省對(duì)象重復(fù)創(chuàng)建和初始化所耗費(fèi)的時(shí)間。對(duì)那些被系統(tǒng)頻繁請(qǐng)求和使用的對(duì)象,使用此機(jī)制可以提高系統(tǒng)運(yùn)行性能。 ”池”是一種”以空間換時(shí)間”的做法,我們?cè)趦?nèi)存中保存一系列整裝待命的對(duì)象,供人隨時(shí)差遣。與系統(tǒng)效率相比,這些對(duì)象所占用的內(nèi)存空間太微不足道了。

🍳class文件常量池

我們寫(xiě)的每一個(gè)Java類被編譯后,就會(huì)形成一份class文件;class文件中除了包含類的版本、字段、方法、接口等描述信息外,還有一項(xiàng)信息就是常量池(constant pool table),用于存放編譯器生成的各種 字面量 (Literal)和 符號(hào)引用 (Symbolic References),每個(gè)class文件都有一個(gè)class常量池。 其中 字面量 包括:

  1. 文本字符串
  2. 2.八種基本類型的值
  3. 3.被聲明為final的常量等;

符號(hào)引用 包括:

  1. 類和方法的全限定名
  2. 字段的名稱和描述符
  3. 方法的名稱和描述符。

🥯運(yùn)行時(shí)常量池

運(yùn)行時(shí)常量池存在于內(nèi)存中,也就是class常量池被加載到內(nèi)存之后的版本,是方法區(qū)的一部分。不同之處是:它的字面量可以動(dòng)態(tài)的添加(String類的intern()),符號(hào)引用可以被解析為直接引用。 JVM在執(zhí)行某個(gè)類的時(shí)候,必須經(jīng)過(guò)加載、連接、初始化,而連接又包括驗(yàn)證、準(zhǔn)備、解析三個(gè)階段。而當(dāng)類加載到內(nèi)存中后,jvm就會(huì)將class常量池中的內(nèi)容存放到運(yùn)行時(shí)常量池中,由此可知,運(yùn)行時(shí)常量池也是每個(gè)類都有一個(gè)。在解析階段,會(huì)把符號(hào)引用替換為直接引用,解析的過(guò)程會(huì)去查詢字符串常量池,也就是我們下面要說(shuō)的StringTable,以保證運(yùn)行時(shí)常量池所引用的字符串與字符串常量池中是一致的。

2.9.2 🐳 字符串常量池(StringTable)

在HotSpot VM里實(shí)現(xiàn)的string pool功能的是一個(gè)StringTable類,它是一個(gè)Hash表,默認(rèn)值大小長(zhǎng)度是1009;這個(gè)StringTable在每個(gè)HotSpot VM的實(shí)例只有一份,被所有的類共享。字符串常量由一個(gè)一個(gè)字符組成,放在了StringTable上。 在JDK6.0中,StringTable的長(zhǎng)度是固定的,長(zhǎng)度就是1009,因此如果放入String Pool中的String非常多,就會(huì)造成hash沖突,導(dǎo)致鏈表過(guò)長(zhǎng),當(dāng)調(diào)用String#intern()時(shí)會(huì)需要到鏈表上一個(gè)一個(gè)找,從而導(dǎo)致性能大幅度下降;在JDK7.0中,StringTable的長(zhǎng)度可以通過(guò)參數(shù)指定。

JDK版本字符串常量池位置大小設(shè)置
Java6方法區(qū)固定:1009
Java7可設(shè)置,無(wú)大小限制,默認(rèn)60013
Java8可設(shè)置,有限制,最小1009

總結(jié)🍔 :

  1. 單獨(dú)使用””引號(hào)創(chuàng)建的字符串都是常量,編譯期就已經(jīng)確定存儲(chǔ)到Constant Pool中
  2. 使用new String(“”)創(chuàng)建的對(duì)象會(huì)存儲(chǔ)到heap中,是運(yùn)行期新創(chuàng)建的;
  3. 使用只包含常量的字符串連接符如"aa" + "aa"創(chuàng)建的也是常量,編譯期就能確定,已經(jīng)確定存儲(chǔ)到String Pool中,String pool中存有“aaaa”;但不會(huì)存有“aa”。
  4. 使用包含變量的字符串連接符如”aa” + s1創(chuàng)建的對(duì)象是運(yùn)行期才創(chuàng)建的,存儲(chǔ)在heap中;只要s1是變量,不論s1指向池中的字符串對(duì)象還是堆中的字符串對(duì)象,運(yùn)行期s1 + “aa”操作實(shí)際上是編譯器創(chuàng)建了StringBuilder對(duì)象進(jìn)行了append操作后通過(guò)toString()返回了一個(gè)字符串對(duì)象存在heap上。
  5. String s2 = “aa” + s1; String s3 = “aa” + s1; 這種情況,雖然s2,s3都是指向了使用包含變量的字符串連接符如”aa” + s1創(chuàng)建的存在堆上的對(duì)象,并且都是s1 + “aa”。但是卻指向兩個(gè)不同的對(duì)象,兩行代碼實(shí)際上在堆上new出了兩個(gè)StringBuilder對(duì)象來(lái)進(jìn)行append操作。在Thinking in java一書(shū)中285頁(yè)的例子也可以說(shuō)明。
  6. 對(duì)于final String s2 = “111”。s2是一個(gè)用final修飾的變量,在編譯期已知,在運(yùn)行s2+”aa”時(shí)直接用常量“111”來(lái)代替s2。所以s2+”aa”等效于“111”+ “aa”。在編譯期就已經(jīng)生成的字符串對(duì)象“111aa”存放在常量池中。

🍔?注意:

  1. 在JVM中字符串常量池只有一份,是全局共享的
  2. 剛開(kāi)始字符串常量池是空的,隨著程序不斷運(yùn)行,字符串常量池中元素會(huì)越來(lái)越多
  3. 當(dāng)類加載時(shí),字節(jié)碼文件中的常量池也被加載到JVM中,稱為運(yùn)行時(shí)常量池,同時(shí)會(huì)將其中的字符串常量保存在字符串常量池中
  4. 字符創(chuàng)常量池中的內(nèi)容:一部分來(lái)自運(yùn)行時(shí)常量池,一部分來(lái)自程序動(dòng)態(tài)添加

2.9.3 🦐 創(chuàng)建對(duì)象(二)

創(chuàng)建一個(gè)字符串的過(guò)程👇

String str = new String(“abc") 首先定義一個(gè)str的String類型的引用并存放在棧中 先在字符串常量池中找到該常量是否存在 如果存在則創(chuàng)建一個(gè)引用即可,則在字符串常量池中創(chuàng)建一個(gè)內(nèi)容為"abc"的字符串對(duì)象。 執(zhí)行new操作,在堆中創(chuàng)建一個(gè)指定的對(duì)象"abc",這里堆的對(duì)象是字符串常量池“abc”對(duì)象的一個(gè)拷貝對(duì)象 讓str指向堆中“abc”這個(gè)對(duì)象(也就是存儲(chǔ)這個(gè)對(duì)象的在堆中的地址)

?

?

在字節(jié)碼文件加載的時(shí)候,先要將.Class文件中的常量池加載到內(nèi)存中稱為運(yùn)行時(shí)常量池,此時(shí)也會(huì)將"hello"字符串保存到字符串常量池當(dāng)中

?

說(shuō)明:

  1. 在字節(jié)碼文件加載的時(shí)候,"hello"和"world"就已經(jīng)創(chuàng)建好了,并保存在字符串常量池當(dāng)中
  2. 上圖是代表直接用字符串常量進(jìn)行賦值,即:
public static void main(String[] args) {String s1 = "hello";String s2 = "hello";System.out.println(s1 == s2); // true
}
  1. 當(dāng)使用String s1 = "hello";創(chuàng)建對(duì)象的時(shí)候,先要在字符串常量池當(dāng)中找,如果找到就將該字符串引用賦值給s1

🥨 intern方法

intern 是一個(gè)native方法(Native方法指:底層使用C++實(shí)現(xiàn)的,看不到其實(shí)現(xiàn)的源代碼),該方法的作用是手動(dòng)將創(chuàng)建的String對(duì)象添加到常量池中。

public static void main(String[] args) {char[] ch = new char[]{'a', 'b', 'c'};String s1 = new String(ch); // s1對(duì)象并不在常量池中//s1.intern(); //調(diào)用之后,會(huì)將s1對(duì)象的引用放入到常量池中String s2 = "abc"; // "abc" 在常量池中存在了,s2創(chuàng)建時(shí)直接用常量池中"abc"的引用System.out.println(s1 == s2);
}
// 輸出false
// 用intern之后,就會(huì)輸出true

不同版本的Java會(huì)有不同的Intern來(lái)實(shí)現(xiàn)

🍜 面試題

請(qǐng)解釋String類當(dāng)中兩種對(duì)象實(shí)例化的區(qū)別?JDK1.8中

  1. String str = "hello"

只會(huì)開(kāi)辟一塊堆內(nèi)存空間,保存在字符串常量池中,然后str共享常量池中的String對(duì)象

2.?String str = new String("hello")

會(huì)開(kāi)辟兩塊堆內(nèi)存空間,字符串"hello"保存在字符串常量池中,然后用常量池中的String對(duì)象給新開(kāi)辟的String對(duì)象賦值。

3.?String str = new String(new char[]{'h', 'e', 'l', 'l', 'o'})

先在堆上創(chuàng)建一個(gè)String對(duì)象,然后利用copyof將重新開(kāi)辟數(shù)組空間,將參數(shù)字符串?dāng)?shù)組中內(nèi)容拷貝到String對(duì)象中

2.10 🦑 字符串的不可變性

String是一種不可變對(duì)象. 字符串中的內(nèi)容是不可改變的。

不可變性的體現(xiàn)在: 當(dāng)對(duì)字符串重新賦值時(shí)我們需要重新制定一個(gè)內(nèi)存區(qū)域,然后才能賦值,不能對(duì)原有的value進(jìn)行賦值(也就是我們不能改變?cè)械膙alue) 這里其實(shí)就是我們每當(dāng)有一個(gè)對(duì)象就有一次給value賦值的機(jī)會(huì),這次機(jī)會(huì)用了之后,也就是賦值之后,就不可以再改變了 所以我們?cè)谶@里說(shuō),字符串的值一旦給定就不可以再修改,一旦嘗試修改,就會(huì)創(chuàng)建一個(gè)新的字符串對(duì)象,讓這個(gè)新的字符串去接收你想要成為的值。

  1. String類在設(shè)計(jì)時(shí)就是不可改變的,String類實(shí)現(xiàn)描述中已經(jīng)說(shuō)明了

String類中的字符實(shí)際保存在內(nèi)部維護(hù)的value字符數(shù)組中,該圖還可以看出:

  1. String類被final修飾,表明該類不能被繼承
  2. value被修飾被final修飾,表明value自身的值不能改變,即不能引用其它字符數(shù)組,但是其引用空間中的內(nèi)容可以修改。
  1. 所有涉及到可能修改字符串內(nèi)容的操作都是創(chuàng)建一個(gè)新對(duì)象,改變的是新對(duì)象

它是需要新創(chuàng)建一個(gè)對(duì)象再對(duì)其進(jìn)行修改的,并不是在原有的基礎(chǔ)上進(jìn)行修改

有些人說(shuō):字符串不可變是因?yàn)槠鋬?nèi)部保存字符的數(shù)組被final修飾了,因此不能改變。 這種說(shuō)法是錯(cuò)誤的,不是因?yàn)镾tring類自身,或者其內(nèi)部value被final修飾而不能被修改。?final修飾類表明該類不想被繼承,final修飾引用類型表明該引用變量不能引用其他對(duì)象,但是其引用對(duì)象中的內(nèi)容是可以修改的。

public static void main(String[] args) {final int array[] = {1,2,3,4,5};array[0] = 100;System.out.println(Arrays.toString(array));
// array = new int[]{4,5,6}; // 編譯報(bào)錯(cuò):Error:(19, 9) java: 無(wú)法為最終變量array分配值
}

為什么 String 要涉及成不可變的?(不可變對(duì)象的好處是什么?)

  1. 方便實(shí)現(xiàn)字符串對(duì)象池. 如果 String 可變, 那么對(duì)象池就需要考慮寫(xiě)時(shí)拷貝的問(wèn)題了.
  2. 不可變對(duì)象是線程安全的.
  3. 不可變對(duì)象更方便緩存 hash code, 作為 key 時(shí)可以更高效的保存到 HashMap 中.

2.11 🐙 字符串的修改

**注意:盡量避免直接對(duì)String類型對(duì)象進(jìn)行修改,因?yàn)镾tring類是不能修改的,所有的修改都會(huì)創(chuàng)建新對(duì)象,效率 非常低下。 **

比如這么做👇

public static void main(String[] args) {String s = "hello";s += " world";System.out.println(s); // 輸出:hello world
}

這么做中間創(chuàng)建了很多臨時(shí)對(duì)象,非常浪費(fèi)資源
那么我們應(yīng)該怎么做呢?
在對(duì)String類進(jìn)行修改時(shí),效率是非常慢的,因此:盡量避免對(duì)String的直接需要,如果要修改建議盡量 使用StringBuffer或者StringBuilder。

3. 🦞 Java StringBuffer 和 StringBuilder 類

當(dāng)對(duì)字符串進(jìn)行修改的時(shí)候,需要使用 StringBuffer 和 StringBuilder 類。
和 String 類不同的是,StringBuffer 和 StringBuilder 類的對(duì)象能夠被多次的修改,并且不產(chǎn)生新的未使用對(duì)象。
[圖片上傳失敗...(image-b74cd7-1665193486312)]

在使用 StringBuffer 類時(shí),每次都會(huì)對(duì) StringBuffer 對(duì)象本身進(jìn)行操作,而不是生成新的對(duì)象,所以如果需要對(duì)字符串進(jìn)行修改推薦使用 StringBuffer。 StringBuilder 類在 Java 5 中被提出,它和 StringBuffer 之間的最大不同在于 StringBuilder 的方法不是線程安全的(不能同步訪問(wèn))。 由于 StringBuilder 相較于 StringBuffer 有速度優(yōu)勢(shì),所以多數(shù)情況下建議使用 StringBuilder 類。

那么StringBuilder在append的時(shí)候具體是怎么做的呢?

 public static void main(String args[]){StringBuilder sb = new StringBuilder(10);sb.append("Runoob..");System.out.println(sb);  sb.append("!");System.out.println(sb); sb.insert(8, "Java");System.out.println(sb); sb.delete(5,8);System.out.println(sb);  }

運(yùn)行結(jié)果為: Runoob.. Runoob..! Runoob..Java! RunooJava!

而如果我們要求線程安全的話,就必須使用StringBuffer類了

public class Test{public static void main(String args[]){StringBuffer sBuffer = new StringBuffer("Gremmie:");sBuffer.append("http://");sBuffer.append("106.14.57.");sBuffer.append("10/");System.out.println(sBuffer);  }
}

運(yùn)行結(jié)果為👇

那么總結(jié)一下StringBuilder常用的一樣方法:

方法說(shuō)明
StringBuff append(String str)在尾部追加,相當(dāng)于String的+=,可以追加:boolean、char、char[]、 double、float、int、long、Object、String、StringBuff的變量
char charAt(int index)獲取index位置的字符
int length()獲取字符串的長(zhǎng)度
int capacity()獲取底層保存字符串空間總的大小
void ensureCapacity(int mininmumCapacity)擴(kuò)容
void setCharAt(int index, char ch)將index位置的字符設(shè)置為ch
int indexOf(String str)返回str第一次出現(xiàn)的位置
int indexOf(String str, int fromIndex)從fromIndex位置開(kāi)始查找str第一次出現(xiàn)的位置
int lastIndexOf(String str)返回最后一次出現(xiàn)str的位置
int lastIndexOf(String str, int fromIndex)從fromIndex位置開(kāi)始找str最后一次出現(xiàn)的位置
StringBuff insert(int offset, String str)在offset位置插入:八種基類類型 & String類型 & Object類型數(shù)據(jù)
StringBuffer deleteCharAt(int index)刪除index位置字符
StringBuffer delete(int start, int end)刪除[start, end)區(qū)間內(nèi)的字符
StringBuffer replace(int start, int end, String str )將[start, end)位置的字符替換為str
String substring(int start)從start開(kāi)始一直到末尾的字符以String的方式返回
String substring(int start,int end)將[start, end)范圍內(nèi)的字符以String的方式返回
StringBuffer reverse()反轉(zhuǎn)字符串
String toString()將所有字符按照String的方式返回
public static void main(String[] args) {StringBuilder sb1 = new StringBuilder("hello");StringBuilder sb2 = sb1;// 追加:即尾插-->字符、字符串、整形數(shù)字sb1.append(' '); // hellosb1.append("world"); // hello worldsb1.append(123); // hello world123System.out.println(sb1); // hello world123System.out.println(sb1 == sb2); // trueSystem.out.println(sb1.charAt(0)); // 獲取0號(hào)位上的字符 hSystem.out.println(sb1.length()); // 獲取字符串的有效長(zhǎng)度14System.out.println(sb1.capacity()); // 獲取底層數(shù)組的總大小sb1.setCharAt(0, 'H'); // 設(shè)置任意位置的字符 Hello world123sb1.insert(0, "Hello world!!!"); // Hello world!!!Hello world123System.out.println(sb1);System.out.println(sb1.indexOf("Hello")); // 獲取Hello第一次出現(xiàn)的位置System.out.println(sb1.lastIndexOf("hello")); // 獲取hello最后一次出現(xiàn)的位置sb1.deleteCharAt(0); // 刪除首字符sb1.delete(0,5); // 刪除[0, 5)范圍內(nèi)的字符String str = sb1.substring(0, 5); // 截取[0, 5)區(qū)間中的字符以String的方式返回System.out.println(str);sb1.reverse(); // 字符串逆轉(zhuǎn)str = sb1.toString(); // 將StringBuffer以String的方式返回System.out.println(str);
}

從上述例子可以看出:String和StringBuilder最大的區(qū)別在于String的內(nèi)容無(wú)法修改,而StringBuilder的內(nèi)容可 以修改。頻繁修改字符串的情況考慮使用StringBuilder

注意:?String和StringBuilder類不能直接轉(zhuǎn)換。
如果要想互相轉(zhuǎn)換,可以采用如下原則 :

  • String變?yōu)镾tringBuilder: 利用StringBuilder的構(gòu)造方法或append()方法
  • StringBuilder變?yōu)镾tring: 調(diào)用toString()方法

🍜 面試題

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

  1. String的內(nèi)容不可修改,StringBuffer與StringBuilder的內(nèi)容可以修改.
  2. StringBuffer與StringBuilder大部分功能是相似的
  3. StringBuffer采用同步處理,屬于線程安全操作;而StringBuilder未采用同步處理,屬于線程不安全操作
  1. 以下總共創(chuàng)建了多少個(gè)String對(duì)象【前提不考慮常量池之前是否存在】 String str = new String("ab"); // 會(huì)創(chuàng)建多少個(gè)對(duì)象 String str = new String("a") + new String("b"); // 會(huì)創(chuàng)建多少個(gè)對(duì)象
http://m.risenshineclean.com/news/63761.html

相關(guān)文章:

  • 如何找網(wǎng)站制作銷售推廣方案
  • 無(wú)錫網(wǎng)站建設(shè)mkdns如何提高自己在百度的排名
  • 網(wǎng)站建設(shè)不完整軟文推廣代理平臺(tái)
  • 做化妝品注冊(cè)和注冊(cè)的網(wǎng)站有哪些灰色行業(yè)推廣平臺(tái)
  • 移動(dòng)端網(wǎng)站和微信網(wǎng)頁(yè)設(shè)計(jì)關(guān)鍵詞挖掘排名
  • 昆明網(wǎng)站建設(shè)服務(wù)公司上海牛巨微網(wǎng)絡(luò)科技有限公司
  • 廣州優(yōu)質(zhì)網(wǎng)站建設(shè)案例百度人工客服電話24小時(shí)
  • 網(wǎng)站中英文切換代碼線上銷售方案
  • 企業(yè)網(wǎng)站的建設(shè)水平直接關(guān)系到網(wǎng)絡(luò)營(yíng)銷的效果java培訓(xùn)機(jī)構(gòu)
  • 創(chuàng)建網(wǎng)站app靠譜seo整站優(yōu)化外包
  • 蘭州解封最新消息seopeixun com cn
  • Wordpress 分表分庫(kù)電腦優(yōu)化大師有用嗎
  • 做網(wǎng)站的公司術(shù)語(yǔ)杭州網(wǎng)站排名seo
  • 哪有做網(wǎng)站的公司建網(wǎng)站用什么軟件
  • 企業(yè)自助建站360瀏覽器網(wǎng)頁(yè)版入口
  • 吉林智能網(wǎng)站建設(shè)企業(yè)免費(fèi)網(wǎng)站推廣軟文發(fā)布
  • 上海注冊(cè)公司官網(wǎng)seo發(fā)包排名軟件
  • 阿里網(wǎng)站多個(gè)域名作品提示優(yōu)化要?jiǎng)h嗎
  • 淮安網(wǎng)站排名優(yōu)化公司自助建站系統(tǒng)哪個(gè)好
  • 做網(wǎng)站費(fèi)免圖片網(wǎng)站站長(zhǎng)工具
  • 北京移動(dòng)端網(wǎng)站開(kāi)發(fā)網(wǎng)絡(luò)營(yíng)銷策劃總結(jié)
  • 深圳做網(wǎng)站的公司哪家好做網(wǎng)站好的網(wǎng)站建設(shè)公司
  • 網(wǎng)站用什么系統(tǒng)好用網(wǎng)站推廣100種方法
  • 中文 域名的網(wǎng)站網(wǎng)站seo基本流程
  • 健身網(wǎng)站建設(shè)百度收錄快的發(fā)帖平臺(tái)
  • 怎么建企業(yè)網(wǎng)站網(wǎng)絡(luò)平臺(tái)推廣具體是怎么推廣
  • 維護(hù)一個(gè)網(wǎng)站一年多少錢(qián)淘寶關(guān)鍵詞排名查詢工具免費(fèi)
  • .ai域名注冊(cè)網(wǎng)站公司網(wǎng)站建設(shè)代理
  • 編程網(wǎng)課哪家好seo門(mén)戶
  • 網(wǎng)站關(guān)鍵詞怎么做百度怎么推廣自己的視頻