字节流 FileInputStream和FileOutputStream类 三种读写方法对比概述

今天介绍FileInputStream类和FileOutputStream类,从本地文件中读 / 写数据。

1、流的概念
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。

2、流的分类
根据处理数据类型的不同分为:字符流和字节流。

字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符、字符数组或字符串;而字节流处理单元为 1 个字节,操作字节和字节数组。

根据数据流向不同分为:输入流和输出流 。

字节流

1、读写字节流:InputStream 输入流(读)和OutputStream 输出流(写)

如图所示,InputStream 是所有的输入字节流的父类,它是一个抽象类。
ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据

2、FileInputStream通过字节的方式读取文件,适合读取所有类型的文件(图像、视频、文本文件等)。Java也提供了FileReader专门读取文本文件。

FileOutputStream通过字节的方式写数据到文件中,适合所有类型的文件。Java也提供了FileWriter专门写入文本文件。

InputStream特有方法:
int available(); //返回文件中的字节个数
注:可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断。但是如果文件较大,而虚拟机启动分配的默认内存一般为64M。当文件过大时,此数组长度所占内存空间就会溢出。所以,此方法慎用,当文件不大时,可以使用!

3. 字节流缓冲区
1)缓冲区的出现时为了提高流的操作效率而出现的.
2)需要被提高效率的流作为参数传递给缓冲区的构造函数
3)在缓冲区中封装了一个数组,存入数据后一次取出

FileInputStream及FileOutputStream类的构造方法

FileInputStream的构造方法:
1.FileInputStream(File file)------------从File对象的文件读入数据
2.FileInputStream(String name)-------------从指定文件读入数据,这里要写具体文件路径

FileOutputStream的构造方法:
1.FileOutputStream(File file)-------------向File对象的文件写入数据
2.FileOutputStream(File file,boolean append);------向File对象的文件追加写入数据
3.FileOutputStream(String path)-------------向指定文件写入数据
4.FileOutputStream(String path,boolean append);--------向指定文件追加写入数据
当append的值为true时,向文件中写入的数据会追加到原数据的后面,否则会重写该文件的数据。默认为false。

代码测试read()以及write()三个方法各自性能及用法

由于FileInputStream 和 FileOutputStream类是可以读写媒体文件的,我们就来测试一下这两个类中的所有读写方法,让我们对它们有所了解。

大致操作流程:(以File类为例)
使用File类打开一个文件;
通过字节流或字符流的子类,指定输出的位置;
进行读/写操作;
关闭输入/输出。

我们新建两个Folder分别为res 和 tag ,我们在res文件夹下放入一首歌。

刷新后,res出现mp3文件.

我们的目标是,将res下的这个文件,重新写入到另一个文件中,要求能播放且两个文件字节数相同。

第一次读写代码------read()、write()方法

package com.mec.netframework.test;

import java.io.File;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FisReadMethodTest {
	
	public static void main(String[] args) {
		File file = new File("./bin/res/summer.mp3");//得到summer.mp3文件
		
		try {
			FileInputStream fis = new FileInputStream(file);//用file new一个输入流
			FileOutputStream fos = new FileOutputStream("./bin/tag/summer.mp3");
			//“.”代表从当前目录开始,因为src和bin是镜像关系,我们可以在bin中进行测试
			//new 一个输出流,后面的目录路径代表最后“写”的mp3文件生成的位置和命名
			int result ;
			long starttime = System.currentTimeMillis();
			//经过测试发现fis.read()是一个一个字节进行读取的!
			//fis.read()进行“读”操作并返回一个int类型的数据,一旦字节数据被读完,它就会返回-1!
			//所以我们将它作为循环条件。
			//当输入流读取一个字节,输出流就写入一个字节,直到字节数据被读完,循环结束!
			while((result = fis.read()) != -1) {
				fos.write(result);
			}
			long endtime = System.currentTimeMillis();
			int time =(int) (( endtime - starttime)/1000);
			//这是读写过程的总消耗时长
			System.out.println("时间:" + time + "( s )");
			//记得关闭输入输出流!
			fis.close();
			fos.close();
			
			} catch (IOException e) {
				e.printStackTrace();
			}
		
	}
}

结果展示

我们在tag中得到了新写成的summer.mp3
总耗时:30s!!!

现在看看它们的字节数是否相同呢?
一点没错!但是效率太低了,9.86M就要用30s,那再传个电影可还得了!再去试试别的方法吧!
在这里插入图片描述

第二次读写代码------read(byte[] b)、write(byte[] b)方法

