【JAVA】String中兩種初始化方式比較

方法1:String s1 = "abc";

  • 這一句話作了什麼操做:

    • 首先在常量池中查找"abc",若是沒有則在常量池建立該對象
    • 在棧中建立s1的引用,將s1直接指向對象"abc"
    • 所以在這裏"abc"是常量池中的對象,若是聲明另外一個String類型的對象引用,並將它指向對象"abc",則這兩個引用指向的是同一個常量池中的對象。
  • 詳述

    • 建立過程是,"abc"是字符串,是匿名的String對象。
    • 當"abc"被聲明時,會先去字符串常量池中(位於堆內存中)查詢是夠存在該字符串了,若是存在將會返回常量池中的字符串引用地址,將此賦值給s。
    • 若是沒有,將會把字符串"abc"存入到常量池中,而後進行再返回地址。
    • 下次再使用該字符串時,直接返回該字符串再常量池中的地址便可。
    • 這種建立對象的方式叫作享元模式,在Interger類中對[-128~127)之間的數值也是使用了該模式。
  • 代碼示例:

String s1 = "abc";//"abc"是一個對象,將對象賦予類變量s1
String s2 = new String("abc");//這裏是兩個對象,在內存中存在兩個,包括對象abc 和 new 出來的對象
String s3 = "abc";  //由於String類型數據是不可變的,因此‘abc’被放在了常量池中,這裏的‘abc’ַ和s1的‘abc’是
							//同一個常量abc對象,所以兩者的內存地址是同樣的。
							
System.out.println(s1==s2);//false
System.out.println(s1==s3);//true 這是這號i
  • 圖示辨析:

public class StringDemo {
    public static void main(String[] args) {
        String str = "Hello";
        str = str + "World";
        str += "!!!";
        System.out.println(str);
 
    }
}

public static void main(String[] args) {
    String stra = "hello" ;
    String strb = "hello" ;
    String strc = "hello" ;
    System.out.println(stra == strb);//true
    System.out.println(stra == strc);//true
    System.out.println(strb == strc);//true
    }
}

方法2:String s = new String(「abc」);

  • 凡是通過 new 建立出來的對象,都會在堆內存中分配新的空間,建立新的對象,因此s是String類新建立的對象

兩種初始化方法對比

String s = "aa";
s =s + "bb";
String s2 = "aabb";
s == s2;???

這個的結果是false,這時候s 和s2已經不是同樣的了,首先看 s2,s2指向的是常量池中的對象,這是肯定的。因此儘管s的值和s2是同樣的,可是s指向的不是常量池的中的對象,而是一個新的new出來的對象。 解釋以前,先了解一下 + 這個符號,在字符串拼接裏面,至關於+ 源碼大意爲: (new StringBuffer()).append(s3).append(「bbb」).toString; 因此,這裏的s指向的是一個新的對象。緩存

總結: 在String的兩種聲明方式,直接賦予字符值的是,String對象引用獲取常量池中對象的地址,因此String聲明出來是不能夠改變的。new String()出來的是在堆內存建立對象。若是要給每一個對象中的String屬性賦予一個初始值,採用String s = ‘abc’方式,這樣建立的是常量池中的一個對象,其餘對象是獲取這個常量的地址。要是new 則每次都要建立,加大內存消耗。還要注意,字符串拼接不要用+ ,會建立對象。安全

  • 代碼示例

public class Demo1 {

	@Test
	public void test1() {
		String s1 = "abc";//"abc"是一個對象,將對象賦予類變量s1
		String s2 = new String("abc");//這裏是兩個對象,在內存中存在兩個,包括對象abc 和 new 出來的對象
		String s3 = "abc";  //由於String類型數據是不可變的,因此‘abc’被放在了常量池中,這裏的‘abc’ַ和s1的‘abc’是
							//同一個常量abc對象,所以兩者的內存地址是同樣的。
		
		System.out.println(s1==s2);//false
		System.out.println(s1==s3);//true 這是這號i
		
		//+ 源碼大意爲: (new StringBuffer()).append(s3).append("bbb").toString;
		//是新new出一個新的StringBuffer對象,
		s3 = s3+"bbb";//這時候s3已經不指向"abc",源對象依舊存在,s3是新的string類型的對象
		String s4 = "abcbbb";
		String s5 = new String("abcbbb");
		System.out.println(s3);
		System.out.println(s3==s4);//false  s3是一個新的String對象
		System.out.println(s4=="abcbbb");//true  這個「abcbbb」屬於同一個常量池中的對象
		System.out.println(s4==s5);//false 一個在常量池,一個是new的對象
	}
}

intern()方法

  • 概述

    • intern()方法是能使一個位於堆中的字符串在運行期間動態地加入到字符串常量池中(字符串常量池的內容是程序啓動的時候就已經加載好了),若是字符串常量池中有該對象對應的字面量,則返回該字面量在字符串常量池中的引用,不然,建立複製一份該字面量到字符串常量池並返回它的引用。
  • 代碼示例:

String s1 = "abc";
        String s2 = new String("abc");
        String s3 = new String("abc").intern();//s3實際上是字符串常量"abc"
    	
		/*
		s1是常量池中的對象,s2是堆中對象,是不一樣對象
		*/
        System.out.println(s1 == s2);//false
		
		//二者都是表示字符串常量abc,因此是true
        System.out.println(s1 == s3);//true
		
		//s3是常量池中的對象abc,s2是堆中對象,是不一樣對象
        System.out.println(s2 == s3);
		
		//都表示一個值abc
        System.out.println(s1 == "abc");  //true
        System.out.println(s3 == "abc");  //true

字符串內容不可變屬性分析

  • 弊端:

    • 字符串內容的更改,實際上改變的是字符串對象的引用過程,而且會伴隨有大量的垃圾出現,在實際開發中應該避免。
  • 好處:

    • 能夠緩存hash值:
      • String的值是不容許改變的,所以hash值就能夠計算出來,緩存起來,不要要每次使用都計算。
    • String pool的須要
      • 若是一個String對象已經建立過了,那麼將會從String pool中取得引用,只有String不可變,才能保證其餘使用該對象是安全的。
    • 線程安全
      • String 不可變性天生具有線程安全,能夠在多個線程中安全地使用。
    • 安全性
      • String常常做爲參數,String的不可變特性能夠保證參數不可變。列如在做爲網絡鏈接參數的狀況下,若是String可變,那麼在網絡鏈接過程當中,String被改變,改變String對象的哪一方覺得如今鏈接的是其餘主機,而實際狀況卻不是。

參考連接

END

相關文章
相關標籤/搜索