天天看點

關于别名現象 别名現象 三、綜合案例:

别名現象

轉自http://blog.csdn.net/GuLu_GuLu_jp/article/details/49839435 部分自己添加

1 别名現象 假設有一個類,它有兩個執行個體(c1 和 c2),原本c1和c2分别指向兩個不同的對象。如果執行c1=c2, 則c1這個引用就會被覆寫,也就是丢失了;而那個不再被引用的對象會由“垃圾回收器”自動清理。這種特殊的現象通常稱作“别名現象”。

2.1 别名現象的發生場景

  • 對對象進行指派時
  • 方法調用中,傳遞一個對象時

2.2 對對象進行指派時的别名現象

  • Person.java:很簡單的一個類,僅僅擁有一個屬性

[java]  view plain  copy

  1. <span style="font-size:18px;">public class Person {  
  2.     int age;  
  3. }</span>  
  • Client.java:場景類或測試類

[java]  view plain  copy

  1. <span style="font-size:18px;">public class Client {  
  2.     public static void main(String[] args) {  
  3.         Person p1 = new Person();  
  4.         Person p2 = new Person();  
  5.         p1.age = 18;  
  6.         p2.age = 21;  
  7.         System.out.println("1. p1.age: " + p1.age + ", p2.age: " + p2.age);  
  8.         p1 = p2;        // 使 p2 和 p1 擁有相同的對象引用  
  9.         System.out.println("2. p1.age: " + p1.age + ", p2.age: " + p2.age);  
  10.         // 注意 3 的輸出  
  11.         p1.age = 18;  
  12.         System.out.println("3. p1.age: " + p1.age + ", p2.age: " + p2.age);  
  13.     }  
  14. }</span>  

輸出如下: [html]  view plain  copy

  1. <span style="font-size:18px;"></span>  
  • 分析:

  主要關注第三行的輸出,可以發現當 p1 的 age 值修改為 18 後,p2 的 age 也變為 18 了,這是為什麼?why?   原因是因為,當進行對象指派操作時,如此處的 p2 = p1; 當該條語句執行完畢,p2 和 p1 将擁有對同一個對象的引用。當對 p1 中的屬性進行修改時,因為是相同的對象引用,是以 p2 的值自然也是随之修改了。我們可以類比于C ,在 C 語言中使用指針操作一個記憶體塊,記憶體塊記憶體儲的值被修改了,那麼所有指向該記憶體的指針變量的值都會被修改[注:C語言學的不好]

2.3 方法調用中的别名現象     将一個對象傳遞給方法時,也會産生别名問題。

  • Person.java

[java]  view plain  copy

  1. <span style="font-size:18px;">public class Person {  
  2.     char sex;  
  3. }</span>  
  • Client.java

[java]  view plain  copy

  1. <span style="font-size:18px;">public class Client {  
  2.     public static void f(Person person){  
  3.         person.sex = 'W';  
  4.     }  
  5.     public static void main(String[] args) {  
  6.         Person person = new Person();  
  7.         person.sex = 'M';  
  8.         System.out.println("1. person.sex: " + person.sex);  
  9.         // 注意 2 的輸出  
  10.         f(person);  
  11.         System.out.println("2. person.sex: " + person.sex);  
  12.     }  
  13. }</span>  

輸出如下: [html]  view plain  copy

  1. <span style="font-size:18px;"></span>  
  • 分析

    從輸出也可看到,當調用 f() 函數後,即使 f() 函數是 void 類型,但是第2條輸出中,person 的值還是改變了。這裡我們以為我們給 f() 函數傳遞了一個值,f() 函數會在其作用域内複制參數 Person person 的副本,然後操作副本。但是實際上我們傳遞的是一個引用。是以實際改變的是 f() 函數之外的對象。

關于方法中别名現象問題的特别案例:我們知道對于數組對象或Objec類的導出類對象,在将這些對象作為參數傳遞給方法時,會産生别名現象。但是一個特别要注意的一點就是,别名現象出現的場景!!!隻有我們在方法中,直接操作對象的方法來修改該對象的值時,才會産生别名現象。若是在方法内,對該對象重新指派,即重新 new 一下,那麼是不會出現别名現象的。原因待會兒解釋,先給出案例。如下: [java]  view plain  copy

  1. <span style="font-size:18px;">public class Test {  
  2.     @org.junit.Test  
  3.     public void testArr() {  
  4.         int[] arr = new int[]{1, 2, 3, 4};  
  5.         changeArr(arr);  
  6.         for (int i = 0; i < arr.length; i ++) {  
  7.             System.out.print(arr[i] + " ");  
  8.         }  
  9.     }  
  10.     private void changeArr(int[] arr) {  
  11.         // 若是直接在 new 一個數組,則在 testArr 中列印,, arr 沒有出現别名現象,還是 1 2 3 4  
  12.         arr = new int[]{2, 2, 3, 4};  
  13.         // 直接修改 arr 中元素的值,則 arr 出現改變。 2 2 3 4  
  14. //      arr[0] = 2;  
  15.     }  
  16.     @org.junit.Test  
  17.     public void testPerson () {  
  18.         Person p = new Person();  
  19.         p.setId(1);  
  20.         p.setName("johnnie");  
  21.         p.setAge(22);  
  22.         p.setSex(Sex.MAN);  
  23.         changePerson(p);  
  24.         System.out.println(p);  
  25.     }  
  26.     private void changePerson(Person p) {  
  27. //      p = new Person();  
  28. //      p.setId(1);  
  29. //      p.setName("Lisa");  
  30. //      p.setAge(22);  
  31. //      p.setSex(Sex.WOMAN);  
  32.         p.setAge(21);  
  33.     }  
  34. }</span>  

