关于别名现象

来源:互联网 发布:java 封装规则 编辑:程序博客网 时间:2024/05/21 22:23

别名现象

转自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.   
  3.     int age;  
  4.       
  5. }</span>  

  • Client.java:场景类或测试类
[java] view plain copy
  1. <span style="font-size:18px;">public class Client {  
  2.   
  3.     public static void main(String[] args) {  
  4.         Person p1 = new Person();  
  5.         Person p2 = new Person();  
  6.           
  7.         p1.age = 18;  
  8.         p2.age = 21;  
  9.         System.out.println("1. p1.age: " + p1.age + ", p2.age: " + p2.age);  
  10.           
  11.         p1 = p2;        // 使 p2 和 p1 拥有相同的对象引用  
  12.         System.out.println("2. p1.age: " + p1.age + ", p2.age: " + p2.age);  
  13.           
  14.         // 注意 3 的输出  
  15.         p1.age = 18;  
  16.         System.out.println("3. p1.age: " + p1.age + ", p2.age: " + p2.age);  
  17.     }  
  18.   
  19. }</span>  
输出如下:
[html] view plain copy
  1. <span style="font-size:18px;">/**********************  
  2. * 1. p1.age: 18, p2.age: 21 *  
  3. * 2. p1.age: 21, p2.age: 21 *  
  4. * 3. p1.age: 18, p2.age: 18 *  
  5. *******************************/</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.   
  3.     char sex;  
  4.       
  5. }</span>  
  • Client.java
[java] view plain copy
  1. <span style="font-size:18px;">public class Client {  
  2.   
  3.     public static void f(Person person){  
  4.         person.sex = 'W';  
  5.     }  
  6.       
  7.     public static void main(String[] args) {  
  8.         Person person = new Person();  
  9.         person.sex = 'M';  
  10.         System.out.println("1. person.sex: " + person.sex);  
  11.           
  12.         // 注意 2 的输出  
  13.         f(person);  
  14.         System.out.println("2. person.sex: " + person.sex);  
  15.           
  16.     }  
  17.       
  18. }</span>  
输出如下:
[html] view plain copy
  1. <span style="font-size:18px;">/**********************  
  2. * 1. person.sex: M *  
  3. * 2. person.sex: W *  
  4. **********************/</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.   
  3.     @org.junit.Test  
  4.     public void testArr() {  
  5.         int[] arr = new int[]{1234};  
  6.         changeArr(arr);  
  7.         for (int i = 0; i < arr.length; i ++) {  
  8.             System.out.print(arr[i] + " ");  
  9.         }  
  10.     }  
  11.       
  12.     private void changeArr(int[] arr) {  
  13.         // 若是直接在 new 一个数组,则在 testArr 中打印,, arr 没有出现别名现象,还是 1 2 3 4  
  14.         arr = new int[]{2234};  
  15.         // 直接修改 arr 中元素的值,则 arr 出现改变。 2 2 3 4  
  16. //      arr[0] = 2;  
  17.     }  
  18.       
  19.     @org.junit.Test  
  20.     public void testPerson () {  
  21.         Person p = new Person();  
  22.         p.setId(1);  
  23.         p.setName("johnnie");  
  24.         p.setAge(22);  
  25.         p.setSex(Sex.MAN);  
  26.         changePerson(p);  
  27.         System.out.println(p);  
  28.     }  
  29.   
  30.     private void changePerson(Person p) {  
  31. //      p = new Person();  
  32. //      p.setId(1);  
  33. //      p.setName("Lisa");  
  34. //      p.setAge(22);  
  35. //      p.setSex(Sex.WOMAN);  
  36.           
  37.         p.setAge(21);  
  38.     }  
  39.       
  40. }</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.  * Address:负责记录地址信息 
  3.  * @author johnnie 
  4.  * 
  5.  */  
  6. public class Address {  
  7.       
  8.     private String address;  
  9.   
  10.     public String getAddress() {  
  11.         return address;  
  12.     }  
  13.   
  14.     public void setAddress(String address) {  
  15.         this.address = address;  
  16.     }  
  17.   
  18. }</span>  
  • Person.java:
[java] view plain copy
  1. <span style="font-size:18px;">public class Person {  
  2.   
  3.     // 基本属性  
  4.     private String name;  
  5.     private Address address;  
  6.       
  7.     // Getter和Setter   
  8.     public String getName() {  
  9.         return name;  
  10.     }  
  11.     public void setName(String name) {  
  12.         this.name = name;  
  13.     }  
  14.       
  15.     public Address getAddress() {  
  16.         return address;  
  17.     }  
  18.       
  19.     public void setAddress(Address address) {  
  20.         this.address = address;  
  21.     }  
  22.       
  23. }</span>  
  • Client.java:
[java] view plain copy
  1. <span style="font-size:18px;">/** 
  2.  * 场景类 
  3.  * @author johnnie 
  4.  * 
  5.  */  
  6. public class Client {  
  7.   
  8.     public static void main(String[] args) {  
  9.         // 创建一个 Person 和 Address 类实例  
  10.         Person person = new Person();  
  11.         Address address = new Address();  
  12.         address.setAddress("WuHan");  
  13.         System.out.println("1. 将作为参数传递的地址信息为 address:" + address.getAddress());  
  14.           
  15.         // 初始化 Person 类实例  
  16.         String name = "johnnie";  
  17.         person.setName(name);  
  18.         person.setAddress(address);  
  19.         System.out.println("2. " + person.getName() + "住在" + person.getAddress().getAddress());  
  20.           
  21.         // 取得地址信息,并放在另一个 Address 类对象中,修改地址信息  
  22.         Address addr = person.getAddress();  
  23.         addr.setAddress("BeiJing");  
  24.         System.out.println("3. 新修改的地址信息 addr: " + addr.getAddress());  
  25.         System.out.println("4. 当时作为参数传递的地址信息 address: " + address.getAddress());  
  26.         System.out.println("5. Person 类对象中的地址信息 person.address: " + person.getAddress().getAddress());  
  27.           
  28.         // 获取姓名,我要改名字了,从现在开始,我叫小明  
  29.         String xiaoming = person.getName();  
  30.         xiaoming = "XiaoMing";  
  31.         System.out.println("6. xiaoming: " + xiaoming);  
  32.         System.out.println("7. Person 类对象中的姓名 person.name:" + person.getName());  
  33.     }  
  34.       
  35. }</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类型数据不会产生别名现象。
原创粉丝点击