文件操作,是属于操作系统层面提供的一些API.
不同的操作系统,提供的API是不一样的~
Java作为一个跨平台的语言,为了统一代码,就在JVM中把不同系统的操作文件的的API进行了封装~ Java 就可以使用Java中的库的代码来操作文件了~~
Java 中通过 java.io.File
类来对一个文件(包括目录)进行抽象的描述。
注意:有 File 对象,并不代表真实存在该文件
IO:
- I :input 输入
- O output 输出 输入输出的方向,就是以CPU/内存为中心 ~
File类的属性:
修饰符及类型 | 属性 | 说明 |
---|---|---|
static String | pathSeparator | 依赖于系统的路径分隔符,String 类型的表示 |
static char | pathSeparator | 依赖于系统的路径分隔符,char 类型的表示 |
File类的构造方法:
签名 | 说明 |
---|---|
File(File parent, String child) | 根据父目录 + 孩子文件路径,创建一个新的 File 实例 |
File(String pathname) | 根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径 |
File(String parent, String child) | 根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用路径表示 |
File类的方法:
1️⃣ 创建File对象
File(String pathname)
创建File对象的时候,就可以指定一个路径(绝对/相对均可)
File f = new File("./test.txt");
2️⃣获取File对象父目录文件路径
f.getParent();
3️⃣获取File对象的纯文件名字
f.getName();
4️⃣获得File对象的相对路径
f.getPath();
5️⃣获得File对象的绝对路径
f.getAbsolutePath();
6️⃣返回 File 对象的修饰过的绝对路径
System.out.println(f.getCanonicalPath());
依次运行的结果:
7️⃣如果输入路径下的文件不存在,可以把文件创建出来:
8️⃣删除文件
createNewFile
创建空文件
delete
删除文件
deleteOnExit
进程退出时删除文件
重命名
public class TestDemo5 {
//重命名
public static void main(String[] args) {
File srcFire = new File("aaa.txt");
File destFire = new File("bbb.txt");
srcFire.renameTo(destFire);
}
}
1️⃣1️⃣显示目录内容
public class TestDemo6 {
//显示目录内容
public static void main(String[] args) {
File f = new File("./testDir");
String[] results = f.list();
System.out.println(Arrays.toString(results));
}
}
方法总结:
修饰符及返回值类型 | 方法签名 | 说明 |
---|---|---|
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 对象代表的目录下的所有文件,以 File 对象表示 |
boolean | mkdir() | 创建 File 对象代表的目录 |
boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目录 |
boolean | renameTo(File dest) | 进行文件改名,也可以视为我们平时的剪切、粘贴操作 |
boolean | canRead() | 判断用户是否对文件有可读权限 |
boolean | canWrite() | 判断用户是否对文件有可写权限 |
上述的文件操作,主要都是在操作 “文件系统” (操作系统中管理文件的核心功能)
新增文件/删除文件/新增目录/列出目录内容/重命名/获取路径…
针对文件内容操作,涉及到的关键操作,就是 读文件 和 写文件 ~
流(stream
)
把读写文件操作,比喻成"水流"
水的特点,流动起来,绵延不断~
Java
就在"流"的概念上,提供了一组类,完成读写文件的操作!
这组类,再按照不同的特点,再进行细分
- 字节流(以字节为基本单位),适用于二进制文件
InputStream
OutputStream
(父类)- 字符流(以字符为基本单位),适用于文本文件
Reader
Winter
(父类)
标准库中,给这些父类还提供了各种子类的实现,来适应不同场景下的读写操作!!
InputStream
要在构造方法中,确定一个文件~
InputStream inputStream = new FileInputStream("aaa.txt");
InputStream
方法:
修饰符及返回值类型 | 方法签名 | 说明 |
---|---|---|
int | read() | 读取一个字节的数据,返回 -1 代表已经完全读完了 |
int | read(byte[] b) | 最多读取 b.length 字节的数据到 b 中,返回实际读到的数量;-1 代表以及读完了 |
int | read(byte[] b,int off, int len) | 最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返回实际读到的数量;-1 代表以及读完了 |
void | close() | 关闭字节流 |
read()
:read()
一次读一个字节,读出的结果作为read
的返回值~read(byte[] b)
把读到的内容放到参数字节数组b
中.read(byte[] b,int off, int len)
把读到的内容放到参数字节数组b中,从off
开始放,放len
长度.b
用来存放方法读取到的结果b
被称为 “输出型参数”代码演示:
InputStream
是一个抽象类,不能实例化!
public class TestDemo7 {
public static void main(String[] args) throws IOException {
// 点进去发现是抽象类,只能实例化它的子类,
// 这个实例化过程就相当于打开文件!!类似于C中的fopen,想操作就得先打开!
InputStream inputStream = new FileInputStream("bbb.txt");
while (true) {
//一次读一个字节
int b = inputStream.read();
//文件读完了,返回值为-1
if(-1 == b) {
break;
}
System.out.print((char)b);
}
inputStream.close();
}
}
Scanner()
:InputStream
是字节流,Scanner
在InputStream
包装出了一个字符流import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class Demo11 {
//使用Scanner进行读操作
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("./bbb.txt");
//给Scanner传入一个流对象
Scanner scanner = new Scanner(inputStream);
while (scanner.hasNext()) {
System.out.println(scanner.next());
}
inputStream.close();
}
}
代码演示:
public class Demo8 {
//进行写文件
public static void main(String[] args) throws IOException {
OutputStream outputStream = new FileOutputStream("./bbb.txt");
//a.b.c的ascii 码
outputStream.write(97);
outputStream.write(98);
outputStream.write(99);
outputStream.close();
}
}
❗❗注意:
使用OutputStream
写文件的时候,只要打开文件成功,就会把原有文件清空
PrintWriter()
:import java.io.*;
public class Demo12 {
//使用Pirntf来进行写操作
public static void main(String[] args) throws IOException {
OutputStream outputStream = new FileOutputStream("./bbb.txt");
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println();
printWriter.printf("a = %d\n",10);
//刷新缓冲区
printWriter.flush();
outputStream.close();
}
}
public class Demo9 {
//使用字符流读文件
public static void main(String[] args) throws IOException {
Reader reader = new FileReader("./bbb.txt");
while (true) {
int ret = reader.read();
if(-1 == ret) {
break;
}
char a = (char) ret;
System.out.println(a);
}
reader.close();
}
}
我们使用字节流的时候,ret
存的是ascii码,而使用字符流,ret
存的就是字符本身了~
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class Demo10 {
//使用字符流写文件
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("./bbb.txt");
writer.write("hello yuanyuan");
writer.close();
}
}
❓为什么文件操作一定要进行close
?
每个进程都对应着PCB
(可能是多个)
PCB
里面有一个字段,叫做文件描述表 ~~(同一个进程里,多个PCB
共同使用同一份文件描述符表的)
这个文件描述表相当于是一个数组/顺序表. 进程每次打开一个文件,都会在这个表里,创建一个项~
这一项就表示一个文件!
如果关闭一个文件,就会把表里的对应项给释放掉~
如果不关闭,意味着这个表项就在这里占着位置,如果你持续打开文件,并且从来不关,此时就会导致表项被耗尽(最大长度是有上限的),最终导致后续再打开文件,就会打开失败!
这种严重的问题,就叫做文件资源泄露 !!!!(比程序崩溃还要严重!!) |
JVM有自动释放的策略,对应的流对象如果被GC销毁了,是会自动关闭对应的文件(释放文件描述符表元素的)
但是这件事不一定会发生,如果代码写的有问题,仍然保持着流对象的引用,不触发GC,文件就得始终打开状态~
但如果写了close()
也有可能会发生文件资源泄露的问题!
比如:
这个代码如果在close()
前面抛出异常了,那么close就执行不到了!!!客户端不害怕这种资源泄露问题(客户端程序用一会儿就关了)
服务器害怕这种问题(服务器需要长期运行)
保证close()
一定会生效的办法是:
try finally
:close()
都会执行到~try-with-resources
把要关闭的对象写到try()
里,当try
结束,就会自动的调用到对应对象的close
方法!!而且支持一个()
放多个对象,多个对象的创建之间使用 ;
分割就行了~