Java IO 一直以来是大厂面试题中的高频考点,本文将从 Java IO 基础使用说起,以案例 + 源码的方式讲解文件、字节流、字符流、缓冲流、打印流、随机访问流等基础内容,再深入到 Java IO 模型与设计模式,从而构建出对 Java IO 的全面认知。
文章不仅适合完全不了解 Java IO 的新同学,也适合具备一定知识储备的老同学。文中的所有案例代码强烈推荐手写复现一遍,以更好地掌握 Java IO 编程基础。
文章的结尾处给出了更新日志,每次新更新的内容都会写明,便于同学们快速了解更新的内容是否是自己所需要的知识点。
我相信,友好的讨论交流会让彼此快速进步!文章难免有疏漏之处,十分欢迎大家在评论区中批评指正。
文件
文件在程序中是以流的形式来操作的。类关系如下:
创建文件
常用构造方法
常用构造方法 | 描述 |
---|---|
File(String pathname) | 根据路径名构建 |
File(File parent, String child) | 根据父目录文件 + 子路径构建 |
File(String parent, String child) | 根据父目录路径 + 子路径构建 |
要想真正地在磁盘中创建文件,需要执行 createNewFile()
方法。
Tips
- 所有
java.io
中的类的相对路径默认都是从用户工作目录开始的,使用System.getProperty("user.dir")
可以获取你的用户工作目录。- 在 Windows 系统中的分隔符为 "
\\
",在 Linux 系统中分隔符为 "/
",为了保证系统的可移植性,可以通过常量字符串java.io.File.separator
获取(参见案例中的使用)。
使用案例
- 使用
File(String pathname)
创建文件
java
复制代码
@Test public void createFile() { // 更换成你想要存放的文件路径,默认情况为用户工作目录,可以通过 System.getProperty("user.dir") 显示获取 String userDir = System.getProperty("user.dir"); System.out.println("用户工作目录:" + userDir); System.out.println("当前操作系统的文件分隔符为:" + File.separator); String fileName = "createFile.txt"; String path = userDir + File.separator + fileName; // 组装路径 File file = new File(path); // 此时只是程序中的一个对象 // File file = new File(fileName); // 默认会创建到用户工作目录中,和上一面的语句创建的文件路径一致。 try { file.createNewFile(); // 执行该方法才会真正地在磁盘中创建文件 System.out.println("文件创建成功"); } catch (IOException e) { e.printStackTrace(); } }
- 使用
File(File parent, String child)
创建文件
java
复制代码
@Test public void createFile2() { // 更换成你想要存放的文件路径 File parentFile = new File("/Users/sunnywinter/projects/interviewcode/"); String fileName = "testFile2.txt"; File file = new File(parentFile, fileName); try { file.createNewFile(); System.out.println("文件创建成功"); } catch (IOException e) { e.printStackTrace(); } }
- 使用
File(String parent, String child)
创建文件
java
复制代码
@Test public void createFile3() { // 更换成你想要存放的文件路径 String parentFile = System.getProperty("user.dir"); String fileName = "createFile3.txt"; File file = new File(parentFile, fileName); try { file.createNewFile(); System.out.println("文件创建成功"); } catch (IOException e) { e.printStackTrace(); } }
获取文件信息
常用方法
返回值 | 方法名 | 描述 |
---|---|---|
String | getName() | 获取文件名 |
String | getAbsolutePath() | 获取文件绝对路径 |
String | getParent() | 获取文件父级目录 |
long | length() | 返回文件大小(字节) |
boolean | exists() | 判断文件是否存在 |
boolean | isFile() | 判断是否是一个文件 |
boolean | isDirectory() | 判断是否是一个目录 |
使用案例
java
复制代码
@Test public void getFileInfo() { File file = new File("/Users/sunnywinter/projects/interviewcode/testFile.txt"); System.out.println("文件名:" + file.getName()); System.out.println("文件绝对路径:" + file.getAbsolutePath()); System.out.println("文件父级目录:" + file.getParent()); System.out.println("文件大小(字节):" + file.length()); System.out.println("文件是否存在:" + file.exists()); System.out.println("是否是一个文件:" + file.isFile()); System.out.println("是否是一个目录:" + file.isDirectory()); }
目录操作与文件删除
使用方法
返回值 | 方法名 | 描述 |
---|---|---|
boolean | mkdir() | 创建一级目录 |
boolean | mkdirs() | 创建多级目录 |
boolean | delete() | 删除文件或目录 |
使用案例
java
复制代码
@Test public void test() { String parentPath = "/Users/sunnywinter/projects/interviewcode/"; String fileName = "testFile.txt"; String directoryName = "a"; String mulDirectoryName = "b/c/d"; // 删除文件 File file = new File(parentPath, fileName); file.delete(); // 创建一级目录 File directory = new File(parentPath, directoryName); directory.mkdir(); // 创建多级目录 File mulDirectory = new File(parentPath, mulDirectoryName); mulDirectory.mkdirs(); // 删除目录 directory.delete(); }
IO 流概述
IO,Input/Output,即输入/输出。判断输入输出以计算机内存为中心,如果从内存到外部存储就是输出,从外部存储到内存就是输入。数据传输过程类似于水流,因此称为 IO 流。
在 Java 中,根据操作数据单位的不同,IO 流分为字节流和字符流;根据数据流的流向不同,分为输入流和输出流;根据流的角色不同,分为节点流和处理流。
Java IO 流共涉及 40 多个类,但都是从表中的 4 个抽象基类派生而来,派生的子类名称都是以其父类名作为子类名的后缀。
(抽象基类) | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
列举一些常用的类。
字节流
首先,我们先学习如何将数据写入到文件中。
OutputStream(字节输出流)
OutputStream 用于将内存数据(字节信息)写入到文件中,java.io.OutputStream
抽象类是所有字节输出流的父类。
常用方法
返回值 | 方法名 | 描述 |
---|---|---|
void | write(int b) | 将特定字节写入输出流。 |
void | write(byte b[]) | 将数组 b 写入到输出流,等价于 write(b, 0, b.length) 。 |
void | write(byte[] b, int off, int len) | 在 write(byte b[]) 方法的基础上增加了 off 参数(偏移量)和 len 参数(要读取的最大字节数)。 |
void | flush() | 刷新此输出流并强制写出所有缓冲的输出字节。 |
void | close() | 关闭输出流释放相关的系统资源。 |
FileOutputStream
FileOutputStream
是最常用的字节输出流子类,可直接指定文件路径,可以直接输出单字节数据,也可以输出指定的字节数组。
类图关系如下:
Tips
java.io.Closeable
接口扩展了java.lang.AutoCloseable
接口。因此,对任何Closeable
进行操作时,都可以使用 try-with-resource 语句(声明了一个或多个资源的 try 语句,可以自动关闭流,具体使用方法参见使用案例)。 为什么要有两个接口呢?因为Closeable
接口的close
方法只抛出了IOException
,而AutoCloseable.close
方法可以抛出任何异常。
常用构造函数
append
为 true 时,表明追加写入。
使用案例
需求 1:向 mrpersimmon.txt 文件中写入 Hi,Mrpersimmon!
代码:
java
复制代码
@Test public void testFileOutputStream() { // FileOutputStream(String name, boolean append) 追加写入 // FileOutputStream(String name) 覆盖写入 try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mrpersimmon.txt"))) { String str = "Hi,Mrpersimmon!"; // write(byte b[]) : 将字节数组 b 写入到输出流,等价于 write(b, 0, b.length) bos.write(str.getBytes("UTF-8")); // str.getBytes() 字符串 -> 字节数组 } catch (IOException e) { e.printStackTrace(); } }
Tips
FileOutputStream
在使用中要和BufferedOutputStream
一起使用,性能更好。- try(...OutputStream) 可以自动关闭输出流