Java中的IO流操作

IO流

一、IO产生的原因

1、在操作系统中,一切数据都是以文件的形式存储。

2、需要将文件长久存储在外部设备。

3、程序运行时,所有的数据都需要在内存中,而内存的大小有限,因此常常需要在内存和外设之间交换数据,即IO。

4、在Java语言中,主要通过输入流和输出流完成I/O功能,实现和外设之间的数据交互。


二、IO流

1、IO流用来处理JVM和外部设备之间的数据传输,Java通过流(Stream)的方式,完成数据的传输过程。

2、IO流的分类:

(1)按数据流动的方向:

a、输入流:对应数据的读入,将外设数据读入JVM内存

b、输出流:对于数据的写出,将数据写入到外设

(2)、按流中的内容():

a、字节流:流中的数据都是以字节为单位的二进制数据。字节流可以操作一切类型的数据,只是有时操作字符数据不太方便,所有字符数据专门交给字符流。

b、字符流:流中的数据都是以字符为单位的二进制数据。文本编辑器可以打开,并且人可以看懂的数据。

3、IO流的基类:

(1)、字节流:

a、输入流:InputStream (抽象类)

b、输出流:OutputStream (抽象类)

(2)、字符流:

a、输入流:Reader(抽象类)

b、输出流:Writer(抽象类)

(3)、由这四个类派生出来的子类名字都是以其父类名作为子类名的后缀。如:InputStream的子类FileInputStream;Reader的子类FileReader。

4、字节流(重要):

(1)输出流(OutputStream):

a、实例化:OutputStream是抽象类,无法直接实例化,只能间接实例化,因为通常操作的都是文件数据,所以使用其操作文件的具体子类FileOutputStream实例化

FileOutputStream的构造方法:

(1)、FileOutputStream(File file)
(2)、FileOutputStream(String name)

b、使用,即向外设写入数据:

// write()的三个重载方法
public void write(int b)
public void write(byte[] b)
public void write(byte[] b, int off, int len)

以下分别实现三个重载方法:

// void write(int b) 
// 将指定的字节写入此输出流。
// write 的常规协定是:向输出流写入一个字节。要写入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。

package com.io.review;

import java.io.*;

/**
 * @author: Lucas
 * @version: 1.0
 * @date: 2020/6/26 20:25
 *  void write(int b)方法
 */
public class WriteDemo1 {

    public static void main(String[] args) throws IOException {
        // 创建File对象
        File file = new File("E:\\TestDir\\a.txt");

        // 1、利用OutputStream的子类FileOutputStream创建输出流对象
        OutputStream out = new FileOutputStream(file);

        // 2、向目标目录写入数据(写入字符串"Hello,World")
        byte[] bytes = "Hello,World".getBytes();
        for (int i = 0; i < bytes.length; i++) {
            // 逐个读入字符
            out.write(bytes[i]);
        }
        // 3、关闭输出流对象,并释放资源
        out.close();

    }
}

// void write(byte[] b)
// 将 b.length 个字节从指定的 byte 数组写入此输出流。

package com.io.review;

import java.io.*;

/**
 * @author: Lucas
 * @version: 1.0
 * @date: 2020/6/26 20:48
 *      实现 void write(byte[] b)
 */
public class WriteDemo2 {

    public static void main(String[] args) throws IOException {
        // 创建File对象
        File file = new File("E:\\TestDir\\b.txt");

        // 1、创建输出流对象
        OutputStream out = new FileOutputStream(file);

        // 2、将数据写入目标目录(向b.txt中写入
        //"zhangsan
        // lisi
        // wangwu")
        byte[] bytes = "zhangsan\nlisi\nwangwu\n".getBytes();
        out.write(bytes);

        // 关闭输出流对象,并释放资源
        out.close();
    }
}
// void write(byte[] b, int off, int len)
// 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
// 参数:
// b - 数据。
// off - 数据中的初始偏移量。
// len - 要写入的字节数。

package com.io.review;

import java.io.*;

/**
 * @author: Lucas
 * @version: 1.0
 * @date: 2020/6/26 20:57
 *
 *  void write(byte[] b, int off, int len)
 */
public class WriteDemo3 {

