Java IO流操作

1,927 阅读6分钟

首先我们得知道:我们存储在硬盘上的文件是byte byte byte...存储的,是数据的集合。

java 中的 io 流主要分为:

  • 字节流
  • 字符流

一、字节流

字节流又分为

  • 输入流 InputStream
  • 输出流 OutputStream

输入流负责从 源(可以是文件) 读取数据到 Java程序 中。

输出流负责把 Java程序 中数据写入到 源(可以是文件) 中。

1.1 输入流 InputStream

InputStream 是一个抽象类,抽象了应用程序读取数据的方式。

输入流基本方法:

  • int b = in.read();读取一个字节无符号填充到int低八位.-1是 EOF
  • in.read(byte[] buf)
  • in.read(byte[] buf,int start,int size)

常用的实现类有以下三个

1.1.1 FileInputStream

FileInputStream 用于从文件读取数据,可以通过 new 关键字构造,比如:

File file = new File(文件路径);
FileInputStream inputStream = new FileInputStream(file);

也可以:

FileInputStream inputStream = new FileInputStream(文件路径);

使用示例: 本示例从项目根目录的 test.txt 读取内容并打印,

public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream inputStream = new FileInputStream("test.txt");
        int len;
        byte[] bytes = new byte[8 * 1024];
        while ((len=inputStream.read(bytes,0,bytes.length))!=-1){
            String s = new String(bytes,0,len,"utf-8");
            System.out.println(s);
        }
        inputStream.close();
    }
}

1.1.1 DataInputStream

DataInputStream 对"流"功能的扩展,可以更加方便的读取 int, long,字符等类型数据。

使用 Demo 如下:

public class DataInputStreamDemo {

    public static void main(String[] args) throws IOException {
        FileInputStream inputStream = new FileInputStream("test.txt");
        DataInputStream dataInputStream = new DataInputStream(inputStream);

        int len;
        byte[] buffer = new byte[8 * 1024];
        while ((len = dataInputStream.read(buffer, 0, buffer.length)) != -1) {
            System.out.println(new String(buffer,0,len));
        }
        inputStream.close();
        dataInputStream.close();
    }
}

1.1.3 BufferedInputStream

BufferedInputStream 为 IO 提供了带缓冲区的操作,一般打开文件进行写入 或读取操作时,都会加上缓冲,这种流模式提高了IO的性能。

使用如下:

public class BufferedInputStreamDemo {
    public static void main(String[] args) throws IOException {
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("test.txt"));
        int len;
        byte[] buffer = new byte[8*1024];
        while ((len=bufferedInputStream.read(buffer,0,buffer.length))!=-1){
            System.out.println(new String(buffer,0,len));
        }
        bufferedInputStream.close();
    }
}

1.2 输出流 OutputStream

OutputStream 也是一个抽象类,抽象了应用程序写数据的方式。

输出流基本方法:

  • out.write(int b) 写出一个byte到流,b的低8位
  • out.write(byte[] buf)将buf字节数组都写入到流
  • out.write(byte[] buf,int start,int size)

同样,常用的输出流实现类有下面三个:

1.2.1 FileOutputStream

FileOutputStream 用于写入数据到文件中,可以通过 new 关键字很轻松的创建

File file = new File(文件路径);
FileOutputStream outputStream = new FileOutputStream(file);

或者

FileOutputStream outputStream = new FileOutputStream(文件路径);

使用示例: 本示例是写文字到项目根目录的文件 test.txt。

public class FileOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        FileOutputStream outputStream = new FileOutputStream("test.txt");
        String  s = "测试FileOutputStreamDemo";
        outputStream.write(s.getBytes("utf-8"));
        outputStream.close();
    }
}

1.2.2 DataOutputStream

DataOutputStream 对"流"功能的扩展,可以更加方便的写入 int, long,字符等类型数据。

使用 demo 如下:

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

        FileOutputStream outputStream = new FileOutputStream("test.txt");
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
        dataOutputStream.writeInt(123321);
        dataOutputStream.writeBoolean(true);
        dataOutputStream.writeUTF("DataOutputStreamDemo测试");
        outputStream.close();
        dataOutputStream.close();
    }
}

如果按照上面执行是会发现,只能在 test.txt 文件中看到

DataOutputStreamDemo测试

前面写入的 123321 和 true 都是乱码,比如我这边看到的是

乱码

那是因为 DataOutputStream 是一种格式化的数据输出方式,而并非都是字符流,如果写到文件中他的数据格式就和在内存中一样,这样他读出来是会很方便,我们打开文件看到的内容是字符编码的,int、boolean 不是字符编码的,所以会乱码。

然后使用 dataOutputStream.writeUTF 可以正确显示 是因为 UTF-8的字符编码是对的。

注意: 用 DataOutputStream 输出的数据并不是为了用记事本打开看的而是为了储存数据的 一般来保存为.dat文件区别开文本本件。

1.2.3 BufferedOutputStream

BufferedOutputStream 为 IO 写操作提供了带缓冲区的操作,提高了IO的性能。

使用如下:

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

        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("test.txt"));
        bufferedOutputStream.write("BufferedOutputStreamDemo测试".getBytes("utf-8"));
        bufferedOutputStream.close();
    }

字节输入流先写这么多,还有很多 InputStream 和 OutputStream 的实现类,有兴趣的可以去看下。

接下来我们通过一个实例,看看使用哪种方式操作更快。

public class IoUtil {

