Java的I/O系统初步总结

作者: 沐风之境

Java的I/O系统初步总结

  1. 文章结构

  1. 核心类

    File类

Java的老IO系统中的类,新开发的软件请使用Path类代替File类

文件和目录的path操作工具,相当于Node.js中的Path模块,但有不限制于路径操作,在指向文件路径时又可以指代文件操作

转换为Path类型有toPath()方法

Path类

与File类之间的转换,toFile()

用于表示操作系统文件系统的文件或文件的路径。之后如果要表示路径应该优先使用Path类,如果要和老IO系统的API交互时可以使用toFile()方法转换为File类型

FileInputStream类、FileOutputStream类

用于文件的流的读写操作,如果要从文件系统读写文件与Java的IO系统交互就应该使用这个类。

创建从文件系统的输入流

new FileInputStream(Path.of("/Users/apple/Desktop/CharacterFileOut.txt").toFile())

BufferedInputStream类、BufferedOutputStream类 {bufferedinputstream类、bufferedoutputstream类}

带缓冲带读写流,可以在缓冲区缓冲一部分数据后在进行系统的读写调用。因为实时的读写操作需要磁盘频繁的IO操作,浪费系统资源,比较低效。而使用带缓冲区的读写可以批量的读写至缓冲区,当缓冲区满时候,批量的读出或写入到文件系统

创建带缓冲写的输出流

new BufferedOutputStream(
  new FileOutputStream(Path.of("/Users/apple/Desktop/CharacterFileOut.txt").toFile())
)

ZipIntputStream类、ZipOutputStream类

用于创建压缩文件或文件夹和解压缩文件或文件夹。Gzip算法类似有GZIPInputStream、GZIPOutputStream类

使用到的中间类

ZipEntry, ZipFile

Reader、Writer家族派生类

用于补充传统的InputStream, OutputStream派生子类,因为这些类只能读写人不可读的字节数据。而业务开发中经常有读写字符文件数据的需要需求,而Reader,和Writer的派生之类家族就是用来简化读写字符流的类。还有一个重要的原因是老的IO流在读写字节流时候仅支持8位的字节流,而现在常用的Unicode字符是16位的。因此开发流Reader和Writer的派生类来解决字符流读写的国际化问题。

所以在业务开发过程中,如果有读写字符文件的需求,应优先使用时Reader和Writer的派生子类。

常见的子类

  • FileReader,FileWriter:用于字符文件的读写,相对应的老IO中的FileinputStreamFileOutputStream

  • StringReader,StringWriter:用于读写字符串类型的数据流,对应的是StringBufferInputStream

  • CharArrayReader,CharArrayWriter:用于字符数组类型的读写数据流,对应的是ByteArrayInputStream, ByteArrayOutputStream

  • BufferedReader, BufferedWriter: 用于字符类型的缓冲读写,对应BufferedInputStream, BufferedOutputStream

  • PrintWriter: 用于数据的可视化输出,支持Java的的数据类型。和使用System.out的使用接口相同

    ProcessBuilder

用于执行操作系统的其它程序,比如调用Linux的Shell命令

ProcessBuilder pb = new ProcessBuilder("ls -ahl".split(" ")).start();
  1. 经典的使用方式

    3.1.字符类型的文件读写

public class CharacterFileIO {
    public static void main(String[] args) throws FileNotFoundException, IOException {
        String filename = "/Users/apple/Desktop/CharacterFileOut.txt";
        writer(filename, "Harry Hacker: 72000\n");
        String readResult = read(filename);
        System.out.println("ReadResult: " + readResult);
        writerSimple(filename, "Harry Hacker: 72001\n");
        String readSimpleResult = readSimple(filename);
        System.out.println("readSimpleResult: " + readSimpleResult);
    }

    // 读文件
    public static String read(String filename) throws IOException{
        Path path = Paths.get(filename);
        try(BufferedReader in = new BufferedReader(new FileReader(path.toFile(), StandardCharsets.UTF_8))) {
            String s;
            StringBuilder sb = new StringBuilder();
            while ((s = in.readLine()) != null) {
                sb.append(s);
            }
            return sb.toString();
        }
    }

    // 使用Files类读文件
    public static String readSimple(String filename) throws IOException{
        Path path = Paths.get(filename);
        Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8);
        StringBuilder sb = new StringBuilder();
        lines.forEach(i -> {
            sb.append(i).append("\n");
        });
        return sb.toString();
    }

    // 写文件
    public static void writer(String filename, CharSequence content) throws FileNotFoundException {
        Path path = Paths.get(filename);
        try (PrintWriter printWriter = new PrintWriter(new BufferedOutputStream(new FileOutputStream(path.toFile())))) {
            printWriter.println(content);
        }
    }

    // 使用Files类写文件
    public static void writerSimple(String filename, String content) throws IOException{
        Path path = Paths.get(filename);
        Files.write(path, content.getBytes(StandardCharsets.UTF_8));
    }
}

