一、概念
字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式储存的
二、字节流之FileInputStream、FileOutputStream类
1、FileInputStream输入流,不使用小数组每次只能读取一个字节(is.read(),返回-1则说明文件被读完)构造方法,可以传路径,可以传封装好的File对象
对于read()为啥返回int,而不是byte作个解释:
因为字节输入流可以操作任何类型的文件,比如图片音频等,这些文件底层都是以二进制形式存储的,如果每次读取都返回byte,有可能读到中间的时候遇到11111111那么这11111111是byte类型的-1,我们程序遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上24个0,那么byte类型的-1就变成255了,这样可以保证整个数据读完,而结束标记的-1就是int类型
2、FileOutputStream输出流,写出字节数不受限制,可以是int(虽然写出一个int数,但是会把前面的24个0去掉,所以写出一个byte),byte[]等(os.write())
new FileOutputStream(".txt");在创建对象的时候如果没有这个文件会帮创建出来,如果有这个文件,会将文件清空,在写入
new FileOutputStream(".txt",true);如果写不清空,续写文件,就得在构造方法,路径后多加参数true
3、下面介绍三种copy文件的方法
(1)、普通的copy文件(效率极低,但是都是围绕这进行扩展的)
核心代码(详细见本篇博客最底部)
//创建输入流和输出流
FileInputStream is = new FileInputStream("create.txt");//将输入流与create.txt文件关联
FileOutputStream os = new FileOutputStream("copy.txt"); //将输出流与文件copy.txt关联,如果没有此文件则自动创建它
int b;
while ((b = is.read()) != -1) { //每次读取一个字节
os.write(b); //每次写出一个字节
}
//关闭流
is.close();
os.close();
(2)、使用avialable()获取输入流的大小来copy文件(也不推荐,JVM是虚拟在操作系统上,如果要传输10G的电影,则创建这么大的字节数组会可能导致内存溢出)
核心代码(详细见本篇博客最底部)
int le = is.available(); //获得create.txt的字节数
byte[] b = new byte[le];
is.read(b);
os.write(b);
(3)、使用小数组(效率高,一般开发使用这种方法进行IO字节流操作)
核心代码(详细见本篇博客最底部)
byte[] by = new byte[1024];
int len;
while ((len = is.read(by)) != -1) {
os.write(by,0,len);
}
三、字节流之BufferedInputStream、BufferedOutputStream类
缓冲思想:内存的运算效率比硬盘要高很多,所以只要降低到硬盘的读写次数就会提高效率。字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多。使用字节缓冲区流效率高。
BufferedInputStream缓冲输入流,是对FileInputStream的包装,内置了一个缓冲区,长度8192的字节数组,原理就像小数组一样。从它读取一个字节时,会一次性从文件中读取8192个,存在缓冲区中,返回给程序一个,程序再次读取时,就不用找文件了,直接从缓冲区中获取,直到缓冲区中所有的都被使用过,才重新从文件中读取8192个字节。
BufferedOutputStream缓冲输出流,是对FileOutputStream的包装,也内置了一个缓冲区(数组)。程序向流中写出字节时,不会直接写到文件,先写到缓冲区中,直到缓冲区写满,它才会把缓冲区中的数据一次性写到文件里。
核心代码(详细见本篇博客最底部)
BufferedInputStream is = new BufferedInputStream(new FileInputStream("create.txt")); //将输入流与create.txt文件关联
BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream("copy4.txt")); //将输出流与文件copy2.txt关联,如果没有此文件则自动创建它
int len;
while ((len = is.read()) != -1) {
os.write(len);
}
is.close();
os.close();
四、close与flush方法
close:具备刷新功能,在关闭流之前,就会先刷新一次缓冲区,将缓冲区的字节全都刷新到文件上。
flush:具备刷新的功能,刷完之后还可以继续写(qq聊天实时刷新可以用到)。
五、标准异常处理
第一种,jdk1.6及以前版本使用,现在也可以使用
finally { //安全的关闭流方式
try {
if (is != null) {
is.close();
}
} finally {
if (os != null) {
os.close();
}
}
}
第二种,jdk1.7以后,在try()中创建的流对象必须实现了AutoCloseable这个接口,在tyr后面的{}(读写代码)执行后就会自动调用,流对象的close方法将流关掉
try(创建流代码区)
{
//操作代码区
}
当操作代码区执行完后,会自动将创建代码区的io流关闭,因为实现了AutoCloseable这个接口。
六、字节流出现的问题
字节流读取中文可能出现问题,因为一个汉字两个字节,如果读取到一个字节,就会乱码。下篇博客将介绍字符流,将解决这个问题。
七、字节流四种方法copy例子详细代码
package com.xue.streamio;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 4种IO流方式实现copy
* @author xue
*
*/
public class Test1 {
public static void main(String[] args) throws IOException {
demo1();
demo2();
demo3();
demo4();
}
/**
* BufferedInputStream是对FileInputStream进行了包装,其实就是在输入和输出定义了两个byte数组:8192大小
* (推荐,效率高(比小数组要低效一点,因为BufferedInputStream是两个数组操作,而小数组是一个),开发经常使用)
* @throws IOException
*/
private static void demo4() throws IOException {
//创建缓冲输入流和输出流
BufferedInputStream is = null;
BufferedOutputStream os = null;
try {
is = new BufferedInputStream(new FileInputStream("create.txt")); //将输入流与create.txt文件关联
os = new BufferedOutputStream(new FileOutputStream("copy4.txt")); //将输出流与文件copy2.txt关联,如果没有此文件则自动创建它
long time1 = System.currentTimeMillis();
int len;
while ((len = is.read()) != -1) {
os.write(len);
}
System.out.println("使用BufferedIuputStream组耗时:"+ (System.currentTimeMillis() - time1));
} catch (IOException e) {
e.printStackTrace();
} finally { //关流
try {
if (is != null) {
is.close();
}
} finally {
if (os != null) {
os.close();
}
}
}
}
/**
* 使用小数组(字节1024的倍数),分别进行输入输出,进行copy
* (推荐,效率高,开发经常使用)
* @throws IOException
*/
private static void demo3() throws IOException {
//创建输入流和输出流
FileInputStream is = null;
FileOutputStream os = null;
try {
is = new FileInputStream("create.txt"); //将输入流与create.txt文件关联
os = new FileOutputStream("copy3.txt"); //将输出流与文件copy2.txt关联,如果没有此文件则自动创建它
long time1 = System.currentTimeMillis();
byte[] by = new byte[1024];
int len;
while ((len = is.read(by)) != -1) {
os.write(by,0,len);
}
System.out.println("使用小数组耗时:"+ (System.currentTimeMillis() - time1));
} catch (IOException e) {
e.printStackTrace();
} finally { //关流
try {
if (is != null) {
is.close();
}
} finally {
if (os != null) {
os.close();
}
}
}
}
/**
* 使用available获得总字节数,在进行copy
* (也不推荐,JVM是虚拟在操作系统上,如果要传输10G的电影,则创建这么大的字节数组会可能导致内存溢出)
* @throws IOException
*/
private static void demo2() throws IOException {
//创建输入流和输出流
FileInputStream is = null;
FileOutputStream os = null;
try {
is = new FileInputStream("create.txt"); //将输入流与create.txt文件关联
os = new FileOutputStream("copy2.txt"); //将输出流与文件copy2.txt关联,如果没有此文件则自动创建它
long time1 = System.currentTimeMillis();
int le = is.available(); //获得create.txt的字节数
byte[] b = new byte[le];
is.read(b);
os.write(b);
System.out.println("使用available耗时:"+ (System.currentTimeMillis() - time1));
} catch (IOException e) {
e.printStackTrace();
} finally { //关流
try {
if (is != null) {
is.close();
}
} finally {
if (os != null) {
os.close();
}
}
}
}
/**
* 最原始的文件读写操作
* (不推荐,效率太低)
* @throws IOException
*/
private static void demo1() throws IOException {
//创建输入流和输出流
FileInputStream is = null;
FileOutputStream os = null;
try {
is = new FileInputStream("create.txt"); //将输入流与create.txt文件关联
os = new FileOutputStream("copy.txt"); //将输出流与文件copy.txt关联,如果没有此文件则自动创建它
int b;
long time1 = System.currentTimeMillis();
while ((b = is.read()) != -1) { //每次读一个字节
os.write(b); //每次写一个字节
}
long time2 = System.currentTimeMillis();
System.out.println("原始FileInputStream用时"+(time2 - time1) + "毫秒");
} catch (IOException e) {
e.printStackTrace();
} finally { //安全的关闭流方式
try {
if (is != null) {
is.close();
}
} finally {
if (os != null) {
os.close();
}
}
}
}
}