Java中关于返回引用可变对象常见问题剖析

来源:互联网 发布:淘宝网服务中心 编辑:程序博客网 时间:2024/06/05 14:17

前言

在工程中,我们创建了一个类,经常包含一些可变类的对象实例,当我们需要提取这些数据的时候,需要格外注意,否则会出一些意料之外的问题

正文

名词解释

可变对象:类中有方法可以改变其对象实例域(数据域)的类

举例

假设我们有一个example类,其中为Date类创建了一个实体birthday,在外部需要知道某个example类实例的birthday值时,会使用到getBirthday()方法,通常我们的getBirthday()方法会写成如下的样子:

class Example{    private Date birthday;    ...    public Date getBirthday{        return birthday;    }    ...}

这样的写法,会破坏example类的封装性,因为当外部调用了getBirthday()这个方法后,生成了另一个对于birthday所引用对象的引用,在新的引用中调用Date类中的方法,同样会改变当前实例中birthday的值,例如如下代码:

Example e = new Example(....);Date d = e.getBirthday();double tenYearsInMilliSeconds = 10 * 365.25 * 24 * 60 * 60 * 1000;d.setTime(d.getTime() - (long) tenYearsInMilliSeconds);

这样的代码,会使得再次调用e.getBirthday()后,得到的值是修改后的值。

原因
上述例子中,d和e.birthday引用了同一个对象,如图

返回可变数据域的引用

改正

如果需要返回一个可变对象的引用,我们应该首先对其进行克隆(clone),这样会创建一个当前对象的副本,而不会对当前对象造成影响,代码改正如下

class Example{    private Date birthday;    ...    public Date getBirthday{        return birthday.clone();    }    ...}

2017.6.10更新

看过Date(java.util.Date)的源码的各位应该知道,Date里的clone方法返回的是一个Object对象,这会导致上面的 getBirthday 函数报错(类型不匹配,不能从Object类型转为Date),所以,这里需要将代码更正如下

class Example{    private Date birthday;    ...    public Date getBirthday{        return (Date) birthday.clone();    }    ...}

经过这样的转换,也不会破坏封装性(因为传回来的是 birthday 的一个复制品)

后记

在编写Java代码的时候,一定要记得,Java中每声明一个类的实例名称,事实上只是声明了一个引用名称,当前实例创建是在调用new + 构造器后才创建的(类似于C/C++中的指针),所以在使用过程中一定要慎重慎重再慎重!