硅基计划6.0 JavaEE 叁 文件IO

文章目录
- 一、起源
- 二、路径&目录结构
- 三、文件类型
- 四、文件操作
- 五、File类
- 1. 内部属性
- 2. 主要构造方法
- 3. 主要方法
- 1. 返回值是String
- 2. 返回值是boolean
- 3. 返回File
- 4. 返回`String[]`
- 5. 返回`File[]`
- 6. 文件目录重命名或移动
- 7. 针对文件内容的操作
- 1. `InputStream`抽象类
- 2. `OutputStream`抽象类
- 3. `Reader`抽象类
- 4. `Write`抽象类
- 六、使用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[]对象中
我们用一个代码实际演示下几个方法使用,我么使用下图目录作为演示

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"这个目录下
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为偏移量即相对于文件开头第一个字节的位置
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码值}}
但是每次都是一个字节一个字节从硬盘读取,开销太大且效率慢,因此我们使用输出型参数
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捕获异常
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()方法
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,表示你现在做的操作是针对内容进行拼接,并且每一次拼接都在内容末尾
public static void main(String[] args) {try(OutputStream outputStream = new FileOutputStream("C:\\JavaCode\\java-ee-career\\TestProjectNewForEE20251107\\test.txt")){outputStream.write(65);//对于字符AoutputStream.write(66);//对于字符Bbyte[] bytes = {67,68};//对应字符C,DoutputStream.write(bytes);}catch (IOException e){e.printStackTrace();}}
3. Reader抽象类
这个是字符流的文件读取操作,对于read()有四个版本
- 版本一:一次性读取一个字符
- 版本二:读取一次字符数组
- 版本三:读取一个字符数组封装的
charBuffer - 版本四:对字符数组的一部分内容读取
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();//读取到末尾返回-1if (n == -1) {break;}char c = (char)n;System.out.println(c);}}catch (IOException e){e.printStackTrace();}}
4. Write抽象类
这个是字符流的文件写操作,对于wirte()有五个版本
- 版本一:一次性写一个字符
- 版本二:一次性写一个字符串
- 版本三:一次性写一个字符数组
- 版本四:一次性写一个字符串的子串
- 版本五:写一个字符数组的一部分
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
我们这样搭配可以对文件的内容进行格式化解析
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
这个类可以让我们可以将内容以固定格式写入文件,写入的数据先在临时缓冲区,当关闭资源的时候自动写入内容
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();}
八、使用递归去查看当前目录的所有子目录
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("删除取消");}}
}
九、复制普通类型文件
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();}}
十、扫描指定目录的所有子目录中普通文件内容是否包含指定内容
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();}}
