Java基礎——字符串

1. 字符串不是基本數據類型,而是一個類

Java沒有內置的字符串類型,而是在標準Java類庫中提供了一個預定義類,叫做String。每個用雙引號括起來的字符串都是String類的一個實例。

2. 字符串的存儲機制 

這裏會用到Java虛擬機的知識,關於此部分不做贅述。

字符串的初始化有兩種方式: String s1 = "abc";   String s3 = new String("abc");

 (1)以字面量形式創建字符串對象

(字面量:文本字符串、聲明爲final的常量值等)

String s1 = "abc";  對象引用存放在Java虛擬機棧的局部變量表中,而字符串對象”abc“存儲在方法區中的運行時常量池中。當代碼中出現以字面量形式創建字符串對象時,JVM首先會對這個字面量進行檢查,如果字符串常量池中存在相同內容的字符串對象,判斷依據String.equals(Object obj),也是就判斷的內容是否相同,如果存在則將這個字符串的引用返。如果不存在則創建該字符串對象,然後把它加入到字符串常量池中,並返回它的引用。

String s1 = "abc"; // 1

String s2= "abc"; // 2

當執行第1條語句時,JVM檢測“abc”這個字面量,這裏我們認爲沒有內容爲“abc”的對象存在。JVM通過字符串常量池查找不到內容爲“abc”的字符串對象存在,那麼就創建這個字符串對象,然後將剛創建的對象放入到字符串常量池中,並且將引用返回給變量s1。當執行第2條語句時,JVM還是要檢測這個字面量,JVM通過查找字符串常量池,發現內容爲“abc”字符串對象存在,於是將已經存在的字符串對象的引用返回給變量s2,這裏不會重新創建新的字符串對象。 

(2)new一個字符串對象

String s3 = new String("abc"); 相當於"abc" 和 new String()兩個操作。先在虛擬機棧中創建一個對String類的對象引用變量s3,然後new()操作會在Java堆中產生一個新的對象"abc",並將s3指向堆中的"abc",同時檢查字符串常量池中是否有對象"abc",如果沒有則產生一個字符串常量"abc",並將它添加到字符串常量池中;如果有則不產生。

new String("abc")創建了幾個對象?

答案:一個或兩個。如果常量池中原來有"abc",那麼只需要在Java堆中創建新的對象;如果常量池中沒有,那麼除了在Java堆中創建對象,還需在字符串常量池中創建”abc“.

	String s1 = "abc"; //在字符串常量池中創建一個“abc”字符串對象
	String s2 = "abc"; //“abc”已經在常量池中存在,只需要將引用s2指向“abc”
	String s3 = new String("abc"); //在堆中創建新對象
	String s4 = new String("abc"); //在堆中又創建一個新對象

 對應的以上代碼存儲位置爲

 

  3. 字符串的"=="、equals 和 hashCode的區別

 (1)”==“運算符用來比較兩個變量的值是否相等。

如果兩個變量是基本數據類型,可以直接使用”==“來比較其對應的值是否相等。

如果變量是引用類型,使用”==“只能判斷兩個變量是否指向同一塊存儲空間。

想要比較這兩個對象的內容是否相等,就需要使用equals方法。

(2)equals方法

equals方法是Object類提供的方法之一。Object類中定義的equals(Object)方法是直接使用”==“運算符比較的兩個對象,所以在沒有覆蓋equals(Object)方法的情況下,equals(Object)與”==“一樣,比較的是引用。

在字符串中,覆蓋了Object的equals方法,比較是兩個對象的內容是否相等。

我們來看下面這一段代碼:

public class StringTest{
	
	public static void main(String[] args) {
		String s1 = "abc"; //在字符串常量池中創建一個“abc”字符串對象
		String s2 = "abc"; //“abc”已經在常量池中存在,只需要將引用s2指向“abc”
		String s3 = new String("abc"); //在堆中創建新對象
		String s4 = new String("abc"); //在堆中又創建一個新對象
		System.out.println(s1==s2);            //true
		System.out.println(s1==s3);            //false
		System.out.println(s3==s4);            //false
		
		System.out.println(s1.equals(s2));     //true
		System.out.println(s1.equals(s3));     //true
		System.out.println(s3.equals(s4));     //true
	}
}

運行結果

true
false
false
true
true
true

 s1和s2都指向字符串常量池中abc,因此這兩個變量所對應內存中的數值(即地址)相等,“s1==s2”返回true;

而s3指向堆中的new String(“abc”),因此返回false; 而s4指向的是堆中另一個new String(“abc”),“s1==s3”和"s3==s4"都返回fasle

equals比較的是字符串內容是否相同,所有後面三個都返回true.