在 Test.java 中,我們給出了 testArr() 和 testPerson() 方法,分别用于測試數組對象和普通對象在将對象作為參數在方法傳遞過程中别名現象的情況。我們使用兩種情況,一種是直接 new 出一個新對象指派給形參,然後再執行 testXXX 方法;另一種是直接操作形參,即使用形參的方法直接修改其内的值,然後再執行 testXXX 方法。分别檢視兩種情況的結果: ① 第一種情況下:

  • testArr() 的顯示:直接就是 1 2 3 4
  • testPerson() 的顯示:直接就是 Person [id=1, name=johnnie, age=22, sex=男]

② 第二種情況下:

  • testArr() 的顯示:直接就是 2 2 3 4 
  • testPerson() 的顯示:直接就是 Person [id=1, name=johnnie, age=21, sex=男]

可以發現,第一種情況下,在外部列印的結果是對象沒有改變。而第二種情況下,對象改變了。那麼這是為什麼呢? 原因很簡單,我們以 changeArr 方法來解釋。

在第一種情況下,我們在 changeArr() 中,是使用 new 來改變的,而使用 new 關鍵字呢,JVM 就會在堆中新開辟一個空間,存儲新的數組對象,而 arr 就指向堆中新記憶體位址的引用。但是,在 changeArr() 中,該 arr 還僅僅是局部變量,其生命周期是和 changeArr() 一樣的,與其共存亡的,而我們 changeArr() 方法是沒有傳回值的,也就是我們沒有把 arr 傳回,讓 testArr() 中的 arr 發生改變,讓其重新指向堆中新開辟的記憶體位址,是以第一種情況是不會發生變化,還是 1 2 3 4。

在第二種情況下,就更容易了解了。因為是直接使用 arr 來修改的,在該片記憶體空間中直接改變值,是以,即使沒有傳回值,形參 arr 與 changeArr() 同時挂逼了,外部的 arr 還是指向原來的空間,但是此時該片空間中的資料卻已經發生了改變。是以,結果就變為 2 2 3 4 了。

三、綜合案例:

     一般我們别寫程式不會剛剛那樣,而是綜合起來的。如下:

  • Address.java:

[java]  view plain  copy

  1. <span style="font-size:18px;">  
  2. public class Address {  
  3.     private String address;  
  4.     public String getAddress() {  
  5.         return address;  
  6.     }  
  7.     public void setAddress(String address) {  
  8.         this.address = address;  
  9.     }  
  10. }</span>  
  • Person.java:

[java]  view plain  copy

  1. <span style="font-size:18px;">public class Person {  
  2.     // 基本屬性  
  3.     private String name;  
  4.     private Address address;  
  5.     // Getter和Setter   
  6.     public String getName() {  
  7.         return name;  
  8.     }  
  9.     public void setName(String name) {  
  10.         this.name = name;  
  11.     }  
  12.     public Address getAddress() {  
  13.         return address;  
  14.     }  
  15.     public void setAddress(Address address) {  
  16.         this.address = address;  
  17.     }  
  18. }</span>  
  • Client.java:

[java]  view plain  copy

  1. <span style="font-size:18px;">  
  2. public class Client {  
  3.     public static void main(String[] args) {  
  4.         // 建立一個 Person 和 Address 類執行個體  
  5.         Person person = new Person();  
  6.         Address address = new Address();  
  7.         address.setAddress("WuHan");  
  8.         System.out.println("1. 将作為參數傳遞的位址資訊為 address:" + address.getAddress());  
  9.         // 初始化 Person 類執行個體  
  10.         String name = "johnnie";  
  11.         person.setName(name);  
  12.         person.setAddress(address);  
  13.         System.out.println("2. " + person.getName() + "住在" + person.getAddress().getAddress());  
  14.         // 取得位址資訊,并放在另一個 Address 類對象中,修改位址資訊  
  15.         Address addr = person.getAddress();  
  16.         addr.setAddress("BeiJing");  
  17.         System.out.println("3. 新修改的位址資訊 addr: " + addr.getAddress());  
  18.         System.out.println("4. 當時作為參數傳遞的位址資訊 address: " + address.getAddress());  
  19.         System.out.println("5. Person 類對象中的位址資訊 person.address: " + person.getAddress().getAddress());  
  20.         // 擷取姓名,我要改名字了,從現在開始,我叫小明  
  21.         String xiaoming = person.getName();  
  22.         xiaoming = "XiaoMing";  
  23.         System.out.println("6. xiaoming: " + xiaoming);  
  24.         System.out.println("7. Person 類對象中的姓名 person.name:" + person.getName());  
  25.     }  
  26. }</span>  

輸出如下: [html]  view plain  copy

  1. <span style="font-size:18px;">1. 将作為參數傳遞的位址資訊為 address:WuHan  
  2. 2. johnnie住在WuHan  
  3. 3. 新修改的位址資訊 addr: BeiJing  
  4. 4. 當時作為參數傳遞的位址資訊 address: BeiJing  
  5. 5. Person 類對象中的位址資訊 person.address: BeiJing  
  6. 6. xiaoming: XiaoMing  
  7. 7. Person 類對象中的姓名 person.name:johnnie</span>  

通過輸出,我們可以發現,String 類型的資料操作,并不會出現别名現象。而在操作對象的過程中,别名現象出現了。

四 pdd總結 基本資料類型和string類型資料不會産生别名現象。