这里介绍两类文件操作、三个文件类。包括文件系统操作(File类)、文件内容操作(操作字节流、操作字符流)
1.文件类File
1.1.认识File类
(1)什么是File类呢?其实就是可以操作文件的一个类。通过File类构造一个文件对象,此时就得到了一个文件对象的引用,通过该引用,可以得到该文件的路径、名字;判断该文件对象是文件目录还是目标文件
(2)构造方法
签名 | 说明 |
File(File parent,String child) | 根据父目录+孩子文件路径,创建一个新的File实例 |
File(String pathname) | 根据文件路径创建一个新的File实例,路径可以是绝对/相对路径 |
File(String parent,String child) | 根据父目录+孩子文件路径,创建一个新的File实例,父目录用路径表示 |
我们常用第二个,pathname就是一个文件路径,绝对路径或者相对路径都可以。
值得注意的点:以上创造的File对象,里面的文件路径,表示的是目录还是最终文件,无论在你的硬盘中是否存在,都不影响,都可以创造出来。
1.2.认识File类的方法
这个有什么用呢?就是在构建好一个文件对象之后,可以通过File类中的方法去操作文件。
(1)返回路径型
返回值类型 | 方法签名 | 说明 |
String | getParent() | 返回File对象的父目录文件路径 |
String | getName() | 返回File对象的纯文件名称 |
String | getPath() | 返回File对象的文件路径 |
String | getAbsolutePath() | 返回File对象的绝对路径 |
String | getCanonicalPath() | 返回File对象的修饰过的绝对路径 |
(2)返回boolean型
返回值类型 | 方法签名 | 说明 |
boolean | exists() | 判断File对象描述的文件是否真实存在 |
boolean | isDirectory() | 判断File对象代表的文件是否是一个目录 |
boolean | isFile() | 判断File对象代表的文件是否是一个普通文件 |
boolean | createNewFile() | 根据File对象,自带创建一个空文件。成功创建后返回true |
boolean | delete() | 根据File对象,删除该文件。成功删除后返回true |
boolean | mkdirs() | 创建File对象代表的目录,如果必要,会创建中间目录 |
boolean | renameTo(File dest) | 进行文件改名,也可以视为我们平时的剪切、粘贴操作 |
(3)其他类型
返回值类型 | 方法签名 | 说明 |
void | deleteOnExit() | 根据File对象,标注文件将删除,删除动作会到JVM运行结束时 |
String[ ] | list() | 返回File对象代表的目录下的所有文件名 |
File[ ] | listFiles() | 返回File对象代表的目录下的所有文件,以File对象表示 |
在标准库中,还有许多方法,这里就不一一展示了。
很多方法在后续也都会使用到,这里也不做使用的展示,我们接着看后续。
1.3.实例化文本对象
下面介绍三种创造File对象的方式,可以说是不同的情况创造File对象
(1)文件不存在-以相对路径
这个test666.txt文件在实例化对象时,是不存在的,所以最后一行是显示false。其他的路径是因为我们在创造的时候是以一个相对路径,相对路径的现在位置就是该.java文件的位置。
public static void main(String[] args) throws IOException {
File f = new File("./text666.txt");
System.out.println("该文件名字:"+f.getName());
System.out.println("这级目录的名字:"+f.getParent());
System.out.println("文件的路径:"+f.getPath());
System.out.println("文件的绝对路径:"+f.getAbsolutePath());
System.out.println("文件的绝对路径2:"+f.getCanonicalPath());//这个需要抛出异常
System.out.println("文件是否真是存在:"+f.exists());
}
(2)文件不存在--以绝对路径
(3)文件存在--以绝对路径
我们先在d盘上面创造一个文件
然后以创造文件类的方式打开
public static void main(String[] args) throws IOException {
File f = new File("d:/test999.txt");
System.out.println("该文件名字:"+f.getName());
System.out.println("这级目录的名字:"+f.getParent());
System.out.println("文件的路径:"+f.getPath());
System.out.println("文件的绝对路径:"+f.getAbsolutePath());
System.out.println("文件的绝对路径2:"+f.getCanonicalPath());//这个需要抛出异常
System.out.println("文件是否真是存在:"+f.exists());
}
然后以相对路径打开:
第一个绝对路径就是当前的基准路径(在当前项目运行代码)+相对路径拼接而成
绝对路径2就是:拼接后并进行了一些优化操作。
1.4.创造文件
如果文件不存在,我们创造文件要根据下面的方法
(1)相对路径创造文件
public static void main(String[] args) throws IOException {
File f = new File("./test222.txt");
f.createNewFile();//需要抛出异常
}
需要抛出异常的原因:(1)给出的路径非法(2)路径权限不足
(2)以绝对路径创造
public static void main(String[] args) throws IOException {
File f = new File("d:/tttttt.txt");
boolean bool = f.createNewFile();
System.out.println(bool);
}
程序运行后:
文件被真正的创造了出来,如果再运行一次代码,就是返回false,原因是该文件已经存在,无法再创造。
(3)判断文件是目录还是普通文件
public static void main(String[] args) throws IOException {
File f = new File("d:/tttttt.txt");
f.createNewFile();
System.out.println("文件是否真实存在:"+f.exists());
System.out.println("文件是否代表目录:"+f.isDirectory());
System.out.println("文件是否为普通问题:"+f.isFile());
}
1.5.删除文件
删除文件有两个方法,下面一一介绍如果使用,及使用后的结果。
(1)直接删除文件
public static void main(String[] args) throws IOException {
File f = new File("d:/tttttt.txt");
f.createNewFile();
boolean bool = f.delete();
System.out.println(bool);
}
文件已被删除。
(2)延迟删除
public static void main(String[] args) throws IOException {
File f = new File("d:/tttttt.txt");
f.createNewFile();
f.deleteOnExit();//等进程结束后再删除文件
System.out.println("进程结束前"+f.exists());
Scanner in = new Scanner(System.in);
in.next();//阻塞进程结束
System.out.println("进程结束后:"+f.exists());
}
当输入后,进程就会马上结束
文件也就不存在了。这就是延迟删除,当程序彻底结束后文件才会被删除。
接下来的部分是针对文件的内容进行操作。 操作文件内容的三个固定步骤:以二进制/文本方式打开文件--->读文件/写文件--->关闭文件
2.字节流
这样的意思就是:以二进制的方式打开文件,以字节为单位进行读取数据/写数据。
2.1.以字节流读取文件
所谓读取文件,就是读取文件中的数据,但是不会改变里面的内容
(1)打开文件
要读取数据,第一步就是打开文件。这里需要使用两个类,实例化好了对象,就相当于拿到了一个“流”对象,通过流,就可以随时读取文件数据。
InputStream file = new FileInputStream("d:/dog111.txt");//以二进制打开文件(文件必须存在)
使用到两个类:
InputStream//抽象父类
FileInputStream//子类
注意点:
1)文件路径代表的文件必须真实存在,否则会抛出异常
2)必须在方法后面声明异常
(2)读取文件内容
打开了文件,接下来就读取文件内容的操作,读取文件内容有下面三种方式:
返回值类型 | 方法签名 | 说明 |
int | read() | 读取一个字节的数据,返回-1代表读取结束(内容已读取完) |
int | read(byte[ ] b) | 最多读取b.length字节的数据到b中,返回时机读到的数量;-1代表读取完 |
int | read(byte[ ],int off,int len) | 最多读取len-off个字节到b中,从b的off位置开始存放;-1读取完 |
第一种读取方式:每次读取一个字节
public static void main(String[] args) throws IOException {
//1.打开文件
InputStream file = new FileInputStream("d:/dog111.txt");//以二进制打开文件(文件必须存在)
//2.读文件
while (true) {
int n = file.read();
if(n==-1) {
System.out.println("文件读取完毕");
break;
}
System.out.printf("%d ",n);
}
}
运行结果
文件内容:
代码解释:我们采取循环读取的方式进行读取,当读取到-1的时候,说明文件里面的内容都已经被读取完毕了,就可以结束循环。
第二种读取方式:每次读取一个字节数组的数据
public static void main(String[] args) throws IOException{
InputStream file = new FileInputStream("d:/dog111.txt");
byte[] bytes = new byte[1024];
while (true) {
int n = file.read(bytes);
if(n==-1) break;
for (int i = 0; i < n; i++) {
System.out.printf("%c ",bytes[i]);
}
System.out.println();
}
}
这种方法读取数据会比第一种快,而且效率也高,
第三种方式:特定场景使用
public static void main(String[] args) throws IOException{
InputStream inputStream = new FileInputStream("d:/dog111.txt");
byte[] bytes = new byte[1024];
while (true) {
int n = inputStream.read(bytes,0,1024);
if(n==-1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.printf("%c ",bytes[i]);
}
}
}
上述都只是打开了流和读取数据,并没有关闭流,下面介绍关闭流。
2.2.关闭流
第一种方式:
使用try finally,目的是防止程序发生特殊情况,导致没有关闭流。但是这样子冗余,不好看。
public static void main(String[] args) throws IOException{
InputStream inputStream = new FileInputStream("d:/dog111.txt");
try{
byte[] bytes = new byte[1024];
while (true) {
int n = inputStream.read(bytes,0,1024);
if(n==-1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.printf("%c ",bytes[i]);
}
}
}finally {
inputStream.close();
}
}
第二种方式:
public static void main(String[] args) throws IOException{
try (InputStream file = new FileInputStream("d:/dog111.txt")){
byte[] bytes = new byte[1024];
while (true) {
int n = file.read(bytes);
if(n==-1) break;
for (int i = 0; i < n; i++) {
System.out.print((char) bytes[i]+" ");
}
}
}
}
将打开流的操作,放在try中。当执行完后,会自动关闭流,也就不不需要在后面加上finally了
2.3.以字节流写文件
(1)第一次写
public static void main(String[] args) throws IOException{
//写文件
try(OutputStream outputStream = new FileOutputStream("d:/dog111.txt")) {
outputStream.write(97);
outputStream.write(98);
outputStream.write(99);
outputStream.write(100);
outputStream.write(101);
}
}
再写一次:
第二次写时会覆盖掉文件内的内容。
原因是:打开文件就会把文件原有的内容给覆盖掉。为了解决这个问题,可以使用追加的方式进行打开文件。
3.字符流
接下来的读取/写文件,都是以字节为单位的。
3.1.读文件
对于读文件,依旧是有三种方式,和上述的字节流类似
(1) 第一种方式:
public static void main(String[] args) throws IOException {
Reader reader = new FileReader("d:/dog111.txt");
while (true) {
int n = reader.read();
if(n==-1) break;
System.out.print((char)n+" ");
}
}
可以读取文字
当去read()的时候,读到的是三个字符的数据,但是返回的时候,会转化成两个字节去接收。
(2)第二种方式:
3.2.写文件
写文件,和上述是一样的。
如果直接打开文件,然后去写文件,就会将原有的内容覆盖,所以要使用追加的方式去写。
(1)不追加写
原有内容被覆盖了。也就是第一次写入的时候,接着往下写是不会被覆盖的。
(2)追加写