一道面试题:实现档案拷贝 简单、轻松地学习流的对称哲学
作者 | Java圣斗士 | 原创图文,转载请注明出处
全文约2000字(含程式码),阅读需静心,学习要踏实

哈喽,小伙伴们大家好,我是又皮又可爱的Java圣斗士,关注我,每天带你飞!
很多小伙伴在学习Java的时候,都会碰到 IO流的使用。Java中的流主要是用于解决一些与外部资源的互动操作。比如通过流实现一些程式资讯储存到一个档案中,或者将档案内容传输到网络中等等。
如果是不经常操作这些流的API,拿到需求之后,很难不去网上搜索一番。今天,我们就通过一道面试题,来聊聊这个流的一些基本知识吧。
一道面试题:程式设计实现档案拷贝
这道面试题是在Java面试中经常会问到的问题,题干非常简约,寥寥几个字就完事了。那么如何通过程式码来实现呢?在Java 7 中有一个新特性叫做TWR——Try With Resource。这种语言特性可以减少实际程式设计中的部分程式码书写,我们可以通过TWR来配合流来完成档案拷贝,那么未来也会对TWR特性做以总结。
public static void fileCopy(String source, String target) throws IOException {
try (InputStream in = new FileInputStream(source)) {
try (OutputStream out = new FileOutputStream(target)) {
byte[] buffer = new byte[1024];
int bytesToRead;
while ((bytesToRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesToRead);
}
}
}
}
(上面的程式码实现了从档案读入到写出的全过程)
首先,我们需要思考的是,实现档案拷贝的功能,就是需要将原始档案通过输入流,读入到程式内存中,然后再通过输出流将其输出到另外一个副本档案中。顺着这个思路,我们首先就要建立基于档案路径引数的两个流的物件:InputStream和OutPutStream。
为了方便操作档案,在流的API中还提供了FileXxputStream。
然后,我们通过byte阵列,来定义一块内存缓冲区域用于储存读入的资料,其中1024代表能容纳1 KB字节数量的容量大小:
byte[] buffer = new byte[1024];
这个buffer作为一个缓冲区为InputStream所利用,将buffer传入read()方法,可以得到一个返回值,这个返回值的意思是读入到缓冲区的byte字节总数,是一个int值,如果read方法返回是一个 -1 ,那么就代表已经没有资料。
while ((bytesToRead = in.read(buffer)) != -1) { ... }
上面这句while语句会循环判断read()方法是否返回了-1,当不是-1的时候,就将读入的字节总数传递给OutputStream物件的write()方法,同时对副本档案进行写出操作。
out.write(buffer, 0, bytesToRead);
我们可以看到write()方法的源代码中有很明确的引数说明:

·引数:b 代表需要写出的资料
·引数:off 代表资料其实位置,因为是byte阵列,那么一般就是从0开始写出
·引数:len 代表byte的数量
len虽然是length的缩写,代表长度,但是其实数量也同样是资料的长度,主要也是为了和read()方法的“the total number of bytes”做一个对称的解释。
解释完了上面的程式码逻辑,那么我们来看一下呼叫效果,首先我们找到一个图片档案,然后指定相关的档案路径:
public static void main(String[] args) {
String source = "D:\档案拷贝\小熊.gif";
String target = "D:\档案拷贝\小熊-副本.gif";
try {
long start = System.currentTimeMillis();
fileCopy(source, target);
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + " ms");
} catch (IOException e) {
e.printStackTrace();
}
}
执行结果:

对流的理解与哲学思考
流的设计始终都是对称的。在java.io包中,我们有最基本的两个抽象类:InputStream和OutputStream,那么我们在操作档案的时候,还会用到继承自他们的FileInputStream和FileOutputStream。
流中的输入输出是针对程式而言的,将档案内容读入到程式管理的内存中我们成为输入,将内存中的资料读写到磁盘上我们称之为输出。在使用的过程中好好掌握住输入与输出的对应关系,那么学习IO的话就会事半功倍。
另外,在上面的程式中,还需要注意一个小细节,就是必须为副本档案指定名称才能够成功备份,否则的话,如果只写到所备份的资料夹是会报FileNotFountException的,并提示“拒绝访问”,这点一定要注意。
好了,综上就是关于IO流的一点入门知识,通过一道简单的面试题,简单剖析了IO流的使用,由于篇幅限制,不能做到大而全、小而精。
快速学习是Java的一个必备学习技能,通过小的Demo示例快速掌握一项技能是非常重要的,我会在未来补充更多关于IO流的相关知识,如果想学习其他的关于Java技术的知识比如设计模式,JVM等,也欢迎私信,相互学习,相互提高!
往期精彩:
《能否成为架构师?抽象工厂模式,真正考验你抽象能力的时候到了》
《对话式情景剖析,String被final修饰的真正原因!一篇足矣》
《情景对话,一篇带你参悟工厂模式》
《情景对话,一篇带你参悟单例模式》
《Runnable定义任务,跑完就拉倒?不,我就想要个返回值》
---欢迎关注【Java圣斗士】,我是你们的小可爱(✪ω✪) Morty---
---专注IT职场经验、IT技术分享的灵魂写手---
---每天带你领略IT的魅力---
---期待与您陪伴!---