一、String類
String的實(shí)例化方式
- 通過(guò)字面量定義的方式
String str = "hello world";
- 通過(guò)new+構(gòu)造器的方式
String str = new String("hello world");
- 面試題:
String s = new String("asf");
方式創(chuàng)建對(duì)象,在內(nèi)存中創(chuàng)建了幾個(gè)對(duì)象金闽?
答:兩個(gè)搬葬,一個(gè)是堆空間中new出來(lái)的,一個(gè)是char[ ]對(duì)應(yīng)的常量池中的數(shù)據(jù):“abc”
String類的內(nèi)部細(xì)節(jié)
- String表示字符串汁展,使用一對(duì)“”引起來(lái)表示
- String聲明為final,不可被繼承
- String實(shí)現(xiàn)了Serializable接口,表示字符串是支持序列化的虏等;實(shí)現(xiàn)了Comparable接口弄唧,表示String是可以比較大小的
- String內(nèi)部定義了final char[ ] value 用于存儲(chǔ)字符串?dāng)?shù)據(jù)
- String代表不可變的字符序列,簡(jiǎn)稱:不可變性??體現(xiàn)在如下三個(gè)方面
①對(duì)字符串重新賦值時(shí)霍衫,需要重新指定內(nèi)存區(qū)域賦值候引,不能使用原有的value進(jìn)行賦值
②當(dāng)對(duì)現(xiàn)有字符串進(jìn)行連接操作時(shí),也需要重新指定內(nèi)存區(qū)域敦跌,不能對(duì)原有的value進(jìn)行賦值
③當(dāng)調(diào)用String的replace方法修改指定字符或者字符串時(shí)澄干,也需要重新指定內(nèi)存區(qū)域 - 通過(guò)字面量的方式(區(qū)別于new)給一個(gè)字符串賦值,此時(shí)的字符串聲明在字符串常量池中
- 字符串常量池中是不會(huì)存儲(chǔ)相同的字符串的
String賦值的各種內(nèi)存結(jié)構(gòu)
- String的部分構(gòu)造器源碼(用于輔助理解其不可變性以及兩種初始化方式)
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/**
* Initializes a newly created {@code String} object so that it represents
* an empty character sequence. Note that use of this constructor is
* unnecessary since Strings are immutable.
*/
public String() {
this.value = "".value;
}
/**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of {@code original} is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
* @param original
* A {@code String}
*/
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
/**
* Allocates a new {@code String} so that it represents the sequence of
* characters currently contained in the character array argument. The
* contents of the character array are copied; subsequent modification of
* the character array does not affect the newly created string.
*
* @param value
* The initial value of the string
*/
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length); //見(jiàn)如上注釋柠傍,copy的目的是對(duì)于數(shù)組的操作不會(huì)影響String的值
}
}
- 例子
public class StringTest1 {
public static void main(String[] args) {
String str1="abc";
String str2="abc";
String str3=new String("abc");
String str4=new String("def");
System.out.println(str1==str2);
System.out.println(str1==str3);
}
}
內(nèi)存結(jié)構(gòu)圖
(現(xiàn)在的知識(shí)有限麸俘,具體的細(xì)節(jié)我也不太懂,比如我不知道常量池里放的是否是對(duì)象惧笛,他們和數(shù)組的關(guān)系是不是應(yīng)該這樣放从媚,我是按照視頻里老師講的和源碼,自己理解為這樣的患整,有錯(cuò)的話希望幫忙指出)
String的常用操作
- 連接字符串(+)
String s1="hello";
String s2="world";
String s3="hello"+"world";
String s4=s1+"world";
String s5=s1+s2;
String s6=(s1+s2).intern();
System.out.println(s3==s4);
System.out.println(s3==s5);
System.out.println(s4==s5);
System.out.println(s3==s6);
注意事項(xiàng):
①常量與常量的拼接結(jié)果放在常量池中拜效,且常量池中不會(huì)存在相同內(nèi)容的常量
②只要其中一個(gè)是變量,結(jié)果就在堆中
③如果拼接的結(jié)果調(diào)用intern方法各谚,返回值就在常量池中
題目:
final String str="123";
String str1=str+"abc";
String str2="123abc";
System.out.println(str1==str2); //true
解釋:此時(shí)的str是常量拂檩,參照①
- int length() 輸出數(shù)組的長(zhǎng)度,實(shí)際上是底層的數(shù)組的長(zhǎng)度
- char chatAt(int index) 返回index索引處的字符嘲碧,實(shí)際上是return value[index];
??區(qū)別于C++的可以對(duì)字符串按照字符數(shù)組的方式進(jìn)行訪問(wèn)修改稻励,在java中,字符串是對(duì)象愈涩,應(yīng)該使用對(duì)象的方法對(duì)字符串進(jìn)行訪問(wèn)望抽、修改等操作 - boolean isEmpty() 判斷當(dāng)前數(shù)組是否為空,return value.length==0;
- String toLowerCase() / toUpperCase() 將字符都變?yōu)樾懟虼髮?/li>
String str1="ASDFG";
String str2=str1.toLowerCase();
//str1不變履婉,印證了String的不變性
System.out.println(str1);
System.out.println(str2);
- String trim() 返回值是去除原字符串首尾空格的副本煤篙,原字符串不變
- boolean equals(Object obj) 比較字符串的內(nèi)容是否相同
boolean equalsIgnoreCase(Object obj) 忽略大小寫比較 - String concat(String str) 將指定字符串連接到此字符串的結(jié)尾,等價(jià)于“+”
- int compareTo(String str) 比較兩個(gè)字符串的大小 負(fù)數(shù)毁腿,則當(dāng)前對(duì)象屑巍;整數(shù)已烤,則str小
- String substring(int beginIndex) 返回一個(gè)新的字符串鸠窗,該字符串是從beginIndex開(kāi)始截取到最后
- String substring(int beginIndex, int endIndex) 返回一個(gè)新的字符串,該字符串是從beginIndex開(kāi)始截取到endIndex-1的位置(左閉右開(kāi))
??在上述方法中胯究,只要是對(duì)字符串進(jìn)行了操作的稍计,原字符串仍然保持不變,最終生成的結(jié)果是通過(guò)return的形式返回裕循,所以對(duì)字符串操作的方法的返回值都是String類型的(再次注意字符串的不變性)
- boolean endsWith(String suffix) 測(cè)試此字符串是否以指定的后綴結(jié)束
boolean startsWith(String prefic) 測(cè)試字符串是否以指定的前綴開(kāi)始
boolean startsWith(String prefic臣嚣,int toffset) 測(cè)試此字符串從指定位置開(kāi)始的子串是否以指定的前綴開(kāi)始 - boolean contains(CharSequence s)當(dāng)前字符串中是否包含字符序列s净刮,CharSequence 是一個(gè)字符序列接口(典型的實(shí)現(xiàn)類是String,StringBuffer,StringBuilder), 此處可以傳入一個(gè)String硅则,但不能是字符數(shù)組
- int indexOf(String str) 返回指定字符串在此字符串中第一次出現(xiàn)的索引位置
int indexOf(String str, int fromIndex) 返回指定字符串在此字符串中從fromIndex開(kāi)始第一次出現(xiàn)的索引位置(可以用來(lái)尋找當(dāng)前字符串中str出現(xiàn)的次數(shù))
int lastIndexOf(String str) 返回最右出現(xiàn)的索引位置
int lastIndexOf(String str, int fromIndex) 返回最右出現(xiàn)的索引位置,從指定的索引開(kāi)始反向搜索(從index開(kāi)始向左搜索淹父,如果index指的位置正好是最后一次出現(xiàn)的位置,則返回index)
- String replace(char oldChar, char newChar) 返回一個(gè)新的字符串怎虫,它是通過(guò)用newchar替換此字符串中出現(xiàn)的所有oldChar得到的
- String replace(CharSequence target, CharSequence replacement) 返回一個(gè)新的字符串弹灭,使用指定的字符序列替換此字符串中所有匹配的字符序列
- 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á)式的匹配拆分此字符串(符合正則表達(dá)式的部分不出現(xiàn)在最終結(jié)果中)
- String[] split(String regex, int limit) 根據(jù)給定正則表達(dá)式的匹配拆分此字符串,最多不超過(guò)limit個(gè)揪垄,如果超過(guò)了剩下的全部放在最后一個(gè)元素中
String test="hhh,fff,ggg,aaa,ddd,eee,rrrr";
String[] res=test.split(",");
for(int i=0;i<res.length;i++){
System.out.println(res[i]);
}
System.out.println("-------------");
String[] res2=test.split(",",3);
for(int i=0;i<res2.length;i++){
System.out.println(res2[i]);
}
image.png
String與其他類型的轉(zhuǎn)換
- String與基本數(shù)據(jù)類型和包裝類的轉(zhuǎn)換
基本數(shù)據(jù)類型/包裝類 --> String :String.valueOf()或者與空字符串進(jìn)行連接
String --> 基本數(shù)據(jù)類型/包裝類 :包裝類.parseXXX()
(以int為例)
(好像valueOf方法都是把形參轉(zhuǎn)換成當(dāng)前類對(duì)象的)
public class StringTest1 {
@Test
public void test1(){
//int轉(zhuǎn)String
int a=123;
String str1=String.valueOf(a);
String str2=""+a;
System.out.println(str1);
System.out.println(str2);
//String轉(zhuǎn)int
int b=Integer.parseInt(str1);
System.out.println(b);
}
}
- public void getChars(int start,int end,char c[],int offset)
該方法的作用是將當(dāng)前字符串從start到end-1位置上的字符復(fù)制到字符數(shù)組c中穷吮,并從c的offset處開(kāi)始存放 - String與char[]的轉(zhuǎn)換
String --> char[] : toCharArray()
char[] --> String : 構(gòu)造器
@Test
public void test2(){
//string to char[]
String str="asf";
char[] chars=str.toCharArray();
for(int i=0;i<chars.length;i++){
System.out.println(chars[i]);
}
//char[] to String
String newStr=new String(chars);
System.out.println(newStr);
}
- String與byte[ ]轉(zhuǎn)化
String轉(zhuǎn)化為byte數(shù)組是字符編碼的過(guò)程,底層都是以字節(jié)碼的形式進(jìn)行存儲(chǔ)饥努;byte數(shù)組轉(zhuǎn)化為String是解碼的過(guò)程捡鱼。String轉(zhuǎn)化為字節(jié)碼的時(shí)候可以選擇編碼集,在utf-8中一個(gè)中文字符用三個(gè)字節(jié)表示酷愧,在gbk中用兩個(gè)字節(jié)表示驾诈,所以解碼使用的編碼集應(yīng)該與編碼使用的相同,才能解出正確的結(jié)果
String --> byte[] : getBytes()
byte[] --> String : 構(gòu)造器
@Test
public void test3(){
//String to byte[]
String str="123abc中國(guó)";
byte[] bytes_utf8=str.getBytes();
for(int i=0;i<bytes_utf8.length;i++){
System.out.print(bytes_utf8[i]+" ");
}
System.out.println();
byte[] bytes_gbk=null;
try {
bytes_gbk=str.getBytes("gbk");
for(int i=0;i<bytes_gbk.length;i++){
System.out.print(bytes_gbk[i]+" ");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println();
//byte[] to String
String res1=new String(bytes_utf8);
String res2=new String(bytes_gbk); //我的編譯器使用的是utf-8解碼溶浴,所以會(huì)亂碼
System.out.println(res1);
System.out.println(res2);
}
image.png
二乍迄、StringBuffer與StringBuilder
- String、StringBuffer與StringBuilder三者的異同
String:不可變的字符序列士败;底層使用char[ ]存儲(chǔ)
StringBuffer:可變的字符序列闯两;線程安全的(synchronized),效率低谅将;底層使用char[ ]存儲(chǔ)
StringBuilder:可變的字符序列漾狼;(jdk1.5)線程不安全的,效率高饥臂;底層使用char[ ]存儲(chǔ) - StringBuffer的部分源碼
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/**
以下用到的super構(gòu)造器為
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
*/
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
/**
* Constructs a string buffer with no characters in it and
* the specified initial capacity.
*
* @param capacity the initial capacity.
* @exception NegativeArraySizeException if the {@code capacity}
* argument is less than {@code 0}.
*/
public StringBuffer(int capacity) {
super(capacity);
}
/**
* Constructs a string buffer initialized to the contents of the
* specified string. The initial capacity of the string buffer is
* {@code 16} plus the length of the string argument.
*
* @param str the initial contents of the buffer.
*/
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
@Override
public synchronized int length() {
return count;
}
@Override
public synchronized int capacity() {
return value.length;
}
}
- StringBuffer的容量問(wèn)題
StringBuffer內(nèi)部的兩個(gè)重要成員變量value[ ](存放字符串)和count(有效字符的個(gè)數(shù))逊躁,初始化StringBuffer時(shí)會(huì)多開(kāi)16個(gè)字符的大小作為value的長(zhǎng)度,調(diào)用length()返回的是有效字符的個(gè)數(shù)count隅熙,調(diào)用capacity()返回的是value數(shù)組的真實(shí)長(zhǎng)度 - StringBuffer的擴(kuò)容問(wèn)題
如果要append的東西稽煤,底層數(shù)組放不下了(超出了capacity),需要擴(kuò)容底層的數(shù)組囚戚。默認(rèn)情況下酵熙,擴(kuò)充為原來(lái)容量的2倍+2,同時(shí)將原有數(shù)組的元素復(fù)制到新數(shù)組中
具體可以查看append的源碼 - String由于不可變弯淘,效率最差绿店,開(kāi)發(fā)過(guò)程中如果字符串需要改變,建議使用StringBuffer和StringBuilder庐橙。并且假勿,總是擴(kuò)容也會(huì)影響到程序的效率,所以建議使用
StringBuffer(int capacity)
構(gòu)造器态鳖,提前設(shè)置好容量
StringBuffer(StringBuilder)的常用方法
- StringBuffer append(xxxx) 提供了很多的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, xxxx) 在指定位置插入xxxx
- StringBuffer reverse() 把當(dāng)前字符序列逆轉(zhuǎn)
(以上操作都是在StringBuffer/StringBuilder原底層數(shù)組上進(jìn)行操作浆竭,改變?cè)讓訑?shù)組存放的內(nèi)容浸须,返回類型是StringBuffer/StringBuilder,是因?yàn)閳?zhí)行了return this;
邦泄,所以支持方法鏈的操作删窒,例如str.append("123456").reverse();
)
- int indexOf(String str) 出現(xiàn)str的位置
- String substring(int start, int end) 返回一個(gè)[start,end)的字符串
- int length() 返回實(shí)際存放的元素的格式顺囊,與capacity區(qū)別開(kāi)
- char charAt(int n) 返回下標(biāo)為n的字符
- void setCharAt(int n, char ch) 將下標(biāo)為n的字符替換為ch
例題:
- 創(chuàng)建一個(gè)字符串“ab12345cd"肌索,對(duì)字符串進(jìn)行操作,使其變?yōu)椤盿b54321cd"
import org.junit.Test;
/**
* @Author: ssy
* @Description:
* @Date: Created in 9:28 2020/11/23
* @Modified By:
*/
public class StringTest2 {
@Test
public void StringBuilderTest(){
StringBuilder str=new StringBuilder("ab12345cd");
str.replace(2,7,(new StringBuilder(str.substring(2,7))).reverse().toString());
System.out.println(str);
}
}
寫的時(shí)候還是應(yīng)該多注意返回類型的特碳;另外想不起庫(kù)函數(shù)诚亚,這種題完全可以自己手寫,和底層沒(méi)有太大的差別
- 查找str1在str2中出現(xiàn)的次數(shù)
僅僅是熟悉一下相關(guān)函數(shù)的操作午乓,不涉及算法知識(shí)站宗,追求高效的話還是要學(xué)KMP算法
import org.junit.Test;
/**
* @Author: ssy
* @Description:
* @Date: Created in 16:38 2020/11/23
* @Modified By:
*/
public class AlgorithmTest2 {
@Test
public void test(){
String str1="aba";
String str2="ababahjjlkhhlkabaljlnmababa";
System.out.println(findCount(str1,str2));
}
/**
*
* @Description:
* 查找str1在str2中出現(xiàn)的次數(shù)
* @auther: ssy
* @date: 16:40 2020/11/23
* @param: [str1, str2]
* @return: int
*
*/
public int findCount(String str1,String str2){
int count=0;
int index=0;
if(str1.length()<str2.length()){
while((index=str2.indexOf(str1,index))!=-1){
count++;
index++;
}
return count;
}
else return 0;
}
}
??面試題
說(shuō)出每一個(gè)輸出的執(zhí)行結(jié)果
@Test
public void testDebug(){
String str=null;
StringBuffer sb=new StringBuffer();
sb.append(str);
System.out.println(sb.length()); //4
System.out.println(sb); //"null" 沒(méi)有引號(hào),只是用來(lái)表示存儲(chǔ)內(nèi)容
StringBuffer sb1=new StringBuffer(str); //空指針異常
System.out.println(sb1); //由于出現(xiàn)異常不會(huì)執(zhí)行
}
查看過(guò)源碼的話益愈,就會(huì)比較簡(jiǎn)單了
- append的源碼對(duì)于添加空字符串做了特殊處理梢灭,會(huì)將null這四個(gè)字符存放在數(shù)組中,所以長(zhǎng)度增加4蒸其,數(shù)組中的內(nèi)容為‘n’ ‘u’ ‘l’ ‘l’
- 構(gòu)造器StringBuffer(str)中執(zhí)行的操作為super(str.length()+16) 此時(shí)的str是空或辖,所以會(huì)出現(xiàn)空指針異常