String相關知識

1.字符串存儲位置:字符串的存儲位置在堆中;

2.創建字符串方式:

(1)使用引號來創建字符串
單獨(注意是單獨)使用引號來創建字符串的方式,字符串都是常量,在編譯期已經確定存儲在常量池中了。
用引號創建一個字符串的時候,首先會去常量池中尋找有沒有相等的這個常量對象,沒有的話就在常量池中創建這個常量對象;有的話就直接返回這個常量對象的引用。
所以看這個例子:

String str1 ="hello";
String str2 ="hello";
System.out.println(str1 == str2);//true
== 對比的是對象;
(2)new的方式創建字符串

String a = new String("abc");
String str3 =new String("abc");
new這個關鍵字,毫無疑問會在堆中分配內存,創建一個String類的對象。因此,a這個在棧中的引用指向的是堆中的這個String對象的。
然后,因為"abc"是個常量,所以會去常量池中找,有沒有這個常量存在,沒的話分配一個空間,放這個"abc"常量,并將這個常量對象的空間地址給到堆中String對象里面;如果常量池中已經有了這個常量,就直接用那個常量池中的常量對象的引用唄,就只需要創建一個堆中的String對象。

String str1 = "abc";
String str2 = new String("abc");

if (str1==str2){
Log.i("lrf","str1==str2 is true");
}else {
Log.i("lrf","str1==str2 is false");
}
if (str1.equals(str2)){
Log.i("lrf","str1.equals(str2) is true");
}else {
Log.i("lrf","str1.equals(str2) is false");
}

日志打印是:

2023-06-14 09:54:44.907 20259-20259/com.example.myapplication I/lrf: str1==str2 is false

2023-06-14 09:54:44.907 20259-20259/com.example.myapplication I/lrf: str1.equals(str2) is true
說明:str1與 str2 引用同兩個 String 對象 -- "abc"!
String str2 = new String("abc");
String str3 = new String("abc");
if (str3==str2){

Log.i("lrf","str3==str2 is true");

}else {

Log.i("lrf","str3==str2 is false");

}

if (str3.equals(str2)){

Log.i("lrf","str3.equals(str2) is true");

}else {

Log.i("lrf","str3.equals(str2) is false");

}

日志打印是:

2023-06-14 10:09:35.420 20653-20653/com.example.myapplication I/lrf: str3==str2 is false

2023-06-14 10:09:35.420 20653-20653/com.example.myapplication I/lrf: str3.equals(str2) is true
String str3 = new String("abc");
String str4 = new String();
str4 = "abc";
if (str3 == str4) {
Log.i("lrf", "str3==str4 is true");
} else {
Log.i("lrf", "str3==str4 is false");
}
if (str3.equals(str4)) {
Log.i("lrf", "str3.equals(str4) is true");
} else {
Log.i("lrf", "str3.equals(str4) is false");
}
2023-06-19 08:21:37.419 26795-26795 str3==str4 is false
2023-06-19 08:21:37.419 26795-26795 str3.equals(str4) is true
String str4 = new String();
String str5 = new String();
str4 = "abc";
str5 = "abc";
if (str5 == str4) {
Log.i("lrf", "str5==str4 is true");
} else {
Log.i("lrf", "str5==str4 is false");
}
if (str5.equals(str4)) {
Log.i("lrf", "str5.equals(str4) is true");
} else {
Log.i("lrf", "str5.equals(str4) is false");
}
2023-06-19 08:21:37.419 26795-26795 str5==str4 is true
2023-06-19 08:21:37.419 26795-26795 str5.equals(str4) is true
String str5 = new String();
String str6 = new String("abcc");
if (str5 == str6) {
Log.i("lrf", "str5==str6 is true");
} else {
Log.i("lrf", "str5==str6 is false");
}
if (str5.equals(str6)) {
Log.i("lrf", "str5.equals(str6) is true");
} else {
Log.i("lrf", "str5.equals(str6) is false");
}
2023-06-19 08:21:37.419 26795-26795 str5==str6 is true
2023-06-19 08:21:37.419 26795-26795 str5.equals(str6) is true

2.改變String發生了什么?:每次對 String 類型進行改變的時候其實都等同于生成了一個新的 String 對象,然后將指針指向新的 String 對象。

String a = "a"和String a = new String("a")有什么不同?

第一個顯然只有一個對象,第二個卻有兩個對象"a"和new String("a")。

String ab = "cd";String ac = "cd";

這里還是一個對象!

