目录
- 一、操作系统层面:到底什么是文件?
- 1.1 目录(文件夹)的树形结构
- 1.2 两种路径:绝对路径 vs 相对路径
- 1.3 文件分类:文本文件 & 二进制文件
- 二、Java File类:操作文件元信息(不读写内容)
- 2.1 构造方法
- 2.2 常用API梳理
- 2.3 getPath / getAbsolutePath / getCanonicalPath 区别
- 三、IO流:真正读写文件内容
- 3.1 字节输入流 FileInputStream(读文件)
- 3.2 字节输出流 FileOutputStream(写文件)
- 3.3 PrintWriter:方便的打印输出
- 四、实战小项目
- 案例1:文件复制
- 案例2:递归扫描目录,查找文件名包含关键词的文件,可选删除
- 五、小贴士
- 六、知识回顾
一、操作系统层面:到底什么是文件?
硬盘是持久化存储设备,操作系统将硬盘上的数据抽象为文件。
一个文件包含两部分信息:
- 文件内容:真正保存业务数据(文本、图片、视频二进制数据)
- 文件元信息:文件名、大小、修改时间、权限等,不属于文件内容本身。
1.1 目录(文件夹)的树形结构
磁盘上文件成千上万,操作系统采用树形结构组织文件。
目录(文件夹)本质上是一种特殊文件,保存子文件、子目录的元信息。
- Windows:此电脑 → C盘 → Program Files,反斜杠
\作为路径分隔符 - Linux:
/作为根目录,正斜杠/分隔路径,经典思想:万物皆文件,把硬件IO设备也抽象成文件来访问。
1.2 两种路径:绝对路径 vs 相对路径
- 绝对路径:从根节点出发完整路径,唯一定位一个文件。Windows示例:
C:\Program Files\test.txt - 相对路径:从当前工作目录出发去找文件。
..代表父目录,.代表当前目录。
注意:Windows依靠后缀名识别文件类型(
.exe、.txt),Linux不依赖后缀;文件还具备读、写、执行权限,操作系统依靠权限做数据安全保护。
1.3 文件分类:文本文件 & 二进制文件
- 文本文件:内容经过字符集编码(UTF‑8/GBK),如txt、java源代码。
- 二进制文件:没有字符编码,直接原始二进制存储,图片、exe、压缩包都属于二进制。
快捷方式/软链接:不是真实文件,只是对原文件的引用。
二、Java File类:操作文件元信息(不读写内容)
java.io.File,专门描述文件或者目录的抽象对象。
⚠️ 重要:new File对象,磁盘上不一定真的有这个文件;File类只能操作元信息(创建、删除、获取路径、判断类型),不能读写文件里面的数据。
2.1 构造方法
// 直接传入路径(绝对路径 / 相对路径)Filefile1=newFile("hello.txt");// 父路径字符串 + 子文件名Filefile2=newFile("./dir","hello.txt");// File对象作为父目录 + 子文件名Fileparent=newFile("./dir");Filefile3=newFile(parent,"hello.txt");2.2 常用API梳理
| 方法 | 作用 |
|---|---|
getParent() | 获取父目录路径 |
getName() | 获取文件名(不含路径) |
getPath() | 创建File时传入的原始路径 |
getAbsolutePath() | 获取完整绝对路径 |
getCanonicalPath() | 解析..等符号后的规范化绝对路径 |
exists() | 判断文件/目录是否真实存在 |
isFile() | 是否普通文件 |
isDirectory() | 是否目录 |
createNewFile() | 创建空文件,已存在返回false |
delete() | 立刻删除文件/空目录 |
deleteOnExit() | JVM退出的时候才删除(临时文件) |
mkdir() | 创建单层目录,父目录不存在创建失败 |
mkdirs() | 创建多级目录,不存在的父目录一并创建 |
list() | 获取目录下所有子项名字(String数组) |
listFiles() | 获取目录下所有子项File对象数组 |
renameTo(File dest) | 文件重命名/剪切粘贴 |
易错提醒:
mkdir()只能创建一层文件夹;多级目录请一定用mkdirs()!
2.3 getPath / getAbsolutePath / getCanonicalPath 区别
举个例子,相对路径..\\hello‑world.txt
getPath():原样输出:..\hello‑world.txtgetAbsolutePath():拼接上项目工作目录:D:\code\demo\..\hello‑world.txtgetCanonicalPath():把..解析抵消掉,得到干净路径D:\code\hello‑world.txt。
三、IO流:真正读写文件内容
流的比喻:把数据比作水池里面的水。
输入流InputStream:读,从文件流向程序(接水)
输出流OutputStream:写,从程序流向文件(往水池灌水)
InputStream、OutputStream是抽象类;操作文件使用实现类:
FileInputStream、FileOutputStream。
3.1 字节输入流 FileInputStream(读文件)
核心方法:
int read():读1个字节;读到文件末尾返回**‑1**int read(byte[] buf):批量读取到字节数组buf,返回读到字节数量;末尾返回‑1close():关闭文件资源,释放句柄
最佳实践:使用try‑with‑resources,自动调用close,避免资源泄露,Java7及以上支持,资源会自动关闭,不需要写finally手动关闭流。
示例:批量读取(性能优于单字节读取)
importjava.io.FileInputStream;importjava.io.IOException;importjava.io.InputStream;publicclassReadDemo{publicstaticvoidmain(String[]args)throwsIOException{// try()括号中声明流,代码结束自动closetry(InputStreamis=newFileInputStream("hello.txt")){byte[]buf=newbyte[1024];// 缓冲区intlen;while((len=is.read(buf))!=-1){// buf[0 ~ len‑1] 是本次读到的有效字节for(inti=0;i<len;i++){System.out.print((char)buf[i]);}}}}}注意:字节流拿到原始字节,如果直接强转char,UTF‑8中文占3字节,直接单字节转换会乱码。处理中文文本,推荐配合Scanner或者转换流。
Scanner包装字节流读取文本
try(InputStreamis=newFileInputStream("hello.txt");Scannerscanner=newScanner(is,"UTF‑8")){while(scanner.hasNextLine()){Stringline=scanner.nextLine();System.out.println(line);}}3.2 字节输出流 FileOutputStream(写文件)
核心API:
write(int b):写入一个字节write(byte[] b):写入整个字节数组write(byte[] b,int off,int len):数组从off开始写len长度字节flush():刷新缓冲区!数据先放在内存缓冲区,调用flush才真正刷入磁盘;close也会自动flush。close()关闭流
FileOutputStream默认会清空原有文件内容,从头覆盖写入。
简单写文件示例:
importjava.io.FileOutputStream;importjava.io.IOException;importjava.io.OutputStream;publicclassWriteDemo{publicstaticvoidmain(String[]args)throwsIOException{try(OutputStreamos=newFileOutputStream("output.txt")){Stringcontent="你好,JavaIO";byte[]data=content.getBytes("UTF‑8");os.write(data);os.flush();// 强制刷盘}}}3.3 PrintWriter:方便的打印输出
如果想直接使用print / println / printf,可借助转换流OutputStreamWriter将字节流转为字符流,再套上PrintWriter:
try(OutputStreamos=newFileOutputStream("out.txt");OutputStreamWriterwriter=newOutputStreamWriter(os,"UTF‑8");PrintWriterprintWriter=newPrintWriter(writer)){printWriter.println("第一行文本");printWriter.printf("数字:%d\n",100);printWriter.flush();}四、实战小项目
案例1:文件复制
读取源文件,写入目标文件,对二进制数据通用,文本、图片都支持。
importjava.io.*;importjava.util.Scanner;publicclassFileCopy{publicstaticvoidmain(String[]args)throwsIOException{Scannersc=newScanner(System.in);System.out.print("输入源文件路径:");StringsrcPath=sc.next();Filesrc=newFile(srcPath);if(!src.exists()||!src.isFile()){System.out.println("源文件错误!");return;}System.out.print("输入目标路径:");StringdestPath=sc.next();Filedest=newFile(destPath);if(dest.exists()){System.out.print("目标已存在,是否覆盖 y/n:");Stringop=sc.next();if(!"y".equalsIgnoreCase(op)){return;}}try(InputStreamin=newFileInputStream(src);OutputStreamout=newFileOutputStream(dest)){byte[]buf=newbyte[1024];intlen;while((len=in.read(buf))!=-1){out.write(buf,0,len);}out.flush();}System.out.println("复制完成");}}案例2:递归扫描目录,查找文件名包含关键词的文件,可选删除
文件系统是树形,使用深度优先递归遍历目录:
importjava.io.File;importjava.io.IOException;importjava.util.ArrayList;importjava.util.List;importjava.util.Scanner;publicclassScanFileDemo{publicstaticvoidmain(String[]args)throwsIOException{Scannersc=newScanner(System.in);System.out.print("输入扫描根目录:");StringrootPath=sc.next();Fileroot=newFile(rootPath);if(!root.exists()||!root.isDirectory()){System.out.println("目录无效");return;}System.out.print("输入要匹配文件名关键字:");Stringkeyword=sc.next();List<File>result=newArrayList<>();scanDir(root,keyword,result);System.out.println("找到"+result.size()+"个匹配文件");for(Filef:result){System.out.println(f.getCanonicalPath());System.out.print("是否删除(y/n):");Stringop=sc.next();if("y".equalsIgnoreCase(op)){f.delete();System.out.println("已删除");}}}privatestaticvoidscanDir(Filedir,Stringkeyword,List<File>res){File[]subs=dir.listFiles();if(subs==null)return;for(Filef:subs){if(f.isDirectory()){scanDir(f,keyword,res);}else{if(f.getName().contains(keyword)){res.add(f);}}}}}五、小贴士
File对象创建不等于磁盘存在,必须调用createNewFile/mkdir/mkdirs;判断存在必须调用exists()。- 创建多级目录不要用mkdir(),要用mkdirs()。
- IO流一定要关闭!优先
try‑with‑resources自动关闭,杜绝文件句柄泄露。 - 写输出流切记
flush(),缓冲区没刷盘会出现数据丢失。 - 中文读写务必指定编码(UTF‑8),否则系统默认编码不一致产生乱码。
read()返回‑1代表读取结束,不要把读到的字节直接当成char处理中文。delete()删除文件不走回收站,删除前一定要确认业务逻辑。
六、知识回顾
- 操作系统层:文件(内容+元信息)→目录树结构→绝对/相对路径→文本/二进制文件
- File类:只操作元信息,创建对象≠真实存在,熟悉路径获取、创建删除、遍历目录
- IO流:InputStream读,OutputStream写;字节流通用所有文件;try‑with‑resources管理资源;flush刷缓冲区
- 实战:文件复制、递归遍历目录搜索文件
写IO代码的时候,记住:File管文件本身,流管文件里面的数据,把两件事分开,就不容易混淆。