直接赋值、浅拷贝和深拷贝

**

直接对象赋值

**
我们经常使用的:

Student s1 = new Student();
Student s2 = s1;

直接对象赋值,它只是拷贝了对象引用地址而已,并没有在堆内存重新生成一个新的对象(如下图)。上面的s1和s2引用其实都是指向堆中同一个Student实例地址。所以如果改下s1中的任何成员变量(基本类型或者引用类型),s2中相对应的成员变量也会改变。
在这里插入图片描述

@Data
@Builder
public class Student{
    String name;
    Integer age;
    Body body;

    public Student(String name, int age, Body body) {
        this.age = age;
        this.name = name;
        this.body = body;
    }

    public Student(Student s) {
        this.body = s.body;
        this.age = s.age;
        this.name = s.name;
    }

    public void setBody(Body body) {
        this.body = body;
    }

}

@Data
@Builder
public class Body {
    int high;

    public Body (int high) {
        this.high = high;
    }

}

public static void main(String[] args) throws IOException, ClassNotFoundException{
    Body body1 = Body.builder().high(152).build();
    Student stu1 = Student.builder()
            .name("lyt")
            .age(24)
            .body(body1)
            .build();
    Student stu2 = stu1;
    System.out.println("stu1: " + stu1);
    System.out.println("stu2: " + stu2 );
    stu1.setAge(25);
    stu1.setName("lst");

    stu1.setBody(new Body(170)); 
    System.out.println("-----------------------------------------");
    System.out.println("stu1: " + stu1);
    System.out.println("直接赋值 stu2: " + stu2);


// 输出
//stu1: Student(name=lyt, age=24, body=Body(high=152))
//stu2: Student(name=lyt, age=24, body=Body(high=152))
//-----------------------------------------
//stu1: Student(name=lst, age=25, body=Body(high=170))
//直接赋值 stu2: Student(name=lst, age=25, body=Body(high=170))

**

浅拷贝

**
浅拷贝对象,对于对象中的基本类型成员变量进行值赋值,就是将基本类型的成员变量的值赋给拷贝对象中对应成员变量,所以如果对其中一个对象的基本类型成员变量进行修改,不会影响另一个对象中对应的成员变量的值。对于对象中的引用类型成员变量,是将其引用地址赋值给拷贝对象中对应的引用类型成员变量,所以如果对其中一个对象的引用类型成员变量进行修改,会影响另一个对象。
总的来说,浅拷贝是一种不完全拷贝,也就是它只复制他本身和其包含的基本类型数据的成员变量给拷贝对象(也就是拷贝对象的基本类型成员变量在堆中有不同于被拷贝对象中的基本类型成员变量的内存地址),而引用类型数据的成员变量没有复制。
如下图:
在这里插入图片描述
实现浅拷贝的方法有两种,一种就是使用拷贝构造函数来实现浅拷贝。

@Data
@Builder
public class Student{
    String name;
    Integer age;
    Body body;

    public Student(String name, int age, Body body) {
        this.age = age;
        this.name = name;
        this.body = body;
    }

    public Student(Student s) {
        this.body = s.body;
        this.age = s.age;
        this.name = s.name;
    }

    public void setBody(Body body) {
        this.body = body;
    }

}

@Data
@Builder
public class Body {
    int high;

    public Body (int high) {
        this.high = high;
    }

}

public static void main(String[] args) throws IOException, ClassNotFoundException{
    Body body4 = Body.builder().high(160).build();
    Student stu4 = Student.builder()
            .name("xm")
            .age(24)
            .body(body4)
            .build();
    // 利用拷贝构造函数实现浅拷贝
    System.out.println("利用拷贝构造函数实现浅拷贝");
    Student student = new Student(stu4);
    System.out.println("stu4: "  + stu4);
    System.out.println("student: "  + student);
    body4.setHigh(180);
    stu4.setBody(body4);
    stu4.setName("bobokou");
    stu4.setAge(18);

System.out.println("stu4: "  + stu4);
System.out.println("student: "  + student);


// 输出
//利用拷贝构造函数实现浅拷贝
//stu4: Student(name=xm, age=24, body=Body(high=160))
//student: Student(name=xm, age=24, body=Body(high=160))
//stu4: Student(name=bobokou, age=18, body=Body(high=180))
//student: Student(name=xm, age=24, body=Body(high=180))

一种就是使用clone()方法来进行浅拷贝。
clone()是Object类的一个protected成员方法,我们不能直接调用该方法,需要重写该方法,然后在方法内调用super.clone();并且使用clone方法的类必须实现Cloneable接口,否则会抛出异常CloneNotSupportedException。

@Data
@Builder
public class Student implements Cloneable {
    String name;
    Integer age;
    Body body;

    public Student(String name, int age, Body body) {
        this.age = age;
        this.name = name;
        this.body = body;
    }

    public Student(Student s) {
        this.body = s.body;
        this.age = s.age;
        this.name = s.name;
    }

    public void setBody(Body body) {
        this.body = body;
    }
    
    @Override
    public Object clone() {
       Object object = null;
       try {
          object = super.clone();
       } catch (CloneNotSupportedException ex) {
           ex.printStackTrace();
        }
       return object;
}

}

@Data
@Builder
public class Body {
    int high;

    public Body (int high) {
        this.high = high;
    }

}

public static void main(String[] args) throws IOException, ClassNotFoundException{
    Body body1 = Body.builder().high(152).build();
    Student stu1 = Student.builder()
            .name("lyt")
            .age(24)
            .body(body1)
            .build();
    Student stu3 = (Student) stu1.clone();
    System.out.println("stu1: " + stu1);
    System.out.println("stu3: " + stu3 );
    
    stu1.setAge(25);
    stu1.setName("lst");
    
    //Body body3 = new Body(170);
    //body3.setHigh(170);
    //stu4.setBody(body3);
    body1.setHigh(170);
    //stu1.setBody(new Body(170)); // 如果是body3,body3是Body类的一个新的实例,这样是把stu1的body成员变量指向body3,
    // 但并没有改变body1中的属性值,stu3的body成员变量还是指向的body1,所以stu3中的body成员变量里的high没有改变
    stu1.setBody(body1);
    
    System.out.println("-----------------------------------------");
    System.out.println("stu1: " + stu1);
    System.out.println("浅拷贝 stu3: " + stu3 );


// 输出
//stu1: Student(name=lyt, age=24, body=Body(high=152))
//stu3: Student(name=lyt, age=24, body=Body(high=152))
//-----------------------------------------
//stu1: Student(name=lst, age=25, body=Body(high=170))
//浅拷贝 stu3: Student(name=lyt, age=24, body=Body(high=170))

注意:Student中的name成员变量是String类型,String类型其实也是引用类型,但是我们看到浅拷贝时对stu1的name的修改也不会影响stu3中的name。这是因为String类型被申明为final类型,是不可被修改的(String类型数据在常量池中)。所以当将stu1的name属性从“lyt”改为“lst”时,不是修改了它的值,而是将它的引用由“lyt”指向“lst”。

**

深拷贝

**
深拷贝是一种完全拷贝,也就是说不论是基本类型的成员变量还是引用类型的成员变量都会拷贝一份给拷贝对象。(在堆内存中都会有新的内存地址)在内存中生成一个新的对象。也就是说,此时对其中一个对象做的任何修改都不会影响另一个对象。
在这里插入图片描述
可以使用clone()来实现深拷贝。与浅拷贝不同的时,对于对象中包含的所有对象类型的成员变量都要实现Cloneable接口并重写clone()方法,如果对象类型的成员变量还包含对象类型的成员变量,也要实现Cloneable接口并重写clone()方法,层层嵌套。这样对于属性数量比较多、层次比较深的类而言,每个类都要重写clone方法太过繁琐。
也可以使用序列化方式来实现深拷贝。所有对象都要实现 Serializable 接口。

@Data
@Builder
public class Student1 implements Serializable {
    String name;
    Integer age;
    Body body;
}

@Data
@Builder
public class Body implements Serializable {
    int high;

    public Body (int high) {
        this.high = high;
    }

}

public static void main(String[] args) throws IOException, ClassNotFoundException{
    // 利用序列化实现深拷贝
    System.out.println("-----------------------------------------");
    System.out.println("利用序列化实现深拷贝");
    Student1 student1 = Student1.builder()
            .name("lyt")
            .age(24)
            .body(body1)
            .build();
    // 将对象序列化为流
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
    objectOutputStream.writeObject(student1);

    // 将流反序列为对象
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
    ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
    Student1 student2 = (Student1) objectInputStream.readObject();

    //Student1 student2= student1;
    System.out.println("student1: " + student1);
    System.out.println("深拷贝:student2: " + student2 );

    //Student1 student2= student1;
    System.out.println("修改值后");
    student1.setAge(25);
    student1.setName("lst");
    //body3.setHigh(170);
    //stu4.setBody(body3);
    body1.setHigh(200);
    student1.setBody(body1);
    System.out.println("student1: " + student1);
    System.out.println("深拷贝:student2: " + student2 );

}

//输出:
//利用序列化实现深拷贝
//student1: Student1(name=lyt, age=24, body=Body(high=152))
//深拷贝:student2: Student1(name=lyt, age=24, body=Body(high=152))
//修改值后
//student1: Student1(name=lst, age=25, body=Body(high=200))
//深拷贝:student2: Student1(name=lyt, age=24, body=Body(high=152))
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值