前言

java中得复制,到底有哪些? 浅拷贝和深拷贝,有什么区别呢? 我们平时习惯属性间得复制,beanUtils.copyproperties,属于哪种类型得复制? 我们常用得数组间得复制转换,又分为哪些? 再IO中,零拷贝又是什么意思?

复制有哪些类型?

将一个对象的引用复制给另外一个对象,一共有三种方式。
第一种方式是直接赋值,
第二种方式 是浅拷贝,
第三种是深拷贝。

浅拷贝

对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝

如何实现浅拷贝?

类实现implements Cloneable接口,然后重写clone方法

  1. package com.haoker.copy;
  2. /**
  3. * @ClassName SimpleCopy
  4. * @Description TODO
  5. * @Author 豪
  6. * @Date 2021/8/3 23:44
  7. * @Version I. 0
  8. **/
  9. public class SimpleCopy {
  10. public static void main(String[] args) throws CloneNotSupportedException {
  11. PersonDest2 personDest2 = new PersonDest2(1, "12345", 21);
  12. Person p = new Person(23, "zhang",personDest2);
  13. Person p1 = (Person) p.clone();
  14. System.out.println(p.getPersonDest2());
  15. System.out.println(p1.getPersonDest2());
  16. }
  17. }
  18. class Person implements Cloneable{
  19. //private Integer age;
  20. private int age;//阿里规范中规定pojo类中的属性强制使用包装类型,这里只是测试
  21. private String name;
  22. private PersonDest2 personDest2;
  23. public Person(Integer age, String name,PersonDest2 personDest2) {
  24. super();
  25. this.age = age;
  26. this.name = name;
  27. this.personDest2 = personDest2;
  28. }
  29. public Integer getAge() {
  30. return age;
  31. }
  32. public void setAge(Integer age) {
  33. this.age = age;
  34. }
  35. public String getName() {
  36. return name;
  37. }
  38. public void setName(String name) {
  39. this.name = name;
  40. }
  41. public PersonDest2 getPersonDest2() {
  42. return personDest2;
  43. }
  44. public void setPersonDest2(PersonDest2 personDest2) {
  45. this.personDest2 = personDest2;
  46. }
  47. @Override
  48. public String toString() {
  49. return super.toString();
  50. }
  51. @Override
  52. protected Object clone() throws CloneNotSupportedException {
  53. return super.clone();
  54. }
  55. }

深拷贝

对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

如何实现深拷贝?

1、与通过重写clone方法实现浅拷贝的基本思路一样,只需要为对象图的每一层的每一个对象都实现Cloneable接口并重写clone方法,最后在最顶层的类的重写的clone方法中调用所有的clone方法即可实现深拷贝。简单的说就是:每一层的每个对象都进行浅拷贝=深拷贝。

  1. import java.io.ByteArrayInputStream;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import java.io.ObjectInputStream;
  5. import java.io.ObjectOutputStream;
  6. import java.io.Serializable;
  7. /* 通过序列化实现深拷贝 */
  8. public class DeepCopyBySerialization {
  9. public static void main(String[] args) throws IOException, ClassNotFoundException {
  10. Age a=new Age(20);
  11. Student stu1=new Student("摇头耶稣",a,175);
  12. //通过序列化方法实现深拷贝
  13. ByteArrayOutputStream bos=new ByteArrayOutputStream();
  14. ObjectOutputStream oos=new ObjectOutputStream(bos);
  15. oos.writeObject(stu1);
  16. oos.flush();
  17. ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
  18. Student stu2=(Student)ois.readObject();
  19. System.out.println(stu1.toString());
  20. System.out.println(stu2.toString());
  21. System.out.println();
  22. //尝试修改stu1中的各属性,观察stu2的属性有没有变化
  23. stu1.setName("大傻子");
  24. //改变age这个引用类型的成员变量的值
  25. a.setAge(99);
  26. stu1.setLength(216);
  27. System.out.println(stu1.toString());
  28. System.out.println(stu2.toString());
  29. }
  30. }
  31. /*
  32. * 创建年龄类
  33. */
  34. class Age implements Serializable{
  35. //年龄类的成员变量(属性)
  36. private int age;
  37. //构造方法
  38. public Age(int age) {
  39. this.age=age;
  40. }
  41. public int getAge() {
  42. return age;
  43. }
  44. public void setAge(int age) {
  45. this.age = age;
  46. }
  47. public String toString() {
  48. return this.age+"";
  49. }
  50. }
  51. /*
  52. * 创建学生类
  53. */
  54. class Student implements Serializable{
  55. //学生类的成员变量(属性),其中一个属性为类的对象
  56. private String name;
  57. private Age aage;
  58. private int length;
  59. //构造方法,其中一个参数为另一个类的对象
  60. public Student(String name,Age a,int length) {
  61. this.name=name;
  62. this.aage=a;
  63. this.length=length;
  64. }
  65. //eclipe中alt+shift+s自动添加所有的set和get方法
  66. public String getName() {
  67. return name;
  68. }
  69. public void setName(String name) {
  70. this.name = name;
  71. }
  72. public Age getaAge() {
  73. return this.aage;
  74. }
  75. public void setaAge(Age age) {
  76. this.aage=age;
  77. }
  78. public int getLength() {
  79. return this.length;
  80. }
  81. public void setLength(int length) {
  82. this.length=length;
  83. }
  84. //设置输出的字符串形式
  85. public String toString() {
  86. return "姓名是: "+this.getName()+", 年龄为: "+this.getaAge().toString()+", 长度是: "+this.getLength();
  87. }
  88. }

