2025年深入理解Java中的引用、复制、克隆和拷贝

深入理解Java中的引用、复制、克隆和拷贝含义 引用 在 Java 中 所有对象变量都是引用 它们存储的是对象的地址 在堆内存中 而不是对象本身 当你创建一个对象并将其赋值给一个变量时 这个变量实际上存储的是对象的引用 这个引用指向对象在内存中的位置 Person person1 new

大家好,我是讯享网,很高兴认识大家。

含义

引用在Java中,所有对象变量都是引用,它们存储的是对象的地址(在堆内存中),而不是对象本身。当你创建一个对象并将其赋值给一个变量时,这个变量实际上存储的是对象的引用,这个引用指向对象在内存中的位置。

Person person1 = new Person("Alice"); // 可以理解为创建一个Person对象,然后返回的对象引用被赋给了 person1 变量 
讯享网

在上面的代码中,person1 是一个引用,它指向了一个 Person 对象在内存中的地址。

  • 复制(赋值)当你将一个对象变量赋值给另一个变量时,实际上是在复制对象的引用,而不是对象本身。这意味着两个变量指向同一个对象,它们共享同一块内存空间,因此复制也可以理解为引用。
讯享网Person person1 = new Person("Alice"); Person person2 = person1; // 复制(引用)了 person1,其实person1也是引用了Person对象 

在这个例子中,person2 和 person1 都指向同一个 Person 对象。

  • 克隆(拷贝):克隆可以理解为拷贝,分为浅拷贝(浅复制)、深拷贝(深复制)对象的克隆意味着创建一个新的对象,它的值和原始对象相同,但它在内存中占据不同的位置。Java 中的 clone 方法可以用于实现对象的浅拷贝,需要注意的是默认的 clone 方法执行的是浅拷贝,即它只复制对象的字段值,而不复制对象引用的内容。
Person person2 = (Person) person1.clone(); // 与 Person person2 = person1; 不同的是,克隆是引用对象变量的内存地址;复制是直接引用对象内存地址 // 上面一行代码和这两行代码一个效果,copyProperties也是浅复制,针对对象里面的变量进行内存地址复制 Person person2 = new Person(); BeanUtils.copyProperties(person1, person2); 

在这个例子中,person2 和 person1 是两个不同的对象,但它们的值相同,但在内存中存储在不同的位置。

综上所述,引用是指对象变量存储的是对象的地址;复制是将对象的引用赋值给另一个变量;克隆是创建一个新的对象,其值和原始对象相同;拷贝是将对象的属性值复制到另一个对象中。需要注意的是克隆和拷贝的方式可以是浅复制,也可以是深复制,具体取决于实现方式。

举例

基础实体类

讯享网@Data @AllArgsConstructor class Address { 
    private String city; } @Data @NoArgsConstructor @AllArgsConstructor class Person implements Cloneable { 
    private String name; private Address address; @Override public Object clone() throws CloneNotSupportedException { 
    return super.clone(); } } 

1、复制、引用