再添幾行代碼:

		String s5 = s1;
		String s6 = s1 + "";
		String s7 = "ab"+"c";
		System.out.println(s1==s5);      //true
		System.out.println(s1==s6);      //false
		System.out.println(s1==s7);      //true

運行結果:

true
false
true 

語句“String s5 = s1;”直接將s1的引用賦給s5; 所以“s1==s5”返回true;

而語句“String s6 = s1 +"" ”;採用拼接的方式將變量s1和空字符串拼接起來,雖然沒有看到new關鍵詞,但是由於String類“+”操作符被重載之後,重載的方法之中一定會重新new一個String,所以s6存儲在堆中。 “s1==s6”返回false;

語句“String s7 = "ab" +"c" ”;採用拼接的方式將常量“ab”和常量“c”拼接起來,在編譯器就轉換爲“abc”,由於常量池中已經存在“abc”,所以直接將引用賦給s7。 “s1==s7”返回true;

此外,字符串還提供了intern()方法,String的intern()方法是一個本地方法,定義爲public native String intern(); intern()方法的價值在於讓開發者能將注意力集中到String池上。當調用 intern 方法時,如果池已經包含一個等於此 String 對象的字符串(該對象由 equals(Object) 方法確定),則返回池中的字符串。否則,將此 String 對象添加到池中,並且返回此 String 對象的引用。

再添加代碼

		System.out.println(s3.intern() == s1);             //true
		System.out.println(s3.intern() == s4.intern());    //true

運行結果:

true
true

 s3.intern( ) 和 s4.intern( ) 都指向字符串常量池中的“abc”,所以都返回true.

(3) hashCode()方法

 hashCode()方法也是從Object類中繼承過來的,Object類中的hashCode()方法返回對象在內存中地址轉換成的一個int值,所以如果沒有重寫hashCode()方法,任何對象的hashCode()方法都是不相等的。

一般來講,equals()方法是給用戶調用的,如果需要判斷兩個對象是否相等,可以重寫equals()方法,然後在代碼中調用。對於hashCode()方法用戶一般不去調用它,例如在hashmap中,由於key是不可以重複的,它在判斷key是否重複時就判斷了hashCode()方法,而且也用到了equals()方法。此處“不可重複”指的是equals()和hashCode()只要有一個不等就可以了。

一般覆蓋equals()方法的同時也要覆蓋hashCode()方法,否則,會違反Object.hashCode的通用約定。

x.equals(y)返回true ——> x.hashCode() 等於 y.hashCode();

x.equals(y)返回false ——> x.hashCode() 有可能等於 y.hashCode(); 也有可能不等。

x.hashCode() 不等於 y.hashCode();  ——> x.equals(y)一定返回false;

x.hashCode() 等於 y.hashCode();  ——> x.equals(y)可能返回true,也可能返回false。

4. 字符串的常用方法

(1) 創建並初始化字符串:

  1). 使用字符串常量直接初始化 String s="hello!";

  2). 使用構造方法創建並初始化

    new String();//初始化一個對象,表示空字符序列

    new String(value);//利用已存在的字符串常量創建一個新的對象

    new String (char[] value);//利用一個字符數組創建一個字符串

    new String(char[] value,int offset,int count);//截取字符數組offset到count的字符創建一個非空串

    new String(StringBuffer buffer);//利用StringBuffer對象初始化String對象

(2) 獲取字符串信息:

   下標:int indexOf(String str);    int lastIndexOf(String str); 

   字符:char charAt(int index)

   字節數組:byte[ ] getBytes()

   長度:int length()

(3)判斷字符串

    相等:bool equals(String str)

    前綴:startsWith(前綴)

    後綴:endsWith(後綴)

    大小:int compareTo()

(4)替換字符串

    去掉前後空格:String trim()

    替換:String replace(CharSequence oldString, CharSequence newString)

(5)截取字符串

    單點截取:subString(int beginIndex)

    雙點截取:subString(int beginIndex,int endIndex)

(6)轉換

            轉換爲字符數組:char[ ] toCharArray()

              分割爲字符串數組:String[] split(分隔符)

              基本數據型態轉換爲字符串: String.valueOf();

5. 空串與null串 

(1)空串是一個Java對象,是長度爲0的字符串,有自己的串長度(0)和內容(空)。

判斷一個字符串是否爲空:

if(str.length() == 0) 或  if(str.equals(“”))

 (2)String s  = null; 表示目前沒有任何對象與變量s關聯。

判斷一個字符串是否爲空:

if(str == null)

檢查一個字符串既不是null也不是空串: if(str != null && str.length() != 0)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章