引用:在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();
}
}
以下就是一个简单的对象复制,或者说是引用的例子
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=北京)
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=重庆)
例子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中复制就是对象地址的引用
拷贝(克隆):分为浅拷贝、深拷贝
浅复制(浅拷贝):在复制对象时,只会复制对象本身,而不会复制对象内部的引用类型成员变量,这样会导致新对象和原对象共享引用类型成员变量
。
深复制(深拷贝):在复制对象时,会递归地复制对象本身以及其内部的引用类型成员变量,以确保新对象和原对象完全独立。