一骑篙、String類蜕提,StringBuffer類,StringBuilder類
String類靶端,StringBuffer類谎势,StringBuilder類都是Java中表示字符串的類,底層數(shù)據(jù)是char型的字符數(shù)組杨名。三個類都被final修飾脏榆,表示不可再被繼承。
String類是不可變的台谍,創(chuàng)建一個字符串對象之后须喂,對象的字符序列就不能被改變,因為String的成員char型數(shù)組是被定義為private final的趁蕊。
String類被設(shè)計為不可變的原因:
1)安全性:并發(fā)場景下坞生,由于String不能被改變,所以不會存在寫競爭的問題掷伙,任何線程讀到的都是同樣的字符串是己。另外一個安全是比如在類加載器加載類時,不可變類名保證了類被正確加載任柜。
2)String的hashCode是根據(jù)字符串內(nèi)容來的卒废,不可變保證了hashCode的不變性寒波,對于hashMap等容器,不可變的String類使其比其他對象更適合當(dāng)容器的鍵值升熊。
3)性能:String類型不可變時俄烁,字符串常量池才有意義,字符串常量池的出現(xiàn)级野,可以減少創(chuàng)建相同字面量的字符串页屠,讓不同的引用指向池中同一個字符串,為運(yùn)行時節(jié)約很多的堆內(nèi)存蓖柔。
StringBuilder和StringBuffer相似辰企,創(chuàng)建一個字符串對象之后是可以改變,他們的父類AbstractStringBuilder中成員char型數(shù)組沒有被定義為final况鸣,而且還提供一系列append方法和insert方法牢贸,將append或insert的東西加入到對象的char型數(shù)組。兩者的區(qū)別在于StringBuffer是線程安全的镐捧,因為此類中的絕大多數(shù)方法都是用synchronized修飾的潜索。
三、詳細(xì)了解String類
String類的常用方法
1.構(gòu)造方法
String s1 = new String(); // 定義一個空字符串""
String s2 = new String("hello"); //?聲明并初始化為"hello"字符串
char[] ch =new char[]{'h','e','l','l','o'}; String s3 =new String(ch);// 字符數(shù)組
byte[] b =new byte[]{1,3,4}; String s4 =new String(b);// byte數(shù)組
int[] i =new int[]{2,4,5,6}; String s5 =new String(i,0,3);// int數(shù)組
2.其他方法
int length() // 字符串長度
boolean isEmpty() // 是否為空字符串懂酱,即字符串長度是否為0
char charAt(int index) // 某位置上的字符竹习,從0開始。會拋StringIndexOutOfBoundsException
byte[] getBytes()列牺、byte[] getBytes(Charset charset)整陌、byte[] getBytes(String charsetName) // 轉(zhuǎn)換成字節(jié)數(shù)組,可傳指定字符集或字符集名字瞎领,字符集名字非法會拋UnsupportedEncodingException
boolean equals(Object anObject) // 比較此字符串是否和另一個對象相等泌辫,先用==比較是否是同一個對象,再看anObject是否是String的實例九默,再看兩字符串長度是否相等震放,再比較每一個字符是否相同
boolean equalsIgnoreCase(String anotherString) // 忽略大小寫比較兩個字符串是否相同
int compareTo(String anotherString) // 比較此字符串和anotherString的大小(每個字符的Unicode編碼)荤西,當(dāng)前字符串大于anotherString返回正數(shù)澜搅,等于返回0,小于返回負(fù)數(shù)
int compareToIgnoreCase(String str) // 同上邪锌,只是不區(qū)分字符的大小寫
boolean startsWith(String prefix)勉躺、boolean startsWith(String prefix,int toffset)、boolean endsWith(String suffix) // 以某字符串開頭或以某字符串結(jié)尾
int hashCode() // 字符串的hash碼觅丰,h=s[0] * 31^(n-1) + s[1] * 31^(n-2) + ... + s[n-1] (n為字符串長度)饵溅,選擇31的原因是:1)31是一個素數(shù),與素數(shù)相乘的方式更容易產(chǎn)生唯一性妇萄;2)如果是更大的素數(shù)蜕企,乘出來的結(jié)果可能會溢出咬荷,因為結(jié)果是int類型;3)31 * i 在計算時可為 (i<<5)- i
int indexOf(int ch)轻掩、int indexOf(int ch,int fromIndex)幸乒、int lastIndexOf(int ch)、int lastIndexOf(int ch,int fromIndex) // 字符第一次出現(xiàn)的位置或字符最后一次出現(xiàn)的位置唇牧,參數(shù)int類型的ch是字符的Unicode code point
int indexOf(String str)罕扎、int indexOf(String str,int fromIndex)、int lastIndexOf(String str)丐重、int lastIndexOf(String str,int fromIndex) // 字符串第一次出現(xiàn)的位置或最后一次出現(xiàn)的位置
String substring(int beginIndex) // 從beginIndex開始的子字符串腔召,包括beginIndex的位置,比如"hello".subString(2)返回從第二個位置開始的字串"llo"扮惦。
String substring(int beginIndex,int endIndex) //?從beginIndex開始到endIndex的子字符串,包括beginIndex的位置臀蛛,不包括endIndex的位置
String concat(String str) // 連接2個字符串 str1.concat(str2) 產(chǎn)生一個新的字符串,str1崖蜜、str2不變
boolean contains(CharSequence s) // 判斷一個字符串里是否包含某字符序列
String replace(char oldChar,char newChar) // 用新字符取代字符串中原來的字符浊仆,如 "hello".replace('e', 'i')返回"hillo",返回的"hillo"是一個新字符串纳猪,原來的"hello"不變
String replaceFirst(String regex, String replacement) // 將字符串中第一個匹配到regex字符串替換為replacement氧卧,產(chǎn)生一個新字符串桃笙,原字符串不變氏堤,"hellohe".replace("he","uo") 返回uollohe
String replaceAll(String regex, String replacement) // 將字符串中全部匹配到regex字符串替換為replacement,產(chǎn)生一個新字符串搏明,原字符串不變鼠锈,"hellohe".replace("he","uo") 返回uollouo
String replace(CharSequence target, CharSequence replacement) // 將字符串中target字符序列全部替換為
replacement字符序列,產(chǎn)生一個新字符串星著,原字符串不變购笆,"hellohe".replace("he","uo") 返回uollouo
replace與replaceAll的區(qū)別是:replace支持字符和字符串的替換,而repalceAll支持正則表達(dá)式替換虚循。
String[] split(String regex,int limit) // 用給定字符串作為分隔同欠,打斷字符串,返回字符串?dāng)?shù)組横缔,例子:
原字符串:str = "boo:and:foo"
str.split(":", 2); //?"boo", "and:foo"
str.split(":", 5); // "boo", "and", "foo"
str.split(":", -2); //?"boo", "and", "foo"
str.split("o", 5); // "b", "", ":and:f", "", ""
str.split("o", -2); //?"b", "", ":and:f", "", ""
str.split("o", 0); //?"b", "", ":and:f"
String[] split(String regex) // 同上铺遂,只是limit固定為0
static String join(CharSequence delimiter, CharSequence... elements) //字符串拼接,String.join("-", "Java", "is", "cool") 為"Java-is-cool"
static String join(CharSequence delimiter,Iterable elements) // 字符串拼接茎刚,List<String>strings = new LinkedList<>();strings.add("Java");strings.add("is"); strings.add("cool");String message = String.join(" ", strings); 結(jié)果為Java is cool
String toLowerCase() // 轉(zhuǎn)為小寫襟锐,生成新字符串
String toUpperCase() // 轉(zhuǎn)為大寫,生成新字符串
String trim() // 去掉字符串2端的空格
static String format(String format, Object... args) // 格式化膛锭,類似printf
3.本地方法
String類中唯一的一個本地方法:native String intern();?
由此方法引出常量池的概念粮坞,在Java中為了使程序運(yùn)行的更快蚊荣,更節(jié)省內(nèi)存,為8種基本類型及字符串常量引入了常量池莫杈,常量池類似Java系統(tǒng)級別提供的緩存互例。
對于字符串常量池,在JDK6及之前筝闹,存儲在永久代(PermGen Space)即方法區(qū)中敲霍,在JDK7及之后,字符串常量池被移到了堆中丁存,因為Perm區(qū)空間有限肩杈,如果程序中大量調(diào)用intern方法的話會產(chǎn)生java.lang.OutOfMemoryError:PermGen sapce錯誤。
對于直接使用雙引號聲明出的字符串解寝,會直接放入字符串常量池中扩然,如String s = "abc";
對于其他的String對象,調(diào)用intern方法也會被放入字符串常量池中聋伦,因為intern方法會先檢查常量池中是否有此字符串(有此字符串的含義是equals方法比較相等的字符串)夫偶,如果沒有,就將其放入到字符串常量池并返回其引用觉增。
一個例子:
String s1 = new StringBuilder("計算機(jī)").append("軟件").toString();
s1.intern() == s1;
JDK6為false兵拢;JDK7為true
解釋:JDK6中,new一個String對象時逾礁,在堆中創(chuàng)建了一個對象说铃,s1指向堆內(nèi)存中的地址,調(diào)s1.intern()方法時會先檢查常量池中是否有"計算機(jī)軟件"此字符串嘹履,發(fā)現(xiàn)沒有腻扇,于是在常量池中創(chuàng)建一個對象,并返回此對象的引用砾嫉,堆中的地址跟永久代中的地址肯定不一樣幼苛,所以結(jié)果為false;JDK7中焕刮,也是在堆中創(chuàng)建一個對象舶沿,s1指向堆內(nèi)存中的地址,當(dāng)調(diào)s1.intern()方法時配并,先檢查常量池中是否有"計算機(jī)軟件"此字符串括荡,因為此時字符串常量池就在堆中,所以認(rèn)為常量池中已有此字符串荐绝,于是返回了堆中的引用一汽,所以結(jié)果為true。
此例子為深入理解java虛擬機(jī)上的例子,更常見的例子見各博客召夹。