本文为社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究!

前面四篇文章给咱们汇总了一下 Java 编程中那些绕不开的接口,给咱们介绍了 Java 的几个根底 Interface 以及内置的函数式接口,接下来两篇文章咱们简略的过一下Java IO 方面的根底,本篇文章会先大概说下 Java 的 IO 流,以及怎样用它们读写文件,下一篇会讲一些文件系统相关的操作,比方怎样创立删去文件或许目录这样的常用操作。话不多说,一同开始咱们的学习吧!

前言

在计算机范畴里 IO,有时也写作 I/O,是Input / Output的缩写,也就是输入和输出。这儿的输入和输出是指不同系统之间的数据输入和输出,比方读写文件数据,读写网络数据等等。

本文内容大纲如下:

用 Java 的 IO 流进行读写文件操作

Java 有哪些IO结构

Java 中有三代 IO 结构,分别是第一代的同步堵塞 IO (也叫 BIO, Blocking IO),第二代的NIO ,可以构建多路复用的、同步非堵塞 IO 程序,一起供给了更挨近操作系统底层的高性能数据操作办法。第三代 NIO2 有的地方也叫 AIO,即Async IO,进一步支撑了异步IO。

这些 IO 结构都是针对文件的,网络通信相同属于 IO 行为,但是被 Java 单独放在了 java.net 包下,不在这儿说的 IO 系统内。

这个教程中咱们来学习 Java IO 系统中最简略和易于了解的同步堵塞 IO,后边有了这儿的常识堆集后再去进一步学习 NIO 和 AIO。

BIO 简介

同步堵塞 IO 即 BIO(blocking IO),指的首要是传统的 java.io 包,它根据流模型实现。java.io 包供给了咱们最熟知的一些 IO 功用,比方 File 方针供给的文件和目录操作,还有一大块就是经过输入输出流读写文件等。

BIO 交互办法是同步、堵塞的办法,也就是说,在读取输入流或许写入输出流时,在完结之前,线程会一向堵塞在那里。多个 IO 调用的履行顺序是线性顺序。不过 BIO 的优点是代码比较简略、直观,虽然不适合在高并发场景下运用,但满足应对普通场景,一起也更简单学习和把握。

IO 流

IO 流是 Java IO 中的中心概念。流是在概念上表明无穷无尽的数据流。IO 流连接到数据源或数据的目的地,连接到数据源的叫输入流,连接到数据目的地的叫输出流。 Java 程序不能直接从数据源读取和向数据源写入,只能借助 IO 流从输入流中读取数据,向输出流中写入数据。

Java IO 中的流可以是根据字节的(读取和写入字节)也可以根据字符的(读取和写入字符),所以分为字节省和字符流,两类流根据流的方向都可以再细分出输入流和输出流。

  • 字节省
    • 输入字节省:InputStream
    • 输出字节省:OutputStream
  • 字符流
    • 输入字符流:Reader
    • 输出字符流:Writer

用 Java 的 IO 流进行读写文件操作

这儿有一点可能简单让人利诱的是,IO中的输入和输出指的是相对于程序的输入和输出,程序向外输出内容,会向输出流里写入,虽然写入操作看似是输入,但相对于程序本身而言它是在向外输出内容。所以程序写的是OutputStream 读的是InputStream

字节省

字节省首要操作字节数据或二进制方针。 字节省有两个中心抽象类:InputStream 和 OutputStream。一切的字节省类都继承自这两个抽象类。 ##

用 Java 的 IO 流进行读写文件操作

字符流

字符流操作的是字符,字符流有两个中心类:Reader 类和 Writer 。一切的字符流类都继承自这两个抽象类。

用 Java 的 IO 流进行读写文件操作

字节省、字符流怎样选择

字节省和字符流都有 read()write()flush()close() 这样的办法,这决议了它们的操作办法近似。

  • 字节省的数据是字节(二进制方针)。首要中心类是 InputStream 类和 OutputStream 类。
  • 字符流的数据是字符,首要中心类是 Reader 类和 Writer 类。

一切的文件在硬盘或传输时都是以字节办法保存的,例如图片,影音文件等都是按字节办法存储的。字符流无法读写这些文件。

