背景 由于业务代码的方法将传递的引用数据类型参数进行了修改,后续业务又需要修改前的参数,所以需要在修改前进行深拷贝保持修改前的对象。
java值传递 基本数据类型 代码:
1 2 3 4 5 6 7 8 9 10 11 12 public class TestJavaPass { public static void main (String[] args) { TestJavaPass tj = new TestJavaPass(); int i=10 ; tj.pass(i); System.out.println("print in main, i is " +i); } public void pass (int j) { j=20 ; System.out.println("print in pass, j is " +j); } }
结果:
1 2 print in pass, j is 20 print in main, i is 10
结论:
传入基本数据类型时,为值传递不会改变其本身
引用数据类型 情况一,传入对象并改变其自身属性 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class TestJavaPass { public static void main (String[] args) { TestJavaPass tj = new TestJavaPass(); User user = new User(); user.setName("hollis" ); user.setGender("Male" ); tj.pass(user); System.out.println("print in main , user is " +user); } public void pass (int j) { j=20 ; System.out.println("print in pass, j is " +j); } public void pass (User user) { user.setName("hollischuang" ); System.out.println("print in pass , user is " +user); } }
结果:
1 2 print in pass , user is User{name='hollischuang', gender='Male'} print in main , user is User{name='hollischuang', gender='Male'}
结论:
传入为引用类型时,对象的属性改变,注:这种情况属于特殊的值传递,传递的是实际参数的地址的复制。
情况二,传入对象的引用,不改变对象的属性 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class TestJavaPass { public static void main (String[] args) { TestJavaPass tj = new TestJavaPass(); User user = new User(); user.setName("hollis" ); user.setGender("Male" ); tj.pass(user); System.out.println("print in main , user is " +user); } public void pass (int j) { j=20 ; System.out.println("print in pass, j is " +j); } public void pass (User user) { user = new User(); user.setName("hollischuang" ); System.out.println("print in pass , user is " +user); } }
结果:
1 2 print in pass , user is User{name='hollischuang', gender='null'} print in main , user is User{name='hollis', gender='Male'}
结论:
方法pass中形参user首先指向main方法中的user,然后再新建user类的时候会重新指向新建的user,后续改变新建user,main方法中的user并未改变,这里是值传递,传递的是对象的引用。
java是值传递,基本数据类型传递复制的对象,引用数据类型传递其引用,如果在方法中提供了修改引用对象的方法则会修改其对象,否则不会修改
几种深拷贝方式 1.构造函数深拷贝 我们可以调用构造函数进行深拷贝,形参如果是基本类型和字符串则是直接赋值,如果是对象,则是重新new一个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.lyj.demo.pojo.cloneTest;import lombok.Getter;@Getter public class UserConstruct { private String userName; private AddressConstruct address; public UserConstruct () { } public UserConstruct (String userName, AddressConstruct address) { this .userName = userName; this .address = address; } public static void main (String[] args) { AddressConstruct address = new AddressConstruct("小区1" , "小区2" ); UserConstruct user = new UserConstruct("小李" , address); UserConstruct copyUser = new UserConstruct(user.getUserName(), new AddressConstruct(address.getAddress1(), address.getAddress2())); user.getAddress().setAddress1("小区3" ); System.out.println(user == copyUser); System.out.println(user.getAddress().getAddress1() == copyUser.getAddress().getAddress1()); System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); System.out.println(user.getAddress().getAddress2().equals(copyUser.getAddress().getAddress2())); } } package com.lyj.demo.pojo.cloneTest;import lombok.Getter;import lombok.Setter;@Getter @Setter public class AddressConstruct { private String address1; private String address2; public AddressConstruct () { } public AddressConstruct (String address1, String address2) { this .address1 = address1; this .address2 = address2; } }
2.重载Clone()方法深拷贝 Object父类有个clone()的拷贝方法,不过它是protected类型的 ,我们需要重写它并修改为public类型,除此之外,子类还需要实现Cloneable接口来告诉JVM这个类上是可以拷贝的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 package com.lyj.demo.pojo.cloneTest;import lombok.Getter;import lombok.Setter;@Setter @Getter public class AddressClone implements Cloneable { private String address1; private String address2; public AddressClone () { } public AddressClone (String address1, String address2) { this .address1 = address1; this .address2 = address2; } @Override protected AddressClone clone () throws CloneNotSupportedException { return (AddressClone) super .clone(); } } package com.lyj.demo.pojo.cloneTest;import lombok.Getter;import lombok.Setter;@Setter @Getter public class UserClone implements Cloneable { private String userName; private AddressClone address; public UserClone () { } public UserClone (String userName, AddressClone address) { this .userName = userName; this .address = address; } @Override protected UserClone clone () throws CloneNotSupportedException { UserClone userClone = (UserClone) super .clone(); userClone.setAddress(this .address.clone()); return userClone; } public static void main (String[] args) throws CloneNotSupportedException { AddressClone address = new AddressClone("小区1" , "小区2" ); UserClone user = new UserClone("小李" , address); UserClone copyUser = user.clone(); user.getAddress().setAddress1("小区3" ); System.out.println(user == copyUser); System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } }
3.Apache Commons Lang序列化方式深拷贝 Java提供了序列化的能力,我们可以先将源对象进行序列化,再反序列化生成拷贝对象。但是,使用序列化的前提是拷贝的类(包括其成员变量)需要实现Serializable接口。
Apache Commons Lang包对Java序列化进行了封装,我们可以直接使用它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.lyj.demo.pojo.cloneTest;import lombok.Getter;import lombok.Setter;import java.io.Serializable;@Getter @Setter public class AddressSerializable implements Serializable { private String address1; private String address2; public AddressSerializable () { } public AddressSerializable (String address1, String address2) { this .address1 = address1; this .address2 = address2; } } package com.lyj.demo.pojo.cloneTest;import lombok.Getter;import lombok.Setter;import org.apache.commons.lang3.SerializationUtils;import java.io.Serializable;@Getter @Setter public class UserSerializable implements Serializable { private String userName; private AddressSerializable address; public UserSerializable () { } public UserSerializable (String userName, AddressSerializable address) { this .userName = userName; this .address = address; } public static void main (String[] args) { AddressSerializable address = new AddressSerializable("小区1" , "小区2" ); UserSerializable user = new UserSerializable("小李" , address); UserSerializable copyUser = SerializationUtils.clone(user); user.getAddress().setAddress1("小区3" ); System.out.println(user == copyUser); System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } }
4. 将对象序列化成JSON,再将JSON反序列化成对象 4.1 Gson深拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.lyj.demo.pojo.cloneTest;import lombok.Data;@Data public class AddressGson { private String address1; private String address2; public AddressGson () { } public AddressGson (String address1, String address2) { this .address1 = address1; this .address2 = address2; } } package com.lyj.demo.pojo.cloneTest;import com.google.gson.Gson;import lombok.Data;@Data public class UserGson { private String userName; private AddressGson address; public UserGson () { } public UserGson (String userName, AddressGson address) { this .userName = userName; this .address = address; } public static void main (String[] args) { AddressGson address = new AddressGson("小区1" , "小区2" ); UserGson user = new UserGson("小李" , address); Gson gson = new Gson(); UserGson copyUser = gson.fromJson(gson.toJson(user), UserGson.class ) ; user.getAddress().setAddress1("小区3" ); System.out.println(user == copyUser); System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } }
4.2 Jackson深拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.lyj.demo.pojo.cloneTest;import lombok.Data;@Data public class AddressJackson { private String address1; private String address2; public AddressJackson () { } public AddressJackson (String address1, String address2) { this .address1 = address1; this .address2 = address2; } } package com.lyj.demo.pojo.cloneTest;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import lombok.Data;@Data public class UserJackson { private String userName; private AddressJackson address; public UserJackson () { } public UserJackson (String userName, AddressJackson address) { this .userName = userName; this .address = address; } public static void main (String[] args) throws JsonProcessingException { AddressJackson address = new AddressJackson("小区1" , "小区2" ); UserJackson user = new UserJackson("小李" , address); ObjectMapper objectMapper = new ObjectMapper(); UserJackson copyUser = objectMapper.readValue(objectMapper.writeValueAsString(user), UserJackson.class ) ; user.getAddress().setAddress1("小区3" ); System.out.println(user == copyUser); System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } }
4.3 fastjson深拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.lyj.demo.pojo.cloneTest;import lombok.Data;@Data public class AddressGson { private String address1; private String address2; public AddressJson () { } public AddressJson (String address1, String address2) { this .address1 = address1; this .address2 = address2; } } package com.lyj.demo.pojo.cloneTest;import com.alibaba.fastjson.JSONObject;import lombok.Data;@Data public class UserJson { private String userName; private AddressJson address; public UserJson () { } public UserJson (String userName, AddressJson address) { this .userName = userName; this .address = address; } public static void main (String[] args) { AddressJson address = new AddressJson("小区1" , "小区2" ); UserJson user = new UserJson("小李" , address); JSONObject.parseObject(JSONObject.toJSONBytes(condition), UserJson.class ) ; user.getAddress().setAddress1("小区3" ); System.out.println(user == copyUser); System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } }
5.反射实例化新对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 public static <T> T deepCopy (Object source, Class<T> target) { if (Objects.isNull(source) || Objects.isNull(target)) { return null ; } List<Field> sourceFields = getFields(source.getClass()); List<Field> targetFields = getFields(target); T t = null ; try { t = newInstance(source, target, sourceFields, targetFields); } catch (Exception e) { e.printStackTrace(); } return t; } private static List<Field> getFields (Class<?> c) { List<Field> fieldList = new ArrayList<>(); Field[] fields = c.getDeclaredFields(); if (fields.length > 0 ) { fieldList.addAll(Arrays.asList(fields)); } return getSuperClassFields(c, fieldList); } private static List<Field> getSuperClassFields (Class<?> o, List<Field> allFields) { Class<?> superclass = o.getSuperclass(); if (Objects.isNull(superclass) || Object.class .getName ().equals (superclass .getName ())) { return allFields; } Field[] fields = superclass.getDeclaredFields(); if (fields.length == 0 ) { return allFields; } allFields.addAll(Arrays.asList(fields)); return getSuperClassFields(superclass, allFields); } private static <T> T newInstance (Object source, Class<T> target, List<Field> sourceFields, List<Field> targetFields) throws Exception { T t = target.newInstance(); if (targetFields.isEmpty()) { return t; } for (Field field : sourceFields) { field.setAccessible(true ); Object o = field.get(source); Field sameField = getSameField(field, targetFields); if (Objects.nonNull(sameField)) { sameField.setAccessible(true ); sameField.set(t, o); } } return t; } private static Field getSameField (Field field, List<Field> fields) { String name = field.getName(); String type = field.getType().getName(); for (Field f : fields) { if (name.equals(f.getName()) && type.equals(f.getType().getName())) { return f; } } return null ; }
额外注意 平常经常使用的org.springframework.beans.BeanUtils.copyProperties(object1,object2)是浅拷贝。
This is copyright.