java对象clone

java克隆

为什么需要克隆

我们在很多时候需要使用一个对象去记录另外一个对象的当前状态,对象中可能会有很多属性,如果我们一个一个去设置,不仅不方便,而且效率很低,我们看一个初学者可能遇到的问题

class Person{
	String name;
    int age;

	public Person() {}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}

}

@Test
	public void test2() {
		Person p1=new Person("tom",20);
		Person p2=p1;
		System.out.println(p1);
		System.out.println(p2);
		System.out.println("---------");
		p1.age=30;
		System.out.println(p1);
		System.out.println(p2);
	}
输出:
Person [name=tom, age=20]
Person [name=tom, age=20]
---------
Person [name=tom, age=30]
Person [name=tom, age=30]

也许有的人认为Person p2=p1这样的方式就可以克隆一个对象,这种想法是错误的,这种使用等号赋值的方式只是将p1的地址赋值给了p2对象,那么p1和p2都指向了堆中的同一个对象,所以,修改p1那么p2也就变了

如果一个一个属性的去手动设置不仅麻烦,而且属性可能也有属性,修改起来不容易

public void test2() {
		Person p1=new Person("tom",20);
		Person p2=new Person();
		p2.age=p1.age;
		p2.name=p1.name;
	}

这里Person的属性层级很简单,修改起来看起来很简单,但是如果Person多了一个Address类型的属性,那么手动修改就必须要去new一个Address并赋值属性,假如属性的嵌套层级深了,就很难了

所以我们可以使用Object提供的clone方法来克隆对象,由于clone方法是用protected关键字修饰的,我们如果想在类外使用就需要重写父类的方法,Object的clone方法是一个native关键字修饰的方法,即调用其他语言的方法,效率很高,值得注意的一点是要克隆对象的类必须实现Cloneable接口,这个接口是一个标记接口,里面并没有定义任何方法,如果没事实现Cloneable接口使用clone方法,会抛出CloneNotSupportedException异常

浅克隆

如果原型对象的属性是值类型,那么复制一份给克隆对象,如果属性是引用类型,那么把属性的引用赋值给克隆对象

class Person implements Cloneable{//必须实现Cloneable接口
	String name;
    int age;

	public Person() {}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	@Override
	protected Object clone(){
		Person p=null;
		try {
			p=(Person)super.clone();
		}catch(CloneNotSupportedException e) {//实现了Cloneable接口这个异常就不可能发生
			e.printStackTrace();
		}
		return p;
	}

}

@Test
	public void test2() {
		Person p1=new Person("tom",20);
		Person p2=(Person)p1.clone();
		System.out.println(p1);
		System.out.println(p2);
		System.out.println("---------");
		p1.age=30;
		System.out.println(p1);
		System.out.println(p2);
	}
输出:
Person [name=tom, age=20]
Person [name=tom, age=20]
---------
Person [name=tom, age=30]
Person [name=tom, age=20]

这种Object提供的clone在类中的属性全是值类型的时候不会出现问题,但是如果类属性有引用类型就会出现问题

class Person implements Cloneable{
	String name;
    int age;
    Address address;

	public Person() {}
	public Person(String name, int age,Address addr) {
		super();
		this.name = name;
		this.age = age;
		this.address=addr;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";
	}
	@Override
	protected Object clone(){
		Person p=null;
		try {
			p=(Person)super.clone();
		}catch(CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return p;
	}

}
class Address{
	String addr;

	public Address(String addr) {
		super();
		this.addr = addr;
	}

