浅拷贝:当拷贝对象包含基本数据类型(如int、long)或者不可变的对象(如字符串、基本类型的包装类)时,会直接将这些属性复制到新的对象中。而原型对象中的引用对象会把内存中的地址复制给克隆对象。此时,两个对象共享了一个私有变量,你改我改大家都能改。
深拷贝:不管原型对象属性是简单数据类型还是引用对象类型都会完全的复制一份到新的对象中。两个对象之间互不影响。
1、什么叫Java浅拷贝?
浅拷贝是会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。
2、什么叫Java深拷贝?
深拷贝复制变量值,对于引用数据,则递归至基本类型后,再复制。深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。
3、Java浅拷贝和深拷贝的区别是什么?
通俗来讲浅拷贝的复制其引用,当引用指向的值改变时也会跟着变化;而深拷贝则是与原来的对象完全隔离,互补影响。
*java的浅拷贝的实现方式:
3种:
1.通过拷贝构造方法实现
/* 拷贝构造方法实现浅拷贝 */
public class CopyConstructor {
public static void main(String[] args) {
Age a=new Age(20);
Person p1=new Person(a,"摇头耶稣");
Person p2=new Person(p1);
System.out.println("p1是"+p1);
System.out.println("p2是"+p2);
//修改p1的各属性值,观察p2的各属性值是否跟随变化
p1.setName("小傻瓜");
a.setAge(99);
System.out.println("修改后的p1是"+p1);
System.out.println("修改后的p2是"+p2);
}
}
class Person{
//两个属性值:分别代表值传递和引用传递
private Age age;
private String name;
public Person(Age age,String name) {
this.age=age;
this.name=name;
}
//拷贝构造方法
public Person(Person p) {
this.name=p.name;
this.age=p.age;
}
public void setName(String name) {
this.name=name;
}
public String toString() {
return this.name+" "+this.age;
}
}
class Age{
private int age;
public Age(int age) {
this.age=age;
}
public void setAge(int age) {
this.age=age;
}
public int getAge() {
return this.age;
}
public String toString() {
return getAge()+"";
}
}
2.重写clone方法实现浅拷贝
/* clone方法实现浅拷贝 */
public class ShallowCopy {
public static void main(String[] args) {
Age a=new Age(20);
Student stu1=new Student("摇头耶稣",a,175);
//通过调用重写后的clone方法进行浅拷贝
Student stu2=(Student)stu1.clone();
System.out.println(stu1.toString());
System.out.println(stu2.toString());
//尝试修改stu1中的各属性,观察stu2的属性有没有变化
stu1.setName("大傻子");
//改变age这个引用类型的成员变量的值
a.setAge(99);
//stu1.setaAge(new Age(99)); 使用这种方式修改age属性值的话,stu2是不会跟着改变的。因为创建了一个新的Age类对象而不是改变原对象的实例值
stu1.setLength(216);
System.out.println(stu1.toString());
System.out.println(stu2.toString());
}
}
/*
* 创建年龄类
*/
class Age{
//年龄类的成员变量(属性)
private int age;
//构造方法
public Age(int age) {
this.age=age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return this.age+"";
}
}
/*
* 创建学生类
*/
class Student implements Cloneable{
//学生类的成员变量(属性),其中一个属性为类的对象
private String name;
private Age aage;
private int length;
//构造方法,其中一个参数为另一个类的对象
public Student(String name,Age a,int length) {
this.name=name;
this.aage=a;
this.length=length;
}
//eclipe中alt+shift+s自动添加所有的set和get方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Age getaAge() {
return this.aage;
}
public void setaAge(Age age) {
this.aage=age;
}
public int getLength() {
return this.length;
}
public void setLength(int length) {
this.length=length;
}
//设置输出的字符串形式
public String toString() {
return "姓名是: "+this.getName()+", 年龄为: "+this.getaAge().toString()+", 长度是: "+this.getLength();
}
//重写Object类的clone方法
public Object clone() {
Object obj=null;
//调用Object类的clone方法,返回一个Object实例
try {
obj= super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
3.通过Arrays工具类实现浅拷贝
int[] copy = Arrays.copyOf(original, newLen);
使用方便,且可以指定新数组长度。常用于数组扩容。
Object[] copy = Arrays.copyOf(original, newLen, Object[].class);
对于引用类型数组,还可以指定新数组类型。
Object[] copy = Arrays.copyOfRange(original, from, to);
还提供了范围拷贝的方法,将original数组[from, to)范围内的元素拷贝到新数组。
System.arraycopy
System.arraycopy(original, 0, copy, 0, len);
复杂灵活,需要自己创建新数组。
Arrays.copyOf和Arrays.copyOfRange都是基于这个方法实现的。
作者:M_lear
链接:https://www.jianshu.com/p/5e5180ce95ba
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
实现深拷贝的方式
- 构造函数
2.重载clone()方法
3.Serializable序列化
1.构造函数
通过对克隆对象构造函数传入原对象的值来构造
public void constructorCopy() {
Student student = new Student ("小李",21,"男");
School school = new School ("xx大学",100, student);
// 调用构造函数时进行深拷贝
School copySchool = new School (school.getSchoolName(),school.getStuNums(), new Student(student.getName(), student.getAge(),student.getSex()));
// 修改源对象的值
copySchool .getStudent().setSex("女");
// 检查两个对象的值不同
System.out.println(school.hashCode()==school2.hasCode())
}
2.重载clone方法,通过对对象中的所有子对象都进行进行克隆达到实现最后全为基本类型的复制。
public class School implements Cloneable{
private String schoolName;
private int stuNums;
private Student stu;
public String getSchoolName() {
return schoolName;
}
public void setSchoolName(String schoolName) {
this.schoolName = schoolName;
}
public int getStuNums() {
return stuNums;
}
public void setStuNums(int stuNums) {
this.stuNums = stuNums;
}
public Student getStu() {
return stu;
}
public void setStu(Student stu) {
this.stu = stu;
}
@Override
protected School clone() throws CloneNotSupportedException {
School school = (School) super.clone();
school.stu = (Student) stu.clone();
return school;
}
@Override
public String toString() {
return "School [schoolName=" + schoolName + ", stuNums=" + stuNums + ", stu=" + stu + "]";
}
}
public class Student implements Cloneable{
private String name;
private int age;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
@Override
protected Student clone() throws CloneNotSupportedException {
return (Student)super.clone();
}
}
3.serializable序列化
import java.io.Serializable;
public class User implements Serializable {
private String name;
private Address2 address;
public User(String name, Address2 address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address2 getAddress() {
return address;
}
public void setAddress(Address2 address) {
this.address = address;
}
public Object deepClone() throws Exception
{
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
import java.io.Serializable;
public class Address2 implements Serializable {
private String city;
private String country;
public Address2(String city, String country) {
this.city = city;
this.country = country;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
@Override
public String toString() {
return "Address2{" +
"city='" + city + '\'' +
", country='" + country + '\'' +
'}';
}
}
public static void main(String[] args) throws Exception {
Address2 address = new Address2("大同", "中国");
User user = new User("yznl", address);
User user2 = (User) user.deepClone();
System.out.println(user.toString());
System.out.println(user2.toString());
}