所以,除了纯文本数据文件运用字符流以外,其他文件类型都应该运用字节省办法。

字节省到字符流的转换可以运用 InputStreamReader 和 OutputStreamWriter。运用 InputStreamReader 可以将输入字节省转化为输入字符流,运用OutputStreamWriter可以将输出字节省转化为输出字符流。

下面咱们经过一个读写文本文件的程序来演示一下字节省到字符流的转换以及 Java IO 的根本操作。

实践–Java IO 读写文本文件

Java IO 中的类非常多,对应的办法也很多,逐个罗列会导致内容过于单调,所以咱们写两个用 IO 流写文件和读文件的比如,来展示下怎样运用 IO 流读写文件。

下面首要运用的是 FileOutputStream / FileInputStream IO 流绑定到 File 方针上,然后将这两个字节省经过OutputStreamReader / InputStreamReader 转换为字符流,并设置字符编码,最终再用 PrintWriter / BufferedReader 给字节省添加缓冲更能,让程序能更便利地以行为单位操作 IO 流。

了解和把握了这两个根本的用法后,其他 IO 流的运用也就不是什么难事儿了。

写文件示例程序

咱们先来个写文件的示例小程序,在这个程序里边除了用到了 Java 文件、字节输出流等相关的常识外,还会用到咱们前面在 Java 反常通关指南里讲过的协助咱们主动收回已翻开资源的 try-with-resource办法的反常处理,Java 交互式获取命令行输入的 Scanner东西等。算是对咱们专栏曾经常识的一个实践运用和温习。

假如你对这些常识还有点生疏或许忘记了,也不必先着急回看,在这个示例程序的注释里会把这些常识点进行相关提示,下面也有对程序每个重要部分的具体解说,咱们先来看比如。

这个比如里咱们运行程序后,Java 程序会在命令行界面等待用户的输入,先让用户从命令行界面输入想要保存内容的文件的名字,再让用户输入内容。内容支撑多行输入,直到遇到空行,程序会认为输入完毕,然后 Java 用用户指定的名字在项目目录下创立一个文件,最终把程序读取到的一切内容输入,写到文件里去。

package com.learnfile;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
public class WriteToFilesAppMain {
    private static final Scanner in = new Scanner(System.in);
    public static void main(String[] args) throws IOException {
        File targetFile = createFile();
        writeToFile(targetFile);
        System.out.println("程序履行完毕");
    }
    private static void writeToFile(File targetFile) throws IOException {
        // 运用 try with resource 主动收回翻开的资源
        try (
                // 创立一个outputstream 树立一个从程序到文件的byte数据传输流
                FileOutputStream fos = new FileOutputStream(targetFile);
                // 创立一个可以运用outputstream的Writer,并制定字符集,这样程序就能一个一个字符地写入
                OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
                // 运用PrintWriter, 可以便利的写入一行字符
                PrintWriter pw = new PrintWriter(osw);
         ) {
            System.out.println("输入的内容会实时写入文件,假如输入空行则完毕");
            while (true) {
                String lineToWrite = in.nextLine().trim();
                System.out.println("输入内容为:" + lineToWrite);
                if (lineToWrite.trim().isBlank()) {
                    System.out.println("输入完毕");
                    break;
                } else {
                    pw.println(lineToWrite);
                    // 真正用的时分不要写一行就flush() 这儿只是演示
                    pw.flush();
                }
            }
            // 平常用的时分放在外面 flush
            // pw.flush();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    private static File createFile() throws IOException {
        System.out.println("请输入文件名:");
        String fileName = in.nextLine().trim();
        File f = new File("." + File.separator + fileName +".txt");
        if (f.isFile()) {
            System.out.println("方针文件存在,删去:" + f.delete());
        }
        System.out.println(f.createNewFile());
        return f;
    }
}

这个示例程序里咱们需求要点关注以下几个方面的常识点