	@Override
	public String toString() {
		return "Address [addr=" + addr + "]";
	}

}

@Test
	public void test2() {
		Address addr=new Address("成都市郫都区");
		Person p1=new Person("tom",20,addr);
		Person p2=(Person)p1.clone();
		System.out.println(p1);
		System.out.println(p2);
		System.out.println("---------");
		p1.address.addr="成都市金牛区";
		System.out.println(p1);
		System.out.println(p2);
	}
输出:
Person [name=tom, age=20, address=Address [addr=成都市郫都区]]
Person [name=tom, age=20, address=Address [addr=成都市郫都区]]
---------
Person [name=tom, age=20, address=Address [addr=成都市金牛区]]
Person [name=tom, age=20, address=Address [addr=成都市金牛区]]

修改p1的address根据p1克隆出来的p2的address也改变了,这就是所说的当克隆对象的属性是引用类型时,只会复制它的引用,而这种情况一般是我们不希望的,所以需要使用深克隆

深克隆

1.克隆对象所有的引用类型都重写clone方法

这里所说的引用类型重写clone方法,是指克隆对象本身属性是引用类型的必须重写clone方法且引用类型中的引用类型也必要要重写,且必须在clone方法中显式条用

class Person implements Cloneable{
	String name;
    int age;
    Address address;//这个属性是引用类型,必须实现Cloneable接口重写clone方法

	public Person() {}
	public Person(String name, int age,Address addr) {
		super();
		this.name = name;
		this.age = age;
		this.address=addr;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";
	}
	@Override
	protected Object clone(){
		Person p=null;
		try {
			p=(Person)super.clone();
		}catch(CloneNotSupportedException e) {
			e.printStackTrace();
		}
      //显式调用克隆引用数据类型
		p.address=(Address)address.clone();
		return p;
	}

}
class Address implements Cloneable{
	String addr;

	public Address(String addr) {
		super();
		this.addr = addr;
	}

	@Override
	public String toString() {
		return "Address [addr=" + addr + "]";
	}

	@Override
	protected Object clone() {
		Address addr=null;
		try {
			addr=(Address)super.clone();
		}catch(CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return addr;
	}

}

@Test
	public void test2() {
		Address addr=new Address("成都市郫都区");
		Person p1=new Person("tom",20,addr);
		Person p2=(Person)p1.clone();
		System.out.println(p1);
		System.out.println(p2);
		System.out.println("---------");
		p1.address.addr="成都市金牛区";
		System.out.println(p1);
		System.out.println(p2);
	}
输出:
Person [name=tom, age=20, address=Address [addr=成都市郫都区]]
Person [name=tom, age=20, address=Address [addr=成都市郫都区]]
---------
Person [name=tom, age=20, address=Address [addr=成都市金牛区]]
Person [name=tom, age=20, address=Address [addr=成都市金牛区]]

使用序列化方法

需要克隆的对象类必须实现Serializable接口,这个接口也是一个标记接口,接口中没有任何方法,这个方法的实现类表示可以将这个类的对象写入到IO流中,那么久可以把对象在网络中发送或保存到本地磁盘

class Person implements Cloneable,Serializable{
	/**
	 *
	 */
	private static final long serialVersionUID = 8990580911834489134L;
	String name;
    int age;
    Address address;

	public Person() {}
	public Person(String name, int age,Address addr) {
		super();
		this.name = name;
		this.age = age;
		this.address=addr;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";
	}
	@Override
	protected Object clone(){
		Person p=null;
		try {
			//将对象写入流中
			ByteArrayOutputStream baos=new ByteArrayOutputStream();
			ObjectOutputStream oos=new ObjectOutputStream(baos);
			oos.writeObject(this);
			//将对象从流中读取出来
			ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
			ObjectInputStream ois=new ObjectInputStream(bais);
			p=(Person)ois.readObject();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return p;
	}

}
class Address implements Serializable{
	/**
	 *
	 */
	private static final long serialVersionUID = 328854588872604721L;
	String addr;

	public Address(String addr) {
		super();
		this.addr = addr;
	}