2、使用序列化。可以先使对象实现 Serializable 接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象

  1. /**
  2. * 使用java的序列化实现深层复制
  3. */
  4. @SuppressWarnings("unchecked")
  5. public static <T extends Serializable> T deepClone(T a){
  6. T t = null;
  7. if(a != null){
  8. try{
  9. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  10. ObjectOutputStream oos = new ObjectOutputStream(baos);
  11. oos.writeObject(a);
  12. oos.flush();
  13. oos.close();
  14. byte[] arrByte = baos.toByteArray();
  15. ByteArrayInputStream bais = new ByteArrayInputStream(arrByte);
  16. ObjectInputStream ois = new ObjectInputStream(bais);
  17. t = (T)ois.readObject();
  18. ois.close();
  19. }catch(Exception e){
  20. }
  21. }
  22. return t;
  23. }

数组间得复制,常用得哪几种?

1、利用for循环依次检索

  1. package com.haoker.ArraysCopy;
  2. /**
  3. * 功能描述:通过循环赋值
  4. * @author: 豪
  5. * @date: 2021/8/3 23:59
  6. **/
  7. public class Arrays {
  8. public static void main(String[] args) {
  9. int[] arrays1 = {3,4,6,8,9};
  10. int[] ret = copyArray(arrays1);
  11. System.out.println("拷贝后:");
  12. for (int x:ret) {
  13. System.out.print(" "+x+" ");
  14. }
  15. }
  16. public static int[] copyArray(int[]a){
  17. int[] ret = new int[a.length];
  18. for(int i = 0; i<ret.length; i++){
  19. ret[i]=a[i];
  20. }//利用for循环
  21. return ret;//返回数组
  22. }
  23. }

2、利用System.arraycopy()方法

  1. package com.haoker.ArraysCopy;
  2. /**
  3. * 功能描述:利用System.arraycopy()方法
  4. * @author: 豪
  5. * @date: 2021/8/3 23:59
  6. **/
  7. public class SystemArraycopy {
  8. public static void main(String[] args) {
  9. int[] arrays1 = {3,4,6,8,9};
  10. int[] ret = copyArray(arrays1);
  11. System.out.println("拷贝后:");
  12. for (int x:ret) {
  13. System.out.print(" "+x+" ");
  14. }
  15. }
  16. public static int[] copyArray(int[]a){
  17. int[] ret = new int[a.length];
  18. //参数一:被拷贝得源数组
  19. //参数二:被拷贝得源数组下标开始得位置
  20. //参数三:目标数组
  21. //参数四:目标数组下标开始得位置
  22. //参数五:复制多少长度
  23. System.arraycopy( a,1,ret,0,ret.length-1);
  24. return ret;
  25. }
  26. }

3、利用Arrays.copyof()方法,底层还是System.arraycopy方法

  1. package com.haoker.ArraysCopy;
  2. import java.util.Arrays;
  3. /**
  4. * 功能描述:利用Arrays.copyof()方法
  5. * @author: 豪
  6. * @date: 2021/8/3 23:59
  7. **/
  8. public class ArraysCopyof {
  9. public static void main(String[] args) {
  10. int[] arrays1 = {3,4,6,8,9};
  11. System.out.println("拷贝后:");
  12. copyArray(arrays1);
  13. }
  14. public static void copyArray(int[]a){
  15. int[] ret =Arrays.copyOf(a, a.length-1);
  16. for (int x:ret) {
  17. System.out.print(" "+x+" ");
  18. }
  19. }
  20. }

4、通过clone()

  1. package com.haoker.ArraysCopy;
  2. import java.util.Arrays;
  3. /**
  4. * 功能描述:通过clone()方法
  5. * @author: 豪
  6. * @date: 2021/8/3 23:59
  7. **/
  8. public class ArraysClone {
  9. public static void main(String[] args) {
  10. int[] arrays1 = {3,4,6,8,9};
  11. System.out.println("拷贝后:");
  12. copyArray(arrays1);
  13. }
  14. public static void copyArray(int[]a){
  15. int[] ret =a.clone();
  16. for (int x:ret) {
  17. System.out.print(" "+x+" ");
  18. }
  19. }
  20. }

