一、String str = "eee" 和String str = new String("eee")的區別java
先看一小段代碼,ide
1 public static void main(String[] args) { 2 String str1 = "eee"; 3 String str2 = "eee"; 4 String str3 = new String("eee"); 5 System.out.println("str1 == str2 is " + (str1 == str2)); 6 System.out.println("str1 == str3 is " + (str1 == str3)); 7 System.out.println("str1.equals(str2) is " + str1.equals(str2)); 8 System.out.println("str1.equals(str3) is " + str1.equals(str3)); 9 }
運行結果爲:函數
str1 == str2 is true str1 == str3 is false str1.equals(str2) is true str1.equals(str3) is true
二、從JVM角度分析post
《深刻理解Java虛擬機》一書指出,JVM運行時數據區以下:this
全部線程共享區域包括:spa
方法區:用於存儲已被虛擬機加載的類信息、常亮、靜態變量、即時編譯器編譯後的代碼等數據,以及運行時常量池。線程
Java堆:在虛擬機啓動時建立,存放對象實例,幾乎全部的對象實例都在這裏分配內存。code
線程私有區域包括:對象
虛擬機棧:用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。blog
本地方法棧:與虛擬機棧相似, 區別主要是本地方法棧爲Native方法服務。
程序計數器:一塊較小的內存空間,看成當前線程所執行字節碼的行號指示器。字節碼解釋器工做時經過改變這個計數器的值來選取下一條須要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能須要依賴這個計數器來完成。
String是一個不可變對象,能夠認爲是特殊的常量,所以存在方法區的運行時常量池中,能夠被共享使用,以提升效率。
從JVM角度分析以上代碼:
1 String str1 = "eee"; //一、在運行時常量池中建立新的對象"eee",若是常量池中不存在的話;二、棧中建立對象的引用str1 2 String str2 = "eee"; //因爲運行時常量池中已經存在該對象,直接在棧中建立對象的引用str2便可。 3 String str3 = new String("eee"); //一、經過new指令,在堆中建立新的對象,二、在棧中建立對象的引用str3。
對象之間經過==來比較,比較的是對象的引用。所以也就不難理解爲何str1 == str2, 而str != str3了。
而equals方法比較的是什麼呢?若是類沒有重寫Object類中equals方法時,比較的也就是對象的引用;若是重寫了equals方法,那麼就要看重寫的方法了。
三、從代碼角度分析
在jdk1.8中查看String類的源碼,
1 public final class String 2 implements java.io.Serializable, Comparable<String>, CharSequence { 3 private final char value[]; 4 private int hash; // Default to 0 5 6 public String(String original) { 7 this.value = original.value; 8 this.hash = original.hash; 9 } 10 11 /** 實際比較的是value[]是否相等 */ 12 public boolean equals(Object anObject) { 13 if (this == anObject) { 14 return true; 15 } 16 if (anObject instanceof String) { 17 String anotherString = (String)anObject; 18 int n = value.length; 19 if (n == anotherString.value.length) { 20 char v1[] = value; 21 char v2[] = anotherString.value; 22 int i = 0; 23 while (n-- != 0) { 24 if (v1[i] != v2[i]) 25 return false; 26 i++; 27 } 28 return true; 29 } 30 } 31 return false; 32 } 33 }
根據源代碼能夠看出,String類的equals方法比較的實際是value[]是否相等。根據構造函數以及以前的JVM內存模型,能夠分析出str1,str2,str3在內存中關係以下:
能夠很容易的理解,str1.equals(str3)爲true。
四、不建議String對象做爲鎖去同步
直接看一個例子,
1 public class StringAsSynchronized { 2 public static class Service { 3 public void print(String stringParam) { 4 try { 5 synchronized (stringParam) { 6 while (true) { 7 System.out.print(Thread.currentThread().getName()); 8 Thread.sleep(1000); 9 } 10 } 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 } 15 } 16 17 public static class ThreadA extends Thread { 18 private Service service; 19 private String stringA = "synchronized"; 20 21 public ThreadA(Service service) { 22 this.service = service; 23 } 24 25 @Override 26 public void run() { 27 service.print(stringA); 28 } 29 } 30 31 public static class ThreadB extends Thread { 32 private Service service; 33 private String stringB = "synchronized"; 34 35 public ThreadB(Service service) { 36 this.service = service; 37 } 38 39 @Override 40 public void run() { 41 service.print(stringB); 42 } 43 } 44 45 public static void main(String[] args) { 46 Service service = new Service(); 47 ThreadA a = new ThreadA(service); 48 a.setName("A"); 49 ThreadB b = new ThreadB(service); 50 b.setName("B"); 51 a.start(); 52 b.start(); 53 } 54 }
運行結果爲:AAAAAAAAA。。。。
緣由爲ThreadA類以及ThreadB類中的成員變量stringA以及stringB指向的是同一個對象。
改正方法爲
一、第33行修改成private String stringB = new String("synchronized");
二、更好的作法是不使用String對象用來同步鎖。