操作系统会将硬件设备和软件资源都抽象为 ”文件“,统一进行管理。大部分情况下,文件指的是硬盘的文件,即对硬盘数据的抽象,因此可以通过文件来操作硬盘。
计算机上的文件通过文件系统来进行组织和管理,操作系统通过目录的结构来组织文件。
文本文件:保存的都是字符串,所有数据都能够在字符集中找到相应的字符(合法的字符)
二进制文件:保存的是二进制数据
1. 操作文件系统
Java 中通过 File 对文件或目录进行抽象的描述
File 常用属性及方法
1.1 属性
类型 | 字段名 | 解释 |
---|---|---|
public static final String | pathSeparator | 依赖于系统的路径分隔符 |
public static final char | pathSeparatorChar | 依赖于系统的路径分隔符 |
1.2 构造方法
签名 | 解释 |
---|---|
File(File parent, String child) | 根据父目录+孩子文件路径创建 |
File(String pathname) | 根据文件路径创建 |
File(String parent, String child) | 根据父目录+孩子文件路径创建 |
1.3 方法
修饰符及返回值类型 | 签名 | 解释 |
---|---|---|
String | getParent() | 返回 File 对象的父目录文件路径 |
String | getName() | 返回 File 对象的纯文件名称 |
String | getPath() | 返回 File 对象的文件路径 |
String | getAbsolutePath() | 返回 File 对象的绝对路径 |
String | getCanonicalPath() | 返回 File 对象的修饰过的绝对路径 |
boolean | exists() | 判断 File 对象描述的的文件是否真实存在 |
boolean | isDirectory() | 判断 File 对象代表的文件是否是一个目录 |
boolean | isFile() | 判断 File 对象代表的文件是否是一个普通文件 |
boolean | createNewFile() | 根据 File 对象,自动创建一个空文件,成功创建后返回 true |
boolean | delete() | 根据 File 对象,删除该文件,成功删除后返回 true |
void | deleteOnExit() | 根据 File 对象,删除该文件,删除动作在 JVM 运行结束时执行 |
String[] | list() | 返回 File 对象代表的目录下的所有文件名 |
File[] | listFiles() | 返回 File 对象代表的目录下的所有文件 |
boolean | mkdir() | 创建 File 对象代表的目录 |
boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目录 |
boolean | renameTo(File dest) | 文件重命名(剪切、粘贴) |
boolean | canRead() | 判断用户是否对文件有可读权限 |
boolean | canWrite() | 判断用户是否对文件有可写权限 |
2. 操作文件内容——数据流
标准库中提供的读写文件的流对象,有很多个类,主要分为两种,一种是读取二进制文件的字节流,另一种是读取文本文件的字符流
2.1 字节流
每次读 / 写的最小单位是 ”字节“
基本父类:InputStream
, OutputStream
2.2 字符流
每次读 / 写的最小单位是 ”字符“。本质上是对字节流进行了封装。
无论是文本文件还是二进制文件,在硬盘上都是以二进制方式存储的,字符流能自动将文件中相邻的几个字节转换成一个字符(自动查字符集表)
基本父类:Reader
, Writer
2.2.1 Reader
Reader reader = new FileReader("D:/test.txt");
方法
返回值 | 签名 | 解释 |
---|---|---|
int | read() | 一次读取一个字符,返回 unicode 编码,若读到文件结束,返回 -1 |
int | read(char cbuf[]) | 一次读取多个字符,将参数指定的 cbuf 填充满,返回实际读取到的字符个数,若读到文件结束,返回 -1 |
int | read(char cbuf[], int off, int len) | 一次读取多个字符,将参数指定的 cbuf 的从 off 到 len这么长的范围填充满 |
Java 标准库内部,如果只使用 char,此时使用的字符集就是 unicode(一个汉字 2 个字节),如果使用 String,此时就会自动把每个字符的 unicode 转为 utf8(一个汉字 3 个字节)
public static void main(String[] args) throws IOException {
Reader reader = new FileReader("D:/test.txt");
// 一次读一个字符
while (true) {
int c = reader.read();
if (c == -1) {
break;
}
char ch = (char)c;
System.out.println(ch);
}
// 一次读取多个字符
while (true) {
char[] cbuf = new char[3];
int n = reader.read(cbuf);
if (n == -1) {
break;
}
System.out.println("n = " + n);
for (int i = 0; i < n; i++) {
System.out.println(cbuf[i]);
}
}
// 释放文件描述符
reader.close();
}
close():为了释放文件描述符。PCB 中的文件描述符表(顺序表),记录了当前进程所持有的硬盘资源,当前进程每打开一个文件,就会在这个表中添加元素,但数组的长度是存在上限的,如果代码只打开文件而不关闭,这个表中的元素越来越多,最终会占满,后续再尝试打开文件就会报错
按照上面的方式来写的话,如果 close 之前的代码执行过程中出现异常,那么 close 便不会执行到,这里使用 try…catch…来解决,这样的话,无论 try 中的代码是否执行出现异常,都能保证 finally 中的 close 的执行
// 1. try…catch
try {
// 一次读取多个字符
while (true) {
char[] cbuf = new char[3];
int n = reader.read(cbuf);
if (n == -1) {
break;
}
System.out.println("n = " + n);
for (int i = 0; i < n; i++) {
System.out.println(cbuf[i]);
}
}
} finally {
// 释放文件描述符
reader.close();
}
// 2. try with resources
// 在 try 代码块结束时自动调用 reader 的 close 方法
// 写到 () 中的对象必须要实现 closeable 接口
try(Reader reader = new FileReader(("d:/123.txt"))) {
while (true) {
char[] cbuf = new char[3];
int n = reader.read(cbuf);
if (n == -1) {
break;
}
System.out.println("n = " + n);
for (int i = 0; i < n; i++) {
System.out.println(cbuf[i]);
}
}
}
2.2.2 Writer
返回值 | 签名 | 解释 |
---|---|---|
void | write(int c) | 一次写一个字符 |
void | write(String str) | 一次写多个字符 |
void | write(char[] cbuf) | 一次写多个字符(字符数组) |
void | write(String str, int off, int len) | 一次写一个字符串(从 off 开始 len 长度) |
void | write(char[] cbuf, int off, int len) | 一次写一个字符数组(从 off 开始 len 长度) |
// FileWriter 构造方法的第二个参数表示是否追加写入
try (Writer writer = new FileWriter("d:/123.txt", true)) {
writer.write("hello");
} catch (IOException e) {
throw new RuntimeException(e);
}
字节流转字符流
- 读数据
try (InputStream inputStream = new FileInputStream("d:/123.txt")) {
Scanner scanner = new Scanner(inputStream);
String s = scanner.next();
System.out.println(s);
} catch (IOException e) {
e.printStackTrace();
}
- 写数据
try (OutputStream outputStream = new FileOutputStream("d:/123.txt")) {
// 字节流转字符流
PrintWriter writer = new PrintWriter(outputStream);
// PrintWriter 在写入的时候,并非直接写硬盘,为了效率,先将数据写入到一个由内存构成的“缓冲区”中
// 如果还没来得及将缓冲区中的数据写入硬盘,进程就结束了,那么缓冲区的数据就丢失了
// 解决办法:在合适的时机使用 flush 方法刷新缓冲区
writer.printf("123");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}