前言
java中得复制,到底有哪些? 浅拷贝和深拷贝,有什么区别呢? 我们平时习惯属性间得复制,beanUtils.copyproperties,属于哪种类型得复制? 我们常用得数组间得复制转换,又分为哪些? 再IO中,零拷贝又是什么意思?
复制有哪些类型?
将一个对象的引用复制给另外一个对象,一共有三种方式。
第一种方式是直接赋值,
第二种方式 是浅拷贝,
第三种是深拷贝。
浅拷贝
对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝
如何实现浅拷贝?
类实现implements Cloneable接口,然后重写clone方法
package com.haoker.copy;
/**
* @ClassName SimpleCopy
* @Description TODO
* @Author 豪
* @Date 2021/8/3 23:44
* @Version I. 0
**/
public class SimpleCopy {
public static void main(String[] args) throws CloneNotSupportedException {
PersonDest2 personDest2 = new PersonDest2(1, "12345", 21);
Person p = new Person(23, "zhang",personDest2);
Person p1 = (Person) p.clone();
System.out.println(p.getPersonDest2());
System.out.println(p1.getPersonDest2());
}
}
class Person implements Cloneable{
//private Integer age;
private int age;//阿里规范中规定pojo类中的属性强制使用包装类型,这里只是测试
private String name;
private PersonDest2 personDest2;
public Person(Integer age, String name,PersonDest2 personDest2) {
super();
this.age = age;
this.name = name;
this.personDest2 = personDest2;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public PersonDest2 getPersonDest2() {
return personDest2;
}
public void setPersonDest2(PersonDest2 personDest2) {
this.personDest2 = personDest2;
}
@Override
public String toString() {
return super.toString();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
深拷贝
对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
如何实现深拷贝?
1、与通过重写clone方法实现浅拷贝的基本思路一样,只需要为对象图的每一层的每一个对象都实现Cloneable接口并重写clone方法,最后在最顶层的类的重写的clone方法中调用所有的clone方法即可实现深拷贝。简单的说就是:每一层的每个对象都进行浅拷贝=深拷贝。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/* 通过序列化实现深拷贝 */
public class DeepCopyBySerialization {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Age a=new Age(20);
Student stu1=new Student("摇头耶稣",a,175);
//通过序列化方法实现深拷贝
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(stu1);
oos.flush();
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
Student stu2=(Student)ois.readObject();
System.out.println(stu1.toString());
System.out.println(stu2.toString());
System.out.println();
//尝试修改stu1中的各属性,观察stu2的属性有没有变化
stu1.setName("大傻子");
//改变age这个引用类型的成员变量的值
a.setAge(99);
stu1.setLength(216);
System.out.println(stu1.toString());
System.out.println(stu2.toString());
}
}
/*
* 创建年龄类
*/
class Age implements Serializable{
//年龄类的成员变量(属性)
private int age;
//构造方法
public Age(int age) {
this.age=age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return this.age+"";
}
}
/*
* 创建学生类
*/
class Student implements Serializable{
//学生类的成员变量(属性),其中一个属性为类的对象
private String name;
private Age aage;
private int length;
//构造方法,其中一个参数为另一个类的对象
public Student(String name,Age a,int length) {
this.name=name;
this.aage=a;
this.length=length;
}
//eclipe中alt+shift+s自动添加所有的set和get方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Age getaAge() {
return this.aage;
}
public void setaAge(Age age) {
this.aage=age;
}
public int getLength() {
return this.length;
}
public void setLength(int length) {
this.length=length;
}
//设置输出的字符串形式
public String toString() {
return "姓名是: "+this.getName()+", 年龄为: "+this.getaAge().toString()+", 长度是: "+this.getLength();
}
}
2、使用序列化。可以先使对象实现 Serializable 接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象
/**
* 使用java的序列化实现深层复制
*/
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepClone(T a){
T t = null;
if(a != null){
try{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(a);
oos.flush();
oos.close();
byte[] arrByte = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(arrByte);
ObjectInputStream ois = new ObjectInputStream(bais);
t = (T)ois.readObject();
ois.close();
}catch(Exception e){
}
}
return t;
}
数组间得复制,常用得哪几种?
1、利用for循环依次检索
package com.haoker.ArraysCopy;
/**
* 功能描述:通过循环赋值
* @author: 豪
* @date: 2021/8/3 23:59
**/
public class Arrays {
public static void main(String[] args) {
int[] arrays1 = {3,4,6,8,9};
int[] ret = copyArray(arrays1);
System.out.println("拷贝后:");
for (int x:ret) {
System.out.print(" "+x+" ");
}
}
public static int[] copyArray(int[]a){
int[] ret = new int[a.length];
for(int i = 0; i<ret.length; i++){
ret[i]=a[i];
}//利用for循环
return ret;//返回数组
}
}
2、利用System.arraycopy()方法
package com.haoker.ArraysCopy;
/**
* 功能描述:利用System.arraycopy()方法
* @author: 豪
* @date: 2021/8/3 23:59
**/
public class SystemArraycopy {
public static void main(String[] args) {
int[] arrays1 = {3,4,6,8,9};
int[] ret = copyArray(arrays1);
System.out.println("拷贝后:");
for (int x:ret) {
System.out.print(" "+x+" ");
}
}
public static int[] copyArray(int[]a){
int[] ret = new int[a.length];
//参数一:被拷贝得源数组
//参数二:被拷贝得源数组下标开始得位置
//参数三:目标数组
//参数四:目标数组下标开始得位置
//参数五:复制多少长度
System.arraycopy( a,1,ret,0,ret.length-1);
return ret;
}
}
3、利用Arrays.copyof()方法,底层还是System.arraycopy方法
package com.haoker.ArraysCopy;
import java.util.Arrays;
/**
* 功能描述:利用Arrays.copyof()方法
* @author: 豪
* @date: 2021/8/3 23:59
**/
public class ArraysCopyof {
public static void main(String[] args) {
int[] arrays1 = {3,4,6,8,9};
System.out.println("拷贝后:");
copyArray(arrays1);
}
public static void copyArray(int[]a){
int[] ret =Arrays.copyOf(a, a.length-1);
for (int x:ret) {
System.out.print(" "+x+" ");
}
}
}
4、通过clone()
package com.haoker.ArraysCopy;
import java.util.Arrays;
/**
* 功能描述:通过clone()方法
* @author: 豪
* @date: 2021/8/3 23:59
**/
public class ArraysClone {
public static void main(String[] args) {
int[] arrays1 = {3,4,6,8,9};
System.out.println("拷贝后:");
copyArray(arrays1);
}
public static void copyArray(int[]a){
int[] ret =a.clone();
for (int x:ret) {
System.out.print(" "+x+" ");
}
}
}
常用属性间得复制,beanUtils.copyproperties,属于哪种类型复制?
浅拷贝,因为复制对象得时候,源对象进行改变后,复制过来得值也会跟着变
package com.haoker.copy;
import org.springframework.beans.BeanUtils;
/**
* @ClassName Copy
* @Description TODO
* @Author 豪
* @Date 2021/8/3 23:17
* @Version I. 0
**/
public class Copy {
public static void main(String[] args){
//测试是否是浅拷贝
PersonDest2 personDest2 = new PersonDest2(1, "12345", 21);
PersonSource personSource = new PersonSource(1, "pjmike", "12345", 21,personDest2);
PersonDest personDest = new PersonDest();
BeanUtils.copyProperties(personSource,personDest);
System.out.println("persondest: "+personDest);
personDest2.setUsername("xiugai");
System.out.println("persondest: "+personDest);
System.out.println("personSource: "+personSource);
}
}
class PersonDest {
private Integer id;
private String username;
private Integer age;
private PersonDest2 personDest2;
// getters/setters omiited
public PersonDest() {
}
public PersonDest(Integer id, String username, Integer age) {
this.id = id;
this.username = username;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public PersonDest2 getPersonDest2() {
return personDest2;
}
public void setPersonDest2(PersonDest2 personDest2) {
this.personDest2 = personDest2;
}
@Override
public String toString() {
return "PersonDest{" +
"id=" + id +
", username='" + username + '\'' +
", age=" + age +
", personDest2=" + personDest2 +
'}';
}
}
class PersonDest2 {
private Integer id;
private String username;
private Integer age;
// getters/setters omiited
public PersonDest2() {
}
public PersonDest2(Integer id, String username, Integer age) {
this.id = id;
this.username = username;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "PersonDest{" +
"id=" + id +
", username='" + username + '\'' +
", age=" + age +
'}';
}
}
class PersonSource {
private Integer id;
private String username;
private String password;
private Integer age;
private PersonDest2 personDest2;
public PersonSource() {
}
public PersonSource(Integer id, String username, String password, Integer age,PersonDest2 personDest2) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
this.personDest2 = personDest2;
}
public Integer getId() {
return id;
}
public void setId(Integer 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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public PersonDest2 getPersonDest2() {
return personDest2;
}
public void setPersonDest2(PersonDest2 personDest2) {
this.personDest2 = personDest2;
}
@Override
public String toString() {
return "PersonSource{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
", personDest2=" + personDest2 +
'}';
}
}