深拷贝:
=创建一个新对象,然后将当前对象的非静态字段复制到该新对象。
==无论该字段是基本类型的还是引用类型,都复制独立的一份。当你修改其中一个对象的任何内容时,都不会影响另一个对象的内容。
浅拷贝:
=创建一个新对象,然后将当前对象的非静态字段复制到该新对象。
==基本数据类型复制的是值;
==引用数据类型复制的是对象的引用(不可变类型特殊)。(原引用对象发生改变时,复制的新对象的值也会发生改变。)
==注意:String类型、Integer等基本数据类型的包装类型,因为是不可变类型,所以即使进行的是浅拷贝,原始对象的改变并不会影响目标对象。
1)通过clone方法(浅拷贝)
Student s1 = new Student();
s1.setName("1");
s1.setAge(12);
s1.setSex("man");
Student s2 = (Student) s1.clone();
2)重新定义clone方法(深拷贝);区分final对象
class Student implements Cloneable{
@Override
protected Student clone() throws CloneNotSupportedException {
Student newStudent = (Student) super.clone();
newStudent.teacher = (Teacher) teacher.clone();
return newStudent;
}
}
class Teacher implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
$:要实现彻底的深拷贝,必须从被拷贝对象中所引用的对象属性层层往下实现Cloneable接口,并重写@Override clone方法,把该 对象引用的其他对象也clone一份。
3)序列化克隆(深拷贝)
序列化和反序列化:对象的寿命通常随着生成该对象的程序的终止而终止,有时候需要把在内存中的各种对象的状态(也就是实例变量,不是方法)保存下来,并且可以在需要时再将对象恢复。
将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。
=使用场景:
想把的内存中的对象状态保存到一个文件中或者数据库中时候;
想把对象通过网络进行传播的时候;
=原理:
要保存的对象实现Serializable接口,定义serialVersionUID;
通过ObjectOutputStream中的writeObect序列化一个对象,并发送到输出流进行转移复制;
通过ObjectInputStream中的readObject从流中取出下一个对象,并进行反序列化。
代码如下:
Person.class
/**
* 对象类需要实现序列化接口Serializable
*/
public class Person implements Serializable {
//对象属性和方法定义好后自动生成序列化ID
private static final long serialVersionUID = -7814987656430454056L;
private String name;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
CopyUtil.class
public class CopyUtil {
/**
* 通过ObjectOutputStream的writeObject方法将对象写出到某个路径下的文件
*
* @param path
* @param person
*/
public static void writeObject(String path, Person person) {
File file = new File(path);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
ObjectOutputStream oos = null;
try {
System.out.println("person:" + person.toString());
oos = new ObjectOutputStream(fos);
oos.writeObject(person); //写入对象,序列化
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
System.out.println("OOS关闭失败:" + e.getMessage());
}
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
/**
* 通过ObjectInputStream的readObject方法读取某个路径下的文件
* @param path
*/
public static void readObject(String path) {
File file = new File(path);
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(fis);
Person person = (Person) ois.readObject();
System.out.println("person:" + person.toString());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
Main.class
public static void main(String[] args) {
Person person = new Person("小王",18);
String path = "copy/out.txt";
System.out.println("开始写出文件到:");
CopyUtil.writeObject(path,person);
System.out.println("开始读取序列化后的文件:");
CopyUtil.readObject(path);
}
控制台输出:
开始写出文件到:
person:Person{name=‘小王’, age=18}
开始读取序列化后的文件:
person:Person{name=‘小王’, age=18}
Process finished with exit code 0
序列化文件保存的对象数据:
aced 0005 7372 002c 636f 6d2e 7374 7564
792e 6261 7369 632e e5ba 8fe5 8897 e58c
96e5 afb9 e8b1 a1e6 8bb7 e8b4 9d2e 5065
7273 6f6e 938b 9621 ce66 7ad8 0200 0249
0003 6167 654c 0004 6e61 6d65 7400 124c
6a61 7661 2f6c 616e 672f 5374 7269 6e67
3b78 7000 0000 1274 0006 e5b0 8fe7 8e8b
总结:
- 查看了文件序列化后保存的对象数据内容,可以得出通过序列化这种方式来实现对象拷贝或者存储,本质上就是用序列化的方式保存了对象的属性状态,并进行存储,后续再次读取出来的时候保证了一致性;
- 核心方法为ObjectOutputStream.writeObject()和ObjectInputStream.readObject()。
- 对于ObjectOutputStream如何去实现序列化输出的,可以详细参考这篇文章:
深入理解Java序列化机制:ObjectOutputStream源码简要分析
4)BeanUtils.copyProperties()函数(浅拷贝)
使用org.springframework.beans.BeanUtils包中的函数: BeanUtils.copyProperties(a, b); //a对象拷贝到b
#:避免使用org.apache.commons.beanutils.BeanUtils
5)MapStruct编译期在接口的实现类中进行了转换(深拷贝)
依赖:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.3.1.Final</version>
</dependency>
由于在编译期生成代码,需要在maven-compiler-plugin插件中配置:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.3.1.Final</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
</configuration>
</plugin>
①定义mapper接口:
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@Mappings({
@Mapping(source = "id", target = "id"),
@Mapping(source = "createTime", target = "createTime")
})
UserVO dtoToVo(UserDTO userDTO);
}
②实现类:文章来源:https://www.toymoban.com/news/detail-612513.html
public class UserMapperImpl implements UserMapper {
public UserMapperImpl() {
}
public UserVO dtoToVo(UserDTO userDTO) {
if (userDTO == null) {
return null;
} else {
UserVO userVO = new UserVO();
userVO.setIdd(userDTO.getId());
userVO.setCreateTime(userDTO.getCreateTime());
userVO.setUserName(userDTO.getUserName());
return userVO;
}
}
}
③使用:文章来源地址https://www.toymoban.com/news/detail-612513.html
public static void main(String[] args) {
UserDTO userDTO = new UserDTO();
userDTO.setId(1);
userDTO.setUserName("哈哈");
userDTO.setCreateTime(new Date());
UserVO userVO = UserMapper.INSTANCE.dtoToVo(userDTO);
System.out.println(JSON.toJSONString(userVO));
}
到了这里,关于【Java基础】2.对象拷贝的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!