【从零开始学Java | 第十七篇】浅克隆、深克隆和对象工具类Objects

【从零开始学Java | 第十七篇】浅克隆、深克隆和对象工具类Objects 目录前言一、对象克隆1.浅克隆2.深克隆二、Objects的成员方法总结前言在写代码时我们经常需要复制一个对象。很多新手会顺手写出User u2 u1;这样的代码。 但你很快就会发现一个恐怖的现象当你修改了u2的数据u1竟然也跟着变了那么我们应该如何在Java中真正地复制一个对象呢今天我们来学习面试的高频考点——克隆Clone。一、对象克隆1.浅克隆Object类给我们提供了一个对象克隆的方法protected Objectclone()场景演示我们首先创建一个User的JavaBean用来演示对象的克隆。import java.util.StringJoiner; public class User { private int id; private String username; private String password; private int[] data; public User() { } public User(int id, String username, String password, int[] data) { this.id id; this.username username; this.password password; this.data data; } public int getId() { return id; } public void setId(int id) { this.id id; } public String getUsername() { return username; } public void setUsername(String username) { this.username username; } public String getPassword() { return password; } public void setPassword(String password) { this.password password; } public int[] getData() { return data; } public void setData(int[] data) { this.data data; } public String toString(){ return 角色编号为 id 用户名 username 密码 password 进度 arrToString(); } public String arrToString(){ StringJoiner sj new StringJoiner(,, [, ]); for (int i 0; i data.length; i) { sj.add(data[i] ); } return sj.toString(); } }由于clone方法是受保护的方法因此我们需要在我们的类中重写这个方法继承父类的clone方法在User类中加上这一段重写的函数Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }除此之外我们还需要在User类的开头去实现一个Cloneable的接口这个接口没有任何的抽象方法表示当前的接口是一个标记性的接口现在Cloneable一旦实现了那么当前类的对象就可以被克隆。public class User implements Cloneable{运行代码及结果public class Test { public static void main(String[] args) throws CloneNotSupportedException { int[] data {1, 2, 3, 4, 5, 6, 7}; User u1 new User(1, Steve, 123asd, data); User u2 (User) u1.clone(); System.out.println(u1); System.out.println(u2); } }细节重写Object类中的Clone方法。让JavaBean类实现Cloneable接口。创建原对象并调用clone方法。浅克隆的致命缺陷浅克隆在拷贝时对于基本数据类型会直接复制值但对于引用数据类型它依然只复制内存地址。public class Test { public static void main(String[] args) throws CloneNotSupportedException { int[] data {1, 2, 3, 4, 5, 6, 7}; User u1 new User(1, Steve, 123asd, data); User u2 (User) u1.clone(); int[] arr u1.getData(); arr[0] 99; System.out.println(u1); System.out.println(u2); } }运行结果这就是浅拷贝/克隆。2.深克隆特点基本数据类型直接拷贝值过来字符串复用引用数据类型会重新创建新的场景实战重写User类中的clone方法Override protected Object clone() throws CloneNotSupportedException { int[] data this.data; int[] newData new int[data.length]; for (int i 0; i data.length; i) { newData[i] data[i]; } User u (User)super.clone(); u.data newData; return u; } }运行结果在现代开发中最流行的深克隆方式是利用 JSON 序列化把对象转成 JSON 字符串再把字符串转换回新对象。import com.google.gson.Gson; public class DeepCloneDemo { public static void main(String[] args) { Student s1 new Student(18, new int[]{100, 90}); Gson gson new Gson(); // 1. 对象 - 字符串 String jsonString gson.toJson(s1); // 2. 字符串 - 新对象 (彻底的深克隆完成) Student s2 gson.fromJson(jsonString, Student.class); s2.scores[0] 50; System.out.println(s1.scores[0]); // 输出100 (s1 毫发无损深克隆成功) } }二、Objects的成员方法方法名说明public static booleanequals(Object a, Object b)先做非空判断再比较两个对象public static booleanisNull(Object obj)判断对象是否为Nullpublic static booleannonNull(Object obj)判断对象是否非Null在判断两个对象是否相等时我们通常用a.equals(b)。但这有个巨大的坑如果a是null直接调用equals会触发恐怖的空指针异常 (NullPointerException)。Objects工具类完美解决了这个问题import java.util.Objects; public class ObjectsDemo { public static void main(String[] args) { String s1 null; String s2 Java; // ❌ 传统写法极易引发空指针异常 // System.out.println(s1.equals(s2)); // 报错 // ✅ 优雅写法Objects.equals() 底层自带防空指针逻辑 System.out.println(Objects.equals(s1, s2)); // 安全输出false // 其他常用方法判断是否为 null System.out.println(Objects.isNull(s1)); // 输出true System.out.println(Objects.nonNull(s2)); // 输出true } }总结浅克隆实现了Cloneable接口。基本类型彻底复制引用类型复制地址值。深克隆彻底的复制。推荐使用 JSON 工具如 Gson实现。Objects 类以后比较对象是否相等请养成使用Objects.equals(a, b)的好习惯。