java同类项引用相同对象_关于创建一个相同的对象(引用数据类型)的思考

1.首先想到的是用"="

Object a = new Object();

Object b = a;

除了初学者,稍微懂些java语言的人都会注意到引用数据类型的问题.

b并不是被创建的和a相同的对象,b就是a,b只是获得a指向的对象的引用.

2.其次就是最朴素的,怎么创建的a就怎么创建b,重新走一遍获得a的流程

这算是最基础的方法了,刚学数组的时候会做很多复制数组的练习,这些练习就是这种最简单的方法.

针对于数组有System.arraycopy(Object src ,int srcPos ,Object dest ,int destPos , int length),

和Arrays.copyOf().

在这里存在一个问题,即如果数组里的元素还是引用数据类型,则最后复制的也并不是一个全新的数组对象,而只是一个简单的新的"数组"对象.

3.对于普通的对象的话,有一个api是Object.clone()

这个类的说明是Creates and returns a copy of this object.创建并返回一个此对象的复制.

/*

Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.

The general intent is that, for any object x, the expression:

1) x.clone() != x will be true

2) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.

3) x.clone().equals(x) will be true, this is not an absolute requirement.

*/

protected native Object clone() throws CloneNotSupportedException;

第一次声明,保证克隆对象将有单独的内存地址分配。

第二次声明,表明原始和克隆的对象应该具有相同的类类型,但它不是强制性的。

第三此声明,表明原始和克隆的对象应该是平等的equals()方法使用,但它不是强制性的。

对于2.中提到的重新创建一遍对象存在许多问题,因为原始对象可能在初始化后经历了许多逻辑,如果再走一遍跟提高复用性背道而驰,

所以clone()应该算是最优的解决方案.它即可以复制一个刚初始化的对象,也可以复制一个已经修改过属性的对象.

而对于克隆,针对2.中最后提到的问题,分为浅克隆(ShallowClone)与深克隆(DeepClone),他们的区别就在于是否支持引用数据类型的成员变量的复制

浅克隆的步骤:

1.被复制的类需要实现Cloneable接口

如果不实现此接口而调用clone()方法会抛出CloneNotSupportedException异常.

此接口为标记接口,不含任何方法.

2.重写clone()方法,访问权限设为public,方法中调用super,clone()方法得到需要复制的对象.

class Student implements Cloneable{

private int number;

public int getNumber() {

return number;

}

public void setNumber(int number) {

this.number = number;

}

@Override

public Object clone() {

Student stu = null;

try{

stu = (Student)super.clone();

}catch(CloneNotSupportedException e) {

e.printStackTrace();

}

return stu;

}

}

深克隆的步骤:

1.被复制的类,和类中所有的引用数据类型,都要实现Cloneable接口

2.被复制的类,和类中所有的引用数据类型,都要重写clone()方法.

class Address implements Cloneable {

private String add;

public String getAdd() {

return add;

}

public void setAdd(String add) {

this.add = add;

}

@Override

public Object clone() {

Address addr = null;

try{

addr = (Address)super.clone();

}catch(CloneNotSupportedException e) {

e.printStackTrace();

}

return addr;

}

}

class Student implements Cloneable{

private int number;

private Address addr;

public Address getAddr() {

return addr;

}

public void setAddr(Address addr) {

this.addr = addr;

}

public int getNumber() {

return number;

}

public void setNumber(int number) {

this.number = number;

}

@Override

public Object clone() {

Student stu = null;

try{

stu = (Student)super.clone(); //浅复制

}catch(CloneNotSupportedException e) {

e.printStackTrace();

}

stu.addr = (Address)addr.clone(); //深度复制

return stu;

}

}

可参考java.util.Date;的clone()

/**

* Return a copy of this object.

*/

public Object clone() {

Date d = null;

try {

d = (Date)super.clone();

if (cdate != null) {

d.cdate = (BaseCalendar.Date) cdate.clone();

}

} catch (CloneNotSupportedException e) {} // Won't happen

return d;

}

4.而在实际问题中,3.中提到的深克隆也会面临一个问题--那就是对象中引用数据类型过多过深,引用数据类型属性还有引用数据类型的属性,使用3.中的深克隆还是很麻烦.

其实深克隆还有另外一种实现方式,就是对象的序列化(Serialization)与反序列化(Deserialization).

序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在内存中.通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用数据类型的成员变量.因此通过序列化将对象写到一个流中,再通过反序列化将其读取出来,就可以实现深克隆.

虽然Java的序列化非常简单、强大,但是要用好,还有很多地方需要注意。

比如曾经序列化了一个对象,可由于某种原因,该类做了一点点改动,然后重新被编译,那么这时反序列化刚才的对象,将会出现异常。 你可以通过添加serialVersionUID属性来解决这个问题。

如果你的类是个单例(Singleton)类,是否允许用户通过序列化机制复制该类,如果不允许你需要谨慎对待该类的实现。

public class Attribute {

private String no;

}

public class Product {

private String name;

private Attribute attribute;

public Product clone() {

ByteArrayOutputStream byteOut = null;

ObjectOutputStream objOut = null;

ByteArrayInputStream byteIn = null;

ObjectInputStream objIn = null;

try {

byteOut = new ByteArrayOutputStream();

objOut = new ObjectOutputStream(byteOut);

objOut.writeObject(prototype);

byteIn = new ByteArrayInputStream(byteOut.toByteArray());

objIn = new ObjectInputStream(byteIn);

return (ContretePrototype) objIn.readObject();

} catch (IOException e) {

throw new RuntimeException("Clone Object failed in IO.",e);

} catch (ClassNotFoundException e) {

throw new RuntimeException("Class not found.",e);

} finally{

try{

byteIn = null;

byteOut = null;

if(objOut != null) objOut.close();

if(objIn != null) objIn.close();

}catch(IOException e){

}

}

}

}

注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是优于把问题留到运行时。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值