  • 上面例程里运用了Scanner,以命令行交互的办法让咱们能输入程序即将创立文件的称号和要往文件里写入的内容。
  • Java 的 IO流在运用完结后需求统一调用close()办法把流封闭掉。IO流的封结束会议让程序释放出它们占用的内存资源,而且字符流操作时运用了缓冲区,并在封闭字符流时会强制将缓冲区内容输出,假如不封闭流,则缓冲区的内容是无法输出的。
  • 假如想在不封闭流时,就将缓冲区的内容输出到文件,可以调用流的flush()办法强制清空流运用的缓冲区。
  • 上面的示例程序里咱们运用了try-with-resource办法的反常处理,把资源的封闭交给了Java— 在资源被运用完结后或许程序出现反常终止履行时都会由 Java 主动封闭在try-with-resource中翻开的流资源。
  • 把字符串内容写入到文件的程序中,咱们首要运用了 FileOutputStream 把方针文件绑定到字节输出流,再用 OutputStreamWriter 创立一个可以运用OutputStreamWriter,并指定其字符集为UTF_8,这样程序就能一个字符一个字符地写入文件啦。
  • 接下来程序在OutputStreamWriter 字符流的根底上创立了 PrintWriter,运用 PrintWriter 可以让程序便利地写入字符串,而且也可以经过它的 println 办法来主动处理换行符。

用 Java 程序完结文件的写入操作后,咱们再来看看,给定一个文件,怎样用 Java 程序读取器中的内容。

读文本示例程序

咱们先在要履行程序的目录下,添加一个测验用的名为 file.txt 的文本文件,假如是用Intelij IDEA 这样的 IDE 东西履行程序的话,可以在项目根目录下添加这个文件。

创立好文件后,在文件里随便输入几行内容用于测验:

aaa
一二三
bbb
四五六
ccc
七八九
ddd
锟斤拷
烫烫屯屯

接下来咱们用 Java 程序读取这个文件里的内容,这次咱们则是会用到 IO 输入流相关的几个类: FileInputStream, InputStreamReader, BufferedReader等;他们的具体功用效果我写在了程序的注释里

package com.learnfile;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class ReadStringFromFileAppMain {
    private static final String SOURCE_FILE_NAME = "file1.txt";
    public static void main(String[] args) {
        File sourceFile = new File("." + File.separator + SOURCE_FILE_NAME);
        ReadLineFromFile(sourceFile);
    }
    private static void ReadLineFromFile(File sourceFile) {
        try (
            // 树立从文件到程序的数据输入(input)流
            FileInputStream fis = new FileInputStream(sourceFile);
            // 用 InputStreamReader 包装 byte流,并指定字符编码,让它可以将读出的byte转为字符
            InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
            //  添加缓冲功用,输入输出功率更高,而且可以一次读一行
            BufferedReader reader = new BufferedReader(isr)
        ) {
            String line = null;
            while ((line = reader.readLine()) != null) {
                System.out.println(line.trim().toUpperCase());
            }
            // 还可以从reader里获取 Stream,然后经过流操作+Lambda的办法完结
            // reader.lines().map(String::trim).map(String::toUpperCase).forEach(System.out::println);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

上面这个示例程序会用 Java 的 IO 输入流从文件中每次读取一行,并把行内容运用 toUpperCase() 办法后输出到终端,程序里翻开的流资源的办法仍然是交给try-with-resource机制主动处理,这儿不再赘述。

和输出流相同,在读取文件内容的程序中咱们也做了字节省到字符流的转换,先用 FileInputStream 把文件绑定到了字节省上,然后经过 InputStreamReader 把字节省转换为字符流,在这个过程中也是设置了字符编码,最终又用 BufferedReader 为字符流添加缓冲功用,这样读取的功率会更高一些,经过它程序可以一次读取文件的一整行。

经过 BufferedReaderreadLine()遍历或许 lines() 获取Stream 后进行流处理,都可以用 BufferedReader 完结文件内容的遍历读取,上面在程序代码和注释里咱们演示了两种遍历 BufferedReader 的办法。

总结

这篇文章咱们介绍了 Java IO流里边的各种流,比较难记,常用的怎样把文件转成文件流,文件流通成字节和字符流以及规划字符的编码都给咱们介绍了,把文章中供给的两个比如看明白差不多就算入门了。

下一篇文章咱们在总结梳理一下 Java 中常用的目录和文件操作