今日目标 深拷贝 浅拷贝
一、前言
任何变成语言中,其实都有浅拷贝和深拷贝的概念,Java 中也不例外。在对一个现有的对象进行拷贝操作的时候,是有浅拷贝和深拷贝之分的,他们在实际使用中,区别很大,如果对其进行混淆,可能会引发一些难以排查的问题。
本文就在 Java 中的深拷贝和浅拷贝做一个详细的解说。这也是在面试时面试官可能会让写的。
特点:Java没有引用传递,Java只有值传递。
不想了解的话最后面有一点总结:!!
浅拷贝
- 基本数据类型直接copy值
- 引用类型copy地址
首先implements Cloneable 这个接口
然后重写 clone() 方法
--------先写一个User实体类,实现Cloneable接口 并 并重写 clone()方法--------
public class User implements Cloneable{//Cloneable:JVM的标记接口,//并要重写克隆方法。
private int id;
private String name;
public User(){
super();
}
public User(int id,String name){
super();
this.name=name;
this.id=id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
//需要重写克隆方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
------写完后测试一下---------
class test{
public static void main(String[] args) throws CloneNotSupportedException {
User u1 = new User(1,"张三");
System.out.println(u1);
System.out.println("--------拷贝的u1---------");
User u2 = (User) u1.clone();
System.out.println(u2);
System.out.println("-------他们是否一样呢-----------");
System.out.println(u1==u2);
System.out.println("-------此时我更改U1的值-----------");
u1.setName("李四");
System.out.println("u1:"+u1);
System.out.println("u2:"+u2);
System.out.println(u1==u2);
}
}
------效果-------
可以看出此时 拷贝还没有问题 是两个对象
改变里面一个类型的值 拷贝的不会影响
因为是两个对象
user实体里面存放的都是 基本数据类型
—————现在我们来加一个引用类型来看看——————
Address 实体类 存放一些地址信息
class Address {
private String province;
private String city;
private String area;
public Address() {
super();
}
public Address(String province, String city, String area) {
super();
this.province = province;
this.city = city;
this.area = area;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return Objects.equals(province, address.province) &&
Objects.equals(city, address.city) &&
Objects.equals(area, address.area);
}
@Override
public int hashCode() {
return Objects.hash(province, city, area);
}
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
", area='" + area + '\'' +
'}';
}
}
————在User对象里加入这个引用类型————
User 引入后
public class User implements Cloneable{//Cloneable:JVM的标记接口,//并要重写克隆方法。
private int id;
private String name;
private Address address;
public User(){
super();
}
public User(int id,String name){
super();
this.name=name;
this.id=id;
}
public int getId() {
return id;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", address=" + address +
'}';
}
//需要重写克隆方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
————测试一下————
class test2{
public static void main(String[] args) throws CloneNotSupportedException {
User u1 = new User(1,"张三");
u1.setAddress( new Address ("河北省","保定市","莲池区"));
System.out.println(u1);
System.out.println("--------拷贝的u1---------");
User u2 = (User) u1.clone();
u2.getAddress().setArea("王者大陆");
System.out.println(u2);
System.out.println(u1);
//此时更改后的值(更改的是引用的值Address) 父类也发生了改变
//所以这是浅拷贝 只拷贝一层;
//浅拷贝 拷贝的是引用值过去 具体还是操作的是一个
}
}
------来看看效果——
我们发现,修改Address里面的值后 克隆对象和父类对象都变了
这说明 克隆对象和父类对象修改的 Address 都是指向的一个 印证了上面说的
只拷贝一层 而且引用类型只是copy了地址
看下图 就知道了
这就是所谓的浅拷贝。
接下来看看深拷贝怎么写
深拷贝
深拷贝则需要 我们把Address也要拷贝
首先Address实现Cloneable接口 并 并重写 clone()方法
class Address implements Cloneable{
private String province;
private String city;
private String area;
public Address() {
super();
}
public Address(String province, String city, String area) {
super();
this.province = province;
this.city = city;
this.area = area;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return Objects.equals(province, address.province) &&
Objects.equals(city, address.city) &&
Objects.equals(area, address.area);
}
@Override
public int hashCode() {
return Objects.hash(province, city, area);
}
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
", area='" + area + '\'' +
'}';
}
}
然后User实体类 的clone()方法要改写
也就是在User的克隆方法处要对 Address克隆 并封装进克隆对象里返回给调用方法
public class User implements Cloneable{//Cloneable:JVM的标记接口,//并要重写克隆方法。
private int id;
private String name;
private Address address;
public User(){
super();
}
public User(int id,String name){
super();
this.name=name;
this.id=id;
}
public int getId() {
return id;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", address=" + address +
'}';
}
//需要重写克隆方法
@Override
protected Object clone() throws CloneNotSupportedException {
//深拷贝 克隆User对象
User user=(User) super.clone();
Address address=(Address) user.address.clone();
user.setAddress(address);
return user;
}
}
-——————测试——————
class test2{
public static void main(String[] args) throws CloneNotSupportedException {
User u1 = new User(1,"张三");
u1.setAddress( new Address ("河北省","保定市","莲池区"));
System.out.println(u1);
System.out.println("--------拷贝的u1---------");
User u2 = (User) u1.clone();
u2.getAddress().setArea("王者大陆");
System.out.println(u2);
System.out.println(u1);
//此时更改后的值(更改的是引用的值Address) 父类也发生了改变
//所以这是浅拷贝 只拷贝一层;
//浅拷贝 拷贝的是引用值过去 具体还是操作的额一个
}
}
————效果————
——————看见了没 我们的深拷贝 的两个对象是完全不同的两个对象————
也就是类似下图
总结
浅拷贝
- 基本数据类型直接copy值
- 引用类型copy地址
特点:
(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个。
(2) 对于引用类型,比如数组或者类对象,因为引用类型是引用传递,所以浅拷贝只是把内存地址赋值给了成员变量,它们指向了同一内存空间。改变其中一个,会对另外一个也产生影响。
深拷贝
- 将引用类型也拷贝 并和父类封装返回
- 得到两个完全独立的对象
特点
(1)
对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。
(2)
对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。
(3) 对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。 (4)
深拷贝相比于浅拷贝速度较慢并且花销较大。
举例子的话就是:
浅拷贝:考试场景
小花是个没有感情的机器,啥也不会,考试的时候
小花抄了小明的卷子,但是小明把题解告诉了小花,小花有了自己的思维
##可能小花觉得小明做的不对,因为小花有自己的思维了。
所以小明 小花在更改上述传递的答案的值时,互相都不干扰
但是有一个题 小明超了小黑的 这个题小黑没给小明讲解,所以小明没就没给小花讲解 所以小黑的值改了 他们两个都跟着改。
深拷贝:此时场景
小黑当时给小明抄的时候把题解告诉了小明 , 小明因为暗恋小花多年也直接把题解告诉了小花,这样小花也有了小黑的思想,自己随便更改值了。
那问题来了 小花最后是选择了谁作男朋友呢??
哈哈 !!
其实这个例子不是很好,就大致那么个意思,我就这点水平哈哈。
告辞了 您嘞!!