Java中别名现象和对象引用问题

我们都知道在原始数据类型中,int a = b 表示将b的值赋给a,之后a和b是两个独立的部分。但在操作引用数据类型时比如如 Object a = b 其实是将b在内存中的引用与a共享,可是我们有时还会把对引用数据类型的操作当成第一种情况(原始数据类型的操作),因此我们可能常常会犯这样的错误:对a进行一系列操作后,我们依然以为b还是原来那个b,事实上,对a和b操作都是在对同一个引用进行操作。可以用下面的例子来证明:

public class Alias {
/**
* @param fengkai2016
* @time 2016-3-26 13:42:25
* 理解面向对象中的别名问题,即对象的引用。
* 解决如何避免别名所带来的隐患问题。
*/
public static void main(String[] args) {
// 调用Person的方法grow(),改变当前对象中的变量years值
Person p1 = new Person("Wells" , 20);
Person p2 = p1;
p2.grow();
System.out.println(p1.getYears() +" address:"+ p1.toString());
System.out.println(p2.getYears() +" address:"+ p2.toString());

System.out.println("========Person In Method==========");
// 调用Person的方法 otherGrow(Person p)改变传入的实例中的变量years值
Person p3 = new Person("Patty", 20);
Person p4 = new Person("Barry", 20);
p3.otherGrow(p4);
System.out.println(p3.getYears() +" address:"+ p3.toString());
System.out.println(p4.getYears() +" address:"+ p4.toString());
}
}
//具有年龄和姓名属性的Person类
class Person {
private int years;
private String name;

Person(String name , int years) {
super();
this.name = name;
this.years = years;
}
public void grow(){
years++;
}
//以Person实例作为参数,改变传入实例中year值
public void otherGrow(Person p){
p.years++;
}
public String getYears(){
return name+": "+years;
}
}



在上面的代码中,我打印出了每个对象的年龄和它在内存中地址值

得到的结果如下:

【Wells: 21 address:Person@11671b2
Wells: 21 address:Person@11671b2
========Person In Method==========
Patty: 20 address:Person@82764b
Barry: 21 address:Person@12452e8】

在第一个操作中:

【Person p2 = p1;
p2.grow();】

我们从控制台可得到p2和p1在内存指向同一个引用,自然姓名和年龄也就相等了。

而在第二个操作中:

【p3.otherGrow(p4);】


p4中的years增加,说明以对象作为函数参数可以实现对该对象进行操作。

在上面两个例子中,其实都是Java中的别名现象,只不过表现形式不同,他们都是在对同一个引用进行操作。我们可能并不希望第一个例子中p1中的years也增加了,解决方法如下:

在Java的中有接口Cloneable,实现它方法clone()。然后将 Person p2 = p1替换成Person p2 = p1.clone()即可

代码如下

public class Alias {
/**
* @param fengkai2016
* @time 2016-3-26 13:42:25
* 理解面向对象中的别名问题,即对象的引用。
* 解决如何避免别名所带来的隐患问题。
*/
public static void main(String[] args) {


Person p1 = new Person("Wells" , 20);
// Person p2 = p1;
Person p2 = p1.clone();

p2.grow();
System.out.println(p1.getYears() +" address:"+ p1.toString());
System.out.println(p2.getYears() +" address:"+ p2.toString());

System.out.println("================");
Person p3 = new Person("Patty", 20);
Person p4 = new Person("Barry", 20);

p3.otherGrow(p4);
System.out.println(p3.getYears() +" address:"+ p3.toString());
System.out.println(p4.getYears() +" address:"+ p4.toString());
}
}
//具有年龄和姓名属性的Person类,实现Cloneable接口
class Person implements Cloneable{
private int years;
private String name;


Person(String name , int years) {
super();
this.name = name;
this.years = years;
}

public void grow(){
years++;
}
//以Person实例作为参数,改变传入实例中year值
public void otherGrow(Person p){
p.years++;
}

public String getYears(){
return name+": "+years;
}
// 重点说明一下这个接口方法
// 1.clone()方法是在内存中再次给p2划分出一块资源,p1与p2各自拥有一个引用,
// 此时的p1和p2是在内存中是两个完全独立的部分,谁都不能对另一个对象进行任何操作,
// 但是这两个对象中的值又是完全相同的(刚刚调用clone方法后),包括年龄,姓名等其他变量或者方法,
// 2.不同于之前的clone()方法,表达式 Person p2 = p1 即p1将其在内存中的引用传递给了p2,可看成p1与p2共享同一个引用,
// 此时的p1与p2除了标示符(“p1”与“p2”),其余都是相同的包括引用和对象中的值.

public Person clone(){
Person object = null;
try {
object = (Person) super.clone();

catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return object;
}
}


再次打印如下:

【Wells: 20 address:Person@1bf3d87
Wells: 21 address:Person@60991f
================
Patty: 20 address:Person@1e4f7c2
Barry: 21 address:Person@145f0e3】

由控制台输出可知,此时的p1与p2是两个完全不同的对象,指向内存中不同的地址。

下面的这张图片可以很好的解释对象引用在内存中的区别:



  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值