Interger是值傳遞還是地址(引用)傳遞?
阿新 • • 發佈:2018-12-03
首先,放一句話。引用型別都是傳遞引用。但是對於Integer這種包裝型別來說,可能會讓人產生誤區,比如看下面程式碼片段:
Integer i = new Integer(1);
Integer j = i;
System.out.println(j);
i = 2;
System.out.println(j);
System.out.println(i);
j的輸出結果都是1,i的輸出結果最後是2。
這是因為i這個引用指向的物件改變了,i=2這條語句你可以看成i=new Integer(2),而不是修改i最開始所指向的物件的值,這個值也不能改變。因為在Integer內部也是封裝了一個final修飾的int型別的值,這裡和String型別大同小異。也就是說包裝類和String型別一樣的,不可以改變這個包裝類的例項的值,我們對包裝類的賦值操作實際上是建立了一個新的物件,然後把這個物件交給這個引用去管理,因為包裝類的自動拆箱和裝箱,所以看起來這個操作和基本型別的賦值差不多,但是這裡確確實實是建立了一個新的物件。下面語句是包裝類中找到的一行程式碼:
private final int value;
那麼,如何證明包裝類確實是採用的地址傳遞呢?我們可以採用synchronize這個關鍵字來證明,具體思路如下:兩個執行緒對同一個數字進行自增操作,如果自增到一定大小時停止自增,並且輸出每次自增後的值。如果不使用synchronize這個關鍵字,那麼輸出的值可能會出現一些預料不到的情況,比如下面程式碼:
class Test { public static void main(String[] args) { Thread t1 = new Thread(new A()); Thread t2 = new Thread(new A()); t1.start(); t2.start(); } } class A implements Runnable { static Integer a = 100; static int val = 0; @Override public void run() { while (val < 100) { val++; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(val); } } }
部分執行結果:
如果使用synchronize關鍵字,確保傳入的monitor是同一個物件,那麼執行緒執行的時候將會一切正常。要想確保傳入的是同一個物件,那麼肯定只能使用地址傳遞的物件才可以。因此,我們把Integer的物件作為這個monitor,具體程式碼如下:
class Test { public static void main(String[] args) { Thread t1 = new Thread(new A()); Thread t2 = new Thread(new A()); t1.start(); t2.start(); } } class A implements Runnable { static Integer a = 100; static int val = 0; @Override public void run() { synchronized (a) { while (val < 100) { val++; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(val); } } } }
最後執行結果和預想的一樣,從1到一百挨著輸出,沒有重複,也沒有漏掉某些值的輸出。所以證明了傳入的是同一個物件,既然是同一個物件,那麼肯定就是地址傳遞了。