	@Override
	public String toString() {
		return "Address [addr=" + addr + "]";
	}
}

@Test
	public void test2() {
		Address addr=new Address("成都市郫都区");
		Person p1=new Person("tom",20,addr);
		Person p2=(Person)p1.clone();
		System.out.println(p1);
		System.out.println(p2);
		System.out.println("---------");
		p1.address.addr="成都市金牛区";

		System.out.println(p1);
		System.out.println(p2);
	}
输出:
Person [name=tom, age=20, address=Address [addr=成都市郫都区]]
Person [name=tom, age=20, address=Address [addr=成都市郫都区]]
---------
Person [name=tom, age=20, address=Address [addr=成都市金牛区]]
Person [name=tom, age=20, address=Address [addr=成都市郫都区]]

原文地址:https://www.cnblogs.com/moyuduo/p/12670541.html

时间: 2024-10-18 22:26:17

java对象clone的相关文章

JAVA对象任意深度克隆clone工具类分享

原文:JAVA对象任意深度克隆clone工具类分享 源代码下载地址:http://www.zuidaima.com/share/1550463408114688.htm package com.zuidaima.n_app.util; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import jav

java克隆对象clone()的使用方法和作用

转自:997.html">http://www.okrs.cn/blog/news/?997.html 内容摘要 若需改动一个对象,同一时候不想改变调用者的对象.就要制作该对象的一个本地副本.这也是本地副本最常见的一种用途.若决定制作一个本地副本.仅仅需简单地使用clone()方法就可以.Clone是"克隆"的意思,即制作全然一模一样的副本.这种方法在基础类Object中定义成"protected"(受保护)模式. 但在希望克隆的不论什么衍生类中,必

Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨

Java对象克隆(Clone)及Cloneable接口.Serializable接口的深入探讨 Part I 没啥好说的,直接开始Part II吧. Part II 谈到了对象的克隆,就不得不说为什么要对对象进行克隆.Java中所有的对象都是保存在堆中,而堆是供全局共享的.也就是说,如果同一个Java程序的不同方法,只要能拿到某个对象的引用,引用者就可以随意的修改对象的内部数据(前提是这个对象的内部数据通过get/set方法曝露出来).有的时候,我们编写的代码想让调用者只获得该对象的一个拷贝(也

java 深克隆clone对象或容器的另一种方法

本页地址:http://blog.csdn.net/lpy3654321/article/details/43054557 java 深clone对象的另一种方法 public static <T> T deepCopy(T src) throws IOException, ClassNotFoundException{ ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream out = n

java对象的克隆

java对象克隆方式主要有两种:浅克隆和深克隆 首先,不要把对象的克隆和对象的赋值搞混了,看下图 p2 = p1;就是赋值操作,赋值操作只是让被赋值对象指向之前对象的地址,实际上的物理内存是一块,而克隆操作的结果应该是两个对象分别指向内容相同的两块内存.如下就是克隆操作后的状态: 下面说浅克隆和深克隆: 深克隆和浅克隆的区别主要出现在类里有外部类对象时,如下,Person类中有Address类的对象 1 package cn.itcast.copy; 2 3 import java.io.Ser

Java对象的浅克隆

/**  * Java对象的浅克隆  * 在克隆对象时,如果对象的成员变量是基本类型,则使用浅克隆即可完成.  * 如果对象的成员变量包括可变引用类型,则需要深克隆  * 如果引用类型是不可变的,如String类,则不必进行深克隆  * 当需要克隆对象时,需要使用clone()方法,该方法将类中的各个域进行复制,  * 如果对于引用类型的域,这种操作就会有问题,因此成为浅克隆.  * 提供克隆功能的类需要实现Cloneable接口,否则会抛出异常  */ public class Address

深入理解Java对象的创建过程:类的初始化与实例化

"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 深入理解Java对象的创建过程:类的初始化与实例化 - Rico's Blogs - 博客频道 - CSDN.NET Rico's Blogs 潜心修炼,成为一个更好的人. 目录视图 摘要视图 订阅 [活动]2017 CSDN博客专栏评选 &nbsp [5月书讯]流畅

java对象的生命周期

java对象被创建的过程 1.对象的java类(class文件)被正确的加载.链接和初始化 2.对象的创建:调用当前类和父类的构造函数进行初始化 3.当不需要对象时,可以销毁 可以clone复制这个对象 也可以通过序列化持久化对象

(转)深入理解Java对象的创建过程

参考来源:http://blog.csdn.net/justloveyou_/article/details/72466416 摘要: 在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的.在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类构造器完成类的初始化.在类初始化过程中或初始化完毕后,根据具体情况才会去对类进行实例化.本文试图对JVM执行类初始化和实例化的过程做一个详细深入地介绍,以便从Java虚拟机