Java的面向?qū)ο缶幊?/a>">Java的面向?qū)ο缶幊?/a>
1086
2022-05-30
目錄
一.String類(lèi)
1.初識(shí)String
2.String的特性
3.String對(duì)象的創(chuàng)建(以Java 8為例)
4.String對(duì)象的存儲(chǔ)解析
5.String與byte[]、char[]直之間的轉(zhuǎn)換
6.String中常用方法
二.StringBuffer類(lèi)
1.StringBuffer類(lèi)概述
2.StringBuffer中常用的方法
三.StringBuilder類(lèi)概述
四.String、StringBuffer、StringBuilder三者的異同
1.異同
2.具體分析
3.三者效率比較
一.String類(lèi)
String:字符串,使用一對(duì)""引起來(lái)表示。
1.String聲明為final的,不可被繼承
2.String實(shí)現(xiàn)了Serializable接口:表示字符串是支持序列化的。
實(shí)現(xiàn)了Comparable接口:表示String可以比較大小
3.String內(nèi)部定義了final char[] value用于存儲(chǔ)字符串?dāng)?shù)據(jù)
4.String:代表不可變的字符序列。簡(jiǎn)稱(chēng):不可變性。
體現(xiàn):
1. 當(dāng)對(duì)字符串重新賦值時(shí),需要重寫(xiě)指定內(nèi)存區(qū)域賦值,不能使用原有的value進(jìn)行賦值。
2. 當(dāng)對(duì)現(xiàn)有的字符串進(jìn)行連接操作時(shí),也需要重新指定內(nèi)存區(qū)域賦值,不能使用原有的value進(jìn)行賦值。
3. 當(dāng)調(diào)用String的replace()方法修改指定字符或字符串時(shí),也需要重新指定內(nèi)存區(qū)域賦值,不能使用原有的value進(jìn)行賦值。
5.通過(guò)字面量的方式(區(qū)別于new)給一個(gè)字符串賦值,此時(shí)的字符串值聲明在字符串常量池中。
6.字符串常量池中是不會(huì)存儲(chǔ)相同內(nèi)容的字符串的。
以上部分內(nèi)容可體現(xiàn)在如下代碼中:
String s1 = "abc";//字面量的定義方式 String s2 = "abc"; s1 = "hello"; System.out.println(s1 == s2);//false System.out.println(s1);//hello System.out.println(s2);//abc System.out.println("*****************"); String s3 = "abc"; System.out.println(s3 == s2);//true s3 += "def"; System.out.println(s3 == s2);//false System.out.println(s3);//abcdef System.out.println(s2);//abc System.out.println("*****************"); String s4 = "abc"; String s5 = s4.replace('a', 'm'); System.out.println(s4);//abc System.out.println(s5);//mbc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Java 程序中的所有字符串字面值(如 “abc” )都作為此類(lèi)的實(shí)例實(shí)現(xiàn)。
String是一個(gè)final類(lèi),代表不可變的字符序列。
字符串是常量,用雙引號(hào)引起來(lái)表示。它們的值在創(chuàng)建之后不能更改。(注意是常量,不是變量)
String對(duì)象的字符內(nèi)容是存儲(chǔ)在一個(gè)字符數(shù)組value[]中的。存儲(chǔ)在如下述代碼所示的value中。
Java 8為char類(lèi)型的數(shù)組,如下:
public final class String implements java.io.Serializable, Comparable
1
2
3
4
Java 9以后改為了byte類(lèi)型的數(shù)組,如下:(Java 9引入了Compact Strings來(lái)取代Java 6的Compressed Strings,它的實(shí)現(xiàn)更過(guò)徹底,完全使用byte[]來(lái)替代char[],同時(shí)新引入了一個(gè)字段coder來(lái)標(biāo)識(shí)是LATIN1還是UTF16)
public final class String implements java.io.Serializable, Comparable
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
30
31
Java 9帶來(lái)了一個(gè)經(jīng)過(guò)改進(jìn)的新字符串,在大多數(shù)情況下,它將大大減少String的內(nèi)存消耗。
每個(gè)String的值都內(nèi)部包含在char[]數(shù)組中。每個(gè)字符是兩個(gè)字節(jié),十六位。由于這是UTF-16,因此甚至可以表示所有特殊字符。問(wèn)題在于,使用ISO-8859-1 / Latin-1時(shí),應(yīng)用程序中的絕大多數(shù)字符串只能由一個(gè)字節(jié)表示,因?yàn)樗鼈儾话厥庾址H绻@樣的字符串可以用每個(gè)字符僅一個(gè)字節(jié)表示,那意味著只有一半的內(nèi)存將用于存儲(chǔ)字符串的字符。當(dāng)然,這并不意味著String對(duì)象消耗的全部?jī)?nèi)存現(xiàn)在僅是原始對(duì)象的50%。并非所有由字符串分配的內(nèi)存都用于存儲(chǔ)字符。還有一些其他數(shù)據(jù),例如緩存的哈希碼或填充。
字符串實(shí)例存儲(chǔ)在堆中。字符串實(shí)際上消耗了很大一部分堆內(nèi)存。根據(jù)一些研究,字符串通常占據(jù)堆內(nèi)存的25%。將String設(shè)置為較小的兩倍不僅意味著顯著減少內(nèi)存消耗,而且還意味著大量減少垃圾回收開(kāi)銷(xiāo)。
盡管壓縮字符串的實(shí)現(xiàn)在許多方面都有缺陷,但主要思想仍然有效。實(shí)施還不夠牢固。在Java 9中,引入了一項(xiàng)新功能,引入了Compact Strings來(lái)取代Java 6的Compressed Strings。
字符串現(xiàn)在不再具有char []數(shù)組,而是表示為byte []數(shù)組。根據(jù)它包含的字符,它將使用UTF-16或Latin-1,即-每個(gè)字符一個(gè)或兩個(gè)字節(jié)。字符串類(lèi)中有一個(gè)新字段-編碼器,它指示使用哪個(gè)變體。與Compressed Strings不同,默認(rèn)情況下啟用此功能。如有必要(在主要使用UTF-16字符串的情況下),仍可以通過(guò)-XX:-CompactStrings禁用它。
該更改不會(huì)影響String的任何公共接口或任何其他相關(guān)的類(lèi)。重新設(shè)計(jì)了許多類(lèi)以支持新的String表示形式,例如StringBuffer或StringBuilder。
與Compressed Strings不同,新解決方案不包含任何字符串重新包裝,因此應(yīng)具有更高的性能。除了顯著減少內(nèi)存占用量之外,由于要處理的數(shù)據(jù)要少得多,因此在處理1字節(jié)的字符串時(shí)還應(yīng)該提高性能。垃圾收集的開(kāi)銷(xiāo)也將減少。2字節(jié)字符串的處理確實(shí)意味著對(duì)性能的輕微影響,因?yàn)檫€有一些其他邏輯可以處理字符串的兩種情況。但是總的來(lái)說(shuō),應(yīng)該提高性能,因?yàn)?字節(jié)字符串應(yīng)該只代表所有String實(shí)例的一小部分。如果在大多數(shù)字符串為2字節(jié)的情況下出現(xiàn)性能問(wèn)題,則可以始終禁用該功能。
String的實(shí)例化方式:
方式一:通過(guò)字面量定義的方式
方式二:通過(guò)new + 構(gòu)造器的方式
String str = "hello"; //本質(zhì)上this.value = new char[0]; String s1 = new String(); //this.value = original.value; String s2 = new String(String original); //this.value = Arrays.copyOf(value, value.length); String s3 = new String(char[] a); String s4 = new String(char[] a,int startIndex,int count);
1
2
3
4
5
6
7
8
9
如下代碼:
//通過(guò)字面量定義的方式:此時(shí)的s1和s2的數(shù)據(jù)java聲明在方法區(qū)中的字符串常量池中。 String s1 = "java"; String s2 = "java"; //通過(guò)new + 構(gòu)造器的方式:此時(shí)的s3和s4保存的地址值,是數(shù)據(jù)在堆空間中開(kāi)辟空間以后對(duì)應(yīng)的地址值。 String s3 = new String("java"); String s4 = new String("java"); System.out.println(s1 == s2);//true System.out.println(s1 == s3);//false System.out.println(s1 == s4);//false System.out.println(s3 == s4);//false
1
2
3
4
5
6
7
8
9
10
為何會(huì)這樣,內(nèi)存解析圖:(方法區(qū)中紅色部分為字符串常量池)
可以看出,其實(shí)String s3 = new String(“java”)一共創(chuàng)建兩個(gè)對(duì)象:一個(gè)是堆空間中new結(jié)構(gòu),另一個(gè)是char[]對(duì)應(yīng)的常量池中的數(shù)據(jù):“java”。
接下來(lái)例題進(jìn)一步升級(jí),如下圖:
其原因是因?yàn)椋?/p>
常量與常量的拼接結(jié)果在常量池。且常量池中不會(huì)存在相同內(nèi)容的常量。
只要其中有一個(gè)是變量,結(jié)果就在堆中。
如果拼接的結(jié)果調(diào)用intern()方法,返回值就在常量池中。
ps:
對(duì)于String s1 = "hello"; String s2 = s1 + "world";實(shí)際上原來(lái)的“hello”字符串對(duì)象已經(jīng)丟棄了,現(xiàn)在在堆空間中產(chǎn)生了一個(gè)字符 串s1+“world”(也就是"helloworld")。如果多次執(zhí)行這些改變串內(nèi)容的操作,會(huì)導(dǎo)致大量副本字符串對(duì)象存留在內(nèi)存中,降低效率。如果這樣的操作放到循環(huán)中,會(huì)極大影響程序的性能。
String 與 byte[]之間的轉(zhuǎn)換:
編碼:String --> byte[]:調(diào)用String的getBytes()
解碼:byte[] --> String:調(diào)用String的構(gòu)造器
編碼:字符串 -->字節(jié) (看得懂 —>看不懂的二進(jìn)制數(shù)據(jù))
解碼:編碼的逆過(guò)程,字節(jié) --> 字符串 (看不懂的二進(jìn)制數(shù)據(jù) —> 看得懂)
說(shuō)明:解碼時(shí),要求解碼使用的字符集必須與編碼時(shí)使用的字符集一致,否則會(huì)出現(xiàn)亂碼。
String str1 = "abc123中國(guó)"; byte[] bytes = str1.getBytes();//使用默認(rèn)的字符集UTF-8,進(jìn)行編碼。 System.out.println(Arrays.toString(bytes)); byte[] gbks = str1.getBytes("gbk");//使用gbk字符集進(jìn)行編碼。 System.out.println(Arrays.toString(gbks)); System.out.println("******************"); String str2 = new String(bytes);//使用默認(rèn)的字符集,進(jìn)行解碼。 System.out.println(str2); String str3 = new String(gbks); System.out.println(str3);//出現(xiàn)亂碼。原因:編碼集和解碼集不一致! String str4 = new String(gbks, "gbk"); System.out.println(str4);//沒(méi)有出現(xiàn)亂碼。原因:編碼集和解碼集一致!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
String 與 char[]之間的轉(zhuǎn)換:
String --> char[]:調(diào)用String的toCharArray()
char[] --> String:調(diào)用String的構(gòu)造器
String str1 = "abc123"; char[] charArray = str1.toCharArray(); for (int i = 0; i < charArray.length; i++) { System.out.println(charArray[i]); } char[] arr = new char[]{'h','e','l','l','o'}; String str2 = new String(arr); System.out.println(str2);
1
2
3
4
5
6
7
8
9
10
int length():返回字符串的長(zhǎng)度: return value.length char charAt(int index): 返回某索引處的字符return value[index] boolean isEmpty():判斷是否是空字符串:return value.length == 0 String toLowerCase():使用默認(rèn)語(yǔ)言環(huán)境,將 String 中的所有字符轉(zhuǎn)換為小寫(xiě) String toUpperCase():使用默認(rèn)語(yǔ)言環(huán)境,將 String 中的所有字符轉(zhuǎn)換為大寫(xiě) String trim():返回字符串的副本,忽略前導(dǎo)空白和尾部空白 boolean equals(Object obj):比較字符串的內(nèi)容是否相同 boolean equalsIgnoreCase(String anotherString):與equals方法類(lèi)似,忽略大小寫(xiě) String concat(String str):將指定字符串連接到此字符串的結(jié)尾。 等價(jià)于用“+” int compareTo(String anotherString):比較兩個(gè)字符串的大小。 String substring(int beginIndex):返回一個(gè)新的字符串,它是此字符串的從beginIndex開(kāi)始截取到最后的一個(gè)子字符串。 String substring(int beginIndex, int endIndex) :返回一個(gè)新字符串,它是此字符串從beginIndex開(kāi)始截取到endIndex(不包含)的一個(gè)子字符串。
1
2
3
4
5
6
7
8
9
10
11
12
ps:類(lèi)似于String substring(int beginIndex, int endIndex)方法,一般都是左閉右開(kāi),即[beginIndex, endIndex)。
boolean endsWith(String suffix):測(cè)試此字符串是否以指定的后綴結(jié)束 boolean startsWith(String prefix):測(cè)試此字符串是否以指定的前綴開(kāi)始 boolean startsWith(String prefix, int toffset):測(cè)試此字符串從指定索引開(kāi)始的子字符串是否以指定前綴開(kāi)始 boolean contains(CharSequence s):當(dāng)且僅當(dāng)此字符串包含指定的 char 值序列時(shí),返回 true int indexOf(String str):返回指定子字符串在此字符串中第一次出現(xiàn)處的索引 int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出現(xiàn)處的索引,從指定的索引開(kāi)始 int lastIndexOf(String str):返回指定子字符串在此字符串中最右邊出現(xiàn)處的索引 int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出現(xiàn)處的索引,從指定的索引開(kāi)始反向搜索
1
2
3
4
5
6
7
8
ps:indexOf和lastIndexOf方法如果未找到都是返回-1
替換: String replace(char oldChar, char newChar):返回一個(gè)新的字符串,它是通過(guò)用 newChar 替換此字符串中出現(xiàn)的所有 oldChar 得到的。 String replace(CharSequence target, CharSequence replacement):使用指定的字面值替換序列替換此字符串所有匹配字面值目標(biāo)序列的子字符串。 String replaceAll(String regex, String replacement):使用給定的 replacement 替換此字符串所有匹配給定的正則表達(dá)式的子字符串。 String replaceFirst(String regex, String replacement):使用給定的 replacement 替換此字符串匹配給定的正則表達(dá)式的第一個(gè)子字符串。 匹配: boolean matches(String regex):告知此字符串是否匹配給定的正則表達(dá)式。 切片: String[] split(String regex):根據(jù)給定正則表達(dá)式的匹配拆分此字符串。 String[] split(String regex, int limit):根據(jù)匹配給定的正則表達(dá)式來(lái)拆分此字符串,最多不超過(guò)limit個(gè),如果超過(guò)了,剩下的全部都放到最后一個(gè)元素中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test public void toTest(){ String str1 = "南山南,北秋悲,南山有谷堆,南風(fēng)南,北海北,北海有墓碑!"; String str2 = str1.replace('南', '東'); System.out.println(str1); System.out.println(str2); String str3 = str1.replace("南山", "東海"); System.out.println(str3); System.out.println("*************************"); String str = "12hello34world5java7891mysql456"; //把字符串中的數(shù)字替換成"," String string = str.replaceAll("\\d+", ","); System.out.println(string); //把字符串中的數(shù)字替換成",",如果結(jié)果中開(kāi)頭和結(jié)尾有","的話去掉 String string1 = str.replaceAll("\\d+", ",").replaceAll("^,|,$", ""); System.out.println(string1); System.out.println("*************************"); str = "12345"; //判斷str字符串中是否全部有數(shù)字組成,即有1-n個(gè)數(shù)字組成 boolean matches = str.matches("\\d+"); System.out.println(matches); String tel = "0571-4534289"; //判斷這是否是一個(gè)杭州的固定電話 boolean result = tel.matches("0571-\\d{7,8}"); System.out.println(result); System.out.println("*************************"); str = "hello|world|java"; String[] strs = str.split("\\|"); for (int i = 0; i < strs.length; i++) { System.out.println(strs[i]); } System.out.println(); str2 = "hello.world.java"; String[] strs2 = str2.split("\\.",2); for (int i = 0; i < strs2.length; i++) { System.out.println(strs2[i]); } }
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
上述代碼輸出結(jié)果:
南山南,北秋悲,南山有谷堆,南風(fēng)南,北海北,北海有墓碑! 東山東,北秋悲,東山有谷堆,東風(fēng)東,北海北,北海有墓碑! 東海南,北秋悲,東海有谷堆,南風(fēng)南,北海北,北海有墓碑! ************************* ,hello,world,java,mysql, hello,world,java,mysql ************************* true true ************************* hello world java hello world.java Process finished with exit code 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
二.StringBuffer類(lèi)
StringBuffer就是為了解決大量拼接字符串時(shí)產(chǎn)生很多中間對(duì)象問(wèn)題而提供的一個(gè)類(lèi),提供append和add方法,可以將字符串添加到已有序列的末尾或指定位置,它的本質(zhì)是一個(gè)線程安全的可修改的字符序列,把所有修改數(shù)據(jù)的方法都加上了synchronized。但是保證了線程安全是需要性能的代價(jià)的。
java.lang.StringBuffer代表可變的字符序列,JDK1.0中聲明,可以對(duì)字符 串內(nèi)容進(jìn)行增刪,此時(shí)不會(huì)產(chǎn)生新的對(duì)象。
很多方法與String相同。
作為參數(shù)傳遞時(shí),方法內(nèi)部可以改變值。
StringBuffer類(lèi)不同于String,其對(duì)象必須使用構(gòu)造器生成。有三個(gè)構(gòu)造器:
StringBuffer():初始容量為16的字符串緩沖區(qū)
StringBuffer(int size):構(gòu)造指定容量的字符串緩沖區(qū)
StringBuffer(String str):將內(nèi)容初始化為指定字符串內(nèi)容
StringBuffer append(xxx):提供了很多的append()方法,用于進(jìn)行字符串拼接 StringBuffer delete(int start,int end):刪除指定位置的內(nèi)容 StringBuffer replace(int start, int end, String str):把[start,end)位置替換為str StringBuffer insert(int offset, xxx):在指定位置插入xxx StringBuffer reverse() :把當(dāng)前字符序列逆轉(zhuǎn) public int indexOf(String str):返回指定子字符串第一次出現(xiàn)的字符串內(nèi)的索引 public String substring(int start,int end):返回一個(gè)從start開(kāi)始到end索引結(jié)束的左閉右開(kāi)區(qū)間的子字符串 public int length():返回其真實(shí)存在的字符長(zhǎng)度 public char charAt(int n ):得到指定位置的字符 public void setCharAt(int n ,char ch):設(shè)置指定位置的字符
1
2
3
4
5
6
7
8
9
10
總結(jié):
增:append(xxx) 刪:delete(int start,int end) 改:setCharAt(int n ,char ch) / replace(int start, int end, String str) 查:charAt(int n ) 插:insert(int offset, xxx) 長(zhǎng)度:length(); *遍歷:for() + charAt() / toString()
1
2
3
4
5
6
7
具體可參考Java API文檔。
三.StringBuilder類(lèi)概述
在很多情況下我們的字符串拼接操作不需要線程安全,這時(shí)候StringBuilder登場(chǎng)了,StringBuilder是JDK1.5發(fā)布的,它和StringBuffer本質(zhì)上沒(méi)什么區(qū)別,就是去掉了保證線程安全的那部分,減少了開(kāi)銷(xiāo)。
StringBuilder 和 StringBuffer 非常類(lèi)似,均代表可變的字符序列,而且提供相關(guān)功能的方法也一樣。
四.String、StringBuffer、StringBuilder三者的異同
String:不可變的字符序列;底層使用char[]存儲(chǔ)。(Java 9 之后是byte[])
StringBuffer:可變的字符序列;線程安全的,效率低;底層使用char[]存儲(chǔ)。(Java 9 之后是byte[])
StringBuilder:可變的字符序列;jdk5.0新增的,線程不安全的,效率高;底層使用char[]存儲(chǔ)。(Java 9 之后是byte[])
String str = new String();//相當(dāng)于char[] value = new char[0]; String str1 = new String("abc");//char[] value = new char[]{'a','b','c'}; StringBuffer sb1 = new StringBuffer();//char[] value = new char[16];底層創(chuàng)建了一個(gè)長(zhǎng)度是16的數(shù)組。 System.out.println(sb1.length());//0 sb1.append('a');//value[0] = 'a'; sb1.append('b');//value[1] = 'b'; StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length() + 16];
1
2
3
4
5
6
7
8
9
StringBuffer和StringBuilder擴(kuò)容問(wèn)題: 如果要添加的數(shù)據(jù)底層數(shù)組盛不下了,那就需要擴(kuò)容底層的數(shù)組。默認(rèn)情況下,擴(kuò)容為原來(lái)容量的2倍 + 2,同時(shí)將原有數(shù)組中的元素復(fù)制到新的數(shù)組中。 同時(shí),建議大家開(kāi)發(fā)中使用:StringBuffer(int capacity) 或 StringBuilder(int capacity).
1
2
3
4
對(duì)比String、StringBuffer、StringBuilder三者的效率:
從高到低排列:StringBuilder > StringBuffer > String
Java 數(shù)據(jù)結(jié)構(gòu)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶(hù)投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。