关于对象克隆这部分,
在之前学习原型模式的时候有整理过一部分;
尚硅谷设计模式学习(5)— [原型模式(Prototype模式),深拷贝与浅拷贝]
首先明白这样一个问题,它只是引用复制,而不是克隆
User user1 = new User(); User user2 = user1; User user3 = user1;
注意这样不是克隆;
是引用,即对象在内存中的地址,a 和 b 对象仍然指向了
同一个对象。这种只能称为引用复制,两个引用指向的还是同一个对象
案例
/** * @author by 信计1801 李智青 学号:1809064012 */ public class User { private String account; private Integer age; private String password; public User() { } public User(String account, Integer age, String password) { this.account = account; this.age = age; this.password = password; } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User{" + "account='" + account + '\'' + ", age=" + age + ", password='" + password + '\'' + '}'; } }
测试
/** * @author by 信计1801 李智青 学号:1809064012 */ public class Test { public static void main(String[] args) { User user1 = new User("小智",22,"666666"); User user2 = user1; User user3 = user1; System.out.println(user1); System.out.println(user2); System.out.println(user3); System.out.println(user2 == user1); System.out.println(user3 == user2); } }
结果;实际上仅引用复制
User{account='小智', age=22, password='666666'} User{account='小智', age=22, password='666666'} User{account='小智', age=22, password='666666'} true true
之前写练习项目的时候,用户类中要关联其他的类;也是比较复杂的;
然后我们无论是从前端向后端发来请求的处理时数据,还有后端向后端处理响应时的数据,我们都用一个用户模型类去封装信息;这样的话,可能就会出现数据冗余的问题;
比如说:我这个用户类呢,它里面的属性比较多,账号,密码,性别,年龄,电话,地址,积分,比如说我现在只是要请求到用户的部分的数据,它里面就把所有的数据都包含进去了,即使没有数据,里面有的属性里面会自动赋值null传递出去;
Java中数据类型分为值类型(基本数据类型)和引用类型
是否支持引用类型的成员变量的复制
。浅克隆中,如果原型对象的成员变量是值类型
,将复制一份给克隆对象;
如果原型对象的成员变量是引用类型
,则将引用对象的地址复制一份给克隆对象
,也就是说原型对象和克隆对象的成员变量指向相同的内存地址
。
在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量
,而引用类型的成员对象并没有复制
。
具体实现方式:
clone()方法
可以实现浅克隆。BeanUtils.copyProperties(source,target)
案例
实现Cloneable
接口,重写clone方法试试
/** * @author by 信计1801 李智青 学号:1809064012 */ public class User implements Cloneable{ String account; Integer age; String password; public User() { } public User(String account, Integer age, String password) { this.account = account; this.age = age; this.password = password; } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override protected User clone() throws CloneNotSupportedException { User user = (User)super.clone(); return user; } @Override public String toString() { return "User{" + "account='" + account + '\'' + ", age=" + age + ", password='" + password + '\'' + '}'; } }
测试
/** * @author by 信计1801 李智青 学号:1809064012 */ public class Test { public static void main(String[] args) throws CloneNotSupportedException { User user1 = new User("小智",22,"666666"); User user2 = user1.clone(); System.out.println(user1); System.out.println(user2); System.out.println("----------地址是否相等-------------"); System.out.println(user2 == user1); System.out.println("----------hash值-------------"); System.out.println(user1.hashCode()); System.out.println(user2.hashCode()); } }
结果
User{account='小智', age=22, password='666666'} User{account='小智', age=22, password='666666'} ----------地址是否相等------------- false ----------hash值------------- 1265094477 2125039532
无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象
,深克隆将原型对象的所有引用对象也复制一份给克隆对象。
序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。
通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。
需要注意的是能够实现序列化的对象其类必须实现Serializable 接口
,否则无法实现序列化操作。
案例:
在用户类User中关联了引用类型的属性班级类Grade;
/** * @author by 信计1801 李智青 学号:1809064012 */ public class Grade implements Cloneable{ private String name; public Grade() { } public Grade(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Grade{" + "name='" + name + '\'' + '}'; } @Override protected Grade clone() throws CloneNotSupportedException { return (Grade) super.clone(); } }
虽然,这个班级类已经重写了克隆方法;
但是,我在这用户类这边重写克隆方法时,仅克隆了引用类型Grade的地址
/** * @author by 信计1801 李智青 学号:1809064012 */ public class User implements Cloneable { String account; int age; //关联了班级类型; Grade grade; public User() { } public User(String account, int age) { this.account = account; this.age = age; } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Grade getGrade() { return grade; } public void setGrade(Grade grade) { this.grade = grade; } @Override public String toString() { return "User{" + "account='" + account + '\'' + ", age=" + age + ", grade=" + grade + '}'; } @Override protected User clone() throws CloneNotSupportedException { return (User)super.clone(); } }
测试
/** * @author by 信计1801 李智青 学号:1809064012 */ public class Test { public static void main(String[] args) throws CloneNotSupportedException { //首先创建了一个班级类对象; Grade grade = new Grade(); grade.setName("二年级"); //用户1; User user1 = new User("小智",22); user1.setGrade(grade); //用户2; User user2 = user1.clone(); user2.setAccount("杰哥"); grade.setName("大四"); System.out.println("用户1::"+user1); System.out.println("用户2::"+user2); } }
用户2在克隆时,仅克隆了地址
用户1::User{account='小智', age=22, grade=Grade{name='大四'}} 用户2::User{account='杰哥', age=22, grade=Grade{name='大四'}}
浅克隆
如果一个对象中关联了其他的引用变量, 浅克隆时,只会将关联的对象的引用地址复制出来,并没有创建一个新的对象.
那么稍微改变一下用户类User中重写的克隆方法
@Override protected User clone() throws CloneNotSupportedException { User user = (User)super.clone(); //深度复制; user.grade = grade.clone(); return user; }
测试结果
用户1::User{account='小智', age=22, grade=Grade{name='大四'}} 用户2::User{account='杰哥', age=22, grade=Grade{name='二年级'}}
深克隆
如果一个对象中关联了其他的引用变量, 深克隆时,将此对象中所关联的对象也会进行克隆操作,也就是会创建一个新的关联对象
序列化克隆的案例
班级类;
实现Serializable
接口
package com.xiaozhi.advanced.day07_objclone.objectClone.serializationclone; import java.io.Serializable; /** * @author by 信计1801 李智青 学号:1809064012 */ public class Grade implements Serializable { private String name; public Grade() { } public Grade(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Grade{" + "name='" + name + '\'' + '}'; } }
用户类实现Serializable
接口;
自定义克隆方法;
package com.xiaozhi.advanced.day07_objclone.objectClone.serializationclone; import java.io.*; /** * @author by 信计1801 李智青 学号:1809064012 */ public class User implements Serializable { String account; int age; //关联了班级类型; Grade grade; public User() { } public User(String account, int age) { this.account = account; this.age = age; } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Grade getGrade() { return grade; } public void setGrade(Grade grade) { this.grade = grade; } @Override public String toString() { return "User{" + "account='" + account + '\'' + ", age=" + age + ", grade=" + grade + '}'; } //自定义的克隆方法; public User customizeClone(){ User user = null; try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝, //而原对象仍然存在于JVM中,利用此特性实现对象的深拷贝 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(this); // 将流序列化成对象 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); user = (User) ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return user; } }
测试
/** * @author by 信计1801 李智青 学号:1809064012 */ public class Test { public static void main(String[] args) throws CloneNotSupportedException { //首先创建了一个班级类对象; Grade grade = new Grade(); grade.setName("二年级"); //用户1; User user1 = new User("小智", 22); user1.setGrade(grade); //用户2; User user2 = user1.customizeClone(); user2.setAccount("杰哥"); grade.setName("大四"); System.out.println("用户1::" + user1); System.out.println("用户2::" + user2); } }
同样达到了深克隆的效果
用户1::User{account='小智', age=22, grade=Grade{name='大四'}} 用户2::User{account='杰哥', age=22, grade=Grade{name='二年级'}}