Java四种拷贝方式实战总结(一文扫清所有拷贝问题)
目录
- 前言
- 一、浅拷贝VS深拷贝
- 二、大文件拷贝:别让你的程序“龟速运行”
- 三、对象拷贝的进阶方案:使用造好的轮子
- 1. 序列化与反序列化
- 2. jsON序列化
- 四、数组和集合的拷贝
- 五、第三方库:站在巨人的肩膀上
- 六、总结:看不同场景选择合适的拷贝方式
前言
作为Java开发者,日常开发中经常会遇到数据拷贝的需求。最近在面试中也有被问到一次大文件拷贝,抽空专门总结一下,从基础概念到实战技巧,配合流程图,让原理和流程一目了然~
一、浅拷贝VS深拷贝
一开始做spring项目时,就被浅拷贝坑过。当时想复制一个对象,结果改了新对象的属性,老对象也跟着变了。后来才知道,这就是浅拷贝的“锅”。
浅拷贝就像拍证件照,照片上的人看着和你一模一样,但本质上还是两张纸。Java里的浅拷贝只会复制对象的基本属性,遇到引用类型(比如自定义类),就直接把地址抄过来。所以你改新对象里的引用属性,老对象也会跟着遭殃。
深拷贝就靠谱多了,它会把对象里里外外都复制一遍,就像克隆人,新对象和老对象完全独立。虽然麻烦点,但胜在安全,适合处理复杂对象。
举个例子,有个Person
类包含name
和Address
属性:
class Address { String city; } class Person { String name; Address address; // 浅拷贝 Person shallowCopy() { Person copy = new Person(); copy.name = this.name; copy.address = this.address; return copy; } // 深拷贝 Person deepCopy() { Person copy = new Person(); copy.name = this.name; copy.address = new Address(); copy.address.city = this.address.city; return copy; } }
二、大文件拷贝:别让你的程序“龟速运行”
之前做文件上传功能,用传统IO方式拷贝大文件,结果服务器直接卡住。后来换成NIO,速度直接起飞!
传统IO就像蚂蚁搬家,一次搬一点,频繁读写磁盘;NIO则像卡车运输,直接把数据从一个地方搬到另一个地方,速度快得多。
// 传统IO方式,适合小文件 public static void copyFileByIO(File source, File dest) throws IOException { try (InputStream is = new FileInputStream(source); OutputStream os = new FileOutputStream(dest)) { byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { os.write(buffer, 0, len); } } } // NIO方式,适合大文件 public static void copyFileByNIO(File source, File dest) throws IOException { try (FileChannel sourceChannel = new FileInputStream(source).getChannel(); FileChannel destChannel = new FileOutputStream(dest).getChannel()) { destChannel.transferFrom(sourceChannel, 0, sourceChannel.size()); } }
三、对象拷贝的进阶方案:使用造好的轮子
手动写深拷贝代码太麻烦?别慌,Java提供给我们有不少写好的办法。
1. 序列化与反序列化
就像把对象打包成快递,寄出去再拆开。虽然速度慢点,但能保证完全独立的拷贝。不过要注意,所有相关类都得实现Serializable
接口。
public static <T extends Serializable> T deepCopyBySerialization(T obj) { try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(obj); try (ByteArrayInputStream bais = new ByteArra编程客栈yInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais)) { return (T) ois.readObject(); } } catch (IOException | ClassNotFoundException e) { throw new RuntimeException(e); } }
2. JSON序列化
用Jackson或Gson把对象转成JSON字符串,再转回来,简单粗暴。不过这种方式要求对象的属性都能被JSON正确解析www.devze.com。
import com.fasterXML.jackson.databind.ObjectMapper; public static <T> T deepCopyByJSON(T obj, Class<T> clazz) { try { ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(obj); return mapper.readValue(json, clazz); } catch (Exception e) { throw new RuntimeException(e); } }
四、数组和集合的拷贝
数组的clone()
方法看似简单,实则暗藏玄机:基本类型数组用它是深拷贝,对象数组就是浅拷贝。
int[] intArray = {1, 2, 3}; int[] copiedIntArray = intArray.clone(); // 深拷贝 Person[] personArray = new Person[2]; Person[] copiedPersonArray = personArray.clone(); // 浅拷贝
集合类的构造函数也是浅拷贝,比如new ArrayList<>(originalList)
。想深拷贝集合,得手动遍历每个元素。
List<Person> originalList = new ArrayList<>(); javascriptList<Person> deepCopiedList = originalList.stream() .map(Person::deepCopy) .collect(Collectors.toList());
五、第三方库:站在巨人的肩膀上
不想自己造轮子?Apache Commons Lang和Dozer能帮你大忙。前者提供了便捷的序列化拷贝工具,后者擅长对象属性映射,用起来超方便。
<!-- Apache Commons Lang依赖 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>android;commons-lang3</artifactId> <version>3.12.0</version> </depejavascriptndency> // 使用示例 Person copiedPerson = SerializationUtils.clone(originalPerson);
六、总结:看不同场景选择合适的拷贝方式
- 简单对象用浅拷贝,复杂对象用深拷贝
- 大文件优先用NIO,小文件用传统IO
- 能偷懒就偷懒,善用第三方库
- 多写测试用例,别让拷贝“埋雷”
到此这篇关于Java四种拷贝方式实战文章就介绍到这了,更多相关Java四种拷贝方式内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论