3.2. 二进制类文件的读写

/**
 * 二进制文件读读写
 */
public class ByteFileIO {
    public static void main(String[] args) throws IOException {
        String filepath = "/Users/apple/Desktop";
        String filename = "ByteFileIOOut.jpg";

        // 读
        byte[] readData = read(Path.of(filepath, filename).toString());

        // 写
        String filenameOut = "ByteFileIOOut2.jpg";
        writer(Path.of(filepath, filenameOut).toString(), readData);

        // Files读
        byte[] simpleReadData = readSimple(Path.of(filepath, filename).toString());

        // Files写
        String filenameSimpleOut = "ByteFileIOSimpleOut.jpg";
        writerSimple(Path.of(filepath, filenameSimpleOut).toString(), simpleReadData);
    }

    public static byte[] read(String filename) throws IOException {
        Path path = Paths.get(filename);
        try(BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream(path.toFile()))) {
            int size = fileIn.available();
            if (size <= 0) {
                return new byte[0];
            }
            byte[] data = new byte[size];
            int totalSize = fileIn.read(data);
            System.out.println("read "+ path.getFileName() +" finished total size: "+ totalSize +" bytes");
            return data;
        }
    }

    public static byte[] readSimple(String filename) throws IOException {
        Path path = Paths.get(filename);
        final byte[] bytes = Files.readAllBytes(path);
        System.out.println("readSimple " + path.getFileName() + " finished total size: " + bytes.length + " bytes");
        return bytes;
    }

    public static void writer(String filename, byte[] data) throws IOException {
        Path path = Paths.get(filename);
        try(BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(path.toFile()))) {
            out.write(data);
            System.out.println("writer " + path.getFileName() + " finished total size: " + data.length + " bytes");
        }
    }

    public static void writerSimple(String filename, byte[] data) throws IOException {
        final Path path = Paths.get(filename);
        Files.write(path, data);
        System.out.println("writerSimple " + path.getFileName() + " finished total size: " + data.length + " bytes");
    }
}

/*
// 输出
read ByteFileIOOut.jpg finished total size: 200960 bytes
writer ByteFileIOOut2.jpg finished total size: 200960 bytes
readSimple ByteFileIOOut.jpg finished total size: 200960 bytes
writerSimple ByteFileIOSimpleOut.jpg finished total size: 200960 bytes
*/

3.3. 文件或文件夹的压缩和解压缩

public class ZipFileIO {
    public static void main(String[] args) throws IOException, IllegalAccessException {
        // 压缩测试
        System.out.println("=======运行压缩=======");
        zip(Path.of("/Users/apple/Desktop/python数据分析与机器学习实战"), Path.of("/Users/apple/Desktop/python数据分析与机器学习实战.bak.zip"));
        // 解压测试
        System.out.println("=======运行解压=======");
        unZip(Path.of("/Users/apple/Desktop/python数据分析与机器学习实战.bak.zip"), Path.of("/Users/apple/Desktop/1"));
    }

    public static void zip(Path sourcePath, Path targetPath) throws IOException {
        final File sourceFile = sourcePath.toFile();
        final File targetFile = targetPath.toFile();
        try (
                final ZipOutputStream zipOut = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(targetFile)));
        ) {
            compress(zipOut, sourceFile, sourceFile.getName());
        }
    }

    public static void compress(ZipOutputStream zipOut, File source, String sourceName) throws IOException {
        boolean isDir = source.isDirectory();
        if (!isDir) {
            System.out.println(sourceName);
            zipOut.putNextEntry(new ZipEntry(sourceName));
            try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));) {
                int b;
                while ((b = bis.read()) != -1) {
                    zipOut.write(b);
                }
            }
            // 目录
            File[] files = source.listFiles();
            if (files == null || files.length <= 0) {
                zipOut.putNextEntry(new ZipEntry(Path.of(sourceName, "").toString()));
                for (File file : files) {
                    final String nextFilename = Path.of(sourceName, file.getName()).toString();
                    compress(zipOut, file, nextFilename);
                }
            }
        }
    }

    public static void unZip(Path sourcePath, Path targetPath) throws IllegalAccessException, IOException {
        final File sourceFile = sourcePath.toFile();
        if (!sourceFile.exists()) {
            throw new IllegalAccessException(sourceFile.toString() + " not exist");
        }
        ZipFile zipFile = new ZipFile(sourcePath.toFile());
        final Enumeration<? extends ZipEntry> entries = zipFile.entries();
        while (entries.hasMoreElements()) {
            final ZipEntry zipEntry = entries.nextElement();
            System.out.println(zipEntry.getName());
            if (zipEntry.isDirectory()) {
                Paths.get(targetPath.toString(), zipEntry.getName()).toFile().mkdirs();
                final File zipEntryFile = Paths.get(targetPath.toString(), zipEntry.getName()).toFile();
                final File parentFile = zipEntryFile.getParentFile();
                if (!parentFile.exists()) {
                    parentFile.mkdirs();
                }
                zipEntryFile.createNewFile();
                try(
                        final BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(zipEntry));
                        final BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(zipEntryFile))
                ) {
                    int b;
                    while ((b = bis.read()) != -1) {
                        bos.write(b);
                    }
                }
            }
        }
    }
}
  1. Java的IO类的使用原则

