
文章目录
- 一、起源
- 二、路径&目录结构
- 三、文件类型
- 四、文件操作
- 五、File类
-
- [1. 内部属性](#1. 内部属性)
- [2. 主要构造方法](#2. 主要构造方法)
- [3. 主要方法](#3. 主要方法)
-
- [1. 返回值是String](#1. 返回值是String)
- [2. 返回值是boolean](#2. 返回值是boolean)
- [3. 返回File](#3. 返回File)
- [4. 返回`String[]`](#4. 返回
String[]) - [5. 返回`File[]`](#5. 返回
File[]) - [6. 文件目录重命名或移动](#6. 文件目录重命名或移动)
- [7. 针对文件内容的操作](#7. 针对文件内容的操作)
-
- [1. `InputStream`抽象类](#1.
InputStream抽象类) - [2. `OutputStream`抽象类](#2.
OutputStream抽象类) - [3. `Reader`抽象类](#3.
Reader抽象类) - [4. `Write`抽象类](#4.
Write抽象类)
- [1. `InputStream`抽象类](#1.
- 六、使用Scanner搭配InputStream
- 七、Write子类PrintWrite
- 八、使用递归去查看当前目录的所有子目录
- 九、复制普通类型文件
- 十、扫描指定目录的所有子目录中普通文件内容是否包含指定内容
一、起源
我们为了简化我们开发人员读写硬盘上的数据,操作系统对上述操作进行了封装,提供对外接口
因为操作系统有很多种,因此我们Java又对操作系统的API进行了封装,我们只需要使用相关的文件的API即可
二、路径&目录结构
我们打开电脑上的每一个目录,可能存在很多个文件,因此我们的每一级目录都是一棵N叉树,因此得名目录树
而我们在电脑上的文件位置则使用诸如"C:\JavaCode\java-ee-career\readme.md"这样的路径表示
- 绝对路径:就是从硬盘开始一直到文件位置的路径,就比如上面那个路径
- 相对路径:从一个基准目录出发到对应文件的目录,比如刚刚的路径
"C:\JavaCode\java-ee-career\readme.md"中,我以JavaCode为起始位置,那么这个文件的相对路径就是java-ee-career\readme.md
三、文件类型
如果我们按照分类来说,分为普通的文件和一个目录文件
如果按照内容来分,我们分为文本文件和二进制文件
四、文件操作
这个是一组类的流对象,什么是流,你可以把它比喻成一条河流,通过这条河流传输信息
对于文件的内容操作,顾名思义就是去修改文件内容
而对文件系统操作,它是操作系统的一个子模块,通过文件资源管理器
五、File类
这个类是专门处理文件io的类,放在了java.io.File包下
1. 内部属性
我们常用的就一个,即文件路径分隔符
对于刚刚的路径中"C:\JavaCode\java-ee-career\readme.md"的\
就是分割一级级目录的分隔符,大部分的操作系统都是/,但是windows是老系统为了兼容嘛
2. 主要构造方法
- 版本一
File(String pathname),参数是填写操作文件的路径,可以使用相对路径。但是对于相对路劲,这个比较复杂,如果是这么写"./test.txt"表示的是基于当前程序运行的目录下,而.表示的是当前目录 - 版本二
File(File Parent,String Child)即把路径拆分成两份。比如对于这个路径"C:\JavaCode\java-ee-career\readme.md"中双亲路径Parent是C:\JavaCode\java-ee-career,孩子路径Child是readme.md
3. 主要方法
1. 返回值是String
getParent(),返回File对象所在的双亲路径,例如刚刚的C:\JavaCode\java-ee-careergetName(),获取文件名本身,例如刚刚的readme.mdgetPath(),获取整个路径,比如刚刚的"C:\JavaCode\java-ee-career\readme.md"getAbsolutePath(),获取绝对路径getCanonicalPath(),获取修饰过的路径
什么是修饰过的路径,对于绝对路径我们看不出来,但是对于相对路径
- 方法四的获取绝对路径
Java\.\test.txt - 方法五的获取修饰过的绝对路径
Java\test.txt
去除了字符串中冗余的字符
2. 返回值是boolean
exists(),判断文件或者是目录是否存在isDirectory(),判断一个文件是否是目录isFile()判定一个文件是否真的是文件createNewFile(),创建一个空文件,成功则返回true,如果硬盘满了或者是没有权限等待都会返回false表示创建失败delete(),表示直接从硬盘上物理的删除,不进入回收站mkdir(),创建一层目录mkdirs()创建多级目录
3. 返回File
deleteOnExit(),标记式文件删除。即在JVM运行的时候,只是标记这个文件会删除,此时还没有删,当JVM运行结束之前再把文件删除。在开发中主要应用于临时文件的管理
4. 返回String[]
list(),列出当前目录下面的所有文件的文件名
5. 返回File[]
listFiles(),列出当前目录下所有文件内容,存储在File[]对象中
我们用一个代码实际演示下几个方法使用,我么使用下图目录作为演示

java
public class Demo1 {
public static void main(String[] args) {
File file = new File("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107");
String [] fileNames = file.list();
for(String fileName : fileNames){
System.out.println(fileName);
}
File file1 = new File("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\Ijava\\Ijavas");
file1.mkdirs();
}
}


可以看到我们的文件内容和多级目录已经成功创建
6. 文件目录重命名或移动
使用 boolean ret = file.renameTo(File dest)方法完成
如果目录路径是同一个,则进行文件重命名操作
如果不是同一个路径,则在这个路径下创建你指定的文件,说白了就是移动文件
比如刚刚的例子,我想把"C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\Ijava\\Ijavas\\test.txt"中的文件移动至"C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\test.txt"这个目录下
java
File oldPath = new File("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\Ijava\\Ijavas\\test.txt");
File destPath = new File("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\test.txt");
System.out.println(oldPath.renameTo(destPath));//true
7. 针对文件内容的操作
我们分成两大类,一个是字节流,即逐个字节操作内容。一个是字符流,即逐个字符操作内容
对于字节流,提供了两个抽象类InputStream-->输入流和OutputStream-->输出流
对于字符流,提供了两个抽象类Write-->输入流和Reader-->输出流
1. InputStream抽象类
这个类主要是打开文件进行读取操作,对于read()方法有四种版本
- 版本一:无参数版本,即一次只读取一个直接,读到文件末尾返回
-1 - 版本二:一个参数版本,参数
byte[]是一个输出型参数,所谓输出型参数,就是利用这个参数去接收结果 - 版本三:三个参数版本,在版本二输出型参数
byte[]基础上,len为预期要读取多少个字节,off为偏移量即相对于文件开头第一个字节的位置
java
public static void main(String[] args) throws IOException {
//我们提前在文件内容中写入"abcdefg"内容
InputStream inputStream = new FileInputStream("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\test.txt");
while(true){//不知什么时候读取完,等到读取完毕再判断退出
int n = inputStream.read();
if(n == -1){
//读取完毕
break;
}
//我们使用十六进制打印内容
System.out.printf("%x\n",n);//61 62 63 64 65 66 67 正好对应我们Unicode码值
}
}
但是每次都是一个字节一个字节从硬盘读取,开销太大且效率慢,因此我们使用输出型参数
java
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\test.txt");
while(true){
byte[] bytes = new byte[1024];//每次读取1024个字节
//如果已经读取完毕就退出
int n = inputStream.read(bytes);
if(n == -1){
break;
}
for (int i = 0; i < n; i++) {
System.out.printf("%x\n",bytes[i]);
}
}
}
好,我们使用了资源是不是要进行释放啊,对于我们的文件描述附表,内部是不能扩容的
因此如果我们把这个表填满了就会导致程序出现重大bug,因此我们需要定时重启下以便释放资源
因此我们在每次循环最后加入关闭资源的代码,当然更加简便的做法是把整个代码快用try块包裹,然后在finally关闭资源,catch捕获异常
java
public static void main(String[] args) throws IOException {
InputStream inputStream = null;
try {
inputStream = new FileInputStream("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\test.txt");
while (true) {
byte[] bytes = new byte[1024];//每次读取1024个字节
//如果已经读取完毕就退出
int n = inputStream.read(bytes);
if (n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.printf("%x\n", bytes[i]);
}
}
}catch(IOException e){
e.printStackTrace();
}finally {
//判断对象是否有构建好,避免空指针
if(inputStream != null){
inputStream.close();
}
}
}
你是不是觉得这个代码非常丑,对的!因此我们使用try-with-resource代码块
我们可以在try()参数中指明多个对象,并且在try块执行完代码后,自动调用close()方法关闭资源
但是前提是这个类要实现closable接口并且有close()方法
java
public static void main(String[] args){
try (InputStream inputStream = new FileInputStream("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\test.txt")) {
while (true) {
byte[] bytes = new byte[1024];//每次读取1024个字节
//如果已经读取完毕就退出
int n = inputStream.read(bytes);
if (n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.printf("%x\n", bytes[i]);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}

这样代码不就清爽多了吗!!
2. OutputStream抽象类
这个类主要进行写的操作,对于wirte方法,有三个版本
- 版本一:一次写一个字符串
- 版本二:一次性写一个完整的字节型数组
- 版本三:一次性写一个字节型数组的一部分
这个类的构造方法,如果你只写文件路径一个参数,则默认把文件内容清空
如果你在文件路径后再写一个参数true,表示你现在做的操作是针对内容进行拼接,并且每一次拼接都在内容末尾
java
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\test.txt")){
outputStream.write(65);//对于字符A
outputStream.write(66);//对于字符B
byte[] bytes = {67,68};//对应字符C,D
outputStream.write(bytes);
}catch (IOException e){
e.printStackTrace();
}
}
3. Reader抽象类
这个是字符流的文件读取操作,对于read()有四个版本
- 版本一:一次性读取一个字符
- 版本二:读取一次字符数组
- 版本三:读取一个字符数组封装的
charBuffer - 版本四:对字符数组的一部分内容读取
java
public static void main(String[] args) {
try(Reader reader = new FileReader("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\test.txt")){
while(true) {
int n = reader.read();//读取到末尾返回-1
if (n == -1) {
break;
}
char c = (char)n;
System.out.println(c);
}
}catch (IOException e){
e.printStackTrace();
}
}
4. Write抽象类
这个是字符流的文件写操作,对于wirte()有五个版本
- 版本一:一次性写一个字符
- 版本二:一次性写一个字符串
- 版本三:一次性写一个字符数组
- 版本四:一次性写一个字符串的子串
- 版本五:写一个字符数组的一部分
java
public static void main(String[] args) {
try(Writer writer = new FileWriter("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\test.txt")){
writer.write(66);
char[] ch = {'a','b'};
writer.write(ch);
}catch (IOException e){
e.printStackTrace();
}
}
六、使用Scanner搭配InputStream
我们这样搭配可以对文件的内容进行格式化解析
java
public static void main(String[] args) throws FileNotFoundException {
InputStream inputStream = new FileInputStream("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\test.txt");
Scanner scanner = new Scanner(inputStream);
String a = scanner.next();
System.out.println(a);//Bab
}
七、Write子类PrintWrite
这个类可以让我们可以将内容以固定格式写入文件,写入的数据先在临时缓冲区,当关闭资源的时候自动写入内容
java
public static void main(String[] args) throws FileNotFoundException {
PrintWriter printWriter = new PrintWriter("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\test.txt");
printWriter.printf("%d+%d=%d\n",10,20,30);
printWriter.close();
}
八、使用递归去查看当前目录的所有子目录
java
public class Demo2 {
public static void main(String[] args) {
//使用递归查看当前目录的所有包含指定名字的文件
Scanner scanner = new Scanner(System.in);
String rootPath = scanner.next();
String fileName = scanner.next();
//判断输入合法性
if(rootPath.isEmpty() || fileName.isEmpty()){
System.out.println("输入非法");
return;
}
File rootFile = new File(rootPath);
//判断这个是否存在或者是这个路径是否是目录
if(!rootFile.exists() || !rootFile.isDirectory()){
System.out.println("非法目录");
return;
}
search(rootFile,fileName);
}
private static void search(File rootFile,String fileName){
//列出当前目录中的所有文件包括子目录
File[] files = rootFile.listFiles();
//判定空目录
if(files == null){
return; // 空目录直接返回,不需要打印
}
for(File file : files){
if(file.isFile()){
if(file.getName().contains(fileName)){
//找到了就尝试去删除 - 这里应该传入当前文件,不是根目录
tryToDelete(file,fileName);
}
}else if(file.isDirectory()){
//如果是一个子目录就进行递归操作 - 这里递归参数错了
search(file,fileName); // 应该是file不是rootFile
}
}
}
private static void tryToDelete(File targetFile,String fileName){
Scanner scanner = new Scanner(System.in);
System.out.println("是否要删除 " + targetFile.getAbsolutePath() + " ?(Y/N)");
String choice = scanner.next();
if(choice.equals("Y") || choice.equals("y")){
boolean success = targetFile.delete();
if(success){
System.out.println("删除成功: " + targetFile.getName());
}else{
System.out.println("删除失败: " + targetFile.getName());
}
}else{
System.out.println("删除取消");
}
}
}
九、复制普通类型文件
java
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String oldPath = scanner.next();//原文件路径
String destPath = scanner.next();//目标文件路径
File oldFile = new File(oldPath);
if(!oldFile.isFile()){
//如果传入的不是文件就不能执行复制操作
System.out.println("非法输入");
return;
}
//复制的同时重命名
File destFile = new File(destPath);
//先找到目标路径的双亲路径
File destParent = destFile.getParentFile();
if(!destParent.isDirectory()){
System.out.println("路径非法");
}
//同时打开两个文件路径进行复制操作
try(InputStream inputStream = new FileInputStream(oldFile);
OutputStream outputStream = new FileOutputStream(destFile)){
while(true){
//我们一次性1024字节即1kb/次速度进行复制
byte[] bytes = new byte[1024];
int n = inputStream.read(bytes);
if(n == -1){
break;
}
outputStream.write(bytes,0,n);
}
}catch (IOException e){
e.printStackTrace();
}
}
十、扫描指定目录的所有子目录中普通文件内容是否包含指定内容
java
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String rootPath = scanner.next();//要搜索的根目录
String word = scanner.next();//要搜索的关键字
if(word.isEmpty() || rootPath.isEmpty()){
System.out.println("输入非法");
return;
}
File rootFile = new File(rootPath);
if(!rootFile.isDirectory()){
System.out.println("输入非法,不是目录!");
return;
}
searchs(rootFile,word);
}
private static void searchs(File rootFile,String word){
//列出所有的内容
File[] files = rootFile.listFiles();
if(files == null){
return;
}
for(File file : files){
if(file.isFile()){
//如果是文件,就要看看内容是否包含
trySearch(file,word);
}else if(file.isDirectory()){
//递归子目录
searchs(file,word);
}
}
}
private static void trySearch(File file,String word){
//先判断文件名是不是我们想要的
if(file.getName().contains(word)){
System.out.println("找到了,文件路径是:");
System.out.println(file.getAbsolutePath());
}
//接下来查看文件内容,即使文件名不匹配内容也可能匹配
try(Reader reader = new FileReader(file)){
//把读取到的字符进行拼接
StringBuilder str = new StringBuilder();
while(true){
char [] chars = new char[1024];
int n = reader.read();
if(n == -1){
//文件读取到末尾了
break;
}
str.append(chars);
}
if(str.indexOf(word) >= 0){
System.out.println("找到了");
}
}catch (IOException e){
e.printStackTrace();
}
}
文章可能有错误欢迎指出
END♪٩(´ω`)و♪`