    public static void main(String[] args) throws IOException {
        // 创建File对象
        File file = new File("E:\\TestDir\\c.txt");

        // 1、创建输出流对象
        OutputStream out = new FileOutputStream(file);

        // 2、向目标目录写出数据(向b.txt写出“”)
        byte[] bytes = ("我是个俗气至顶的人,\n" +
                "见山是山,\n" +
                "见海是海,\n" +
                "见花便是花。\n" +
                "唯独见了你,\n" +
                "云海开始翻涌,\n" +
                "江潮开始...\n").getBytes();
        out.write(bytes, 0, bytes.length);

        // 3、关闭输出流,并释放资源
        out.close();
    }
}

c、字节流写数据常见的问题:

创建字节输出流到底做了哪些事情?

1)、FileOutputStream会先在操作系统中找到目标文件:若目标文件不存在,FileOutputStream类会创建这个文件;若文件存在,则不再创建,清空文件内容,准备从文件最开始的地方写入

2)、在内存中,创建FileOutputStream对象

3)、在FileOutputStream和目录之间建立数据传送通道

数据写成功后,为什么要close()?

关闭次输出流并释放与此流有关的所有系统资源

如何实现速记的换行?(利用换行符)

windows:'\r' 或 ’\n‘

linux、mac OS:’\n‘

如何实现数据的追加写入?

1)FileOutputStream(File file, boolean append)

2)创建一个指定File对象表示的文件中写入数据的文件输出流。如果第二个参数为true,则将字节写入文件末尾处(而不是写入文件开始处)

// 实现数据的追加写入
// FileOutputStream(File file, boolean append)
// 创建一个向具有指定 File 的文件中写入数据的输出文件流。
// 如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处

package com.cskaoyan.review;

import java.io.*;

/**
 * @author: Lucas
 * @version: 1.0
 * @date: 2020/6/26 23:27
        实现数据的追加写入:
            FileOutputStream(String name, boolean append)
            创建一个向具有指定 name 的文件中写入数据的输出文件流。
            如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处

            FileOutputStream(File file, boolean append)
            创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
            如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处
 */

public class AppendDemo {

    public static void main(String[] args) throws IOException {

        // 创建File对象
        File file = new File("E:\\TestDir\\d.txt");

        // 1、创建输出流对象
        OutputStream out = new FileOutputStream(file, true);

        // 2、向目标目录文件追加写入数据
        byte[] bytes = ("我们聊天的时候,有的人说“再见”,\n" +
                "后面是一个波浪号,是轻盈的,\n" +
                "这种是就算不约定几月几号也...\n").getBytes();

        out.write(bytes);

        // 追加写入
        byte[] bytes1 = ("我们年轻的时候,总是把创作的冲动误以为是创作的才华;\n" +
                "总是把对孤独的恐惧误以为是对爱情的向往。").getBytes();
        out.write(bytes1);

        // 3、关闭输出流,并释放资源
        out.close();
    }
}



(2)、输出流(InputStream):

a、InputStream是抽象类,无法直接实例化,只能间接实例化,因为通常操作的都是文件数据,所以使用其操作文件的具体子类FileInputStream实例化。

b、FileInputStream的构造方法:

(1)、FileInputStream(File file)
(2)、FileInputStream(String name)
 FileInputStream的构造方法
   // 创建一个读取指定文件(目标文件)数据的,文件字节输入流对象(目标文件以File对象的形式表示)
      FileInputStream(File file)
   // 创建一个读取指定文件(目标文件)数据的,文件字节输入流对象(目标文件以File对象的形式表示)
       FileInputStream(String name)
      

c、使用,即从外设读入数据:

// read()的两个重载方法
public int read() 
public int read(byte[] b) 

以下分别实现read()的两个重载方法:

// int read()
package com.cskaoyan.review;

import java.io.*;

/**
 * @author: Lucas
 * @version: 1.0
 * @date: 2020/6/26 23:53
 *  int read()
 *      从输入流中读取数据的下一个 字节。
 *      返回值:
 *            1.下一个数据字节;
 *            2.如果到达流的末尾(没有数据可读),则返回 -1。
 */
public class ReadDemo1 {

    public static void main(String[] args) throws IOException {
        // 创建File对象
        File file = new File("E:\\testDir\\c.txt");

        // 1、创建输入流对象
        InputStream in = new FileInputStream(file);

        // 2、int read() 逐个字符读入
        int readByte;
        while ((readByte = in.read()) != -1) {
            char c = (char) readByte;
            System.out.print(c);
        }

        // 3、关闭输入流对象,并释放资源
        in.close();
    }
}


