Java学习笔记(二十):IO流之字节流

IO流用来处理设备之间的数据传输,Java对数据的操作是通过流的方式。Java中用于操作流的对象都在IO包中。

IO流按照流向,分为输入流和输出流。按照数据类型分为:

  • 字节流:可以读写任何类型的文件。比如音频、视频 、文本文件;
  • 字符流:只能读写文本文件。
    如果数据所在的文件通过windows自带的记事本打开并能读懂里面的内容,就用字符流。其他用字节流。

输出流超类OutputStream

子类FileOutputStream

一、构造方法

FileOutputStream(File file):
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。

FileOutputStream(String name)
创建一个向具有指定名称的文件中写入数据的输出文件流。

//如果输出流所关联的文件不存在,会自动创建
FileOutputStream fos = new FileOutputStream("b.txt");

注意:
创建字节输出流对象了做了以下几件事情

  1. 调用系统资源创建b.txt文件;
  2. 创建了一个fos对象;
  3. 把fos对象指向这个文件;
    流使用完毕要记得释放资源,通知系统释放关于管理b.txt文件的资源,让Io流对象变成垃圾,等待垃圾回收器对其回收。

二、三个write()方法

public void write(int b):写一个字节  超过一个字节 砍掉前面的字节
	
	public void write(byte[] b):写一个字节数组
	
	public void write(byte[] b,int off,int len):写一个字节数组的一部分
	
	例如创建了一个名为"b.txt"的文本文件,使用以下方法每次一个字节地依次写入了abcd:
        out.write(97);
        out.write(98);
        out.write(99);
        out.write(100);
	使用以下方法一次写入一个字节数组:
        byte[] bytes = {101, 102, 103, 104};
        out.write(bytes);
	使用以下方法一次写入字节数组的一部分:
        out.write(bytes1,3,6);

四、追加写入

FileOutputStream(File file, boolean append)
        创建一个向指定 File 对象表示的文件中写入数据的文件输出流。

FileOutputStream(String name, boolean append)
        创建一个向具有指定 name 的文件中写入数据的输出文件流。

参数2append的意思是是否要追加写入。true为需要追加写入,false或者不填为不需要。如:

public static void main(String[] args) throws IOException {
        FileOutputStream out = new FileOutputStream("c.txt",true);
        out.write("劝君莫取美娇娘,不信你看武大郎".getBytes());
        //写入换行符
      /*  windows下的换行符只用是 \r\n
        Linux \n
        Mac	\r
        */
        out.write("\r\n".getBytes());
        out.write("若是取了美娇娘,时时刻刻也要防,不信你看王宝强".getBytes());
        out.write("\r\n".getBytes());
        out.close();
    }

输入流超类InputStream

子类FileInputStream

一、构造方法

FileInputStream(File file)
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。

FileInputStream(String name)
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

//与输出流不同的是,输入流所关联的文件如果不存在,就会报错
FileInputStream in= new FileInputStream("a.txt");

二、三个read()方法

public int read():一次读取一个字节,如果没有数据返回的就是-1
    //一次读取一个字节
    int by = in.read();
	
	public int read(byte[] b):一次读取一个字节数组
	//创建一个字节数组,来充当容器
    byte[] bytes = new byte[1024];
    //把读取到的字节,装进你传进来的这个容器
    int len = in.read(bytes); //返回的是你一次实际读取到的有效字节个数
	
	public void read(byte[] b,int off,int len):读一个字节数组的一部分
	//建立一个输入流
	FileInputStream in = new FileInputStream(new File("a.txt"));
    byte[] bytes = new byte[1024];
    //一次从文件中读取3个字节,从0索引处开始把读取到的3个字节放入容器bytes中
    //如果读取不到有效字节返回的是 -1
    int len = in.read(bytes,0,3);

	值得注意的是,第三个read方法中的off指的是存放读入数据的地址,在上例中,从数组的0索引处开始储存,如果改为1,则从1索引位置开始储存。
字节流复制文件

