字符串廣泛應(yīng)用 在 Java 編程中袁波,在 Java 中字符串屬于對(duì)象,Java 提供了 String 類來(lái)創(chuàng)建和操作字符串。
創(chuàng)建字符串
- 三種構(gòu)造方法
- public String():創(chuàng)建一個(gè)空白字符串,不含有任何內(nèi)容
- public String(char[] array):根據(jù)字符數(shù)組的內(nèi)容,來(lái)創(chuàng)建對(duì)應(yīng)的字符串宴咧。
- public String(byte[] array):根據(jù)字節(jié)數(shù)組的內(nèi)容,來(lái)創(chuàng)建對(duì)應(yīng)的字符串径缅。
- 一種直接創(chuàng)建(字面量)
- String str = "Hello";//右邊直接用雙引號(hào)
//使用空參構(gòu)造
String str1 = new String();//小括號(hào)留空掺栅,說(shuō)明字符串說(shuō)明內(nèi)容都沒(méi)有
System.out.println("第1個(gè)字符串" +str1);
//根據(jù)字符數(shù)組創(chuàng)建字符串
char[] charArray = {'A','B','C'};
String str2 = new String(charArray);
System.out.println("第2個(gè)字符串" +str2)
;
//根據(jù)字節(jié)數(shù)組的內(nèi)容,來(lái)創(chuàng)建對(duì)應(yīng)的字符串
byte[] byteArray = {97,98,99};
String str3 = new String(byteArray);
System.out.println("第3個(gè)字符串" +str3);
使用直接創(chuàng)建的方式(字面量)創(chuàng)建的字符串放在常量池中纳猪,使用new String() 創(chuàng)建的字符串放在堆內(nèi)存中
String 類是不可改變的
String s = "Hello";
System.out.println("s = " + s);
s = "World";
System.out.println("s = " + s);
// 輸出結(jié)果為:
Hello
World
實(shí)例中的 s 只是一個(gè) String 對(duì)象的引用氧卧,并不是對(duì)象本身,當(dāng)執(zhí)行 s = "Hello"; 創(chuàng)建了一個(gè)新的對(duì)象 "World"氏堤,而原來(lái)的 "Hello" 還存在于內(nèi)存中沙绝。
-
String為什么不可變?
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
查看string的源碼鼠锈,即可知道字符串實(shí)際上就是一個(gè) char 數(shù)組闪檬,并且內(nèi)部就是封裝了一個(gè) char 數(shù)組。并且這里 char 數(shù)組是被 final 修飾的购笆。String 中的所有的方法粗悯,都是對(duì)于 char 數(shù)組的改變,只要是對(duì)它的改變由桌,方法內(nèi)部都是返回一個(gè)新的 String 實(shí)例为黎。
-
那放在內(nèi)存中的hello,什么時(shí)候進(jìn)行釋放呢行您?
這里需要引入一個(gè)新的概念,String 常量池
1.常量池(Constant Pool)
1.1 常量池(Class文件常量池):.java經(jīng)過(guò)編譯后生成的.class文件剪廉,是Class文件的資源倉(cāng)庫(kù)娃循。
1.2 常量池中主要存放倆大常量:字面量(文本字符串,final常量)和符號(hào)引用(類和接口的全局定名斗蒋,字段的名稱和描述捌斧,方法的名稱和描述),如下圖:
- 運(yùn)行時(shí)常量池(Constant Pool)
運(yùn)行時(shí)常量池是方法區(qū)的一部分泉沾。在Class常量池中捞蚂,用于存放編譯期間生成的字面量和符號(hào)量,在類加載完之后跷究,存入運(yùn)行時(shí)常量池中姓迅。而運(yùn)行時(shí)常量池期間也有可能加入新的常量(如:String.intern方法)
- String常量池,
String常量池,JVM為了減少字符串對(duì)象的重復(fù)創(chuàng)建丁存,在堆區(qū)開(kāi)一段內(nèi)存存放字符串肩杈。
根據(jù)JVM相關(guān)知識(shí),可以知道JVM 的GC操作主要發(fā)生在堆區(qū)解寝,但是具體什么時(shí)候釋放扩然,需要根據(jù)JVM具體來(lái)分析,詳見(jiàn)后續(xù)JVM文章
String的長(zhǎng)度
String是使用的一個(gè)char類型的數(shù)組來(lái)存儲(chǔ)字符串中的字符的聋伦,Java中定義數(shù)組是可以給數(shù)組指定長(zhǎng)度的夫偶,當(dāng)然不指定的話默認(rèn)會(huì)根據(jù)數(shù)組元素來(lái)指定。
/**
* Returns the length of this string.
* The length is equal to the number of <a href="Character.html#unicode">Unicode
* code units</a> in the string.
*
* @return the length of the sequence of characters represented by this
* object.
*/
public int length() {
return value.length >> coder();
}
通過(guò)源碼來(lái)看看int類型對(duì)應(yīng)的包裝類Integer可以看到觉增,其長(zhǎng)度最大限制為2^31 -1兵拢,那么說(shuō)明了數(shù)組的長(zhǎng)度是0~231-1,那么計(jì)算一下就是(231-1 = 2147483647 = 4GB)
但是實(shí)際我們創(chuàng)建超過(guò)65535個(gè)字符抑片,就會(huì)編譯報(bào)錯(cuò)卵佛。
這個(gè)又涉及到JVM編譯規(guī)范,如果我們將字符串定義成了字面量的形式敞斋,編譯時(shí)JVM是會(huì)將其存放在常量池中截汪,這時(shí)候JVM對(duì)這個(gè)常量池存儲(chǔ)String類型做出了限制,接下來(lái)我們先看下手冊(cè)是如何說(shuō)的植捎。
在class文件中u2表示的是無(wú)符號(hào)數(shù)占2個(gè)字節(jié)單位衙解,我們知道1個(gè)字節(jié)占8位,2個(gè)字節(jié)就是16位 焰枢,那么2個(gè)字節(jié)能表示的范圍就是2^16- 1 = 65535蚓峦。
所以首先字符串的內(nèi)容是由一個(gè)字符數(shù)組 char[] 來(lái)存儲(chǔ)的,由于數(shù)組的長(zhǎng)度及索引是整數(shù)济锄,且String類中返回字符串長(zhǎng)度的方法length() 的返回值也是int 暑椰,所以通過(guò)查看java源碼中的類Integer我們可以看到Integer的最大范圍是2^31 -1,由于數(shù)組是從0開(kāi)始的,所以數(shù)組的最大長(zhǎng)度可以使【0~2^31】通過(guò)計(jì)算是大概4GB荐绝。
但是通過(guò)翻閱java虛擬機(jī)手冊(cè)對(duì)class文件格式的定義以及常量池中對(duì)String類型的結(jié)構(gòu)體定義我們可以知道對(duì)于索引定義了u2一汽,就是無(wú)符號(hào)占2個(gè)字節(jié),2個(gè)字節(jié)可以表示的最大范圍是2^16 -1 = 65535低滩。其實(shí)是65535召夹,但是由于JVM需要1個(gè)字節(jié)表示結(jié)束指令,所以這個(gè)范圍就為65534了恕沫。超出這個(gè)范圍在編譯時(shí)期是會(huì)報(bào)錯(cuò)的监憎,但是運(yùn)行時(shí)拼接或者賦值的話范圍是在整形的最大范圍。
來(lái)自:CSDN(作者:wellleo)
String拼接
-
String:字符串常量婶溯,字符串長(zhǎng)度不可變鲸阔。Java中String 是immutable(不可變)的偷霉。用于存放字符的數(shù)組被聲明為final的,因此只能賦值一次隶债,不可再更改腾它。
- concat()通過(guò)復(fù)制數(shù)組在通過(guò) char 數(shù)組進(jìn)行拼接生成一個(gè)新的對(duì)象,所以地址值會(huì)有變動(dòng)
- ”+“ 號(hào)死讹,編譯時(shí)不會(huì)進(jìn)行地址值的改變(jvm優(yōu)化)瞒滴,**運(yùn)行時(shí)拼接會(huì)改變地址,底層使用stringbuilder實(shí)現(xiàn)
StringBuffer:字符串變量(Synchronized赞警,即線程安全)妓忍。如果要頻繁對(duì)字符串內(nèi)容進(jìn)行修改,出于效率考慮最好使用 StringBuffer愧旦,如果想轉(zhuǎn)成 String 類型世剖,可以調(diào)用 StringBuffer 的 toString() 方法。Java.lang.StringBuffer 線程安全的可變字符序列笤虫。在任意時(shí)間點(diǎn)上它都包含某種特定的字符序列,但通過(guò)某些方法調(diào)用可以改變?cè)撔蛄械拈L(zhǎng)度和內(nèi)容酬凳≡馐可將字符串緩沖區(qū)安全地用于多個(gè)線程宁仔。
StringBuilder:字符串變量(非線程安全)。在內(nèi)部 StringBuilder 對(duì)象被當(dāng)作是一個(gè)包含字符序列的變長(zhǎng)數(shù)組峦睡。
基本原則:
- 如果要操作少量的數(shù)據(jù)用 String ;
- 單線程操作大量數(shù)據(jù)用StringBuilder 煎谍;
- 多線程操作大量數(shù)據(jù),用StringBuffer龙屉。
常見(jiàn)面試題
- 寫一個(gè)方法來(lái)判斷一個(gè)String是否是回文(順讀和倒讀都一樣的詞)粱快?
// 回文就是正反都一樣的詞叔扼,如果需要判斷是否是回文漫雷,只需要比較正反是否相等即可。String類并沒(méi)有提供反轉(zhuǎn)方法供我們使用与柑,但StringBuffer和StringBuilder有reverse方法。
private static boolean isReversesame(String str) {
if (str == null)
return false;
StringBuilder strBuilder = new StringBuilder(str);
strBuilder.reverse();
return strBuilder.toString().equals(str);
}
// 假設(shè)面試官讓你不使用任何其他類來(lái)實(shí)現(xiàn)的話价捧,我們只需要首尾一一對(duì)比就知道是不是回文了结蟋。
private static boolean isReversesame(String str) {
if (str == null)
return false;
int length = str.length();
System.out.println(length / 2);
for (int i = 0; i < length / 2; i++) {
if (str.charAt(i) != str.charAt(length - i - 1))
return false;
}
return true;
}
- String是不可變的有什么好處?
- 由于String是不可變類嵌屎,所以在多線程中使用是安全的宝惰,我們不需要做任何其他同步操作。
- String是不可變的尼夺,它的值也不能被改變淤堵,所以用來(lái)存儲(chǔ)數(shù)據(jù)密碼很安全。
- 因?yàn)閖ava字符串是不可變的粘勒,可以在java運(yùn)行時(shí)節(jié)省大量java堆空間。因?yàn)椴煌淖址兞靠梢砸贸刂械南嗤淖址硭H绻址强勺兊迷捠赂唬魏我粋€(gè)變量的值改變乘陪,就會(huì)反射到其他變量,那字符串池也就沒(méi)有任何意義了贱勃。
- 允許對(duì)象HashCode谤逼,hash之后值不會(huì)變化,所以hashmap大多數(shù)使用String作為鍵值
String 支持的api
SN(序號(hào)) | 方法描述 |
---|---|
1 | char charAt(int index) 返回指定索引處的 char 值戚绕。 |
2 | int compareTo(Object o) 把這個(gè)字符串和另一個(gè)對(duì)象比較枝冀。 |
3 | int compareTo(String anotherString) 按字典順序比較兩個(gè)字符串。 |
4 | int compareToIgnoreCase(String str) 按字典順序比較兩個(gè)字符串球切,不考慮大小寫吨凑。 |
5 | String concat(String str) 將指定字符串連接到此字符串的結(jié)尾。 |
6 | boolean contentEquals(StringBuffer sb) 當(dāng)且僅當(dāng)字符串與指定的StringBuffer有相同順序的字符時(shí)候返回真费封。 |
7 | static String copyValueOf(char[] data) 返回指定數(shù)組中表示該字符序列的 String蒋伦。 |
8 | static String copyValueOf(char[] data, int offset, int count) 返回指定數(shù)組中表示該字符序列的 String。 |
9 | boolean endsWith(String suffix) 測(cè)試此字符串是否以指定的后綴結(jié)束韧献。 |
10 | boolean equals(Object anObject) 將此字符串與指定的對(duì)象比較研叫。 |
11 | boolean equalsIgnoreCase(String anotherString) 將此 String 與另一個(gè) String 比較嚷炉,不考慮大小寫。 |
12 | byte[] getBytes() 使用平臺(tái)的默認(rèn)字符集將此 String 編碼為 byte 序列绘证,并將結(jié)果存儲(chǔ)到一個(gè)新的 byte 數(shù)組中哗讥。 |
13 | byte[] getBytes(String charsetName) 使用指定的字符集將此 String 編碼為 byte 序列,并將結(jié)果存儲(chǔ)到一個(gè)新的 byte 數(shù)組中魏宽。 |
14 | void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 將字符從此字符串復(fù)制到目標(biāo)字符數(shù)組决乎。 |
15 | int hashCode() 返回此字符串的哈希碼构诚。 |
16 | int indexOf(int ch) 返回指定字符在此字符串中第一次出現(xiàn)處的索引。 |
17 | int indexOf(int ch, int fromIndex) 返回在此字符串中第一次出現(xiàn)指定字符處的索引凳寺,從指定的索引開(kāi)始搜索。 |
18 | int indexOf(String str) 返回指定子字符串在此字符串中第一次出現(xiàn)處的索引嘶卧。 |
19 | int indexOf(String str, int fromIndex) 返回指定子字符串在此字符串中第一次出現(xiàn)處的索引煮寡,從指定的索引開(kāi)始。 |
20 | String intern() 返回字符串對(duì)象的規(guī)范化表示形式脑慧。 |
21 | int lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出現(xiàn)處的索引闷袒。 |
22 | int lastIndexOf(int ch, int fromIndex) 返回指定字符在此字符串中最后一次出現(xiàn)處的索引岩梳,從指定的索引處開(kāi)始進(jìn)行反向搜索。 |
23 | int lastIndexOf(String str) 返回指定子字符串在此字符串中最右邊出現(xiàn)處的索引也物。 |
24 | int lastIndexOf(String str, int fromIndex) 返回指定子字符串在此字符串中最后一次出現(xiàn)處的索引滑蚯,從指定的索引開(kāi)始反向搜索抵栈。 |
25 | int length() 返回此字符串的長(zhǎng)度。 |
26 | boolean matches(String regex) 告知此字符串是否匹配給定的正則表達(dá)式创葡。 |
27 | boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) 測(cè)試兩個(gè)字符串區(qū)域是否相等绢慢。 |
28 | boolean regionMatches(int toffset, String other, int ooffset, int len) 測(cè)試兩個(gè)字符串區(qū)域是否相等胰舆。 |
29 | String replace(char oldChar, char newChar) 返回一個(gè)新的字符串,它是通過(guò)用 newChar 替換此字符串中出現(xiàn)的所有 oldChar 得到的棘幸。 |
30 | String replaceAll(String regex, String replacement) 使用給定的 replacement 替換此字符串所有匹配給定的正則表達(dá)式的子字符串倦零。 |
31 | String replaceFirst(String regex, String replacement) 使用給定的 replacement 替換此字符串匹配給定的正則表達(dá)式的第一個(gè)子字符串吨悍。 |
32 | String[] split(String regex) 根據(jù)給定正則表達(dá)式的匹配拆分此字符串育瓜。 |
33 | String[] split(String regex, int limit) 根據(jù)匹配給定的正則表達(dá)式來(lái)拆分此字符串栽烂。 |
34 | boolean startsWith(String prefix) 測(cè)試此字符串是否以指定的前綴開(kāi)始。 |
35 | boolean startsWith(String prefix, int toffset) 測(cè)試此字符串從指定索引開(kāi)始的子字符串是否以指定前綴開(kāi)始焰手。 |
36 | CharSequence subSequence(int beginIndex, int endIndex) 返回一個(gè)新的字符序列怀喉,它是此序列的一個(gè)子序列磺送。 |
37 | String substring(int beginIndex) 返回一個(gè)新的字符串,它是此字符串的一個(gè)子字符串崇呵。 |
38 | String substring(int beginIndex, int endIndex) 返回一個(gè)新字符串馅袁,它是此字符串的一個(gè)子字符串。 |
39 | char[] toCharArray() 將此字符串轉(zhuǎn)換為一個(gè)新的字符數(shù)組犹褒。 |
40 | String toLowerCase() 使用默認(rèn)語(yǔ)言環(huán)境的規(guī)則將此 String 中的所有字符都轉(zhuǎn)換為小寫弛针。 |
41 | String toLowerCase(Locale locale) 使用給定 Locale 的規(guī)則將此 String 中的所有字符都轉(zhuǎn)換為小寫削茁。 |
42 | String toString() 返回此對(duì)象本身(它已經(jīng)是一個(gè)字符串!)慰丛。 |
43 | String toUpperCase() 使用默認(rèn)語(yǔ)言環(huán)境的規(guī)則將此 String 中的所有字符都轉(zhuǎn)換為大寫瘾杭。 |
44 | String toUpperCase(Locale locale) 使用給定 Locale 的規(guī)則將此 String 中的所有字符都轉(zhuǎn)換為大寫。 |
45 | String trim() 返回字符串的副本贤笆,忽略前導(dǎo)空白和尾部空白。 |
46 | static String valueOf(primitive data type x) 返回給定data type類型x參數(shù)的字符串表示形式银萍。 |
47 | contains(CharSequence chars) 判斷是否包含指定的字符系列。 |
48 | isEmpty() 判斷字符串是否為空搀绣。 |