当前位置: 首页 > news >正文

JAVA重点基础、进阶知识及易错点总结(13)File 类 + 路径操作

🚀 Java 巩固进阶 · 第13天

主题:File 类 + 路径操作 —— IO 体系的第一块基石

📅 进度概览:从今天起,我们正式进入Java IO 流体系。第一站:java.io.File

💡 核心价值

  • 文件操作基石File是操作文件/目录元数据的唯一入口,后续所有流(Stream)都依赖它定位资源。
  • SpringBoot 实战必备:文件上传存储、配置文件加载、日志目录初始化,处处可见File的身影。
  • 路径兼容性:掌握跨平台路径写法,避免 “在我机器上能跑,上线就报错” 的经典坑。
  • 递归思维训练:文件遍历是理解递归的最佳场景,也是面试高频考点。

一、File 类本质:它到底能做什么?

1. 核心定位

┌─────────────────────────────────┐ │ 📁 File 类 │ ├─────────────────────────────────┤ │ ✅ 表示文件/目录的"路径名" │ │ ✅ 操作元数据:创建、删除、重命名│ │ ✅ 判断属性:是否存在、是文件还是目录│ │ ❌ 不负责读写文件内容! │ │ (读写内容 → 交给 IO 流) │ └─────────────────────────────────┘

2. 路径分隔符:跨平台第一坑 ⚠️

系统分隔符错误示例正确写法
Windows\new File("D:\test\a.txt")new File("D:/test/a.txt")
Linux/macOS/new File("/home/user/test")✅ 原生支持
通用方案File.separator-new File("data" + File.separator + "a.txt")

💡最佳实践

  • 代码中优先使用/,Java 在 Windows 上也能自动识别
  • 动态拼接路径时,使用File.separatorPaths.get()(NIO)

二、File 构造方法:三种姿势,推荐一种

// ❌ 姿势1:直接传完整路径(硬编码,不灵活)Filefile1=newFile("D:/workspace/project/data/a.txt");// ✅ 姿势2:父路径 + 子路径(最推荐!解耦、易维护)Filedir=newFile("data");Filefile2=newFile(dir,"a.txt");// → data/a.txt// ✅ 姿势3:字符串数组(适合动态拼接多级路径)Filefile3=newFile("data","sub","a.txt");// → data/sub/a.txt

🔍为什么推荐姿势2?

  • 路径可配置:dir可从application.yml读取
  • 便于单元测试:轻松替换临时目录
  • 避免路径拼接错误:new File(dir, name)自动处理分隔符

三、核心判断方法(必背清单 ✅)

Filefile=newFile("data/a.txt");// 🔍 存在性与类型判断file.exists();// [关键] 文件/目录是否存在?file.isFile();// 是否是普通文件?file.isDirectory();// 是否是目录?// 📋 基本信息获取file.getName();// "a.txt"(最后一部分)file.getPath();// "data/a.txt"(构造时的路径)file.getAbsolutePath();// "/Users/xxx/project/data/a.txt"(完整绝对路径)file.length();// 文件大小(字节),目录返回 0file.lastModified();// 最后修改时间戳(毫秒)// 🔄 转换(后续与 NIO 互操作有用)file.toURI();// 转为 URIfile.toPath();// 转为 Path(Java 7+ NIO)

⚠️高频陷阱

Filefile=newFile("a.txt");System.out.println(file.length());// 如果文件不存在,返回 0!不是异常!// ✅ 正确做法:先判断 exists() && isFile()

四、创建 & 删除:安全操作三板斧

1. 创建文件(注意异常处理)

Filefile=newFile("data/a.txt");// ✅ 标准写法:先判断 + 捕获异常if(!file.exists()){// 确保父目录存在!否则 createNewFile 会静默失败Fileparent=file.getParentFile();if(parent!=null&&!parent.exists()){parent.mkdirs();// 创建父目录}try{booleancreated=file.createNewFile();// 原子操作,线程安全System.out.println("创建成功:"+created);}catch(IOExceptione){e.printStackTrace();// 生产环境建议用日志框架}}

2. 创建目录:mkdir()vsmkdirs()

Filedir1=newFile("single");dir1.mkdir();// 仅创建单层,父目录不存在则失败Filedir2=newFile("a/b/c");dir2.mkdirs();// ✅ 递归创建多级目录(推荐!永远优先用这个)

3. 删除操作:注意"空目录"限制