结合字节流的读写完成一个文件的复制。有两种方法,第一种是将读入文件的每一个字节一个一个的写入新的File对象中;另一种是创建一个用于缓冲的字符数组,每次将一定数量的字节读入数组,再从字符数组写入新的File对象中。

方法一:

//一次读取一个字节,写入一个字节来复制mp3
public class MyTest2 {
    public static void main(String[] args) throws IOException {
        FileInputStream in = new FileInputStream("C:\\Users\\Music\\音乐\\音乐\\辛晓琪 - 领悟.mp3");
        FileOutputStream out = new FileOutputStream("C:\\Users\\Desktop\\辛晓琪 - 领悟.mp3");
        int by = 0;
        while ((by = in.read()) != -1) {
            out.write(b);
            out.flush();
        }
        //释放资源
        in.close();
        out.close();

    }
}

方法二:

//使用缓冲数组一次读取多个字节,写入多个字节来复制mp3
public class MyTest2 {
    public static void main(String[] args) throws IOException {
        FileInputStream in = new FileInputStream("C:\\Users\\Music\\音乐\\音乐\\辛晓琪 - 领悟.mp3");
        FileOutputStream out = new FileOutputStream("C:\\Users\\Desktop\\辛晓琪 - 领悟.mp3");
        //1.定义一个字节数组,充当缓冲区
        byte[] b = new byte[1024 * 8];
        //2.定义一个变量。用来记录每次读取到的有效字节个数
        int by = 0;
        //3.频繁的读写
        while ((by = in.read(b)) != -1) {
            out.write(by,0,len);
            out.flush();
        }
        //释放资源
        in.close();
        out.close();

    }
}

结果发现方法二的效率远远优于方法一,因此在使用IO流复制文件时最好使用缓冲数组来复制文件。

流的异常处理
当使用字节流复制文件时,会出现编译期异常。之前采用的方法是将异常抛出,现在采用的方法是捕获:

public static void main(String[] args) {
        //流的异常处理
        FileInputStream in = null;
        FileOutputStream out = null;
        try {
        in = new FileInputStream("C:\\Users\\Music\\音乐\\音乐\\辛晓琪 - 领悟.mp3");
       	out = new FileOutputStream("C:\\Users\\Desktop\\辛晓琪 - 领悟.mp3");
        //1.定义一个字节数组,充当缓冲区
        byte[] b = new byte[1024 * 8];
        //2.定义一个变量。用来记录每次读取到的有效字节个数
        int by = 0;
        //3.频繁的读写
        while ((by = in.read(b)) != -1) {
            out.write(by,0,len);
            out.flush();
        }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //释放资源
            try {	//close也会出现编译器异常,因此也需要使用try...catch进行捕获
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
高效输入输出流

字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候也考虑到了这样的设计思想,所以提供了字节缓冲区流。

构造方法:

BufferedOutputStream(OutputStream out):
//格式如下
BufferedOutputStream in = new BufferedOutputStream(new FileOutputStream("C:\\Users\\Music\\音乐辛晓琪 - 领悟.mp3"));

BufferedInputStream(InputStream in):
//格式如如下
BufferedInputStream in = new BufferedInputStream(new FileInputStream("C:\\Users\\Music\\音乐辛晓琪 - 领悟.mp3"));
    
相当于把原始的字节流包装了一下,具体过程同单个字节复制文件,如下所示:
private static void test4() throws IOException{
        BufferedInputStream in = new BufferedInputStream(new FileInputStream("C:\\Users\\ShenMouMou\\Music\\音乐\\音乐\\辛晓琪 - 领悟.mp3"));
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("C:\\Users\\ShenMouMou\\Desktop\\辛晓琪 - 领悟buffer.mp3"));

        //一次读取一个字节,写入一个字节来复制mp3
        byte[] bytes = new byte[1024 * 8];
        int by = 0;
        long start = System.currentTimeMillis();
        while ((by = in.read(bytes)) != -1) {
            out.write(bytes,0,by);
            out.flush();
        }
        long end = System.currentTimeMillis();

        System.out.println("复制完成共耗时" + (end - start) + "毫秒");
        //释放资源
        in.close();
        out.close();
    }