拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

JavaEE(4)文件操作和IO

JavaEE(4)文件操作和IO

目录

  • 一、操作系统层面:到底什么是文件?
    • 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. 文件内容:真正保存业务数据(文本、图片、视频二进制数据)
  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

  1. getPath():原样输出:..\hello‑world.txt
  2. getAbsolutePath():拼接上项目工作目录:D:\code\demo\..\hello‑world.txt
  3. getCanonicalPath():把..解析抵消掉,得到干净路径D:\code\hello‑world.txt。

三、IO流:真正读写文件内容

流的比喻:把数据比作水池里面的水。
输入流InputStream:读,从文件流向程序(接水)
输出流OutputStream:写,从程序流向文件(往水池灌水)

InputStream、OutputStream是抽象类;操作文件使用实现类:FileInputStream、FileOutputStream。

3.1 字节输入流 FileInputStream(读文件)

核心方法:

  • int read():读1个字节;读到文件末尾返回**‑1**
  • int read(byte[] buf):批量读取到字节数组buf,返回读到字节数量;末尾返回‑1
  • close():关闭文件资源,释放句柄

最佳实践:使用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);}}}}}

五、小贴士

  1. File对象创建不等于磁盘存在,必须调用createNewFile/mkdir/mkdirs;判断存在必须调用exists()。
  2. 创建多级目录不要用mkdir(),要用mkdirs()。
  3. IO流一定要关闭!优先try‑with‑resources自动关闭,杜绝文件句柄泄露。
  4. 写输出流切记flush(),缓冲区没刷盘会出现数据丢失。
  5. 中文读写务必指定编码(UTF‑8),否则系统默认编码不一致产生乱码。
  6. read()返回‑1代表读取结束,不要把读到的字节直接当成char处理中文。
  7. delete()删除文件不走回收站,删除前一定要确认业务逻辑。

六、知识回顾

  1. 操作系统层:文件(内容+元信息)→目录树结构→绝对/相对路径→文本/二进制文件
  2. File类:只操作元信息,创建对象≠真实存在,熟悉路径获取、创建删除、遍历目录
  3. IO流:InputStream读,OutputStream写;字节流通用所有文件;try‑with‑resources管理资源;flush刷缓冲区
  4. 实战:文件复制、递归遍历目录搜索文件

写IO代码的时候,记住:File管文件本身,流管文件里面的数据,把两件事分开,就不容易混淆。

返回列表