// 删除文件file.delete();// 成功返回 true// 删除目录:必须是空目录!FileemptyDir=newFile("data/empty");emptyDir.delete();// ✅ 成功// ❌ 非空目录直接 delete() 会失败!需要递归删除(见下方实战任务)

💡SpringBoot 实践
文件上传时,先检查存储目录是否存在,不存在则mkdirs()初始化:

@Value("${file.upload-dir}")privateStringuploadDir;@PostConstructpublicvoidinit(){Filedir=newFile(uploadDir);if(!dir.exists()){dir.mkdirs();// 应用启动时自动创建上传目录}}

五、遍历文件夹:listFiles()的正确打开方式

Filedir=newFile("data");// ⚠️ 关键:listFiles() 可能返回 null!(目录不存在/无权限)File[]files=dir.listFiles();if(files==null){System.err.println("无法访问目录:"+dir.getAbsolutePath());return;}// 遍历打印for(Filef:files){Stringtype=f.isDirectory()?"[DIR] ":"[FILE]";System.out.println(type+f.getName()+" ("+f.length()+" bytes)");}

🔥 进阶:文件名过滤器(只查 .txt 文件)

// 方式1:匿名内部类File[]txtFiles=dir.listFiles(newFilenameFilter(){@Overridepublicbooleanaccept(Filedir,Stringname){returnname.endsWith(".txt");}});// 方式2:Lambda 表达式(Java 8+,更简洁)File[]txtFiles=dir.listFiles((d,name)->name.endsWith(".txt"));// 方式3:File 过滤器(可结合文件属性)File[]largeFiles=dir.listFiles(file->file.isFile()&&file.length()>1024*1024);

六、递归遍历:文件扫描的万能钥匙 🔑

/** * 递归打印目录下所有文件(含子目录) * @param dir 起始目录 * @param indent 缩进层级(用于可视化树形结构) */publicstaticvoidlistAll(Filedir,Stringindent){// 1. 参数校验(防御式编程)if(dir==null||!dir.exists()||!dir.isDirectory()){return;}// 2. 获取子项(注意 null 检查!)File[]files=dir.listFiles();if(files==null)return;// 3. 遍历处理for(Filef:files){System.out.println(indent+"├─ "+f.getName()+(f.isDirectory()?"/":"")+(f.isFile()?" ("+f.length()+"B)":""));// 4. 递归:如果是目录,深入一层if(f.isDirectory()){listAll(f,indent+"│ ");// 缩进增加}}}// 调用示例listAll(newFile("data"),"");

输出效果

├─ config/ │ ├─ application.yml (2.1KB) │ └─ logback.xml (1.5KB) ├─ upload/ │ ├─ image.png (3.2MB) │ └─ doc.pdf (1.1MB) └─ readme.md (4.2KB)

💡递归三要素(面试必问):

  1. 终止条件!dir.isDirectory()files == null
  2. 单层逻辑:遍历当前目录所有文件/子目录
  3. 递归调用:遇到子目录时,调用自身处理

七、🎯 今日实战任务:文件管理小工具

任务1:初始化项目目录结构

// 要求:在当前项目下创建多层目录 data/upload/{image, doc, temp}// 提示:使用 mkdirs() + 数组遍历String[]subDirs={"image","doc","temp"};FilebaseDir=newFile("data/upload");// TODO: 补全代码...

任务2:实现"安全删除"工具方法

/** * 递归删除文件或目录(无论是否为空) * @param file 要删除的文件/目录 * @return 是否全部删除成功 */publicstaticbooleandeleteRecursively(Filefile){// TODO: 实现逻辑// 提示:// 1. 如果是目录,先递归删除所有子项// 2. 再删除自身// 3. 注意异常处理和返回值}

任务3:统计目录信息(综合练习)

/** * 统计目录下:文件总数、总大小、最大文件 */publicstaticclassDirStat{intfileCount;longtotalSize;FilelargestFile;// TODO: 添加构造方法/getter}publicstaticDirStatanalyzeDir(Filedir){// TODO: 递归遍历 + 数据统计// 挑战:跳过隐藏文件(file.isHidden())}

任务4:结合 SpringBoot 配置

# application.ymlapp:upload:base-dir:./uploadsallowed-extensions:[jpg,png,pdf]
// 要求:读取配置,初始化目录,并实现文件扩展名过滤的 listFiles@Value("${app.upload.allowed-extensions}")privateList<String>allowedExts;privatebooleanisAllowed(Stringfilename){// TODO: 判断文件名后缀是否在允许列表中(忽略大小写)}

📝 第13天 · 核心总结

  1. File 类定位

    • 操作路径/元数据,不读写内容
    • 所有流(FileInputStream 等)的"入口"
  2. 路径兼容性

    • 优先用/File.separator
    • 构造推荐:new File(parent, child)
  3. 安全操作守则

    • 创建文件前:getParentFile().mkdirs()
    • 创建目录:永远用mkdirs()
    • 遍历目录:listFiles()必须判null
    • 删除目录:非空需递归
  4. 递归遍历模板(背下来!):

    if(dir==null||!dir.isDirectory())return;File[]files=dir.listFiles();if(files==null)return;for(Filef:files){// 处理当前文件if(f.isDirectory())listAll(f,indent+" ");// 递归}
  5. SpringBoot 实践点

    • 应用启动时初始化上传/日志目录
    • 配置文件管理路径,避免硬编码
    • 文件上传前校验扩展名、大小

http://www.jsqmd.com/news/574547/

相关文章:

  • KOReader 2025.04:跨平台电子书阅读器的架构演进与性能突破
  • 亚马逊Buy for Me代购服务全流程实测:从下单到收货的完整避坑手册
  • 阅读记录(2026年4月)
  • DataX 3.0实战:如何用阿里开源工具搞定MySQL到Hive的数据同步(附避坑指南)
  • 通义千问3-VL-Reranker-8B入门指南:小白也能轻松玩转多模态重排序
  • 从404到无损输出:一个Favicon抓取API的三年优化笔记(含CDN、懒加载避坑指南)
  • 2026市面上评价高的次氯酸钠发生器品牌怎么选?看这,一体化净水器/二氧化氯发生器,次氯酸钠发生器供货厂家推荐分析 - 品牌推荐师
  • 阿里云OSS文件上传那些坑:一个苍穹外卖项目中的真实调试案例
  • OpenClaw+千问3.5-9B智能监控:24小时网站异常检测
  • 阿里通义Z-Image-GGUF实测:8GB显存流畅运行,小白也能画出惊艳作品
  • YOLOv8与YOLOv11网络结构对比:从yolov8.yaml到yolo11.yaml的演进与优化
  • 深度学习环境管理指南:如何在一台电脑上安装并切换多个CUDA版本(以CUDA 11.6和12.0为例)
  • Serverless时代Java开发者必学的3种函数封装范式:POJO/Function/Consumer,第2种正在被淘汰!
  • 别再只会接VCC和GND了!HC-SR501人体红外传感器的触发模式、延时和灵敏度到底怎么调?
  • Leather Dress Collection效果展示:Leather Leather Bandeau Cargo Pants机能口袋结构特写
  • GLM-OCR效果展示:94.6分SOTA模型,实测识别发票、合同、论文效果惊艳
  • AMD显卡玩转AI绘画:RX 5600XT安装秋叶SD整合包保姆级避坑指南(HIP+ZLUDA)
  • Typora风格文档化:使用Markdown实时记录PyTorch 2.8实验过程
  • 像素剧本圣殿参数详解:ScriptGen LoRA适配器与8-Bit UI协同机制
  • 实战演练:基于快马平台与opencv,从零构建车牌识别系统
  • 南北阁Nanbeige 4.1-3B企业级应用:软件测试用例的自动化生成与评审
  • VC++6.0调试技巧:如何避免【no matching symbolic information found】错误(新手必看)
  • 开箱即用!圣女司幼幽造相Z-Turbo镜像,三步搭建你的AI画师
  • guiscrcpy跨平台部署指南:Windows、Linux、macOS全攻略
  • 从SLICEM结构图到代码:手把手教你用Vivado玩转7系列FPGA的移位寄存器
  • Phi-3 Forest Lab应用场景:科研人员实验设计思路启发助手
  • 不止是CPU主频:深度拆解Aurix TC3XX的时钟树,如何为CAN、ADC、以太网外设分配最佳时钟?
  • QT桌面应用开发:集成Kandinsky-5.0-I2V-Lite-5s的本地视频创作工具
  • 旋片真空泵厂家有哪些?水环真空泵哪家好?2026年真空泵厂家推荐:盛飞真空设备领衔 - 栗子测评
  • 告别短接!深入OEC-Turbo原系统:通过TTL串口日志分析,寻找无损刷机的可能性