关于字节数组长度定义的原因:(BUFFER_SIZE)

IP数据报报文的报头部分有一个表示报文长度的量,占2B,即,16bit;
这就意味着,一个IP数据报文的最大长度应该是2^16B,即,64KB;
但是,考虑到IP数据报报文包含数据报头部,因此,实际数据量应该小于
64KB;
因此,在考虑网络数据传输时,最好以32KB字节作为每一次传输的数据
量。
原因:对于大于64KB的数据报文,会被路由器自动切割成小于64KB的小
报文片段。这是TCP协议自动完成的。

package com.mec.netframework.test;

import java.io.File;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FisReadByLength {
	//1左一15位就是2的15次方 。这里定义数组容量为32KB
	public static int BUFFER_SIZE = 1 << 15;
	public static void main(String[] args) {
		File file = new File("./bin/res/summer.mp3");
		long len = file.length();
		try {
			FileInputStream fis = new FileInputStream(file);
			FileOutputStream fos = new FileOutputStream("./bin/tag/summer.mp3");
			
			byte[] buffer = new byte[BUFFER_SIZE];//构造了一个类似缓冲区的字节数组
			int resLen = (int) len; 
			long starttime = System.currentTimeMillis();
			
			//从fis中读取长度为字节的数据存到字节数组buffer中,并返回int类型的数据值
			resLen =  fis.read(buffer);
			while(resLen != -1) {
				//再从buffer数组中将字节按顺序写入新的文件
				fos.write(buffer);
				resLen =  fis.read(buffer);
			}
			
			long endtime = System.currentTimeMillis();
			long time =endtime - starttime;
			System.out.println("时间:" + time + "( ms )");
				
			fis.close();
			fos.close();
			
			} catch (IOException e) {
				e.printStackTrace();
			}
	
	}
}


结果展示:
同样生成了相应的summer.mp3文件
耗间:7( ms ) 注意单位变了!
速度可以但是我们发现,得到的文件大小变大了。

			resLen =  fis.read(buffer);
			while(resLen != -1) {
				fos.write(buffer);
				resLen =  fis.read(buffer);
			}

仔细分析代码,可以想到,最后一次将数据读入数组时,它的字节个数大概率会小于2的15次方(数组长度),而不是恰好等于,所以最后一次数据并没有把倒数第二次的后面一些数据覆盖掉,导致在写入操作的时候,把倒数第二次最后面的一些数据又写了进去,相当于垃圾数据!所以字节数增加了,这是不可以的,因为严重时将会导致文件不能播放。
在这里插入图片描述

第三次读写代码------read(byte[] b, int off, int len)、write(byte[] b, int off, int len)方法

package com.mec.netframework.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FisReadThreePara {
		public static int BUFFER_SIZE = 1 << 15;
		
		public static void main(String[] args) throws IOException {
			File file = new File("./bin/res/summer.mp3");
			long fileLen = file.length();
			

				FileInputStream fis = new FileInputStream(file);
				FileOutputStream fos = new FileOutputStream("./bin/tag/summer.mp3");
				
				byte[] buffer = new byte[BUFFER_SIZE];
				
				int length;//将要读进数组的字节个数
				int readLen;//文件剩余字节数(长度)
				int resLen = (int) fileLen;//刚开始剩余长度就等于文件长度
				
				long starttime = System.currentTimeMillis();
				
				while(resLen > 0) {
					//如果剩余长度比定义的数组长度长,就将length赋值为BUFFER_SIZE
					//短的话,就赋值为剩余长度resLen
					//这一步保证读数的准确性
					length = (resLen > BUFFER_SIZE ? BUFFER_SIZE : resLen);
					
					//将输入流中的数据读进buffer数组,从下标0开始读入,读length个字节数据
					readLen = fis.read(buffer, 0, length);
					
					//将字节数组buffer中的数据从下标0开始写,写readLen个字节数据到指定文件
					//这部操作保证了最后一次数据的准确性
					fos.write(buffer, 0, readLen);
					//没读写一次,意味着剩余长度减少BUFFER_SIZE
					resLen -= BUFFER_SIZE;
				}
				
				long endtime = System.currentTimeMillis();
				long time =endtime - starttime;
				System.out.println("时间:" + time + "( ms )");
				
				fis.close();
				fos.close();
				
		}
}

结果展示:
时间:8( ms )
完美撒花!
在这里插入图片描述

参考博文:https://www.cnblogs.com/caixiaohua/p/6737808.html

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值