【JavaEE】文件操作和IO
- 一、认识文件
-
-
- [1.1 狭义和广义的文件概念](#1.1 狭义和广义的文件概念)
- [1.2 文件路径](#1.2 文件路径)
- [1.3 文件的分类](#1.3 文件的分类)
-
- [二、Java 中操作⽂件](#二、Java 中操作⽂件)
-
-
- [2.1 File类](#2.1 File类)
- [2.2 代码演示](#2.2 代码演示)
-
- [三、文件内容的读写 ------ 数据流](#三、文件内容的读写 —— 数据流)
- 四、实战演示
-
-
- [4.1 查找删除文件](#4.1 查找删除文件)
- [4.2 普通文件的复制](#4.2 普通文件的复制)
- [4.3 文件内容查找](#4.3 文件内容查找)
-
博客结尾附有此篇博客的全部代码!!!
一、认识文件
1.1 狭义和广义的文件概念
- 狭义的文件:通常指存储在磁盘中的普通文件,是由字节(或字符)序列构成的数据集合,以一定的结构保存在存储介质中(如 FAT、ext4 等文件系统)。
- 广义的文件:概念则更为广泛,不仅包括磁盘上的普通文件,还涵盖了其他多种资源。例如:
1.设备文件:包括字符设备和块设备,代表硬件设备(如键盘、显示器、硬盘等)
2.目录:存储文件名和相关元数据的特殊文件
3.虚拟文件:如 Linux 系统中 /proc 下的伪文件,用于显示内核和进程状态。
4.其他资源:在某些情况下,网卡、声卡等外设也可以被视为文件
1.2 文件路径
绝对路径 :绝对路径是从文件系统的根目录(通常是 / 或 C:\)开始的完整路径,它唯一地标识了文件或目录的位置。
相对路径:相对路径是从当前工作目录(Current Working Directory,CWD)开始的路径,它描述了从当前目录到目标文件或目录的相对位置。
1.3 文件的分类
文本文件(以记事本打开可以看懂的):是由可打印字符(如字母、数字、标点符号等)组成的文件,通常以人类可读的形式存储数据。
内容表示:文件内容以字符编码(如ASCII、UTF-8、GBK 等)存储,每个字符占用一个或多个字节。
二进制文件(以记事本打开时乱码):二进制文件是以二进制形式存储数据的文件,文件内容可以是任意字节序列,不一定对应可打印字符。内容表示:文件内容以字节序列存储,可以包含任何数值(0-255),不局限于字符编码。
二、Java 中操作⽂件
2.1 File类
Java中将文件的操作封装在File类中。(创建目录、删除文件、重命名文件等...)
属性:
构造方法:
File 的构造方法,能够传入一个路径,来指定一个文件,这个路径可以是绝对路径也可以是相对路径,构造好对象之后,就可以通过这些方法,来完成一些具体的功能了
方法:
2.2 代码演示
在Java代码编写的时候不能用windows默认的分隔符:"/"斜杠
idea中用斜杠可能会被JVM识别成转义字符
我们可以用反斜杠""或者"//"来表示。
java
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
// File file=new File("D:Project/java/java-biog" +
// "/Day20250312/IO测试文件.txt");
File file = new File("./IO测试文件.txt");
System.out.println("File 对象的⽗⽬录⽂件路径:"+file.getParent());
System.out.println("FIle 对象的纯⽂件名称:"+file.getName());
System.out.println("File 对象的⽂件路径:"+file.getPath());
System.out.println("File 对象的绝对路径:"+file.getAbsolutePath());
System.out.println("File 对象的修饰过的绝对路径:"+file.getCanonicalPath());
System.out.println("File 对象描述的⽂件是否真实存在:"+file.exists());
System.out.println("File 对象代表的⽂件是否是⼀个⽬录:"+file.isDirectory());
System.out.println("File 对象代表的⽂件是否是⼀个普通⽂件:"+file.isFile());
}
}
运行结果:
createNewFile()和delete():
java
public class Demo2 {
public static void main(String[] args) throws IOException {
File file=new File("D:/Project/java/java-biog/" +
"Day20250312/hello.txt");
if(!file.exists()){
boolean file1=file.createNewFile();
System.out.println("file文件创建成功:"+file1);
}
System.out.println(file.delete());
}
}
deleteOnExit():
java
public class Demo3 {
public static void main(String[] args) throws IOException, InterruptedException {
File file=new File("D:/Project/java/java-biog/" +
"Day20250312/hello.txt");
if(!file.exists()){
boolean file1=file.createNewFile();
System.out.println("file文件创建成功:"+file1);
}
Thread.sleep(6000);
file.deleteOnExit();
}
}
list()和listFiles():
java
public class Demo4 {
public static void main(String[] args) {
File file=new File("D:/Project/java/java-biog/" +
"Day20250312");
System.out.println(Arrays.toString(file.list()));
System.out.println(Arrays.toString(file.listFiles()));
}
}

mkdir()和mkdirs():
java
public class Demo5 {
public static void main(String[] args) {
File file1=new File("D:/Project/java/java-biog/" +
"Day20250312/hello1");
boolean f1=file1.mkdir();
System.out.println(f1);
File file2=new File("D:/Project/java/java-biog/" +
"Day20250312/hello2/aaa/bbb/ccc");
boolean f2=file2.mkdirs();
System.out.println(f2);
}
}

mkdirs():
mkdir():
renameTo(File dest)、canRead()和canWrite():
java
public class Demo6 {
public static void main(String[] args) {
File file1=new File("D:/Project/java/java-biog/" +
"Day20250312/IO测试文件.txt");
System.out.println(file1.renameTo(new File("D:/Project/java/java-biog/" +
"Day20250312/IO.txt")));
System.out.println(file1.canRead()); // 判断用户是否对文件有可读权限 false
System.out.println(file1.canWrite()); // 判断用户是否对文件有可写权限 false
}
}
三、文件内容的读写 ------ 数据流
文件内容的读写涉及的操作:
打开文件 --》读文件(写文件) --》关闭文件
输入:内存 --》硬盘
输出:硬盘 --》内存
3.1 字节流和字符流
字节流:主要针对二进制文本,是以字节为单位进行读写的
- InputStream:字节输入流的超类,用于从源读取字节。
- OutputStream:字节输出流的超类,用于向目标写入字节。
字符流:主要针对二进制文本,是以字节为单位进行读写的
- Reader:针对文本文件,是以字符为单位进行读写的
- Writer:字符输出流的超类,用于向目标写入字符。
以上面两个文件给大家演示:
字节流
InputStream :
InputStream是一个抽象类,要使用还需要实现具体的类(FileInputStream)。
java
public class Demo8 {
public static void main(String[] args) throws IOException {
FileInputStream fis1=null;
try{
fis1 = new FileInputStream("D:/Project/java/java-biog/" +
"Day20250312/IO测试文件目录/测试图片.png");
while(true){
int ch = fis1.read();
if(ch == -1){
break;
}
System.out.println(ch);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally{
fis1.close();
}
}
}
改进之后,代码显得不美观!
Java 中提供了一个语法,try with resourcestry ( )
java
public class Demo9 {
public static void main(String[] args) throws FileNotFoundException {
try(InputStream inputStream=new FileInputStream("D:/Project/java/java-biog/" +
"Day20250312/IO测试文件目录/测试图片.png")){
while(true){
int n=inputStream.read();
if(n==-1) break;
System.out.println(n);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
java
public class Demo10 {
public static void main(String[] args) throws FileNotFoundException {
try(InputStream i=new FileInputStream("D:/Project/java/java-biog/" +
"Day20250312/IO测试文件目录/测试图片.png")){
while(true){
byte[] b=new byte[1024];
int n=i.read(b);
if(n==-1) break;
for (int j = 0; j < n; j++) {
System.out.println(n);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
FileInputStream是InputStream子类,InputStream实现Closeable接口。所以,当进入 try 块时,inputStream 被初始化。一旦退出 try 块(无论是因为正常结束还是由于异常),JVM 会自动调用 inputStream 的 close() 方法来关闭文件流。
OutputStream :
OutputStream 同样只是一个抽象类,要使用还需要具体的实现类。我们现在还是只关心写入文件中,所以使用 FileOutputStream
java
public class Demo11 {
public static void main(String[] args) throws FileNotFoundException {
String content = "Hello, World!";
try( OutputStream outputStream=new FileOutputStream("D:/Project/java/java-biog/" +
"Day20250312/IO测试文件目录/测试1.txt",true)){
outputStream.write(content.getBytes());
outputStream.flush();//刷新输出流,确保所有数据都被写出
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

红框里面的true,可以让本次写的内容不覆盖上次的内容,在上次内容后面追加本次的内容!
java
public class Demo12 {
public static void main(String[] args) {
try (OutputStream outputStream = new FileOutputStream("D:/Project/java/java-biog/" +
"Day20250312/IO测试文件目录/测试1.txt", true)) {
byte[] b = new byte[]{99, 98, 97};
outputStream.write(b);
outputStream.flush();//刷新输出流,确保所有数据都被写出
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
字符流
Reader:
java
public class Demo13 {
public static void main(String[] args) throws FileNotFoundException {
try(Reader reader=new FileReader("D:/Project/java/java-biog/" +
"Day20250312/IO测试文件目录/测试1.txt")){
while(true){
int ch=reader.read();
if(ch==-1) break;
for (int i = 0; i < ch; i++) {
System.out.println(ch);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
java
public class Demo14 {
public static void main(String[] args) throws FileNotFoundException {
try(Reader reader=new FileReader("D:/Project/java/java-biog/" +
"Day20250312/IO测试文件目录/测试1.txt")){
while(true){
char[] chars=new char[1024];
int ch=reader.read(chars);
if(ch==-1) break;
for (int i = 0; i < ch; i++) {
System.out.println(chars[i]);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Writer :
java
public class Demo15 {
public static void main(String[] args) throws FileNotFoundException {
try(Writer writer=new FileWriter("D:/Project/java/java-biog/" +
"Day20250312/IO测试文件目录/测试1.txt")){
BufferedWriter bufferedWriter=new BufferedWriter(writer);
writer.write("hello world");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
BufferedWriter 可以配置为在特定条件下自动刷新缓冲区(例如,当缓冲区满或遇到换行符时)。这确保了数据及时写入,而不需要手动调用 flush() 方法。
3.2 特别注意
- 不管字节流或字符流读写文件,结束都需要关闭文件?
每次程序打开一个文件,就会在文件的描述表中(固定长度的顺序表),每次打开文件,就相当申请一个表项。如果光打开,不关闭,后续表项申请完,后续再打开文件,就会打开失败。
- 字符流和字节流读相同的字符,输出的字节是不同的?
"你好"这两个字,字节流读取是三个字节,而字符流读取是两个字节。这个是不矛盾的。
- 在字节流中,对于中文字符"你"和"好",在 UTF-8 编码中,每个字符通常占用3个字节。
- 对于 UTF-8 编码的文本,Java 会根据编码规则正确地将3个字节的字节序列转换为一个字符。因此,对于"你好"这两个字,字符流会正确地读取两个字(每个中文字符在 UTF-8 中通常占用3个字节,但字符流按字符读取,所以是两个字)。
通常是因为在字节流读取时没有正确处理 UTF-8 的多字节特性,而字符流读取则自动处理了编码,正确地按字符读取。
四、实战演示
4.1 查找删除文件
扫描指定⽬录,并找到名称中包含指定字符的所有普通⽂件(不包含⽬录),并且后续询问⽤⼾是否要删除该⽂件。
思路:
- 先输入扫描文件目录,并判断是否是目录,不是则退出
- 输入要删除文件的关键字
- traversefile()这个方法是遍历目录下的每个文件
- 判断是否是目录,是目录进行递归,不是目录,则判断是否是空文件;如果不是空文件,则进行deletefile()操作是否删除此文件
java
public class Demo16 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入要扫描的目录:");
String filename = sc.next();
File file = new File(filename);
if (!file.isDirectory()) {
System.out.println("您输⼊的不是目录,退出");
return;
}
System.out.println("请输入要删除的文件:");
String deleteName = sc.next();
traversefile(file, deleteName);
}
private static void traversefile(File file, String deleteName) {
//1.遍历该目录下的文件
File[] files = file.listFiles();
//2.判断是否目录是否为空
if (files == null || files.length == 0) {
return;
}
for (File f : files) {
//判断是否是目录还是普通文件
if (f.isDirectory()) {
traversefile(f, deleteName);
} else {
deletefile(f, deleteName);
}
}
}
private static void deletefile(File f, String deleteName) {
if (f.getName().contains(deleteName)) {
System.out.println("找到包含关键字的文件,是否删除"+f.getName()+"文件,(y/n)");
Scanner sc = new Scanner(System.in);
String answer = sc.next();
if (answer.equals("y")) {
f.delete();
System.out.println("文件删除!!!");
} else {
return;
}
}
}
}
4.2 普通文件的复制
需要让用户指定两个文件路径,一个是源路径 (被复制的文件),一个是目标路径(复制之后生成的文件),打开源路径的文件,读取里面的内容,并写入到目标文件。
思路:
- 输入源文件和目标文件路径(判断源文件路径,因为OutputStream会自动创建一个文件)
- 运用InputStream和OutputStream从源文件读取写入到目标文件
java
public class Demo17 {
public static void main(String[] args) throws FileNotFoundException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入源路径:");
String filename = sc.next();
File file = new File(filename);
System.out.println("请输入目标文件路径:");
String copyName = sc.next();
if(!file.isFile()){
System.out.println("源路径不是一个普通的文件!返回!");
return;
}
try(InputStream in = new FileInputStream(file);
OutputStream ou=new FileOutputStream(copyName) ){
while(true){
byte[] b = new byte[1024];
int len = in.read(b);
if(len==-1){
break;
}
for(int i=0;i<len;i++){
ou.write(b[i]);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
4.3 文件内容查找
不仅要找到文件名包含关键字,还要找到文件内容包含关键字
思路:
1.遍历,递归,找到文件名包含关键字的文件并返回文件名
2.判断文件内容,这里用Reader,因为需要将读到的字符拼接起来(Stringbuffer),将拼接起来的字符和关键字对比
java
public class Demo18 {
public static void main(String[] args) throws FileNotFoundException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入要扫描的目录:");
String filename = sc.next();
File file = new File(filename);
if (!file.isDirectory()) {
System.out.println("您输⼊的不是目录,退出");
return;
}
System.out.println("请输入关键字:");
String impName = sc.next();
traversefile(file, impName);
}
private static void traversefile(File file, String impName) throws FileNotFoundException {
//1.遍历该目录下的文件
File[] files = file.listFiles();
//2.判断是否目录是否为空
if (files == null || files.length == 0) {
return;
}
for (File f : files) {
//判断是否是目录还是普通文件
if (f.isDirectory()) {
traversefile(f, impName);
} else {
foudnfile(f, impName);
}
}
}
private static void foudnfile(File f, String impName) throws FileNotFoundException {
if(f.getName().contains(impName)) {
System.out.println("文件名包含关键字"+f.getName());
}else{
try(Reader reader= new FileReader(f)) {
StringBuffer sb = new StringBuffer();
while(true) {
char[] buf = new char[1024];
int len= reader.read(buf);
if (len == -1) {
break;
}
for(int i=0; i<len; i++) {
sb.append(buf[i]);
// sb.append(buf, 0, len);
}
//return stringBuilder.indexOf(word) != -1;
// stringBuilder.indexOf()是StringBuffer类中的一个方法,找到关键字word方法会返回 true;反之,返回false
if(sb.toString().contains(impName)) {
System.out.println(f.getName());
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}