(转)教你完全理解IO流里的 read(),read(byte[]),read(byte[],int off,int len)以及write
背景:对于IO部分,总是感觉很虚,不能很好的理解其中的要义,其实仔细分析,掌握其中的规律,一切都会看起来十分的自然。
1 理解
1.1 从头总结
长期以来,java中的InputStream OutputStream write read 傻傻的分不清,这次要彻底的捋一捋。
其实上述四者之间的关系,是可以从内存和硬盘两个角度理解:
对于流,站在内存角度考虑,箭头指向内存就是InputStream输入流,箭头由内存指向硬盘就是OutputStream就是输出流。
对于读写,站在硬盘的角度考虑,箭头指向硬盘就是写入,箭头从硬盘指向内存就是读出。
摘抄下《疯狂java讲义》中的相关内容:
输入流:只能从中读取数据,而不能向其写出数据。
输出流:只能向其写出数据,而不能从中读取数据。
虽然很绕,但是站在内存硬盘,两个角度考虑,就很清晰了。
好的我们先来讲它们的作用,然后再用代码来实现给大家看
read():
1.从读取流读取的是一个一个字节
2.返回的是字节的(0-255)内的字节值
3.读一个下次就自动到下一个,如果碰到-1说明没有值了.
read(byte[] bytes)
1.从读取流读取一定数量的字节,如果比如文件总共是102个字节
2.我们定义的数组长度是10,那么默认前面10次都是读取10个长度
3.最后一次不够十个,那么读取的是2个
4.这十一次,每次都是放入10个长度的数组.
read(byte[] bytes,int off ,int len)
1.从读取流读取一定数量的字节,如果比如文件总共是102个字节
2.我们定义的数组长度是10,但是这里我们写read(bytes,0,9)那么每次往里面添加的(将只会是9个长度),就要读12次,最后一次放入3个.
3.所以一般读取流都不用这个而是用上一个方法:read(byte[]);
下面讲解write
write(int i);
直接往流写入字节形式的(0-255)int值.
write(byte[] bytes);
往流里边写入缓冲字节数组中的所有内容,不满整个数组长度的”空余内容”也会加入,这个下面重点讲,
write(byte[] bytes,int off,int len);
1.这个是更严谨的写法,在外部定义len,然后每次len(为的是最后一次的细节长度)都等于流往数组中存放的长度(可以保证每次都输出读取的内容)
2.如上述read(bytes),前面每次都放入十个,第十一次放入的是2个,如果用第二种write(bytes),将会写入输出流十一次,每次写入十个长度,造成后面有8个空的,比原来的内容多了
3.所以用write(byte[] bytes,int off,int len);就不会出现多出来的空的情况,因为最后一次len不同
下面是详细的代码
public class Test{
public static void main(String[] args) throws Exception { UseTimeTool.getInstance().start(); FileInputStream fis = new FileInputStream("D:\\1.mp3");
FileOutputStream fos = new FileOutputStream("D:\\1copy.mp3");
//(PS:一下3个大家分开来写和测试,为了方便我都列出来了)
/*--------------不使用缓冲--------------*/
//如果不缓冲,花了差不多14"秒"
int len = -1;
while ((len = fis.read()) != -1) {
//这里就不是长度的问题了,而是读取的字节"内容",读到一个写一个,相当慢.
System.out.println("len : "+ len);
fos.write(len);
} /*--------------使用缓冲--------------*/
//缓冲方法复制歌曲用了不到20"毫秒"
//创建一个长度为1024的字节数组,每次都读取5kb,目的是缓存,如果不用缓冲区,用fis.read(),就会效率低,一个一个读字节,缓冲区是一次读5000个
byte[] bytes = new byte[1024*5];
//每次都是从读取流中读取(5k)长度的数据,然后再写到文件去(5k的)数据,注意,每次读取read都会不同,是获取到下一个,直到后面最后一个.
while (fis.read(bytes)!=-1) {
//write是最追加到文件后面,所以直接每次添5K.
fos.write(bytes);
}
/*--------------解释len--------------*/
//告诉你为什么用len
byte[] bytes = new byte[1024*5];
int len = -1;
//解释这个fis.read(bytes)的意思:从读取流"读取数组长度"的数据(打印len可知),并放入数组
while ((len = fis.read(bytes,0,1024)) != -1) {
//虽然数组长度的*5,但是这里我们设置了1024所以每次输出1024
System.out.println("len : "+ len);
//因为每次得到的是新的数组,所以每次都是新数组的"0-len"
fos.write(bytes,0,len);
} fis.close();
fos.close();
UseTimeTool.getInstance().stop();
}
为了方便大家,也给大家一个统计时间的工具类
class UseTimeTool {
private static UseTimeTool utt = new UseTimeTool();
private UseTimeTool() {
}
public static UseTimeTool getInstance() {
return utt;
}
private long start;
public void start() {
start = System.currentTimeMillis();
}
public void stop() {
long end = System.currentTimeMillis();
System.out.println("所用時間 : " + (end - start) + "毫秒");
}
}
好了最后一个:len问题 最后多出数组不满的部分我特再写一个出来给大家分析
首先,文本的内容是
public class Test{
public static void main(String[] args) throws Exception { UseTimeTool.getInstance().start();
FileInputStream fis = new FileInputStream("D:\\a.txt");
FileOutputStream fos = new FileOutputStream("D:\\acopy.txt");
不使用len:
byte[] bytes = new byte[1024*5];
while (fis.read(bytes)!=-1) {
fos.write(bytes);
}
得到的效果:
发现后续后很多的空部分,所以说不严谨
ps:上述是数组在初始时候未被占满的情况;
如果倒数第二次循环时候数组是占满的,写入时候使用fos.write(bytes);,那么在最后一次fos.write(bytes)时候就会出现数组前一部分是最后读取的内容,后一部分是倒数第二次数组中存在的内容。原因是数组中的内容并不会每次都清空,只会在原来的基础上进行覆盖。所以从严谨的角度出发,推荐第二种写法——
使用len:
byte[] bytes = new byte[1024*5];
int len = -1;
while ((len = fis.read(bytes,0,1024)) != -1) {
fos.write(bytes,0,len);
}
得到的效果
和原来一模一样,讲了那么多就是希望能帮助大家真正的理解.
1
(转)教你完全理解IO流里的 read(),read(byte[]),read(byte[],int off,int len)以及write的更多相关文章
- Java里的IO流里的 ObjectInputStream 的读取\写入!
各位好!!我又来了!!今天遇见了一个小问题!! IO流里的对象读取总是出错!各种报错!!神烦啊!!百思不得其解啊!然后就上网百度!找了好久终于让我找到了!下面就让我来说一说! 当你用IO流里的对象流写 ...
- io流中的装饰模式对理解io流的重要性
为了说明 io流中的装饰者模式对理解io流的重要性,我想先简要介绍以下io的装饰模式. 装饰(decorator)你也可以翻译成修饰.比如:一个会精通化学数学的物理学家.在这个"物理学家&q ...
- Java里的IO流里的FileReader里的BufferedReader读取并在前打印行数!
哈哈!!我又来了!!这个里面没有运用readLine 的方法!!纯手打!! import java.io.BufferedReader; import java.io.FileNotFoundExce ...
- Java里的IO流里的FileInputStream 的读取并在前打印行数!
大家好!!新人求罩! import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException ...
- 理解Java之IO流
流是一种抽象概念,它代表了数据的无结构化传递.用来进行输入输出操作的流就称为IO流. 一.IO流结构 1.流的分类方式 按流向分: 从文件/网络/内存等(数据源)到程序是输入流:从程序到文件/网络/内 ...
- Java进阶 | IO流核心模块与基本原理
一.IO流与系统 IO技术在JDK中算是极其复杂的模块,其复杂的一个关键原因就是IO操作和系统内核的关联性,另外网络编程,文件管理都依赖IO技术,而且都是编程的难点,想要整体理解IO流,先从Linux ...
- 今天谈谈流,什么是IO流?
无标题 (5) :first-child { margin-top: 0; } blockquote > :last-child { margin-bottom: 0; } img { bord ...
- JavaEE基础(二十)/IO流
1.IO流(IO流概述及其分类) 1.概念 IO流用来处理设备之间的数据传输 Java对数据的操作是通过流的方式 Java用于操作流的类都在IO包中 流按流向分为两种:输入流,输出流. 流按操作类型分 ...
- java IO文件操作简单基础入门例子,IO流其实没那么难
IO是JAVASE中非常重要的一块,是面向对象的完美体现,深入学习IO,你将可以领略到很多面向对象的思想.今天整理了一份适合初学者学习的简单例子,让大家可以更深刻的理解IO流的具体操作. 1.文件拷贝 ...
随机推荐
- 欢迎来怼--第三十六次Scrum会议
一.小组信息 队名:欢迎来怼 小组成员 队长:田继平 成员:李圆圆,葛美义,王伟东,姜珊,邵朔,阚博文 小组照片 二.开会信息 时间:2017/12/1 11:35~11:55,总计20min. 地点 ...
- TeamWork#3,Week5,Scrum Meeting 11.16
到目前为止各方面工作已经基本完成,爬虫程序也调整完毕,正在等待全部整合. 成员 已完成 待完成 彭林江 完成爬虫结构调整 新爬虫与服务器连接 郝倩 完成爬虫结构调整 新爬虫与服务器连接 高雅智 重定位 ...
- Daily Scrum (2015/11/7)
今晚谢金洛同学的UI工作完成,我们进行了UI和后端的拼接,准备开始规范化地进行系统测试. 成员 今日任务及成果 时间 明日任务 符美潇 1.把之前PM分配的编码任务及其说明准备好发给PM 1h 待定 ...
- Task 6.2站立会议二
今天,我们开始自己开始编译运行代码了,服务器端,聊天界面的相关代码经过测试.大家都把自己的实验过程公布了,大家的交流对实验的进度也起到了很大的作用.明天我们要继续修改实验中的错误,然后大家一起把实验基 ...
- matconvnet编译
1.安装matconvnet 网上教程很多 2.编译 cd matconvnet/ addpath matlab/ vl_compilenn('enableGpu',true,'cudaRoot',' ...
- fragment的学习
这个讲的不错 http://blog.csdn.net/lmj623565791/article/details/37992017 Fragment与Activity交互的几种方式(二,使用Bund ...
- <mvc:annotation-driven/>的作用
<mvc:annotation-driven>的作用是: 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapt ...
- mongodb的命令介绍
db.help() 查看库级别的命令 db.stats() 查看数据库状态 db.version() 查看数据库版本 db.serverStatus() 查看数据库服务器状态 db.mycoll.he ...
- 【第十周】psp
代码累计 300+575+475+353+620+703=2926 随笔字数 1700+3000+3785+4210+4333+3032=20727 知识点 机器学习,支持向量机 数据库技术 Acm刷 ...
- Python入门:学会创建并调用函数
这是关于Python的第4篇文章,主要介绍下如何创建并调用函数. print():是打印放入对象的函数 len():是返回对象长度的函数 input():是让用户输入对象的函数 ... 简单来说,函数 ...