為什么?因為jvm中存在一個String池,存放著String對象,用于共享使用,提高效率。當執行ab的語句時,會調用equal去跟String池中比較看存不存在“cd”這個值得對象,如果存在,則返回這個對象給ab,如果不存在,則把這個值得對象加入String池。當執行完ab,再執行ac時,由于String池已經存在值為"cd"的ab對象,因此會把該對象的引用賦給ac。這么看,String池的存在很合理。但是String池只能添加使用引號包含文本的方式產生的對象,而不能添加使用new操作產生的對象。

利用String的split()對字符串進行切割

String text = "Hello, my name is liujianfeng";

System.out.println(Arrays.toString(text.split("\s")));//以空格為切割符

System.out.println(Arrays.toString(text.split("\W+")));//以非單詞為切割符

System.out.println(Arrays.toString(text.split("m")));//以字母m為切割符

String number = "Hello, 985 or 211, I will say ssooss";

System.out.println(Arrays.toString(number.split("\d")));//以數字為切割符

System.out.println(Arrays.toString(number.split("\D")));//以非數字為切割符

System.out.println(Arrays.toString(number.split("[Ho]")));//以非數字為切割符

System.out.println(Arrays.toString(number.split("1{2}")));//以兩個1為切割符

結果如下:

[Hello,, my, name, is, liujianfeng]

[Hello, my, name, is, liujianfeng]

[Hello, , y na, e is liujianfeng]

[Hello, , , , or , , , , I will say ssooss]

[, , , , , , , 985, , , , 211]

[, ell, , 985 , r 211, I will say ss, , ss]

[Hello, 985 or 2, , I will say ssooss]

原文鏈接:https://blog.csdn.net/suyimin2010/article/details/79890568

String str1 = "00";

String str2 = "11";

equals 對比的是值是內容,不管對象,只管值,內容是否相同;

== 對比的是對象,對比的是指向的位置,指針和值;

因為String是引用類型的,不是基本數據類型,所以它們的比較是使用地址和值(相當于C中的指針)來比較的,因為它們是不同的對象,有不同的地址,

  1. == 兩側都是基本數據類型,則判斷的是左右兩邊操作數據的值是否相等

  2. ==兩側都是引用數據類型,則判斷的是左右兩邊操作數的內存地址是否相同。若此時返回 true , 則該操作符作用的一定是同一個對象。

  3. Object 基類的 equals 默認比較兩個對象的內存地址,在構建的對象沒有重寫 equals 方法的時候,與 == 操作符比較的結果相同。

  4. equals 用于比較引用數據類型是否相等。在滿足equals 判斷規則的前提下,兩個對象只要規定的屬性相同我們就認為兩個對象是相同的,比如 String 就重寫了 equals 方法,所以可以用 equals 去實現比較字符串是否相等

equal函數比較的到底是什么,很明顯是比較的值,但是是什么值?與==比較的堆中的內存地址不同,其比較的是對象的值,包括各個屬性的值。我們在重新overriding此方法時首先要判斷是否為同一對象,如果是同一對象那么肯定返回為true,如果不是但比較的屬性相同,那么對象“相等”,否則返回false。

java中的數據類型,可分為兩類:

1.基本數據類型,也稱原始數據類型。byte,short,int,long,float,double,boolean,char,

他們之間的比較,應用雙等號(==),比較的是他們的值。

2.復合數據類型(類)

當他們用(==)進行比較的時候,比較的是他們在內存中的存放地址,所以,除非是同一個new出來的對象,他們的比較后的結果為true,否則比較后結果為false。 JAVA當中所有的類都是繼承于Object這個基類的,在Object中的基類中定義了一個equals的方法,這個方法的初始行為是比較對象的內存地 址,但在一些類庫當中這個方法被覆蓋掉了,如String,Integer,Date在這些類當中equals有其自身的實現,而不再是比較類在堆內存中的存放地址了。

對于復合數據類型之間進行equals比較,在沒有覆寫equals方法的情況下,他們之間的比較還是基于他們在內存中的存放位置的地址值的,因為Object的equals方法也是用雙等號(==)進行比較的,所以比較后的結果跟雙等號(==)的結果相同。

1 public class TestString {

2 public static void main(String[] args) {

3 String s1= "Monday";

4 String s2 = "Monday";

5 if (s1== s2)

6 {

7 System.out.println("s1== s2");}

8 else{

9 System.out.println("s1!= s2");}

10 }

11 }

編譯并運行程序,輸出:s1== s2 說明:s1與 s2 引用同一個 String 對象 -- "Monday"!

2.再稍微改動一下程序,會有更奇怪的發現:

public class TestString {

public static void main(String[] args) {

String s1= "Monday";

String s2 = new String("Monday");

if (s1== s2)

{System.out.println("s1== s2");}

else

{System.out.println("s1!= s2");}

if (s1.equals(s2)) {System.out.println("s1equals s2");}

else{

System.out.println("s1not equals s2");}

}

}

我們將s2用new操作符創建

