深入理解String類


1、String str = "eee" 和String str = new String("eee")的區別

先看一小段代碼,

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

 

 

2、從JVM角度分析

《深入理解Java虛擬機》一書指出,JVM運行時數據區如下:

所有線程共享區域包括:

方法區:用於存儲已被虛擬機加載的類信息、常亮、靜態變量、即時編譯器編譯后的代碼等數據,以及運行時常量池

Java堆:在虛擬機啟動時創建,存放對象實例,幾乎所有的對象實例都在這里分配內存。

線程私有區域包括:

虛擬機棧:用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。

本地方法棧:與虛擬機棧類似, 區別主要是本地方法棧為Native方法服務。

程序計數器:一塊較小的內存空間,當作當前線程所執行字節碼的行號指示器。字節碼解釋器工作時通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能需要依賴這個計數器來完成。

 

String是一個不可變對象,可以認為是特殊的常量,因此存在方法區的運行時常量池中,可以被共享使用,以提高效率。

從JVM角度分析以上代碼:

1   String str1 = "eee";    //1、在運行時常量池中創建新的對象"eee",如果常量池中不存在的話;2、棧中創建對象的引用str1
2   String str2 = "eee";    //由於運行時常量池中已經存在該對象,直接在棧中創建對象的引用str2即可。
3   String str3 = new String("eee");    //1、通過new指令,在堆中創建新的對象,2、在棧中創建對象的引用str3。

對象之間通過==來比較,比較的是對象的引用。因此也就不難理解為什么str1 == str2, 而str != str3了。

而equals方法比較的是什么呢?如果類沒有重寫Object類中equals方法時,比較的也就是對象的引用;如果重寫了equals方法,那么就要看重寫的方法了。

 

3、從代碼角度分析

在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。

 

4、不建議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指向的是同一個對象。

改正方法為

1、第33行修改為private String stringB = new String("synchronized");  

2、更好的做法是不使用String對象用來同步鎖。


免責聲明!

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



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