目录
什么是文件?
文件的分类
文件的路径表示
Java对于文件的操作
File中的一些普通方法
Java对文件内容的操作(读和写)
文件操作字节流
FileInputStream(文件输入流)(读)继承自InputStream
read方法
read无参数
read有参数(byte数组)
FileOutputStream(文件输出流) (写)继承自OutputStream
write方法
close方法
优雅的close写法
close放到finally中
Java中更优雅的写法:(将对象写在try的括号里面)
文件操作字符流
FileReader(输入)读 继承自 Reader
FileWriter(输出)写 继承自 Writer
狭义的文件指的是计算机硬盘上的文件和目录。
比如:
广义的文件是指计算机中的软硬件资源。
文件可以分为两种类型,一种是文本型,一种是二进制型。
如何判断是什么类型的文件呢?可以使用记事本,把文件拖到记事本里面,如果能够看懂就是文本的,如果出现有乱码就属于二进制类型的文件
文件的路径表示可以分为两种,一种是绝对路径,一种是相对路径。
比如Tencent这个文件夹,他的绝对路径就是C:\temp\Tencent
需要注意的是Windows当中文件路径可以使用\(反斜杠)和/(正斜杠)来作为分隔符,但是在写代码的时候我们建议还是使用/正斜杠来作为分隔符,因为\反斜杠容易被当成转义字符。
如果我们当前在C:\temp下,那么Tencent的相对路径就是./Tencent。
./表示当前目录(也就是C:\temp)
当我们进入到Tencent目录下,此时Tencent的相对路径就变成了../Tencent
../是上一级目录的意思
Java对于文件的操作分为两类:
1、对文件的系统操作(比如删除,创建,重命名等等)
2、对文件内容的操作(读和写)
Java对文件的操作主要是用到File这个类
File这个类有三个构造方法:
其中最常用的是File(string pathname)这个方法。
File file=new File("c:/testDemo");
需要注意的是,file中的路径不代表这个文件是否存在,如果不存在,也不会进行创建文件。
创建文件需要掉用mkdir方法来创建,如果文件已经存在,再次是用mkdir是不会影响原来的文件的,也不会重新建,也不会覆盖。
修饰符及返回 值类型 |
方法签名 | 说明 |
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() | 判断用户是否对文件有可写权限 |
这些方法也不需要背,用的时候找到就可以了。
下面是一些示例:
package io;
import java.io.File;
import java.io.IOException;
public class IODemo1 {
public static void main(String[] args) throws IOException {
File file=new File("c:/testDemo");//file对象
System.out.println(file.getName());//获取到文件的名称
System.out.println(file.getPath());//获取到文件的路径
System.out.println(file.isFile());//判断文件是否是普通文件,而不是目录
System.out.println(file.getParent());//获取file对象的父目录路径
System.out.println(file.getAbsolutePath());//获取绝对路径
System.out.println(file.getCanonicalPath());//获取修饰过后的绝对路径
System.out.println(file.exists());//判断当前文件是否存在
if(!file.exists()){
//如果文件不存在,就进行文件的创建
file.mkdir();
}
}
}
我们可以看到,之前没有的文件,现在可以在该目录下找到,说明已经创建了。
需要注意的是我们使用IDEA的时候,如果我们创建的文件是相对路径的话,那么该文件的父路径就是项目所在的路径:
package io;
import java.io.File;
public class IDDemo2 {
public static void main(String[] args) {
File file=new File("./IOTest");
file.mkdir();//mkdir只能用来创建一级目录
}
}
当我们需要创建多级目录的时候,就不能使用mkdir方法,而是使用mkdirs()。
比如我们要在IOTestDemo1目录下面再创建个aaa文件:
package io;
import java.io.File;
public class IDDemo2 {
public static void main(String[] args) {
File file=new File("./IOTestDemo1/aaa");
file.mkdirs();//mkdirs()用来创建多级目录
}
}
文件重命名使用 renameTo(File dest)方法,file. renameTo(File dest)表示把file文件重命名为dest文件。这里我们把IOTest重命名为IOTestDemo2:
package io;
import java.io.File;
public class IODemo3 {
public static void main(String[] args) {
File file=new File("./IOTest");
File dest=new File("./IOTestDemo2");
System.out.println(file.renameTo(dest));//修改成功返回true,修改失败返回false
}
}
注意:new 出来的file对象只是一个对象,并不是文件的实体,需要我们自行去创建文件实体。创建文件时需要我们使用mkdir方法来创建,如果我们的文件已经存在,使用mkdir方法后不会出现创建该文件,也不会对原文件进行覆盖。
首先我们要对输入流和输出流有个宏观的认识:
输入输出流构成我们的IO.
从硬盘读取数据到内存中的流,就是输入流-------------->FileInputStream
从内存中读取数据到硬盘中的流,就是输出流 ------------->FileOutputStream
所谓的输入输出都是相对于应用程序说的,朝向应用程序的是输入方向,远离应用程序的是输出发方向。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class IODemo2 {
public static void main(String[] args) throws FileNotFoundException {
//FileInputStream构造方法中字符串可以是绝对路径。也可以是相对路径,这里采用相对路径
FileInputStream fileInputStream=new FileInputStream("TestDemo1");//文件如果不存在就会抛出FileNotFoundException
}
}
read无参数版本一次读一个字节,返回类型为int,每次读取文件中一个字节,读取其ASCLL码的值,当所有数据读取完毕的时候,返回-1.(作为读取结束的标志)
import java.io.*;
public class IODemo3 {
public static void main(String[] args) throws IOException {
//字节流
InputStream inputStream=new FileInputStream("test.txt");
//进行读操作
while (true){//一般读取文件的条件
int b= inputStream.read();//b存放读取的ascll码
if(b==-1){
//读取完毕
break;
}
//读取打印
System.out.println(""+(byte)b);
}
inputStream.close();
}
}
这个版本的read和无参数read的区别在于这个是一次读取 一个数组长度的字节,并且将读取到的ascll存放到数组中,如果读取完毕所有的数组,同样放回-1,没有读取完的时候就是读取到了多少个字节就返回多少,需要注意的是,使用数组的时候,如果数组一轮读取被装满,下一轮读取的时候就会将数组中存放的数据进行覆盖,所以要及时使用数组中存放的数据。这个数组在这里也是起到了缓冲区的作用。
import java.io.*;
public class IODemo3 {
public static void main(String[] args) throws IOException {
//字节流
InputStream inputStream=new FileInputStream("test.txt");
//进行读操作
while (true){
byte[] buffer=new byte[1024];
int len=inputStream.read(buffer);//输出型参数
System.out.println("len = "+len);//一次读一个数组
if(len==-1){
//读取完毕
break;
}
//读取的结果在buffer数组当中
for (int i = 0; i
read有参数版本的使用效率要比无参数版本要高,因为有参数版本使用数组,一次读取一个数组长度的数据,而无参数版本则一次读取一个字节,这样,同一套数据算下来,无参数read的io操作次数就会比较多,而read数组版本就io操作的次数就会相对减少很多,所以有参数版本可以有效的提高我们的io的一个效率。
这个是文件专门把数据从内存中写到我们的硬盘上的一个操作。
也是需要在构造方法中指明我们需要操作的文件。
OutputStream outputStream=new FileOutputStream("test.txt");
需要注意的是,使用构造方法时的文件,如果是目录下不存在的,就会自动创建出该文件。
所以要确保目录下的文件是存在的,再进行使用。
和FileInputStream一样,这个方法也是存在多种版本的:
只不过这里是写(输出),FileInputStream那里是读(输入)。
使用write需要注意的是,如果write之前,文件中已经存在数据了,那么使用write会清空原有的数据再进行写操作。
close方法就是用来关闭当前的“流”所使用的。
那么为什么要进行关闭“流”的操作?主要是进程的一个关系。我们都知道进程中有个非常重要的东西,那就是文件描述符表,这个东西记录了进程打开了哪些文件。在一个进程当中,所有的线程共用一个文件描述符表,而文件描述符表就类似数组一样,它的大小是及其有限的,一旦装满就会出现问题,当装满后就无法进行文件的打开了,所以我们需要进行一个释放,而close方法就是对该表项的一个释放操作。
也有人会说这个可以解除JVM的GC进行回收,但是GC的回收是不准确的,你不知道他什么时候进行回收,很有可能你打开了很多文件后,文件描述符表已经满了,GC还没有来得及回收的情况。所以我们需要手动进行close操作。
把close操作放到finally中进行。
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class IODemo5 {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream=null;
try {
fileOutputStream=new FileOutputStream("test.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
finally {
fileOutputStream.close();
}
}
}
import java.io.FileOutputStream;
import java.io.IOException;
public class IODemo6 {
public static void main(String[] args) throws IOException, InterruptedException {
try ( FileOutputStream fileOutputStream=new FileOutputStream("test.txt")){
fileOutputStream.wait(99);
}//这样写会进行自动释放
}
}
注意不是所有的对象都可以,一定要实现Closeable接口的才可以这样操作。
上述的FileInputStream和FileOutputStream都是字节流的,这里再介绍字符流的操作
FileReader是以字符为单位进行读取的。
FileReader fileReader=new FileReader("test.txt");
read方法和字节流的差不多,只是这里是以字符为单位读取的。
import java.io.FileReader;
import java.io.IOException;
public class IODemo7 {
public static void main(String[] args) throws IOException {
FileReader fileReader=new FileReader("test.txt");
while (true){
int b=fileReader.read();
if(b==-1){
break;
}
System.out.println("读取的值为:"+(char) b);
}
}
}
FileWriter是以字符为单位进行输出的(写)
其中的write方法和字节流的也差不多
需要注意的是这里的write,如果参数是数字,写入到文件中就会写入对应数字ascll的字符。,如果是单引号中的字符,就直接写入单引号中的字符。
使用完write方法后需要使用一个flush方法,因为使用write方法后相当于把数据写入到缓冲区当中了,没有真正的写到硬盘,此时调用flush方法确保数据写入到硬盘之中。