String類
首先,String是引用類型,也是唯一一個可以不使用關鍵字new創建對象的引用類型,因爲有字符串常量值,使用英文格式的雙引號""括起來就是字符串常量值。創建對象的兩種方式
方式一、String s1 = “abc”;
方式二、String s2 = new String(“abc”);
這兩種方式都是創建了一個字符串對象,值都是字符串abc;
不同的是
方式一是直接使用字符串常量abc賦值給變量s1,此時系統先到常量池中查找有沒有字符串常量abc,如果有將字符串常量abc在常量池中的地址賦值給變量s1,如果沒有會將字符串常量abc加入到常量池中,並把地址賦值給變量s1。
而方式二是在堆內存中創建一個String類型的對象,在這個對象中存儲着字符串abc
其次,String類型被設計成不可變的,即對象不可變;一旦對象被創建,這個對象將無法被修改,引用(變量)是可以修改;如上例中s1是引用(變量),它可以存儲其他字符串的地址,但是字符串abc本身被創建後無法再改變了。
實例:
//創建一個String類型的對象
String s = "abc";
//如果要將變量s的修改爲"abcxyz",代碼如下
s = s+"xyz";
//輸出結果爲:abcxyz
System.out.println(s);
//由於這裏使用兩個字符串常量相連接,系統會默認當成一個字符串,即"abcxyz"
String s1 = "abc" + "xyz";
String s2 = "abcxyz";
其中第二行代碼會先到常量池中查找有沒有字符串xyz,如果沒有將字符串xyz加入到常量池中,此時常量池中有abc和xyz兩個字符串常量;
然後系統底層會默認在堆中創建一個StringBuilder類型的對象;然後用StringBuilder對象的append方法,將兩個字符串連接到一起,最後再調用toString方法返回連接後的字符串abcxyz,並將返回的字符串abcxyz的地址賦值給變量s。
接下來驗證實例中變量s、s1和s2三個變量,引用的是不是一個對象。可以使用雙等號來進行判斷。
//輸出結果爲:false
System.out.println(s == s1);
//輸出結果爲:true
System.out.println(s1 == s2);
由以上結果可以看出,變量s1和s2中存儲的是同一字符串對象的地址,而變量s中存儲的是另一字符串對象的地址,但這兩個字符串對象的內容是相同的(都是abcxyz);那麼如何判斷兩個字符串的內容是否相同呢?
在String類中已經重寫了父類Object中的equals方法,通過equals方法可以判斷兩個字符串內容是否相同
//輸出結果爲:true
System.out.println(s.equals(s1));
總結:
當使用字符串常量時,系統會先到常量池中尋找,如果沒找到,將字符串常量加入到常量池中。
使用關鍵字new創建的字符串對象保存在堆內存中。
使用字符串連接符+連接兩個字符串常量時,系統會自動將它們當成一個字符串常量,並將其存儲在常量池中
使用字符串連接符+字符串變量時,連接後的字符串存儲在堆內存中
比較兩個字符串的內容是否相同使用equals方法
所有引用類型使用雙等號==比較時,比較的是對象地址是否相同,即是否是同一對象
以下內容初學者選讀
//類A的源碼
public class A{
String s = "abc" + "d";
}
//打開字節碼文件的命令:javap -verbose A
//以下是類A編譯後class文件的內容
//關鍵行#2和5: ldc,可以看到Java編譯器是將"abc" + "d"當做"abcd"來處理的
Classfile /H:/A.class
Last modified 2015-5-29; size 239 bytes
MD5 checksum 271095a9d37f742c9bdefa883c6b9a00
Compiled from "A.java"
public class A
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#14 // java/lang/Object."<init>":()V
#2 = String #15 // abcd
#3 = Fieldref #4.#16 // A.s:Ljava/lang/String;
#4 = Class #17 // A
#5 = Class #18 // java/lang/Object
#6 = Utf8 s
#7 = Utf8 Ljava/lang/String;
#8 = Utf8 <init>
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 SourceFile
#13 = Utf8 A.java
#14 = NameAndType #8:#9 // "<init>":()V
#15 = Utf8 abcd
#16 = NameAndType #6:#7 // s:Ljava/lang/String;
#17 = Utf8 A
#18 = Utf8 java/lang/Object
{
java.lang.String s;
descriptor: Ljava/lang/String;
flags:
public A();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String abcd
7: putfield #3 // Field s:Ljava/lang/String;
10: return
LineNumberTable:
line 1: 0
line 2: 4
}
SourceFile: "A.java"
//類B源碼
public class B{
String s = "abc";
String s1 = s+"d";
}
//類B的class文件
//首先看#2和#7,這裏不在是當做一個字符串來處理的了
//然後看5: ldc和25: ldc,這兩行是將兩個字符串常量壓棧
//11: new,這裏創建了一個StringBuilder類的對象
//22: invokevirtual和27: invokevirtual調用了StringBuilder對象的append方法,將兩個字符串追加到一起
//最後30: invokevirtual,調用了StringBuilder對象的toString方法,獲得連接後的字符串"abcd"
Classfile /H:/B.class
Last modified 2015-5-29; size 443 bytes
MD5 checksum 3eaa63a1c104ccfe26e2a6f9df85d93c
Compiled from "B.java"
public class B
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #11.#21 // java/lang/Object."<init>":()V
#2 = String #22 // abc
#3 = Fieldref #10.#23 // B.s:Ljava/lang/String;
#4 = Class #24 // java/lang/StringBuilder
#5 = Methodref #4.#21 // java/lang/StringBuilder."<init>":()V
#6 = Methodref #4.#25 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#7 = String #26 // d
#8 = Methodref #4.#27 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#9 = Fieldref #10.#28 // B.s1:Ljava/lang/String;
#10 = Class #29 // B
#11 = Class #30 // java/lang/Object
#12 = Utf8 s
#13 = Utf8 Ljava/lang/String;
#14 = Utf8 s1
#15 = Utf8 <init>
#16 = Utf8 ()V
#17 = Utf8 Code
#18 = Utf8 LineNumberTable
#19 = Utf8 SourceFile
#20 = Utf8 B.java
#21 = NameAndType #15:#16 // "<init>":()V
#22 = Utf8 abc
#23 = NameAndType #12:#13 // s:Ljava/lang/String;
#24 = Utf8 java/lang/StringBuilder
#25 = NameAndType #31:#32 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#26 = Utf8 d
#27 = NameAndType #33:#34 // toString:()Ljava/lang/String;
#28 = NameAndType #14:#13 // s1:Ljava/lang/String;
#29 = Utf8 B
#30 = Utf8 java/lang/Object
#31 = Utf8 append
#32 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#33 = Utf8 toString
#34 = Utf8 ()Ljava/lang/String;
{
java.lang.String s;
descriptor: Ljava/lang/String;
flags:
java.lang.String s1;
descriptor: Ljava/lang/String;
flags:
public B();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String abc
7: putfield #3 // Field s:Ljava/lang/String;
10: aload_0
11: new #4 // class java/lang/StringBuilder
14: dup
15: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
18: aload_0
19: getfield #3 // Field s:Ljava/lang/String;
22: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
25: ldc #7 // String d
27: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
30: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
33: putfield #9 // Field s1:Ljava/lang/String;
36: return
LineNumberTable:
line 1: 0
line 2: 4
line 3: 10
}
SourceFile: "B.java"
//類C源碼
public class C{
String s = new String("abc");
String s1 = s+"d";
}
//C.class文件內容
//這裏主要演示類B中String s = "abc";和類C中String s = new String("abc");的區別
//首先5: new 這裏創建了一個String對象
//然後9: ldc 字符串常量"abc"被壓棧
//最後11: invokespecial 初始化String對象
//而類B只有ldc將常量壓棧這一步
Classfile /H:/C.class
Last modified 2015-5-29; size 506 bytes
MD5 checksum 97026d45729fe35e1f470e17e1b5f0e0
Compiled from "C.java"
public class C
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #13.#23 // java/lang/Object."<init>":()V
#2 = Class #24 // java/lang/String
#3 = String #25 // abc
#4 = Methodref #2.#26 // java/lang/String."<init>":(Ljava/lang/String;)V
#5 = Fieldref #12.#27 // C.s:Ljava/lang/String;
#6 = Class #28 // java/lang/StringBuilder
#7 = Methodref #6.#23 // java/lang/StringBuilder."<init>":()V
#8 = Methodref #6.#29 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#9 = String #30 // d
#10 = Methodref #6.#31 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#11 = Fieldref #12.#32 // C.s1:Ljava/lang/String;
#12 = Class #33 // C
#13 = Class #34 // java/lang/Object
#14 = Utf8 s
#15 = Utf8 Ljava/lang/String;
#16 = Utf8 s1
#17 = Utf8 <init>
#18 = Utf8 ()V
#19 = Utf8 Code
#20 = Utf8 LineNumberTable
#21 = Utf8 SourceFile
#22 = Utf8 C.java
#23 = NameAndType #17:#18 // "<init>":()V
#24 = Utf8 java/lang/String
#25 = Utf8 abc
#26 = NameAndType #17:#35 // "<init>":(Ljava/lang/String;)V
#27 = NameAndType #14:#15 // s:Ljava/lang/String;
#28 = Utf8 java/lang/StringBuilder
#29 = NameAndType #36:#37 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#30 = Utf8 d
#31 = NameAndType #38:#39 // toString:()Ljava/lang/String;
#32 = NameAndType #16:#15 // s1:Ljava/lang/String;
#33 = Utf8 C
#34 = Utf8 java/lang/Object
#35 = Utf8 (Ljava/lang/String;)V
#36 = Utf8 append
#37 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#38 = Utf8 toString
#39 = Utf8 ()Ljava/lang/String;
{
java.lang.String s;
descriptor: Ljava/lang/String;
flags:
java.lang.String s1;
descriptor: Ljava/lang/String;
flags:
public C();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: new #2 // class java/lang/String
8: dup
9: ldc #3 // String abc
11: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
14: putfield #5 // Field s:Ljava/lang/String;
17: aload_0
18: new #6 // class java/lang/StringBuilder
21: dup
22: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
25: aload_0
26: getfield #5 // Field s:Ljava/lang/String;
29: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: ldc #9 // String d
34: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
37: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
40: putfield #11 // Field s1:Ljava/lang/String;
43: return
LineNumberTable:
line 1: 0
line 2: 4
line 3: 17
}
SourceFile: "C.java"