    /**
     * 文件拷贝,单字节、不带缓冲进行文件拷贝
     *
     * @param srcFile
     * @param destFile
     * @throws IOException
     */
    public static void copyFileByByte(File srcFile, File destFile) throws IOException {
        if (!srcFile.exists()) {
            throw new IllegalArgumentException("srcFile is not exists!");
        }
        if (!srcFile.isFile()) {
            throw new IllegalArgumentException("srcFile is not files");
        }
        FileInputStream in = new FileInputStream(srcFile);
        FileOutputStream out = new FileOutputStream(destFile);
        int len;
        while ((len = in.read()) != -1) {
            out.write(len);
            out.flush();
        }
        in.close();
        out.close();
    }

    /**
     * 文件拷贝,字节批量读取
     *
     * @param srcFile
     * @param destFile
     * @throws IOException
     */
    public static void copyFile(File srcFile, File destFile) throws IOException {
        if (!srcFile.exists()) {
            throw new IllegalArgumentException("srcFile is not exists!");
        }
        if (!srcFile.isFile()) {
            throw new IllegalArgumentException("srcFile is not files");
        }
        FileInputStream in = new FileInputStream(srcFile);
        FileOutputStream out = new FileOutputStream(destFile);
        byte[] buf = new byte[8 * 1024];
        int len;
        while ((len = in.read(buf, 0, buf.length)) != -1) {
            out.write(buf, 0, len);
            out.flush();//最好加上
        }
        in.close();
        out.close();

    }

    /**
     * 文件拷贝,使用带缓冲的字节流进行
     *
     * @param srcFile
     * @param destFile
     */
    public static void copyFileByBuffer(File srcFile, File destFile) throws IOException {
        if (!srcFile.exists()) {
            throw new IllegalArgumentException("srcFile is not exists!");
        }
        if (!destFile.isFile()) {
            throw new IllegalArgumentException("srcFile is not files");
        }
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(destFile));
        int len;
        while ((len = bufferedInputStream.read()) != -1) {
            bufferedOutputStream.write(len);
            bufferedOutputStream.flush();
        }
        bufferedOutputStream.close();
        bufferedOutputStream.close();
    }
}

接下来我们分别使用三种方式拷贝一份文件,看看用时分别多少

测试代码:

public class Main {

    public static void main(String[] args) throws IOException {
        long start1 = System.currentTimeMillis();
        IoUtil.copyFileByByte(new File("alfred.dmg"), new File("alfred1.dmg"));
        long end1 = System.currentTimeMillis();
        System.out.println("使用单字节、不带缓冲拷贝文件用时:" + (end1 - start1));

        long start2 = System.currentTimeMillis();
        IoUtil.copyFile(new File("alfred.dmg"), new File("alfred2.dmg"));
        long end2 = System.currentTimeMillis();
        System.out.println("使用字节批量读取拷贝文件用时:" + (end2 - start2));
        
        long start3 = System.currentTimeMillis();
        IoUtil.copyFileByBuffer(new File("alfred.dmg"), new File("alfred3.dmg"));
        long end3 = System.currentTimeMillis();
        System.out.println("使用使用带缓冲的字节流拷贝文件用时:" + (end3 - start3));
    }
}

运行看看:

使用单字节、不带缓冲拷贝文件用时:28138
使用字节批量读取拷贝文件用时:8
使用使用带缓冲的字节流拷贝文件用时:22406

是不是很恐怖,使用字节批量读取的速度快的不可思议,所以拷贝文件时使用哪种方式,心里应该有数了。

总结下: 可以看到,字节流的实现类一般都是成对出现的,上面讲了三对:

  • FileInputStream 和 FileOutputStream
  • DataInputStream 和 DataOutputStream
  • BufferedInputStream 和 BufferedOutputStream

二、字符流

字符流又分为

输入流 Writer 输出流 Reader

输入流负责从 源(可以是文件) 读取数据到 Java程序 中。

输出流负责把 Java程序 中数据写入到 源(可以是文件) 中。

字节流和字符流很类似,Writer 和 Reader的实现类一般也是成对出现的。

2.1 OutputStreamWriter 和 InputStreamReader

使用如下:

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

        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("test1.txt"));
        outputStreamWriter.write("IsrAndOswDemo测试");
        outputStreamWriter.close();

        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("test1.txt"));
        while (inputStreamReader.ready()) {
            System.out.print((char) inputStreamReader.read());
        }
        int len;
        char[] bytes = new char[8 * 1024];
        //批量读取,放入bytes这个字符数组,从第0个位置开始放置,最多放bytes.length个
        //返回的是读到的字符的个数
        while ((len = inputStreamReader.read(bytes, 0, bytes.length)) != -1) {
            String s = new String(bytes, 0, len);
            System.out.println(s);
        }
        inputStreamReader.close();
    }
}

输出:

IsrAndOswDemo测试

2.2 BufferedReader 、BufferedWriter、PrintWriter

下面的例子是通过 BufferedReader 读取 test1.txt 中的内容,然后分别写入到 test4.txt 和 test5.txt 中。

public class BrAndBwAndPwDemo {
    public static void main(String[] args) throws IOException {
        
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("test1.txt")));

        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("test4.txt")));

        PrintWriter printWriter = new PrintWriter("test5.txt");

        String line;
        //一次读一行,不识别换行
        while ((line=bufferedReader.readLine())!=null){
            System.out.println(line);
            // 这里是BufferedWriter的写操作
            bufferedWriter.write(line);
            bufferedWriter.flush();
            bufferedWriter.newLine();//单独写出换行操作

            // 这里是PrintWriter的写操作
            printWriter.print(line);//不换行
            printWriter.println(line);//换行
        }
        bufferedReader.close();
        bufferedWriter.close();
        printWriter.close();
    }
}

你可以通过以下方式关注我:

  1. CSDN
  2. 掘金
  3. 个人博客
  4. Github