程序輸出:s1!= s2s1equals s2

說明:s1s2分別引用了兩個"Monday"String對象

  1. 字符串緩沖池

原來,程序在運行的時候會創建一個字符串緩沖池當使用 s2 = "Monday" 這樣的表達是創建字符串的時候,程序首先會在這個String緩沖池中尋找相同值的對象,在第一個程序中,s1先被放到了池中,所以在s2被創建的時候,程序找到了具有相同值的 s1

將s2引用s1所引用的對象"Monday"

第二段程序中,使用了 new 操作符,他明白的告訴程序:"我要一個新的!不要舊的!"于是一個新的"Monday"Sting對象被創建在內存中。他們的值相同,但是位置不同,一個在池中游泳一個在岸邊休息。哎呀,真是資源浪費,明明是一樣的非要分開做什么呢?

4.再次更改程序:

public class TestString {

public static void main(String[] args) {

String s1 = "Monday";

String s2 = new String("Monday");

s2 = s2.intern();

if (s1 == s2)

{System.out.println("s1 == s2");}

else

{System.out.println("s1 != s2");}

if (s1.equals(s2)) {System.out.println("s1 equals s2");}

else{

System.out.println("s1 not equals s2");}

}

}

這次加入:s2 = s2.intern();

程序輸出:

s1 == s2

s1 equals s2

原 來,(java.lang.String的intern()方法"abc".intern()方法的返回值還是字符串"abc",表面上看起來好像這個方 法沒什么用處。但實際上,它做了個小動作:檢查字符串池里是否存在"abc"這么一個字符串,如果存在,就返回池里的字符串;如果不存在,該方法會 把"abc"添加到字符串池中,然后再返回它的引用。 )

String.intern();

再補充介紹一點:存在于.class文件中的常量池,在運行期間被jvm裝載,并且可以擴充。String的intern()方法就是擴充常量池的一個方法;當一個String實例str調用intern()方法時,java查找常量池中是否有相同unicode的字符串常量,如果有,則返回其引用,如果沒有,則在常量池中增加一個unicode等于str的字符串并返回它的引用。

例3:

String s0 = ”kvill”;

String s1 = new String(“kvill”);

String s2 = new String(“kvill”);

System.out.println(s0==s1);

S1.intern();

S2=s2.intern();

System.out.println(s0==s1);

System.out.prntln(s0==s1.intern());

System.out.println(s0==s2);

結果為:

False

False//雖然執行了s1.intern(),但它的返回值沒有賦給s1

True

True

最后再破除一個錯誤的理解:

有人說,“使用String.intern()方法可以將一個String類保存到一個全局的String表中,如果具有相同值的unicode字符串已經在這個表中,那么該方法返回表中已有字符串的地址,如果在表中沒有相同值的字符串,則將自己的地址注冊到表中”如果把這個全局的String表理解為常量池的話,最后一句話“如果在表中沒有相同值的字符串,則將自己的地址注冊到表中”是錯的。

例4:

String s1 = new String(“kvill”);

String s2 = s1.intern();

System.out.println(s1==s1.intern());

System.out.println(s1+””+s2);

System.out.println(s2==s1.intern());

結果是:

False

Kvillkvill

True

我們沒有聲明一個”kvill”常量,所以常量池中一開始沒有”kvill”的,當我們調用s1.intern()后就在常量池中新添加了一個”kvill”常量,原來的不在常量池中的”kvill”仍然存在,也就不是“把自己的地址注冊到常量池中”了。

例5:

String str1 =” java”;

String str2 = ”blog”;

String s =str1+str2;

System.out.println(s==”javablog”);

結果是false。Jvm確實對型如String str1=”java”;的String對象放在常量池里,但是它是在編譯時那么做的,而Strings=str1+str2;是在運行時刻才能知道,也就是說str1+str2是在堆里創建的,所以結果為false了。

String,StringBuffer和StringBuilder的區分和使用場景?

一般來說,三者的速度是:StringBuilder > StringBuffer > String。

但是,在String a = "how" + "old" + "are" + "you"。這種直接拼接的情況下,String速度最高。這是因為jvm的優化問題,jvm會自動識別,把"how" + "old" + "are" + "you"直接當做'how old are you"。

StringBuilder是jdk1.5引入的,1.5之前只能使用StringBuffer。

StringBuffer是線程安全的,StringBuilder是非線程安全的。

String的使用場景:在字符串不常變化的情況下,例如進行字符串的復制和簡單得拼接

StringBuffer的使用場景:在字符串經常進行運算的,且在多線程的情況下,例如xml解析和StringBuffer參數的拼接

StringBuilder的使用場景:在字符串經常進行運算的,且在單線程的情況下,例如SQL語句的拼裝。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容