1.String str=“aaa“與 String str=new String(“aaa”)一樣嗎?


String x = "張三";
String y = "張三";
String z = new String("張三");
System.out.println(x == y); // true
System.out.println(x == z); // false

String x = "張三" 的方式,Java 虛擬機會將其分配到常量池中,而常量池中沒有重復的元素,比如當執行“張三”時,java虛擬機會先在常量池中檢索是否已經有“張三”,如果有那么就將“張三”的地址賦給變量,如果沒有就創建一個,然后在賦給變量;

String z = new String(“張三”) 則會被分到堆內存中,即使內容一樣還是會創建新的對象。

public class StringTest {
    public static void main(String[] args){
        String s1="Hello";
        String s2="Hello";
        String s3=new String("Hello");
        System.out.println("s1和s2 引用地址是否相同:"+(s1 == s2));//true
        System.out.println("s1和s2 值是否相同:"+s1.equals(s2));//true
        System.out.println("s1和s3 引用地址是否相同:"+(s1 == s3));//false
        System.out.println("s1和s3 值是否相同:"+s1.equals(s3));//false
    }
}

"==“是判斷兩個對象引用的地址是否相同,也就是判斷是否為同一個對象.

s1與s2 返回為true,s1與s3返回則是false。說明s1與s2 引用的同一個對象的地址,s3則與其它兩個引用不是同一個對象地址。

Java為了避免產生大量的String對象,設計了一個字符串常量池。工作原理是這樣的,創建一個字符串時,JVM首先為檢查字符串常量池中是否有值相等的字符串,如果有,則不再創建,直接返回該字符串的引用地址,若沒有,則創建,然后放到字符串常量池中,並返回新創建的字符串的引用地址。所以上面s1與s2引用地址相同。

那為什么s3與s1、s2引用的不是同一個字符串地址呢?

String s3=new String(“Hello”); JVM首先是在字符串常量池中找"Hello” 字符串,如果沒有就創建字符串常量,然后放到常量池中,若已存在,則不需要創建;當遇到 new 時,還會在內存(不是字符串常量池中,而是在堆里面)上創建一個新的String對象,存儲"Hello"地址,並將內存上的String對象引用地址返回,所以s3與s1、s2引用的不是同一個字符串地址。

s1與s2指向的都是常量池中的字符串常量,所以它們比較的是同一塊內存地址,而s3指向的是堆里面的一塊地址,說的具體點應該是堆里面的Eden區域,s1跟s3,s2跟s3比較都是不相等的,都不是同一塊地址。

了解了String類的工作原理,回歸問題本身:
在String的工作原理中,已經提到了,new一個String對象,是需要先在字符串常量中查找相同值或創建一個字符串常量(有則用沒有則創建),然后再在內存中創建一個String對象,所以String str = new String(“xyz”); 會創建一個或兩個對象。

相關延申:

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

我們可以把上面這行代碼分成String str、=、"abc"和new String()四部分來看待。String str只是定義了一個名為str的String類型的變量,因此它並沒有創建對象;=是對變量str進行初始化,將某個對象的引用(或者叫句柄)賦值給它,顯然也沒有創建對象;現在只剩下new String("abc")了。那么,new String("abc")為什么又能被看成"abc"和new String()呢?
我們來看一下被我們調用了的String的構造器:  
public String(String original) {  //other code ...  }   大家都知道,我們常用的創建一個類的實例(對象)的方法有以下兩種:
一、使用new創建對象。 
二、調用Class類的newInstance方法,利用反射機制創建對象。
我們正是使用new調用了String類的上面那個構造器方法創建了一個對象,並將它的引用賦值給了str變量。同時我們注意到,被調用的構造器方法接受的參數也是一個String對象,這個對象正是"abc"。由此我們又要引入另外一種創建String對象的方式的討論——引號內包含文本。
這種方式是String特有的,並且它與new的方式存在很大區別。  
String str="abc";  
毫無疑問,這行代碼創建了一個String對象。  
String a="abc";  String b="abc";   那這里呢?
答案還是一個。  
String a="ab"+"cd";   再看看這里呢?
答案是三個。
說到這里,我們就需要引入對字符串池相關知識的回顧了。  
在JAVA虛擬機(JVM)中存在着一個字符串池,其中保存着很多String對象,並且可以被共享使用,因此它提高了效率。由於String類是final的,它的值一經創建就不可改變,因此我們不用擔心String對象共享而帶來程序的混亂。字符串池由String類維護,我們可以調用intern()方法來訪問字符串池。  
我們再回頭看看String a="abc";,這行代碼被執行的時候,JAVA虛擬機首先在字符串池中查找是否已經存在了值為"abc"的這么一個對象,它的判斷依據是String類equals(Object obj)方法的返回值。如果有,則不再創建新的對象,直接返回已存在對象的引用;如果沒有,則先創建這個對象,然后把它加入到字符串池中,再將它的引用返回。因此,我們不難理解前面三個例子中頭兩個例子為什么是這個答案了。
只有使用引號包含文本的方式創建的String對象之間使用“+”連接產生的新對象才會被加入字符串池中。對於所有包含new方式新建對象(包括null)的“+”連接表達式,它所產生的新對象都不會被加入字符串池中,對此我們不再贅述。因此我們提倡大家用引號包含文本的方式來創建String對象以提高效率,實際上這也是我們在編程中常采用的。
棧(stack):主要保存基本類型(或者叫內置類型)(char、byte、short、int、long、float、double、boolean)和對象的引用,數據可以共享,速度僅次於寄存器(register),快於堆。 
堆(heap):用於存儲
PS:對於String s5 = new String("Hello World"); 創建了幾個對象,我先前理解有誤。
總結各種看法,因為有new,所以堆中必然有一個對象。另外,如果常量池中已有"Hello World",則不創建,沒有,則在常量池中創建。所以這句代碼,究竟在內存中創建一個還是兩個對象,視情況而定。 

原文參考:https://blog.csdn.net/u013003837/article/details/28862657


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM