zl程序教程

您现在的位置是:首页 >  其他

当前栏目

javacloneable接口_comparable

接口 Comparable
2023-06-13 09:14:38 时间

特点

一个类实现了Cloneable接口 指向@link java.lang.Object#clone()} 方法是合法的 使得一个 field-for-field copy的类的实例的拷贝

在不实现Cloneable接口的对象上调用Object的clone方法 会导致CloneNotSupportedException异常抛出。

按照约定,实现此接口的类应当重写Object.clone方法 (被保护)带有public

注意 此接口不包含clone方法 因此不可能仅仅依赖实现了这个接口就拷贝一个对象 。 即使这个clone方法 被反射调用 也不能保证成功

java.lang.Object#clone()

创建并返回此对象的副本。复制 可能取决于对象的类。总体的意图是 对应任何对象x 表达式

 x.clone() != x

会是true 并且表达

x.clone().getClass() == x.getClass()

将会是true 但是这些不是绝对要求 通常情况下:

 x.clone().equals(x)

将是true,这不是绝对的要求

按照惯例,返回的对象应该通过调用获得{@code super.clone}. 如果一个class 和 它所有的父类 除了 {@code Object})遵循这个惯例, 它应当是这样的

 x.clone().getClass() == x.getClass()

按照惯例,该方法返回的对象应该是独立的。这个对象(正在被克隆) 为了实现这种独立性,可能需要修改返回的对象的一个或多个字段 在返回之前使用{@code super.clone} 通常,这意味着复制包含内部 深层结构的任何可变对象 正在克隆的对象 和替换这些对象的应用 利用 引用的副本。

如果一个类只包含原始字段或者不可变对象的引用 通常意味着 通过{@code super.clone}返回的对象没有fileds字段需要被修改

{@code clone} 方法给Object对象类 操作一个特别的克隆操作。 首先,如果这个对象的类不实现Cloneable接口 CloneNotSupportedException将会抛出,所有的数组将会被考虑实现接口Cloneable。一个数组类型T[] clone方法的返回的类型是T[] T是一个引用或者基本类型 否则,此方法将创建此类的新实例,然后初始化所有的字段 内容完全是对象的对应字段,如果通过赋值,字段的内容不会拷贝他们自己。因此 这种方法执行对象的“shallow copy”,而不是deep copy

对象的类本身不实现Cloneable接口 所以调用clone方法 在一个对象 它的类是 对象 将抛出一个运行时异常。

浅拷贝和深度拷贝

为什么要克隆? 克隆的对象可能包含一些已经修改过的属性,而new出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”就靠clone方法了。那么我把这个对象的临时属性一个一个的赋值给我新new的对象不也行嘛?可以是可以,但是一来麻烦不说,二来,大家通过上面的源码都发现了clone是一个native方法,就是快啊,在底层实现的

Object a=new Object();Object b;b=a;这种形式的代码复制的是引用,即对象在内存中的地址,a和b对象仍然指向了同一个对象。而通过clone方法赋值的对象跟原来的对象时同时独立存在的

ShallowClone 在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。

简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。

  1. 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常), 该接口为标记接口(不含任何方法)
  2. 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象。(native为本地方法)

示例

package abc;  
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;  
}  
}  
public class Test { 
  
public static void main(String args[]) {  
Address addr = new Address();  
addr.setAdd("杭州市");  
Student stu1 = new Student();  
stu1.setNumber(123);  
stu1.setAddr(addr);  
Student stu2 = (Student)stu1.clone();  
System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  
addr.setAdd("西湖区");  
System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  
}  
}

deep copy

在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。

简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。

在Java语言中,如果需要实现深克隆,可以通过覆盖Object类的clone()方法实现,也可以通过序列化(Serialization)等方式来实现。

(如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。)

序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

public class Outer implements Serializable{ 

private static final long serialVersionUID = 369285298572941L;  //最好是显式声明ID
public Inner inner;
 //Discription:[深度复制方法,需要对象及对象所有的对象属性都实现序列化] 
public Outer myclone() {
Outer outer = null;
try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
      // 将流序列化成对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
outer = (Outer) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return outer;
}
}
public class Inner implements Serializable{ 

private static final long serialVersionUID = 872390113109L; //最好是显式声明ID
public String name = "";
public Inner(String name) {
this.name = name;
}
@Override
public String toString() {
return "Inner的name值为:" + name;
}
}

参考资料: https://www.cnblogs.com/Qian123/p/5710533.html#_label2

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/181879.html原文链接:https://javaforall.cn