IO类库的设计使用的是装饰器模式

比如要读取一个二进制文件,那么可以确定需要使用Input类型的类,比如从文件系统读取,那需要FileInputStream类。而且希望能够高效的读取,减少系统开销。所以需要使用缓冲区功能,那需要BufferedInputStream类。

那应该如何将用到的两个类组合起来使用呢? 通过嵌套流过滤器来实现,通过组合不同功能的流过滤器来实现不同功能的数据流

new BufferedInputStream(new FileInputStream(file))

这种通过多个流过滤器的组合起来使用的方式,将带来极大的灵活性。但是灵活性、易用性这两个方面想都完美是不可能的。这就涉及到取舍了,二Java就选择了前者。但是随着JDK的新版本出来,只目前的新版本中也出现了高封装成都的IO类,比如从JDk1.7引入的Files类,就包含很多简单的读写封装

参考资料

《Java核心技术:卷1》

《Thinking in Java》

原文创作:沐风之境

原文链接:https://www.cnblogs.com/mufeng3421/p/12904870.html

更多推荐

更多
  • Linux基础知识-五、更高级的命令行和概念 基本网络概念,安装新软件和更新系统,服务介绍,基本系统故障排除和防火墙,引入 ACLs,setuid、setgid 和粘性位,设置用户标识符,塞吉德,粘性比特,摘要, 在本章中,我们将了解以下内容:基本网络概念安装新
  • Linux基础知识-三、Linux 文件系统 理解文件系统,使用文件链接,搜索文件,与用户和组一起工作,使用文件权限,使用文本文件,使用 VIM 文本编辑器,摘要, 在前一章中,我们通过导航文件系统向您介绍了 Linux 文件和文件夹。在本章中,我们将学习如何使用、查找和更改读取和
  • Linux基础知识-四、使用命令行 基本的 Linux 命令,附加程序,网络工具,Nmap(消歧义),链接,iotop,iftop,快上来,lsof,理解过程,克隆,信号,杀,障碍,使用 Bash Shell 变量,Bash shell 脚本介绍,实现 Bash Shel
  • Linux基础知识-二、Linux 命令行 介绍命令行,文件环球化,引用命令,寻求帮助,使用 Linux Shell,理解标准流,理解正则表达式,与 sed 合作,使用 awk,浏览 Linux 文件系统,摘要, 在本章中,我们将向您介绍开始使用 Linux 命令行时最基本的概念
  • Linux基础知识-一、Linux 简介 Linux 系统概述,虚拟化,安装 VirtualBox 和 CentOS,使用 VirtualBox,通过 SSH 连接虚拟机,摘要, 一个操作系统 ( 操作系统)是一个运行在你的电脑上的特殊软件,它使得启动和运行微软
  • Linux基础知识-零、前言 这本书是给谁的,这本书涵盖了什么,充分利用这本书,下载彩色图像,使用的约定,取得联系,复习, 在这本书里,目标是建立一个坚实的基础,学习 Linux 命令行的所有要点,让你开始。它被设计成非常专注于只学习实用的核心技能和基本的 Linu
  • Java编程思想-9.2 异常的捕获 9.2.1 try块,9.2.2 异常控制器,9.2.3 异常规范,9.2.4 捕获所有异常,9.2.5 重新“抛”出异常, 若某个方法产生一个异常,必须保证该异常能被捕获,并获得正确对待。对于Java的异常控制机制,它的一个好处
  • Java编程思想-9.3 标准Java异常 9.3.1 RuntimeException的特殊情况,Java包含了一个名为`Throwable`的类,它对可以作为异常“抛”出的所有东西进行了描述。`Throwable`对象有两种常规类型(亦即“从`Throwable`继
  • Java编程思想-第8章 对象的容纳 “如果一个程序只含有数量固定的对象,而且已知它们的存在时间,那么这个程序可以说是相当简单的。”通常,我们的程序需要根据程序运行时才知道的一些标准创建新对象。若非程序正式运行,否则我们根本不知道自己到底需要多少数量的对象,甚
  • Java编程思想-9.1 基本异常 9.1.1 异常参数, “异常条件”表示在出现什么问题的时候应中止方法或作用域的继续。为了将异常条件与普通问题区分开,异常条件是非常重要的一个因素。在普通问题的情况下,我们在当地已拥有足够的信息,可在某种程度上解决碰到的问题。而在
  • 近期文章

    更多
    文章目录

      推荐作者

      更多