JavaSE---Object-clone()
一、概述
/** * <what> * 创建并返回此对象的一个副本; * (按照原对象,创建一个新的对象[复制原对象的内容]) */
二、已经存在new或反射等技术,为啥还需要Object的clone方法?
1、new关键字、反射创建对象的弊端
/** * 1、new关键字、反射创建对象的弊端 * 通过new、反射可以创建内容一样的对象; * 但是,创建对象之后,需要通过setter为新对象设置属性值,如果需要创建更多内容一样的对象,setter方法将不断重复; * * 解决: * 使用Object的clone方法; */
public class User{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }
private static void newTest() { User user = new User(); user.setName("rose"); User userCopy = new User(); userCopy.setName(user.getName()); System.out.println(user); System.out.println(userCopy); } private static void reflectTest() throws ClassNotFoundException, IllegalAccessException, InstantiationException { User user = new User(); user.setName("rose"); Class<?> userClazz = Class.forName("com.an.object.User"); User userCopy = (User) userClazz.newInstance(); userCopy.setName(user.getName()); System.out.println(user); System.out.println(userCopy); }
2、使用Object的clone方法
/** * 1、需要调用clone方法的对象实现java.lang.Cloneable接口 * 2、重新clone方法 */
public class User implements Cloneable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public User clone() throws CloneNotSupportedException { return (User) super.clone(); } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }
private static void cloneTest() throws CloneNotSupportedException { User user = new User(); user.setName("rose"); User userCopy = user.clone(); System.out.println(user); System.out.println(userCopy); }
三、浅拷贝
/** * <浅拷贝> * what * clone对象 是一个新对象 * clone对象的成员变量 与 原对象的成员变量 是同一个数据; * * 缺点 * 由于 原对象与clone对象 的成员变量 是同一个数据,任意一个对象对成员变量进行修改,原对象与clone对象的成员变量都会被修改; */
public class User implements Cloneable{ private String name; private Man man; public String getName() { return name; } public void setName(String name) { this.name = name; } public Man getMan() { return man; } public void setMan(Man man) { this.man = man; } @Override public User clone() throws CloneNotSupportedException { return (User) super.clone(); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", man=" + man + '}'; } public static class Man{ private String job; public String getJob() { return job; } public void setJob(String job) { this.job = job; } @Override public String toString() { return "Man{" + "job='" + job + '\'' + '}'; } } }
private static void shallowClone() throws CloneNotSupportedException { User user = new User(); user.setName("rose"); User.Man man = new User.Man(); man.setJob("teacher"); user.setMan(man); User userCopy = user.clone(); System.out.println(user.getMan().hashCode()); // 1872034366 System.out.println(userCopy.getMan().hashCode()); // 1872034366 user.getMan().setJob("coder"); System.out.println(user.getMan()); // Man{job='coder'} System.out.println(userCopy.getMan()); // Man{job='coder'} }
四、深拷贝
1、使用java.lang.Cloneable接口深拷贝
/** * <深拷贝> * what * clone对象是一个新对象 * clone对象的成员变量 也是一个新对象; * * 步骤 * 1、原对象、原对象的成员变量 实现java.lang.Cloneable接口,重写clone方法 * 2、原对象的clone方法调用成员变量的clone方法; * * 使用java.lang.Cloneable接口实现深拷贝的弊端: * 成员变量重复实现java.lang.Cloneable接口 * 成员变量重复实现clone方法 * 原对象的clone方法重复改写 * * 解决 * 使用IO流的方式进行 深拷贝 * (解决重复修改源代码的问题) * */
public class User implements Cloneable{ private String name; private Man man; public String getName() { return name; } public void setName(String name) { this.name = name; } public Man getMan() { return man; } public void setMan(Man man) { this.man = man; } @Override public User clone() throws CloneNotSupportedException { User clone = (User) super.clone(); clone.setMan(man.clone()); return clone; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", man=" + man + '}'; } public static class Man implements Cloneable{ private String job; public String getJob() { return job; } public void setJob(String job) { this.job = job; } @Override public Man clone() throws CloneNotSupportedException { return (Man) super.clone(); } @Override public String toString() { return "Man{" + "job='" + job + '\'' + '}'; } } }
private static void deepClone() throws CloneNotSupportedException { User user = new User(); user.setName("rose"); User.Man man = new User.Man(); man.setJob("teacher"); user.setMan(man); User userCopy = user.clone(); System.out.println(user.getMan().hashCode()); // 1872034366 System.out.println(userCopy.getMan().hashCode()); // 1581781576 user.getMan().setJob("coder"); System.out.println(user.getMan()); // Man{job='coder'} System.out.println(userCopy.getMan()); // Man{job='teacher'} }
2、使用IO流深拷贝
/** * <深拷贝-使用IO流> * API * ByteArrayOutputStream * ByteArrayInputStream * ObjectOutputStream * ObjectInputStream * 步骤 * 1、创建ByteArrayOutputStream,将数据转换为字节 * 2、创建ObjectOutputStream,关联ByteArrayOutputStream * 3、使用ObjectOutputStream的writeObject,读取要复制的对象 * 4、使用ByteArrayInputStream读取ByteArrayOutputStream转换的对象字节数据 * 5、创建ObjectInputStream读取对象字节数据,创建新的对象 * * 原对象及其变量需要实现java.io.Serializable接口 */
package com.an.object; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * @author apy * @description * @date 2022/4/24 17:22 */ public class User implements Serializable{ private String name; private Man man; public String getName() { return name; } public void setName(String name) { this.name = name; } public Man getMan() { return man; } public void setMan(Man man) { this.man = man; } public User copy(){ try { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(this); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); User user = (User) objectInputStream.readObject(); return user; } catch (Exception e) { e.printStackTrace(); } return null; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", man=" + man + '}'; } public static class Man implements Serializable { private String job; public String getJob() { return job; } public void setJob(String job) { this.job = job; } @Override public String toString() { return "Man{" + "job='" + job + '\'' + '}'; } } }
private static void ioDeepClone() { User user = new User(); user.setName("rose"); User.Man man = new User.Man(); man.setJob("teacher"); user.setMan(man); User userCopy = user.copy(); System.out.println(user.hashCode()); // 225534817 System.out.println(userCopy.hashCode()); // 122883338 System.out.println(user.getMan().hashCode()); // 1878246837 System.out.println(userCopy.getMan().hashCode()); // 666641942 user.getMan().setJob("coder"); System.out.println(user.getMan()); // Man{job='coder'} System.out.println(userCopy.getMan()); // Man{job='teacher'} }
五、为什么使用clone方法需要实现java.lang.Cloneable接口
/** * <为什么使用clone方法需要实现java.lang.Cloneable接口> * 下载JDK源码,进入hotspot/src/share/vm/prims/jvm.cpp文件中 * * // Check if class of obj supports the Cloneable interface. * // All arrays are considered to be cloneable (See JLS 20.1.5) * if (!klass->is_cloneable()) { * ResourceMark rm(THREAD); * THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name()); * } * * Check if class of obj supports the Cloneable interface.检查指定的class是否实现java.lang.Cloneable接口(数组默认支持clone); * 如果 !klass->is_cloneable() 抛出CloneNotSupportedException; * * clone代码: * JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle)) * JVMWrapper("JVM_Clone"); * Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); * const KlassHandle klass (THREAD, obj->klass()); * JvmtiVMObjectAllocEventCollector oam; */