// int read(byte[] b) 
package com.cskaoyan.review;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

/**
 * @author: Lucas
 * @version: 1.0
 * @date: 2020/6/27 0:10
 * int read(byte[] b)
 *     从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
 *     返回值:
 *          1. 读入缓冲区的总字节数;
 *          2. 如果因为已经到达流末尾而不再有数据可用,则返回 -1。
 */
public class ReadDemo2 {

    public static void main(String[] args) throws IOException {
        // 1. 创建流对象
        InputStream input = new FileInputStream("E:\\TestDir\\a.txt");

        //2. 从目标文件读取数据
        // int read(byte[] b)
        // 实际开发中,通常给1024的整数倍 2^10
        byte[] buffer = new byte[1024];
        // len 就告诉我们实际向字节数组中填充了多少个字节数据(实际读取到的字节个数)
        int len = input.read(buffer);

        String s = new String(buffer,0, len);
        System.out.println(s);

        // 3. 关闭流并释放资源
        input.close();
    }
}

d、一次读入读入或写出一个字节效率高,还是一次读入或写出一个字节数组效率高
一个字节数组效率高?

==》一个字节数组效率高,原因如下:

每一次,读入或写出,即每次和外设的数据交互都需要依赖操作系统内核实现,这意味着每次读入或写出,都需要付出额外的通信代价。一次读入或写出一个字节数组的数据,平均到每个字节,付出的额外代价少很多

练习:把d:\a.txt内容复制到e:\b.txt中,把e:\**.jpg内容复制到当前项目目录下的mn.jpg

package com.cskaoyan.review;

import java.io.*;

/**
 * @author: Lucas
 * @version: 1.0
 * @date: 2020/6/27 0:19
        把d:\\a.txt内容复制到e:\\b.txt中
        把e:\\**.jpg内容复制到当前项目目录下的mn.jpg
 */


public class Exercise {

    public static void main(String[] args){
        // 复制文本文件
        //copyFile("e:\\a.txt", "d:\\b.txt");

        // 复制图片文件
        //copyFile("e:\\copy\\upload.jpg", "copy.jpg");


        // 演示一次复制一个字节数据
        String srcFilePath = "d:\\a.txt";
        String destFilePath = "e:\\b.txt";
        copyByByte(srcFilePath, destFilePath);

    }

    private static void copyByByte(String srcFilePath, String destFilePath) {
        InputStream input = null;
        OutputStream output = null;
        try {
            // 创建输入流对象,用来读取源文件内容
            input = new FileInputStream(srcFilePath);
            // 创建输出流对象, 用来向目标文件写入源文件内容
            output = new FileOutputStream(destFilePath);

            //读取源文件数据, 以单个字节为单位复制
            int readByte;

            while ((readByte = input.read()) != -1) {
                // 向目标文件写一个字节
                output.write(readByte);
            }


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭流并释放资源
            closeQuietly(input);

            //关闭输出流
            closeQuietly(output);

        }
    }


    private static void copyFile(String srcFilePath, String destFilePath) {
        InputStream input = null;
        OutputStream output = null;
        try {
            // 创建输入流对象,用来读取源文件内容
            input = new FileInputStream(srcFilePath);
            // 创建输出流对象, 用来向目标文件写入源文件内容
            output = new FileOutputStream(destFilePath);

            //读取源文件数据
            byte[] buffer = new byte[2048];
            int len;
            while ((len = input.read(buffer)) != -1) {
                // 将读取到的源文件内容,写入到输出流中
                output.write(buffer, 0, len);
            }

            //close

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭流并释放资源
            closeQuietly(input);

            //关闭输出流
            closeQuietly(output);

        }
    }


    private static void closeQuietly(Closeable closeable) {
        try {
            if (closeable != null) {
                // 多态
                closeable.close();
            }

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

}



(3)、缓冲流

a、产生原因:

1)、字节流一次读写一个数组的速度明显比读一个字节的速度快很多

2)、这是加入了数组这样的缓冲区效果

3)、Java本身在设计的时候,也考虑到了这样的情况,所以提供了字节缓冲区流

b、字节缓冲输出流(BufferedOutputStream)

​ 字节缓冲输入流(BufferedInputStream)

posted on 2020-06-27 00:26  lifelicanpeng  阅读(177)  评论(0编辑  收藏  举报

导航