常用属性间得复制,beanUtils.copyproperties,属于哪种类型复制?

浅拷贝,因为复制对象得时候,源对象进行改变后,复制过来得值也会跟着变
image.png

  1. package com.haoker.copy;
  2. import org.springframework.beans.BeanUtils;
  3. /**
  4. * @ClassName Copy
  5. * @Description TODO
  6. * @Author 豪
  7. * @Date 2021/8/3 23:17
  8. * @Version I. 0
  9. **/
  10. public class Copy {
  11. public static void main(String[] args){
  12. //测试是否是浅拷贝
  13. PersonDest2 personDest2 = new PersonDest2(1, "12345", 21);
  14. PersonSource personSource = new PersonSource(1, "pjmike", "12345", 21,personDest2);
  15. PersonDest personDest = new PersonDest();
  16. BeanUtils.copyProperties(personSource,personDest);
  17. System.out.println("persondest: "+personDest);
  18. personDest2.setUsername("xiugai");
  19. System.out.println("persondest: "+personDest);
  20. System.out.println("personSource: "+personSource);
  21. }
  22. }
  23. class PersonDest {
  24. private Integer id;
  25. private String username;
  26. private Integer age;
  27. private PersonDest2 personDest2;
  28. // getters/setters omiited
  29. public PersonDest() {
  30. }
  31. public PersonDest(Integer id, String username, Integer age) {
  32. this.id = id;
  33. this.username = username;
  34. this.age = age;
  35. }
  36. public Integer getId() {
  37. return id;
  38. }
  39. public void setId(Integer id) {
  40. this.id = id;
  41. }
  42. public String getUsername() {
  43. return username;
  44. }
  45. public void setUsername(String username) {
  46. this.username = username;
  47. }
  48. public Integer getAge() {
  49. return age;
  50. }
  51. public void setAge(Integer age) {
  52. this.age = age;
  53. }
  54. public PersonDest2 getPersonDest2() {
  55. return personDest2;
  56. }
  57. public void setPersonDest2(PersonDest2 personDest2) {
  58. this.personDest2 = personDest2;
  59. }
  60. @Override
  61. public String toString() {
  62. return "PersonDest{" +
  63. "id=" + id +
  64. ", username='" + username + '\'' +
  65. ", age=" + age +
  66. ", personDest2=" + personDest2 +
  67. '}';
  68. }
  69. }
  70. class PersonDest2 {
  71. private Integer id;
  72. private String username;
  73. private Integer age;
  74. // getters/setters omiited
  75. public PersonDest2() {
  76. }
  77. public PersonDest2(Integer id, String username, Integer age) {
  78. this.id = id;
  79. this.username = username;
  80. this.age = age;
  81. }
  82. public Integer getId() {
  83. return id;
  84. }
  85. public void setId(Integer id) {
  86. this.id = id;
  87. }
  88. public String getUsername() {
  89. return username;
  90. }
  91. public void setUsername(String username) {
  92. this.username = username;
  93. }
  94. public Integer getAge() {
  95. return age;
  96. }
  97. public void setAge(Integer age) {
  98. this.age = age;
  99. }
  100. @Override
  101. public String toString() {
  102. return "PersonDest{" +
  103. "id=" + id +
  104. ", username='" + username + '\'' +
  105. ", age=" + age +
  106. '}';
  107. }
  108. }
  109. class PersonSource {
  110. private Integer id;
  111. private String username;
  112. private String password;
  113. private Integer age;
  114. private PersonDest2 personDest2;
  115. public PersonSource() {
  116. }
  117. public PersonSource(Integer id, String username, String password, Integer age,PersonDest2 personDest2) {
  118. this.id = id;
  119. this.username = username;
  120. this.password = password;
  121. this.age = age;
  122. this.personDest2 = personDest2;
  123. }
  124. public Integer getId() {
  125. return id;
  126. }
  127. public void setId(Integer id) {
  128. this.id = id;
  129. }
  130. public String getUsername() {
  131. return username;
  132. }
  133. public void setUsername(String username) {
  134. this.username = username;
  135. }
  136. public String getPassword() {
  137. return password;
  138. }
  139. public void setPassword(String password) {
  140. this.password = password;
  141. }
  142. public Integer getAge() {
  143. return age;
  144. }
  145. public void setAge(Integer age) {
  146. this.age = age;
  147. }
  148. public PersonDest2 getPersonDest2() {
  149. return personDest2;
  150. }
  151. public void setPersonDest2(PersonDest2 personDest2) {
  152. this.personDest2 = personDest2;
  153. }
  154. @Override
  155. public String toString() {
  156. return "PersonSource{" +
  157. "id=" + id +
  158. ", username='" + username + '\'' +
  159. ", password='" + password + '\'' +
  160. ", age=" + age +
  161. ", personDest2=" + personDest2 +
  162. '}';
  163. }
  164. }

零拷贝详解