引言:
在 Object 基类中, 有一个方法叫 clone, 产生一个前期对象的克隆, 克隆对象是原对象的拷贝, 由于引用类型的存在, 有深克隆和浅克隆之分, 若克隆对象中存在引用类型的属性, 深克隆会将此属性完全拷贝一份, 而浅克隆仅仅是拷贝一份此属性的引用. 首先看一下容易犯的几个小问题
clone 方法是 Object 类的, 并不是 Cloneable 接口的, Cloneable 只是一个标记接口, 标记接口是用用户标记实现该接口的类具有某种该接口标记的功能, 常见的标记接口有三个: Serializable,Cloneable,RandomAccess, 没有实现 Cloneable 接口, 那么调用 clone 方法就会爆出 CloneNotSupportedException 异常.
Object 类中的 clone 方法是 protected 修饰的, 这就表明我们在子类中不重写此方法, 就在子类外无法访问, 因为这个 protected 权限是仅仅能在 Object 所在的包和子类能访问的, 这也验证了子类重写父类方法权限修饰符可以变大但不能变小的说法.
protected native Object clone() throws CloneNotSupportedException;
重写 clone 方法, 内部仅仅是调用了父类的 clone 方法, 其实是为了扩大访问权限, 当然你可以把 protected 改为 public, 以后再继承就不用重写了. 当然只是浅克隆的 clone 函数, 深克隆就需要修改了.
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
属性是 String 的情况, String 也是一个类, 那 String 引用类型吗? String 的表现有的像基本类型, 归根到底就是因为 String 不可改变, 克隆之后俩个引用指向同一个 String, 但当修改其中的一个, 改的不是 String 的值, 却是新生成一个字符串, 让被修改的引用指向新的字符串. 外表看起来就像基本类型一样.
浅克隆:
浅克隆就是引用类型的属性无法完全复制, 类 User 中包含成绩属性 Mark,Mark 是由 Chinese 和 math 等等组成的, 浅克隆失败的例子
- class Mark{
- private int chinese;
- private int math;
- public Mark(int chinese, int math) {
- this.chinese = chinese;
- this.math = math;
- }
- public void setChinese(int chinese) {
- this.chinese = chinese;
- }
- public void setMath(int math) {
- this.math = math;
- }
- @Override
- public String toString() {
- return "Mark{" +
- "chinese=" + chinese +
- ", math=" + math +
- '}';
- }
- }
- public class User implements Cloneable{
- private String name;
- private int age;
- private Mark mark;
- public User(String name, int age,Mark mark) {
- this.name = name;
- this.age = age;
- this.mark = mark;
- }
- @Override
- public String toString() {
- return "User{" +
- "name='" + name + '\'' +
- ", age=" + age +
- ", mark=" + mark +
- '}';
- }
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
- public static void main(String[] args) throws CloneNotSupportedException {
- Mark mark = new Mark(100,99);
- User user = new User("user",22,mark);
- User userClone = (User) user.clone();
- System.out.println("原 user:"+user);
- System.out.println("克隆的 user:"+userClone);
- // 修改引用类型的 mark 属性
- user.mark.setMath(60);
- System.out.println("修改后的原 user:"+user);
- System.out.println("修改后的克隆 user:"+userClone);
- }
- }
输出结果为:
原 user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
克隆的 user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
修改后的原 user:User{name='user', age=22, mark=Mark{chinese=100, math=60}}
修改后的克隆 user:User{name='user', age=22, mark=Mark{chinese=100, math=60}}
很清楚的看到 user 的 mark 更改后, 被克隆的 user 也修改了. 而要想不被影响, 就需要深克隆了.
深克隆:
方式一: clone 函数的嵌套调用
既然引用类型无法被完全克隆, 那将引用类型也实现 Cloneable 接口重写 clone 方法, 在 User 类中的 clone 方法调用属性的克隆方法, 也就是方法的嵌套调用
- class Mark implements Cloneable{
- private int chinese;
- private int math;
- public Mark(int chinese, int math) {
- this.chinese = chinese;
- this.math = math;
- }
- public void setChinese(int chinese) {
- this.chinese = chinese;
- }
- public void setMath(int math) {
- this.math = math;
- }
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
- @Override
- public String toString() {
- return "Mark{" +
- "chinese=" + chinese +
- ", math=" + math +
- '}';
- }
- }
- public class User implements Cloneable{
- private String name;
- private int age;
- private Mark mark;
- public User(String name, int age,Mark mark) {
- this.name = name;
- this.age = age;
- this.mark = mark;
- }
- @Override
- public String toString() {
- return "User{" +
- "name='" + name + '\'' +
- ", age=" + age +
- ", mark=" + mark +
- '}';
- }
- @Override
- protected Object clone() throws CloneNotSupportedException {
- User user = (User) super.clone();
- user.mark = (Mark) this.mark.clone();
- return user;
- }
- public static void main(String[] args) throws CloneNotSupportedException {
- Mark mark = new Mark(100,99);
- User user = new User("user",22,mark);
- User userClone = (User) user.clone();
- System.out.println("原 user:"+user);
- System.out.println("克隆的 user:"+userClone);
- // 修改引用类型的 mark 属性
- user.mark.setMath(60);
- System.out.println("修改后的原 user:"+user);
- System.out.println("修改后的克隆 user:"+userClone);
- }
- }
输出结果为:
原 user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
克隆的 user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
修改后的原 user:User{name='user', age=22, mark=Mark{chinese=100, math=60}}
修改后的克隆 user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
方式二: 序列化
上一种方法已经足够满足我们的需要, 但是如果类之间的关系很多, 或者是有的属性是数组呢, 数组可无法实现 Cloneable 接口 (我们可以在 clone 方法中手动复制数组), 但是每次都得手写 clone 方法, 很麻烦, 而序列化方式只需要给每个类都实现一个 Serializable 接口, 也是标记接口, 最后同序列化和反序列化操作达到克隆的目的 (包括数组的复制). 序列化和反序列化的知识请参照下一篇
- import java.io.*;
- class Mark implements Serializable {
- private int chinese;
- private int math;
- public Mark(int chinese, int math) {
- this.chinese = chinese;
- this.math = math;
- }
- public void setChinese(int chinese) {
- this.chinese = chinese;
- }
- public void setMath(int math) {
- this.math = math;
- }
- @Override
- public String toString() {
- return "Mark{" +
- "chinese=" + chinese +
- ", math=" + math +
- '}';
- }
- }
- public class User implements Serializable{
- private String name;
- private int age;
- private Mark mark;
- public User(String name, int age,Mark mark) {
- this.name = name;
- this.age = age;
- this.mark = mark;
- }
- @Override
- public String toString() {
- return "User{" +
- "name='" + name + '\'' +
- ", age=" + age +
- ", mark=" + mark +
- '}';
- }
- public static void main(String[] args) throws IOException, ClassNotFoundException {
- Mark mark = new Mark(100,99);
- User user = new User("user",22,mark);
- ByteArrayOutputStream bo = new ByteArrayOutputStream();
- ObjectOutputStream oo = new ObjectOutputStream(bo);
- oo.writeObject(user);// 序列化
- ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
- ObjectInputStream oi = new ObjectInputStream(bi);
- User userClone = (User) oi.readObject();// 反序列化
- System.out.println("原 user:"+user);
- System.out.println("克隆的 user:"+userClone);
- user.mark.setMath(59);
- System.out.println("修改后的原 user:"+user);
- System.out.println("修改后的克隆 user:"+userClone);
- }
- }
输出结果:
原 user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
克隆的 user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
修改后的原 user:User{name='user', age=22, mark=Mark{chinese=100, math=60}}
修改后的克隆 user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
带数组属性的克隆:
- import java.io.*;
- import java.util.Arrays;
- public class User implements Serializable{
- private String name;
- private int age;
- private int[] arr;
- public User(String name, int age, int[] arr) {
- this.name = name;
- this.age = age;
- this.arr = arr;
- }
- @Override
- public String toString() {
- return "User{" +
- "name='" + name + '\'' +
- ", age=" + age +
- ", arr=" + Arrays.toString(arr) +
- '}';
- }
- public static void main(String[] args) throws IOException, ClassNotFoundException {
- int[] arr = {1,2,3,4,5,6};
- User user = new User("user",22,arr);
- ByteArrayOutputStream bo = new ByteArrayOutputStream();
- ObjectOutputStream oo = new ObjectOutputStream(bo);
- oo.writeObject(user);// 序列化
- ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
- ObjectInputStream oi = new ObjectInputStream(bi);
- User userClone = (User) oi.readObject();// 反序列化
- System.out.println("原 user:"+user);
- System.out.println("克隆的 user:"+userClone);
- user.arr[1] = 9;
- System.out.println("修改后的原 user:"+user);
- System.out.println("修改后的克隆 user:"+userClone);
- }
- }
- View Code
来源: https://www.cnblogs.com/gollong/p/9668699.html