目录
一、认识文件
1、文件的概念
文件的概念分为狭义的和广义的:
狭义:存储在硬盘上的数据,以“文件”为单位,进行组织管理(文件夹也是一种特殊的文件)。
常见的文件有:音频、视频、图片、可执行程序、文本文件……
广义:操作系统在管理软硬件资源时,通常会把这些资源抽象成一个“文件”来进行管理。
例如:从键盘读取数据时,就是把键盘抽象成一个文件,读这个文件里的内容就可以获取到用户输入的内容。
本文中的文件指的是狭义的文件。
2、目录结构
当文件数量较多时,我们就需要对文件进行分层分类的管理——通过树形结构进行管理,每个用来存放管理信息的特殊文件就叫做“文件夹”,或者“目录”。
3、文件路径
路径是用来描述一个文件在电脑中的具体位置,分为以下两种:
绝对路径:描述一个文件在电脑中的具体位置的一串字符。
例如:
jconsole.exe的绝对路径就是: C:\Program Files\Java\jdk1.8.0_192\bin\jconsole.exe
其中,C: 叫做“盘符”,每个 \ 分割的部分,都是一个目录。
相对路径:表示相对路径的前提得有一个“基准路径”(也叫工作路径),从基准路径到目标文件的路径就是相对路径。
例如:
例1:从下图这个基准路径 C:\Program Files\Java到jconsole.exe文件的
相对路径:./jdk1.8.0_192/bin/jconsole.exe
例2:从下图这个基准路径 C:\Program Files\Java\jdk1.8.0_192\jre\bin到jconsole.exe文件的
相对路径:../../bin/jconsole.exe
注意:
(1) 两个目录之间既可以使用 \ 来分割,也可以使用 / 来分割,推荐使用 / 。
(2) . 表示当前目录。
(3) ..表示返回上一级目录。
例2中描述相对路径时,第一次..表示返回到jre目录,第二次..表示返回到jdk1.8.0_192目录。
(4) 使用IDEA执行代码时,IDEA打开的项目所在的目录就是基准目录(工作目录)。
二、文件操作
1、操作文件系统
文件系统是操作系统中管理文件的核心功能。
Java中通过File类来对一个文件进行抽象的描述,通过对象可以进行新增文件、新增目录、删除文件、文件重命名等操作。
File类的构造方法:
File(File parent,String child) | 根据File类型的父目录和孩子文件路径,创建一个File对象 |
File(String pathname) | 根据文件路径创建一个File对象(相对路径或绝对路径都可以) |
File(String parent,String child) | 根据父目录的路径和孩子文件路径,创建一个File对象 |
上表中三个构造方法的参数本质上都是用来描述一个文件的路径,一般使用第二个。
File类的方法:
方法名 | 说明 |
getParent() | 获取File对象的父目录文件的路径 |
getName() | 获取File对象的文件名 |
getPath() | 获取File对象的文件路径 |
getAbsolutePath() | 获取File对象的绝对路径 |
getCanonicalPath() | 获取修饰过的File对象的绝对路径 |
exists() | 判断File对象描述的文件是否存在于电脑中 |
isDirectory() | 判断File对象描述的文件是否是一个目录 |
isFile() | 判断File对象描述的文件是否是一个普通文件 |
createNewFile() | 根据File对象的描述,创建一个空文件,创建成功返回true |
delete() | 根据File对象的描述,删除该文件,删除成功返回true |
deleteOnExit() | 进程结束时,根据File对象的描述,删除该文件 |
list() | 返回File对象代表的目录下的所有文件名,返回值是String[]类型 |
listFiles() | 返回File对象代表的目录下的所有文件名,返回值是File[]类型 |
mkdir() | 创建File对象代表的目录 |
mkdirs() | 创建File对象代表的多层目录 |
renameTo(File dest) | 更改文件名 |
canRead() | 判断用户对文件是否有可读权限 |
canWrite() | 判断用户对文件是否有可写权限 |
注意:File对象描述的文件不一定真实存在。
2、文件内容的读写
我们把读写文件的操作称为输入和输出:
输入:把硬盘的数据读取到CPU上。
输出:把CPU的数据写回到硬盘中。
Java标准库中提供了一组类,来完成文件的读写操作,这些类按照不同的特点可以分为:
字节流:以字节为基本单位,每次至少读取一个字节的内容,适用于二进制文件。
字节流中用InputStream类表示输入流,OutputStream类表示输出流,这两个类都是抽象类。
字符流:以字符为即办单位,一个字符至少是一个字节,适用于文本文件。
字符流中用Reader类表示输入流,Writer类表示输出流,这两个类也都是抽象类。
2.1、InputStream
InputStream的方法:
返回值类型 | 方法名 | 说明 |
int | read() | 读取一个字节的数据,返回-1表示全部读完了 |
int | read(byte[] b) | 把读取的数据到b数组中,返回-1表示全部读取完了 |
int | read(byte[] b,int off, int len) | 读取len个字节数据,从b的off下标开始存放,返回-1表示读完了 |
void | close | 关闭字节流 |
InputStream是一个抽象类,无法实例化对象,所以我们要用它的具体实现类,而它的实现类由很多个,读文件时我们使用FileInputStream。
FileInputStream的构造方法:
方法名 | 说明 |
FileInputStream(File file) | 使用File对象构造文件输入流 |
FileInputStream(String pathname) | 使用文件路径构造文件输入流 |
注意:
创建出一个流对象,就会打开相应的文件,操作完文件之后,需要使用close()方法关闭文件。
如果持续多次打开文件不关闭,可能会造成文件资源泄漏的问题,导致后续无法再打开文件。
每个线程都是一个PCB,而多个PCB共用一个文件描述符表,每打开一个文件,就相当于在表里创建了一个项(给表里增加了一个元素),关闭一个文件,就会释放对应的项。如果打开文件后没有关闭文件,对应的项就会在表中占着位置,当表里的位置被占满,就会出现文件资源泄漏问题。
为了保证程序一定能够执行到关闭文件的操作,我们可以使用try-finally语句:
也可以使用try with resources,把要关闭的对象写到try后面的()中,当try中的代码执行完后,就会自动调用对应的close()方法,多个对象之间用;分隔开~
Reader的用法和InputStream类似~
2.2、OutputStream
写文件时,我们使用OutputStream的具体实现类:FileOutputStreatm
OutputStream中的方法:
返回值类型 | 方法 | 说明 |
void | write(int b) | 写入一个字节的数据到文件中 |
void | write(byte[] b) | 把b数组的内容都写入到文件中 |
int | write(byte[] b,int off,int len) | 从b数组的off下标开始,写入len个字节的数据到文件中 |
void | close() | 关闭文件 |
void | flush() | 把缓冲区中遗留的数据,刷新到文件中 |
代码示例:
运行结果:
注意:使用OutputStream打开文件时,会把文件中的内容清空。
Writer的用法和OutputStream类似~
3、使用Scanner读文件
针对文本文件,使用字符流的时候,也可以使用我们熟悉的Scanner来进行读操作~
示例:读取test.txt文件中的字符
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream("./Directory/test.txt")) {
Scanner scanner = new Scanner(inputStream);
while (scanner.hasNext()){
System.out.println(scanner.next());
}
} catch (IOException e) {
e.printStackTrace();
}
}
代码运行结果:
4、使用PrintWriter写文件
针对文本文件,我们还可以使用PrintWriter来写文件,简化开发~
示例:
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("./Directory/test.txt")) {
PrintWriter writer = new PrintWriter(outputStream);
writer.println("java");
writer.print('i');
writer.print('s');
writer.println();
writer.printf("%d\n",1);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
代码运行结果: