Java中的文件操作
目录
1 文件的相关知识
1.1 文件是什么
1.2 文件的分类
1.3 文件路径
1.4 文件的存储
2 File类
2.1 File类的属性
2.2 File类的构造方法
2.3 File类中的方法
2.4 示例演示
3 流对象
3.1 字节流和字符流
3.2 InputStream 和 OutputStream
3.3 Reader 和 Writer
1 文件的相关知识
1.1 文件是什么
文件是计算机中用于存储和组织数据的基本单位,它可以包含各种类型的数据,如程序代码、文本文件、音频文件、视频文件和图像等。这些文件被存储再磁盘或者其他的存储介质上。
文件分为狭义上的文件和广义上的文件,狭义上的文件就是指存储在硬盘上的文件;广义上的文件是指操作系统进行资源管理的一种机制,很多软件/硬件资源,抽象为“文件”来进行表示。(我们在这里介绍的文件是狭义的文件)
注意:在我们的生活中,经常提到的“文件夹”是不是文件?答案是,是文件。文件夹其实是通俗的说法,专业术语应该叫“目录”。在“目录”下也存储着很多文件。
1.2 文件的分类
文件主要分为两大类:文本文件和二进制文件。
文本文件用于存储可读的字符信息;
二进制文件是以二进制形式进行存储的,例如图片、音频等。
判断一个文件是二进制文件还是文本文件最简单的办法就是,使用电脑中自带的记事本打开文件,如果打开是一些有意义的字符组成的文本(例如中文、英文等)就说明是文本文件,如果打开是乱码,就说明是二进制文件。
特殊地,我们平时使用的word文件,其实也是二进制文件,因为这个文件中不仅有文本内容,还有一些格式信息等。
1.3 文件路径
在一台计算机中,允许有多个同名的文件存在,但是这些文件存在的地址不同,那么如何区分这些文件呢?就使用文件的路径进行区分。
文件路径分为绝对路径和相对路径。
以test.txt文件为例:
绝对路径是从盘符开始,逐级表示出来的路径,例如C:\Users\Lenovo\Desktop\test.txt这个就是绝对路径。
相对路径是有一个目录作为基准的,例如以C:\Users\Lenovo\Desktop作为基准,那么用相对路径表示test.txt就是./test.txt,.表示当前目录;如果以C:\Users\Lenovo\Desktop\a为基准,那么用相对路径表示test.txt就是../test.txt,..表示上一级目录。
1.4 文件的存储
计算机中能存储信息的一共有三个地方:硬盘、内存、CPU寄存器
这三者的对比如下:
存储空间 | 访问速度 | 成本 | 持久化 | |
硬盘 | 很大 | 很慢 | 便宜 | 能够持久存储信息,断电后数据依然存在 |
内存 | 小 | 快 | 较贵 | 不能持久化存储信息 |
CPU寄存器 | 很小 | 很快 | CPU一般是在计算机中安装的,不单独售卖 | 不能持久化存储信息 |
文件一般是存储在硬盘中的
2 File类
在Java中,使用java.io.File这个类来对文件进行描述。下面我们对File类进行一个简单的介绍。
2.1 File类的属性
- public static final char pathSeparatorChar 依赖于系统的路径分隔符,char 类型的表示。此字符用于在座位路径列表给出的文件系列中分割文件名。在UNIX系统上,此字符为“:”在Windows系统上,它为“;”。
- public static final String pathSeparator 依赖于系统的路径分隔符,String类型的表示。
2.2 File类的构造方法
方法名称 | 说明 |
File(String child,File parent) | 根据父目录+孩子文件路径,创建一个新的File实例 |
File(String parent,String child) | 根据父目录+孩子文件路径,创建一个新的File实例,其中父目录用路径表示 |
File(String pathname) | 根据文件路径创建一个新的File实例,路径可以是绝对路径或者相对路径 |
最经常使用的构造方法是第三个,在下面的示例中我们也使用第三个构造方法创建对象。
2.3 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对象,自动创建一个空文件,创建成功之后返回 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() | 判断用户是否对该文件有可写权限 |
2.4 示例演示
用代码进行演示上面提到的方法(除canRead()和canWrite()外)
public static void main(String[] args) throws IOException {File file = new File("D:/JavaEE/test.txt");
// File file = new File("./test.txt");// 使用绝对路径时,这个方法返回的就是父目录文件路径 但是使用相对路径时,这个方法返回的就是 .System.out.println(file.getParent());// 返回 file 对象的文件名System.out.println(file.getName());// 使用绝对路径时,返回的也是绝对路径 使用相对路径的时候,返回的也是相对路径System.out.println(file.getPath());// 使用绝对路径时,返回的也是绝对路径 // 使用相对路径的时候,返回的是父目录的路径拼接file对象的相对路径System.out.println(file.getAbsolutePath());// 和 getAbsolutePath() 相似 但是是经过简化的System.out.println(file.getCanonicalPath());
}
// 运行结果
// 使用绝对路径
D:\JavaEE
test.txt
D:\JavaEE\test.txt
D:\JavaEE\test.txt
D:\JavaEE\test.txt
// 使用相对路径
.
test.txt
.\test.txt
D:\JavaEE\.\test.txt
D:\JavaEE\test.txt
// 首先 这个test.txt文件并不存在,所以这三个方法的返回值都是 false
public static void main(String[] args) {File file = new File("D:/JavaEE/test.txt");System.out.println(file.exists());System.out.println(file.isDirectory());System.out.println(file.isFile());
}// 进行创建之后 这三个方法的返回值分别为 true、false、true
public static void main(String[] args) throws IOException {File file = new File("D:/JavaEE/test.txt");// 创建文件System.out.println(file.createNewFile());System.out.println(file.exists());System.out.println(file.isDirectory());System.out.println(file.isFile());
}
public static void main(String[] args) {File file = new File("D:/JavaEE/test.txt");// 立即进行删除 并且删除成功后返回 trueSystem.out.println(file.delete());// file.deleteOnExit() 会先不进行删除,先继续执行后面的代码,在程序最后结束的时候进行删除
}
public static void main(String[] args) {// 针对目录才能进行 list 和 listFiles 操作File file = new File("D:/JavaEE");String[] list = file.list();// 返回的结果是类型为 String 的列表System.out.println(Arrays.toString(list));File[] files = file.listFiles();// 返回的结果是类型为 File 的列表System.out.println(Arrays.toString(files));
}// 运行结果
[.git, .gitignore, .idea, JavaEE, JavaEE.iml, out, README.md, src, test.txt]
[D:\JavaEE\.git, D:\JavaEE\.gitignore, D:\JavaEE\.idea, D:\JavaEE\JavaEE, D:\JavaEE\JavaEE.iml, D:\JavaEE\out, D:\JavaEE\README.md, D:\JavaEE\src, D:\JavaEE\test.txt]
ublic static void main(String[] args) {File file = new File("D:/JavaEE/hello");File file1 = new File("D:/JavaEE/abc/hello");// 不需要创建中间目录 直接可以进行创建// 结果是返回 trueSystem.out.println(file.mkdir());// 创建了中间目录 “abc” 之后进行创建“hello”// 结果是返回 trueSystem.out.println(file1.mkdirs());
}
public static void main(String[] args) {File file1 = new File("D:/JavaEE/test.txt");File file2 = new File("D:/JavaEE/test2.txt");// 返回trueSystem.out.println(file1.renameTo(file2));
}public static void main(String[] args) {File file1 = new File("D:/JavaEE/test2.txt");File file2 = new File("D:/JavaEE/hello/test2.txt");// 相当于把 test2.txt 这个文件 移动到了 hello 这个目录下System.out.println(file1.renameTo(file2));
}
3 流对象
Java中针对文件内容的操作主要是通过一组“流对象”来进行实现的。Java中提供了很多类来表示流对象,主要分为字节流和字符流。
3.1 字节流和字符流
字节流在读写文件的时候,是以字节为单位,主要是针对处理二进制文件的时候使用的,包括两个抽象类:InputStream和OutputStream。InputStream是从文件中读取数据,是输入;OutputStream是往文件中写入数据,是输出。
字符流在读写文件的时候,是以字符为单位,主要是针对处理文本文件的时候使用的,包括两个抽象类:Reader和Writer。Reader是从文件中读取数据,是输入;Writer是往文件中写入数据,是输出。
一个字符对应多少个字节是不确定的,这由编码方式决定。
3.2 InputStream 和 OutputStream
由于InputStream和OutputStream都是抽象类,所以创建对象的时候使用FileInputStream和FileOutputStream,这两个类继承于InputStream和OutputStream。
创建一个FileInputStream对象,在使用结束后需要进行关闭,这里我们使用try with resource来关闭字节流。
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表示已经全部读取结束 |
OutputStream将数据写入文件的方法有三个:
返回值 | 方法签名 | 说明 |
void | write(int b) | 将数据写入outputStream(这里的int是编码值) |
void | write(byte[] b) | 将b这个字符数组中的数据全部写入outputStream中 |
int | write(byte[] b,int off,int len) | 将b这个字符数组从off的位置开始,把数据写入到 outputStream 中,一共写len个 |
示例:
// test.txt 文件中的内容为 hello
public static void main(String[] args) throws IOException {File file = new File("D:/JavaEE/test.txt");try(InputStream inputStream = new FileInputStream(file)){while(true){int data = inputStream.read();if(data == -1){break;}System.out.println(data);// b 中能存下文件中的所有数据byte[] b = new byte[1024];int data = inputStream.read(b);if (data == -1){break;}System.out.println(data);System.out.println(Arrays.toString(b));// b中存不下文件中的数据,就会进行循环,直到data返回-1为止byte[] b = new byte[3];int data = inputStream.read(b);if (data == -1){break;}System.out.println(data);System.out.println(Arrays.toString(b));byte[] b = new byte[1024];// 从下标为5的位置开始写从文件中读到的数据int data = inputStream.read(b,5,7);if (data == -1){break;}System.out.println(data);System.out.println(Arrays.toString(b));}}
}
public static void main(String[] args) throws FileNotFoundException {File file = new File("D:/JavaEE/output.txt");// 默认是false 即在下一次写入数据时,会进行覆盖try(OutputStream outputStream = new FileOutputStream((file))){// true 则是在下一次写入数据时,之前写入的不会被覆盖
// try(OutputStream outputStream = new FileOutputStream(file,true)){outputStream.write(104);outputStream.write(101);outputStream.write(108);outputStream.write(108);outputStream.write(111);byte[] b = {104,101,108,108,111};outputStream.write(b);// 写入b数组中 从下标1开始 程度为3 的部分outputStream.write(b,1,3);} catch (IOException e) {throw new RuntimeException(e);}
}
3.3 Reader 和 Writer
Reader和Writer与InputStream和OutputStream类似,只不过读取和写入是以字符为单位的。
由于Reader和writer都是抽象类,所以创建对象的时候使用FileReader和FileWriter,这两个类继承于Reader和writer。
示例:
public static void main(String[] args) throws FileNotFoundException {File file = new File("D:/JavaEE/output.txt");try(Reader reader = new FileReader(file)){while(true){int data = reader.read();if(data == -1){break;}// 返回结果是十六进制System.out.printf("0x%x\n",data);}} catch (IOException e) {throw new RuntimeException(e);}
}
// 运行结果
0x4f60
0x597d
// 这两个十六进制的数字 在unicode编码中对应的是 你好 两个汉字
注意:但是如果使用字节流对该文件进行读取,会发现读取出来的是六个字节的数据。这是因为字节流读取到的是文件中的原始数据,在硬盘上保存的文件使用utf-8编码,就是6个字节。字符流在进行读取的时候,会根据文件的编码进行解析,读一次就会读到3个字节,将这三个字节组合起来发现是‘你’这个汉字,然后在unicode码表中找到‘你’对应的编码值返回。
public static void main(String[] args) throws IOException {File file = new File("D:/JavaEE/output.txt");try(Writer writer = new FileWriter(file)){writer.write("你好");}
}