以下就是一个简单的对象复制,或者说是引用的例子

 public static void main(String[] args) { 
    Address address = new Address("重庆"); Person person1 = new Person("小明", address); Person person2 = person1; // 引用 person1 对象的地址(在堆内存中),person2 和 person1 都指向同一个 Person 对象。 System.out.println("原始值 ---> " + person2); System.out.println("person1.equals(person2) ---> " + person1.equals(person2)); System.out.println("person1 == person2 ---> " + (person1 == person2)); // 修改 person2 的字段 person2.setName("小白"); // 修改了引用对象的值 person2.getAddress().setCity("北京"); // address.setCity("北京"); 一个意思,修改的是源地址 address 对象中的变量 System.out.println("person1 ---> " + person1); System.out.println("person2 ---> " + person2); System.out.println("address ---> " + address); } 

打印:

// 因为 person2 和 person1 都指向同一个 Person 对象,所以无论修改person2 还是 person1,都会改变 原始值 ---> Person(name=小明, address=Address(city=重庆)) person1.equals(person2) ---> true person1 == person2 ---> true person1 ---> Person(name=小白, address=Address(city=北京)) person2 ---> Person(name=小白, address=Address(city=北京)) address ---> Address(city=北京) 

2、浅拷贝

public static void main(String[] args) { 
    Address address = new Address("四川"); Person person1 = new Person("小明", address); try { 
    // 浅拷贝,先声明一个person类的person2对象,然后把person1里面的变量(name,address)地址引用复制到了person2中, Person person2 = (Person) person1.clone(); System.out.println("原始值 ---> " + person2); System.out.println("person1.equals(person2) ---> " + person1.equals(person2)); System.out.println("person1 == person2 ---> " + (person1 == person2)); person2.setName("小白"); // String是定长,字符串小白会分配另一个内存地址,所以其实是重新把name变量指向了另一个地址 person2.getAddress().setCity("重庆"); System.out.println("person1 ---> " + person1); System.out.println("person2 ---> " + person2); System.out.println("address ---> " + address); } catch (CloneNotSupportedException e) { 
    e.printStackTrace(); } } 

打印

原始值 ---> Person(name=小明, address=Address(city=四川)) person1.equals(person2) ---> true person1 == person2 ---> false // 跟前面对象引用不一样的就是,浅拷贝是复制的对象里面变量的内存地址, // 而name是个定长字符串,修改为小白后,person2中name的引用地址就变了,而person1中name还是指向原来的小明 // 而address因为是个对象,person2.getAddress().setCity("重庆")修改的是address中city这个字段的引用,而person1和person2是同一个address对象,所以同时改变 person1 ---> Person(name=小明, address=Address(city=重庆)) person2 ---> Person(name=小白, address=Address(city=重庆)) address ---> Address(city=重庆) 

3、深拷贝

例子1(序列化):

package org.swp.controller; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.*; public class Test { 
    public static void main(String[] args) { 
    Address address = new Address("四川"); Person person1 = new Person("小明", address); // 缓存原始对象 Person person2 = person1.deepCopy(); System.out.println("原始值 ---> " + person2); System.out.println("person1.equals(person2) ---> " + person1.equals(person2)); System.out.println("person1 == person2 ---> " + (person1 == person2)); // 修改缓存中的对象 person2.setName("小白"); person2.getAddress().setCity("重庆"); System.out.println("person1 ---> " + person1); System.out.println("person2 ---> " + person2); System.out.println("address ---> " + address); } } @Data @AllArgsConstructor @NoArgsConstructor class Address implements Serializable { 
    private String city; } @Data @NoArgsConstructor @AllArgsConstructor class Person implements Serializable { 
    private String name; private Address address; public Person deepCopy() { 
    try { 
    ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeObject(this); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream in = new ObjectInputStream(bis); return (Person) in.readObject(); } catch (IOException | ClassNotFoundException e) { 
    e.printStackTrace(); return null; } } } 

打印:

原始值 ---> Person(name=小明, address=Address(city=四川)) person1.equals(person2) ---> true person1 == person2 ---> false person1 ---> Person(name=小明, address=Address(city=四川)) person2 ---> Person(name=小白, address=Address(city=重庆)) address ---> Address(city=四川) 

例子2(close):

package org.swp.controller; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; public class Test { 
    public static void main(String[] args) throws CloneNotSupportedException { 
    Address address = new Address("四川"); Person person1 = new Person("小明", address); // 缓存原始对象 Person person2 = (Person) person1.clone(); System.out.println("原始值 ---> " + person2); System.out.println("person1.equals(person2) ---> " + person1.equals(person2)); System.out.println("person1 == person2 ---> " + (person1 == person2)); // 修改缓存中的对象 person2.setName("小白"); person2.getAddress().setCity("重庆"); System.out.println("person1 ---> " + person1); System.out.println("person2 ---> " + person2); System.out.println("address ---> " + address); } } @Data @AllArgsConstructor @NoArgsConstructor class Address implements Serializable { 
    private String city; @Override public Object clone() throws CloneNotSupportedException { 
    return super.clone(); } } @Data @NoArgsConstructor @AllArgsConstructor class Person implements Cloneable { 
    private String name; private Address address; @Override public Object clone() throws CloneNotSupportedException { 
    Person clonedPerson = (Person) super.clone(); clonedPerson.address =new Address(this.address.getCity()); return clonedPerson; } } 

打印:

原始值 ---> Person(name=小明, address=Address(city=四川)) person1.equals(person2) ---> true person1 == person2 ---> false person1 ---> Person(name=小明, address=Address(city=四川)) person2 ---> Person(name=小白, address=Address(city=重庆)) address ---> Address(city=四川) 

总结

引用:内存地址的指向
复制:java中复制就是对象地址的引用
拷贝(克隆):分为浅拷贝、深拷贝
浅复制(浅拷贝):在复制对象时,只会复制对象本身,而不会复制对象内部的引用类型成员变量,这样会导致新对象和原对象共享引用类型成员变量
深复制(深拷贝):在复制对象时,会递归地复制对象本身以及其内部的引用类型成员变量,以确保新对象和原对象完全独立。

小讯
上一篇 2025-01-27 09:42
下一篇 2025-01-11 09:17

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/58233.html