文章目录
0 复制的方式
将一个对象的引用复制给另外一个对象,一共有三种方式:
- 直接赋值
- 浅拷贝
- 深拷贝
首先看看深拷贝和浅拷贝的概念,在 Java 中,除了基本数据类型之外,还有引用数据类型。而一般使用 "="
做赋值操作的时候,对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,他们实际上还是指向的同一个对象。而浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。
1)浅拷贝:(复制引用但不复制引用的对象)
对基本数据类型进行值传递,对引用数据类型进行了引用传递
2)深拷贝:(复制对象和其应用对象)
对基本数据类型进行值传递,对引用数据类型,则创建了一个新的对象,并复制了其内容
1 for循环(数值拷贝)
public static void main(String[] args) {
int[] array = new int[]{1,2,3,4,5};
int[] arraycopy = new int[5];
for(int i=0;i<array.length;i++){
arraycopy[i]=array[i];
}
arraycopy[0]=90;
for(int x:array){
System.out.print(x+" "); //1 2 3 4 5
}
System.out.println();
for(int x:arraycopy){
System.out.print(x+" ");//90 2 3 4 5
}
}
2 System.arraycopy()
class Student{
private int age=10;
public Student(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
Student[] students=new Student[2];
students[0]=new Student(15);
students[1]=new Student(25);
Student[] teachers = new Student[2];
System.arraycopy(students,0,teachers,0,students.length);
for(Student x:students){
System.out.print(x.getAge()+" ");
}
System.out.println();
for(Student x:teachers){
System.out.print(x.getAge()+" ");
}
System.out.println();
System.out.println("************");
students[0].setAge(99);
for(Student x:students){
System.out.print(x.getAge()+" ");
}
System.out.println();
for(Student x:teachers){
System.out.print(x.getAge()+" ");
}
}
}
浅拷贝:
15 25
15 25
99 25
99 25
//System.arraycopy()方法java源代码中的代码
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
native方法为本地方法,效率较高。
src :源地址
srcPos:源地址开始位置
dest:目的地
destPos:目的地的开始位置
length:拷贝的长度
3 Arrays.copyOf()方法
Arrays.copyOf(源数组,指定数组长度),底层调用的是System.arraycopy()
, Arrays.copyOf()方法会产生一个新的对象,但依然是浅拷贝。
class Student{
private int age=10;
public Student(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
Student[] students=new Student[2];
students[0]=new Student(15);
students[1]=new Student(25);
Student[] teachers =
Arrays.copyOf(students,students.length);
for(Student x:students){
System.out.print(x.getAge()+" ");
}
System.out.println();
for(Student x:teachers){
System.out.print(x.getAge()+" ");
}
System.out.println();
System.out.println("************");
students[0].setAge(99);
for(Student x:students){
System.out.print(x.getAge()+" ");
}
System.out.println();
for(Student x:teachers){
System.out.print(x.getAge()+" ");
}
}
}
15 25
15 25
99 25
99 25
参考:
https://www.cnblogs.com/xingzc/p/9646923.html
4 Clone, Serializable实现深拷贝
(1)数组元素为数值
public static void main(String[] args) {
int[] array = new int[]{1,2,3,4,5};
int[] arraycopy = new int[5];
arraycopy=array.clone();
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(arraycopy));
System.out.println("*****************");
array[0]=100;
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(arraycopy));
}
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[100, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
(2)数组元素为引用类型
class Student{
private int age=10;
public Student(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
Student[] students=new Student[2];
students[0]=new Student(15);
students[1]=new Student(25);
Student[] teachers = students.clone();
for(Student x:students){
System.out.print(x.getAge()+" ");
}
System.out.println();
for(Student x:teachers){
System.out.print(x.getAge()+" ");
}
System.out.println();
System.out.println("************");
students[0].setAge(99);
for(Student x:students){
System.out.print(x.getAge()+" ");
}
System.out.println();
for(Student x:teachers){
System.out.print(x.getAge()+" ");
}
}
}
15 25
15 25
99 25
99 25
数组之间的引用类型元素实现的也是浅拷贝, 当调用 students[0].setAge(99);后,两个对象的内容都改变了。
(3)对象之间的拷贝
java.lang.Object类的clone()方法为protected类型,不可直接调用,被克隆的类要实现Cloneable接口;然后在该类中覆盖clone()方法,并且在该clone()方法中调用super.clone()。
class Money {
public double money = 12.5;
}
class Person implements Cloneable{
public String name;
public Money m;
public Person() {
this.m = new Money();
}
// 重写clone方法:Object
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
Person person2 = (Person)person.clone();
System.out.println(person.m.money);
System.out.println(person2.m.money);
System.out.println("===================");
person2.m.money = 99.9;
System.out.println(person.m.money);
System.out.println(person2.m.money);
}
结果显示此时的clone为浅拷贝。
12.5
12.5
99.9
99.9
为了实现深拷贝,可以继续使用clone方法,将其内的引用类型变量m进行clone
class Money implements Cloneable{
double money = 12.5;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable{
public String name;
public Money m;
public Person() {
this.m = new Money();
}
//重写clone方法:Object
@Override
protected Object clone() throws CloneNotSupportedException {
//return super.clone();
Person per = (Person) super.clone();
per.m = (Money)this.m.clone();
return per;
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
Person person2 = (Person)person.clone();
System.out.println(person.m.money);
System.out.println(person2.m.money);
System.out.println("===================");
person2.m.money = 99.9;
System.out.println(person.m.money);
System.out.println(person2.m.money);
}
}
结果显示为深拷贝:
12.5
12.5
12.5
99.9
对象之间进行拷贝时,除了本类需要重写clone方法,其成员变量中若含引用类型,那么该成员变量对应的类也要重写clone方法。
对象克隆有两种方式:
- 实现
Cloneable接口
并重写Object类中的clone()方法; - 实现
Serializable接口
,在Java 语言里深复制一个对象,常常可以先让对象实现Serializable 接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
注意:
基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是可以通过泛型类型进行限定,可以判断出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。