文章目录
File 类
想要使用代码来操作操作一个 文件/目录, 比如 创建, 删除, 重命名 等等操作, 在 Java 中提供了一个类: File. 在代码中, 可以使用 File 对象来表示一个 文件/目录 并对这个它进行基本的操作.
注意: File 类只针对 文件/目录 本身的操作, 不涉及文件内容读写操作
通过 File 类定义的对象是对一个 文件/目录 的抽象描述, 并不代表系统中真实存在这个 文件/目录
构造方法
方法 | 说明 |
---|---|
File(File parent, String child) | 根据 父目录 + 孩子文件路径, 创建一个 File 实例 |
File(String pathname) | 根据文件路径创建一个新的 File 实例, 路径可以是绝对路径或者相对路径 |
File(String parent, String child) | 根据 父目录 + 孩子文件路径, 创建一个新的 File 实例, 父目录用路径来表示 |
平时最常用的构造方法是第二种构造方法, 使用一个绝对路径或者相对路径来表示一个文件.
例如,
我需要表示我的 D 盘目录下的 test.txt 文件, 可以这么表示:
public static void main(String[] args) {
File file = new File("D/test.txt");
}
这样, 就能使用 file 对象来表示相应的文件了 (无论它是否存在)
方法
返回值类型 | 方法 | 说明 |
---|---|---|
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 对象, 自动创建一个空文件 |
boolean | delete() | 根据 File 对象, 删除该文件 |
void | deleteOnExist() | 根据 File 对象, 删除该文件, 删除动作会在 JVM 运行结束时进行 |
String[] | list() | 返回 File 对象所代表目录下的所有文件名 |
File[] | listFiles() | 返回 File 对象所代表的目录下所有的文件, 以 File 对象表示 |
boolean | mkdir() | 创建 File 对象所代表的目录 |
boolean | mkdirs() | 创建 File 对象所代表的多级目录 |
boolean | renameTo(File dest) | 进行文件的重命名 |
boolean | canRead() | 判断该用户是否对文件有可读权限 |
boolean | canWrite() | 判断该用户是否对文件有可写权限 |
IO 流
上面提到的是文件的基本操作, 而这里讲的是针对文件进行读写数据
IO 流, 也称为 输入/输出 流
- I: Input, 将数据从某个地方读取到内存的过程 (输入/读)
- O: OutPut, 将数据从内存写入到某个地方的过程 (输出/写)
在 Java 标准库中, 文件的流操作, 基本可以分为两个部分:
- 字节流 - InputStream / OutPutStream (操作二进制数据)
- 字符流 - Reader / Writer (操作文本数据)
但是 InputStream / OutputStream 以及 Reader / Writer 在 Java 中都是抽象类, 不能直接创建对象, 但是可以使用它们的实现类来进行文件的流操作:
- FileInputStream / FileOutputStream
- FileReader / FileWriter
虽然上述涉及到的类很多, 但是这些类的使用方式是非常固定的:
- 打开文件 (构造对象)
- 读/写 文件 (read/write)
- 关闭文件 (close)
字节流
FileInputStream
例如, 要对 D:/test.txt 这个文本文件进行读文件操作:
- 首先先要打开文件 (构造 FileInputStream 对象)
public static void main(String[] args) throws FileNotFoundException {
InputStream inputStream = new FileInputStream("D:/test.txt");
}
- 读取文件
FileInputStream 类提供了 read 方法, 以二进制的方式读取文件中的内容, read 方法有三种版本
方法 | 说明 |
---|---|
read() | 一次读取一个字节 |
read(byte[] b) | 将读取到的内容填充到 byte[] 类型的字节数组中 |
read(byte[] b, int off, int len) | 和第二个类似, 但是指定了数组填充的范围 |
现在 test.txt 文件中的内容为 hello, 使用 read() 方法一次一个字节的读取
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:/test.txt");
while (true) {
int b = inputStream.read();
if (b == -1) {
// 文件内容已经读取完毕
break;
}
System.out.println(b);
}
inputStream.close();
}
运行程序, 得到读取的结果为 hello 的 ASCII 码值:
但是一个字节一个字节读取的效率并不高, 想要提高读取的效率, 也可以用字节数组来接收读取到的字节, 一次读取的字节数就为字节数组的初始大小, 下面的代码中一次读取会读取 1024 个字节填充到字节数组中:
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:/test.txt");
while (true) {
byte[] buffer = new byte[1024];
int len = inputStream.read(buffer);
if (len == -1) {
// 文件读取完毕
break;
}
for (int i = 0; i < len; i++) {
System.out.println(buffer[i]);
}
}
inputStream.close;
}
最终读取到的结果也是一样的
FileOutputStream
对 test.txt 进行写文件的操作
- 打开文件 (构造对象)
public static void main(String[] args) throws FileNotFoundException {
OutputStream outputStream = new FileOutputStream("D:/test.txt");
}
- 对文件写入数据
注意: 如果按照上述的方法打开文件, 会在打开的时候先清空文件里面的内容, 如果不想清空文件的内容, Java 也提供了追加写的操作, 可以这样打开文件:
OutputStream outputStream = new FileOutputStream("D:/test.txt", true);
这样就能不清空文件的内容进行追加写操作了
写入 ASCII 码为 97, 98, 99, 100 的字符
public static void main(String[] args) throws IOException {
OutputStream outputStream = new FileOutputStream("D:/test.txt");
outputStream.write(97);
outputStream.write(98);
outputStream.write(99);
outputStream.write(100);
outputStream.flush();
outputStream.close();
}
运行程序, 可以看到, test.txt 文件中写入了 abcd 四个字母:
写入文件的时候, 不能保证文件实时的写入到硬盘中, 所以就需要在写入操作完成之后使用 flush 方法刷新缓冲区, 保证数据的实时写入
注意事项
根据上述两个实例中的代码, 可以看到, 使用 FileInputStream 或者 FileOutputStream 之后, 都要进行 close 操作, 这个操作是关闭文件的操作.
为什么要关闭文件呢? 在程序创建出流对象的时候, 就会在进程 PCB 中的文件描述符表中申请一个空间, 里面记录了打开文件的信息, 而关闭文件则会释放这块空间. 如果不手动释放这块空间, 并且程序长时间运行时需要不停地对很多文件进行操作, 文件描述符很快就会被占满, 被占满之后这个程序就打不开新的文件了, 这样程序就会抛出异常. 所以当文件使用完毕之后, 应该习惯性的关闭文件.
但是关闭文件这个操作往往会被遗忘, 所以 Java 中有一种更推荐的写法: 就是将打开文件的操作放入 try() 中, 这样虽然没有显示的写 close 方法, 但是实际上当 try 语句块执行结束的时候会自动执行 close 方法. 这种语法在 Java 中被称为 try with resources.
public static void main(String[] args) throws IOException {
try (OutputStream outputStream = new FileOutputStream("D:/test.txt")) {
outputStream.write(97);
outputStream.write(98);
outputStream.write(99);
outputStream.write(100);
outputStream.flush();
}
}
但是, 并不是随便拿个对象放进 try 中创建就能自动被释放, 只有实现了 Closeable 接口的类放入 try 的 () 中才会自动关闭.
字符流
FileReader
FileReader 跟 FileInputStream 的操作基本相似, 但是 FileReader 所读取的数据是以字符为单位的
当文件中的数据为 你好 的时候, 使用 FileReader 来进行读取:
public static void main(String[] args) throws IOException {
try (Reader reader = new FileReader("D:/test.txt")) {
while (true) {
int ch = reader.read();
if (ch == -1) {
break;
}
System.out.println("" + (char) ch);
}
}
}
运行程序, 可以看到 FileReader 的 read 方法可以正常读取中文字符:
当然, 使用字符数组是一种效率更高的方式:
public static void main(String[] args) throws IOException {
try (Reader reader = new FileReader("D:/test.txt")) {
while (true) {
char[] buffer = new char[1024];
int len = reader.read(buffer);
if (len == -1) {
break;
}
for (int i = 0; i < len; i++) {
System.out.println(buffer[i]);
}
}
}
}
当然, 结果也是一样的
FileWriter
用法跟 FileInputStream 大致相同, 但是 FileWriter 写入文件是以字符的形式写入
写入 hello world:
public static void main(String[] args) throws IOException {
try (Writer writer = new FileWriter("D:/test.txt")) {
// 也可以写入一个字符串
writer.write("hello world");
// 手动刷新缓冲区
writer.flush();
}
}
运行程序, 可以看到已经将 hello world 成功写入文件:
使用 Scanner 进行文件的读取
根据上面读取文件的例子来看, 使用 FileInputStream/FileReader 来读取文件非常的麻烦, Java 中提供了一个类: Scanner, 来搭配流对象进行使用
public static void main(String[] args) throws IOException {
try (InputStream inputStream = new FileInputStream("D:/test.txt");
Scanner scanner = new Scanner(inputStream)) {
while (scanner.hasNext()) {
String str = scanner.next();
System.out.println(str);
}
}
}
得到结果:
使用 PrintWriter 进行文件的写入
PrintWriter 类提供了 print, println, printf 等方法, 可以满足大部分日常的写入操作, 可以使写入变得更加容易, 甚至 FileOutputStream 也可以使用 PrintWriter 类来写入字符串等操作
public static void main(String[] args) throws IOException {
try (OutputStream outputStream = new FileOutputStream("D:/test.txt");
PrintWriter printWriter = new PrintWriter(outputStream)) {
printWriter.println("你好, PrintWriter");
}
}
也可以使用 PrintWriter 类来写入字符串等操作