一 引用拷贝
创建一个指向对象的引用变量的拷贝。
Teacher teacher = new Teacher("Taylor",26);
Teacher otherteacher = teacher;
System.out.println(teacher);
System.out.println(otherteacher);
blog.Teacher@355da254
blog.Teacher@355da254
由输出结果可以看出,它们的地址值是相同的,那么它们肯定是同一个对象。teacher和otherteacher的只是引用而已,他们都指向了一个相同的对象Teacher(“Taylor”,26)。 这就叫做引用拷贝。
二 对象拷贝
创建对象本身的一个副本。
Teacher teacher = new Teacher("Swift",26);
Teacher otherteacher = (Teacher)teacher.clone();
System.out.println(teacher);
System.out.println(otherteacher);
blog.Teacher@355da254
blog.Teacher@4dc63996
结果分析:由输出结果可以看出,它们的地址是不同的,也就是说创建了新的对象, 而不是把原对象的地址赋给了一个新的引用变量,这就叫做对象拷贝。
注:深拷贝和浅拷贝都是对象拷贝
浅拷贝
对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。"里面的对象“会在原来的对象和它的副本之间共享。
简而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象
深拷贝
对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。
简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。
三 四种拷贝:
- for()循环拷贝
- clone()拷贝
- System.arraycopy() 拷贝
- Arrays.copyOf ()拷贝
1 两种类型的运用:
- 基本类型 (深拷贝):以下例子中,改变拷贝数组元素,源数组不变,则深拷贝
- 引用类型 (浅拷贝):以下例子中,改变拷贝数组元素,源数组不变,则浅拷贝
2 几种拷贝的介绍:
1.for()循环拷贝
通过循环赋值来将元数组里面的元素拷贝到目标数组
2.clone() 拷贝
protected Object clone()
throws CloneNotSupportedException
clone的方法Object执行特定的克隆操作。 首先,如果此对象的类不实现接口Cloneable ,则抛出CloneNotSupportedException 。 请注意,所有数组都被认为是实现接口Cloneable ,并且数组类型T[]的clone方法的返回类型是T[] ,其中T是任何引用或原始类型。 否则,该方法将创建该对象的类的新实例,并将其所有字段初始化为完全符合该对象的相应字段的内容,就像通过赋值一样。 这些字段的内容本身不被克隆。 因此,该方法执行该对象的“浅拷贝”,而不是“深度拷贝”操作。
3.System.arraycopy 拷贝
System中提供了一个native静态方法arraycopy(),可以使用这个方法来实现数组之间的复制。
public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
参数
src - 源数组。
srcPos - 源数组中的起始位置。
dest - 目标数组。
destPos - 目的地数据中的起始位置。
length - 要复制的数组元素的数量
4.Arrays.copyOf 拷贝
public static <T> T[] copyOf(T[] original,int newLength)
参数类型
T - 数组中对象的类
参数
original - 要复制的数组
newLength - 要返回的副本的长度
3 当对象是“基本类型”(代码)
四种拷贝对应方式为“深拷贝”)
1 for()循环拷贝
import java.util.*;
public class SimpleCharcter {
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6,7,8,9};
int[] brray = new int[array.length];
for(int i = 0;i < array.length;i++) {
brray[i] = array[i];
}
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(brray));
brray[0] = 1000;
System.out.println("=================");
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(brray));
}
}
/*输出结果:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
=================
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1000, 2, 3, 4, 5, 6, 7, 8, 9] (深拷贝)
*/
2 clone()拷贝
//clone()拷贝
import java.util.*;
public class Clone {
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int[] brray = array.clone();
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(brray));
brray[0] = 1000;
System.out.println("=================");
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(brray));
}
}
/*输出结果:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
=================
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1000, 2, 3, 4, 5, 6, 7, 8, 9] (深拷贝)
*/
3 System.arraycopy ()
//System.arraycopy()
import java.util.*;
public class System_Arraycopy {
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6,7,8,9};
int[] brray = new int[array.length];
System.arraycopy(array,0,brray,0,array.length);
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(brray));
brray[0] = 1000;
System.out.println("=================");
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(brray));
}
}
/*输出结果:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
=================
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1000, 2, 3, 4, 5, 6, 7, 8, 9] (深拷贝)
*/
4 Arrays.copyOf ()
//Arrays.copyOf
import java.util.*;
public class TestArray1 {
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6,7,8,9};
int[] brray = Arrays.copyOf(array,array.length);
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(brray));
brray[0] = 1000;
System.out.println("=================");
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(brray));
}
}
/*输出结果:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
=================
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1000, 2, 3, 4, 5, 6, 7, 8, 9] (深拷贝)
*/
4 当对象是“引用类型”(代码)
1 for()循环拷贝及打印结果代码
import java.util.*;
public class TestArray {
private int val = 10;
public void setVal(int val) {
this.val = val;
}
public int getVal() {
return this.val;
}
public static void print(TestArray[] t1, TestArray[] t2){
for(int i = 0;i < t1.length;i++) {
t2[i] = t1[i];//赋值的是引用,最后t1[i] t2[i] 都指向的是引用的地址,一个变都变,浅拷贝
}
for(int i = 0;i < t1.length;i++) {
System.out.print(t1[i].getVal()+" ");
}
System.out.println();
for(int i = 0;i < t2.length;i++) {
System.out.print(t2[i].getVal()+" ");
}
System.out.println();
System.out.println();
}
//for()
public static void main(String[] args) {
TestArray[] t1 = new TestArray[4];//TestArray 已经是引用类型
t1[0] = new TestArray();// t1[0] 链接的是对象的引用
t1[1] = new TestArray();
t1[2] = new TestArray();
t1[3] = new TestArray();
TestArray[] t2 = new TestArray[4];//t2[0]
print(t1, t2);
t2[0].setVal(100000);
print(t1, t2);
}
/*输出结果:
10 10 10 10
10 10 10 10
100000 10 10 10
100000 10 10 10 (浅拷贝)
*/
2 clone()
//clone()
public static void main(String[] args) {
TestArray[] t1 = new TestArray[4];
t1[0] = new TestArray();
t1[1] = new TestArray();
t1[2] = new TestArray();
t1[3] = new TestArray();
TestArray[] t2 = t1.clone();//t2[0]
print(t1, t2);
t2[0].setVal(99999);
print(t1, t2);
}
/*输出结果:
10 10 10 10
10 10 10 10
99999 10 10 10
99999 10 10 10 (浅拷贝)
*/
3 System.arraycopy ()
//System.arraycopy()
public static void main(String[] args) {
TestArray[] t1 = new TestArray[4];
t1[0] = new TestArray();
t1[1] = new TestArray();
t1[2] = new TestArray();
t1[3] = new TestArray();
TestArray[] t2 = new TestArray[4];//t2[0]
System.arraycopy(t1,0,t2,0,t1.length);
print(t1, t2);
t2[0].setVal(88888);
print(t1, t2);
}
/*输出结果:
10 10 10 10
10 10 10 10
88888 10 10 10
88888 10 10 10 (浅拷贝)
*/
4 Arrays.copyOf ()
//Arrays.copyOf ()
public static void main(String[] args) {
TestArray[] t1 = new TestArray[4];
t1[0] = new TestArray();
t1[1] = new TestArray();
t1[2] = new TestArray();
t1[3] = new TestArray();
TestArray[] t2 = Arrays.copyOf(t1,t1.length);
print(t1, t2);
t2[0].setVal(666666);
print(t1, t2);
/*输出结果:
10 10 10 10
10 10 10 10
666666 10 10 10
666666 10 10 10 (浅拷贝)
*/
5 Java对象-深拷贝两种方式
实现对象克隆两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
1继承Cloneable重写clone方法
public class Attachment implements Serializable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class YuelyLog implements Serializable,Cloneable {
private Attachment attachment;
private String name;
private String date;
@Override
protected YuelyLog clone() throws CloneNotSupportedException {
return (YuelyLog)super.clone();
}
public Attachment getAttachment() {
return attachment;
}
public void setAttachment(Attachment attachment) {
this.attachment = attachment;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException, CloneNotSupportedException {
Attachment attachment = new Attachment();
attachment.setName("wj");
YuelyLog yuelyLog = new YuelyLog();
yuelyLog.setAttachment(attachment);
yuelyLog.setDate(new Date().toString());
yuelyLog.setName("dn");
YuelyLog clone = yuelyLog.clone();
clone.getAttachment().setName("clone");
System.out.println("yelyLog:name="+(yuelyLog.getAttachment().getName()));
}
2 下面通过序列化实现深拷贝
public class YuelyLog implements Serializable,Cloneable {
private Attachment attachment;
private String name;
private String date;
@Override
protected YuelyLog clone() throws CloneNotSupportedException {
return (YuelyLog)super.clone();
}
public Attachment getAttachment() {
return attachment;
}
public void setAttachment(Attachment attachment) {
this.attachment = attachment;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
/**
* 使用序列化技术实现深拷贝
* @return
*/
public YuelyLog deepClone() throws IOException,ClassNotFoundException{
//将对象写入流中
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(this);
//从流中取出
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
return (YuelyLog)objectInputStream.readObject();
}
}
深拷贝浅拷贝示例
假如说你想复制一个简单变量。很简单:
int apples = 5;
int pears = apples;
不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。
但是如果你复制的是一个对象,情况就有些复杂了。
class Student {
private int number;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
public class Test {
public static void main(String args[]) {
Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = stu1;
System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
}
}
打印结果:
学生1:12345
学生2:12345
这里我们自定义了一个学生类,该类只有一个number字段。
我们新建了一个学生实例,然后将该值赋值给stu2实例。(Student stu2 = stu1;)
再看看打印结果,作为一个新手,拍了拍胸腹,对象复制不过如此,
难道真的是这样吗?
我们试着改变stu2实例的number字段,再打印结果看看:
stu2.setNumber(54321);
System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
打印结果:
学生1:54321
学生2:54321
这就怪了,为什么改变学生2的学号,学生1的学号也发生了变化呢?
原因出在(stu2 = stu1) 这一句。该语句的作用是将stu1的引用赋值给stu2,这样,stu1和stu2指向内存堆中同一个对象。
那么,怎样才能达到复制一个对象呢?
是否记得万类之王Object。它有11个方法,有两个protected的方法,其中一个为clone方法。
该方法的签名是:
protected native Object clone() throws CloneNotSupportedException;
因为每个类直接或间接的父类都是Object,因此它们都含有clone()方法,但是因为该方法是protected,所以都不能在类外进行访问。
要想对一个对象进行复制,就需要对clone方法覆盖。
一般步骤是(浅复制):
1. 被复制的类需要实现Clonenable接口(不实现的话在调用clone方***抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法)
2. 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)
下面对上面那个方法进行改造:
一般步骤是(浅复制):
1. 被复制的类需要实现Clonenable接口(不实现的话在调用clone方***抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法)
2. 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)
下面对上面那个方法进行改造:
打印结果:
学生1:12345
学生2:12345
学生1:12345
学生2:54321
如果你还不相信这两个对象不是同一个对象,那么你可以看看这一句:
System.out.println(stu1 == stu2); // false
上面的复制被称为浅复制(Shallow Copy),还有一种稍微复杂的深度复制(deep copy):
我们在学生类里再加一个Address类。
class Address {
private String add;
public String getAdd() {
return add;
}
public void setAdd(String add) {
this.add = add;
}
}
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();
}
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());
}
}
打印结果:
学生1:123,地址:杭州市
学生2:123,地址:杭州市
乍一看没什么问题,真的是这样吗?
我们在main方法中试着改变addr实例的地址。
addr.setAdd("西湖区");
System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
打印结果:
学生1:123,地址:杭州市
学生2:123,地址:杭州市
学生1:123,地址:西湖区
学生2:123,地址:西湖区
这就奇怪了,怎么两个学生的地址都改变了?
原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。
所以,为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法,完整代码如下:
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());
}
}
打印结果:
学生1:123,地址:杭州市
学生2:123,地址:杭州市
学生1:123,地址:西湖区
学生2:123,地址:杭州市
这样结果就符合我们的想法了。
最后我们可以看看API里其中一个实现了clone方法的类:
java.util.Date:
这样结果就符合我们的想法了。
最后我们可以看看API里其中一个实现了clone方法的类:
java.util.Date:
该类其实也属于深度复制。