今天不学习,明天变垃圾!
本文主要内容:字节流、字符流的输入输出,文件的基本操作及文件内容读写操作(有练习题!)。
狭义的文件:存储在硬盘上的数据,以“文件”为单位进行组织。
1)常见的就是平时的文件,如文本文件、图片、office系列、视频、音频、可执行程序等
2)文件夹也叫做“目录”,是一种特殊的文件。
3)硬盘的特点:
① 硬盘存储空间大,内存存储空间小
② 硬盘访问速度慢,内存访问速度快,可以差3-4个数量级
③ 硬盘的成本比较低,内存的成本比较高
④ 硬盘上的数据断电不会丢失,内存上的数据断电会丢失(也就是“持久化”的问题)
4)【ps: 操作系统都有“交换文件”操作,内存不够的时候就会往硬盘上写数据,使用硬盘来代偿内存(也就是让程序慢点,但是不会崩溃)】
5)(ps:台式机加内存一般好加,但是换CPU一般不好换,因为CPU是和主板适配的,换CPU很可能就一起换主板;主板又是和硬盘/内存都是适配的,一部小心就全换了。
笔记本一般是没法升级硬件的,因为笔记本厂商给的扩展性是非常低的)
广义的文件
操作系统是要负责管理软硬件资源的,操作系统(如:Linux、Unix)往往就会把这些资源都统一成抽象的“文件”来进行管理。也就是“一切皆文件”的思想。
注:目录之间的分隔符,可以使用\(反斜杠),也可以使用/(斜杠)——这只是针对Windows系统,但是其默认还是使用反斜杆\。
对于Linux等系统,只支持斜杠/
(但是在我们自己写代码的时候,建议使用斜杠/表示路径)
(ps:注意区分内存和外存)
2)相对路径:(从当前目录开始。)首先得有一个“基准路径”,也叫“工作路径”。相对路径就是以基准路径为起点,往下继续咋走才能到达目标的路径表示方式。
在相对路径中使用.表示“当前目录”。
如:D:\Navicat\Navicat for MySQL\navicat.exe 是绝对路径,而如果基准路径是D:\Navicat,那么相对路径就是:.\Navicat for MySQL\navicat.exe
如:当前的工作路径是:D:/program/Wechat/Bin,所要找的文件的绝对路径是D:/program/qq/Bin/QQ.exe,则使用相对路径该怎么找呢?
. ./ ->返回当前路径D:/program/Wechat/Bin的上一级路径D:/program/Wechat ;此时还是不行,再进行
. ./ -> 返回当前路径D:/program/Wechat的上一级路径D:/program
此时已经可以开始继续向下查找了,所以最后的相对路径就是: . ./. ./qq/Bin/QQ.exe
谈到相对路径,务必要明确工作目录/基准目录是啥。
Windows下是无法通过. .来到“此电脑”这一级的,也就是盘之间没法使用. .切换;但是Linux下没有盘符的概念,也就不涉及这个问题。 Windows上盘之间的切换是通过直接输入盘符来改变的,如D:
如何确定工作目录是啥?(几种常见情况)
① 如果是通过命令行来执行程序,此时当前命令所在的目录就是工作目录
② 如果是使用IDEA来执行Java程序,此时的工作目录就是IDEA打开的项目的目录
③ 如果是使用TomCat来运行一个war包,工作目录就是TomCat的bin目录
实际开发中以相对路径为主,很少使用绝对路径,绝对路径可能导致程序在不同主机上无法正常运行,因为找不到文件。
文件操作是属于操作系统层面提供的一些API,不同的操作系统提供的API是不一样的。
Java作为一个跨平台的语言,为了统一代码,就在JVM中把不同系统的操作文件的API进行了封装;java就可以使用java中的库的代码来操作文件了。



- 以上的方法/操作,主要都是在操作“文件系统”,而不是针对文件内容的操作。
(“文件系统”是操作系统中管理文件的核心功能。)- 方法/操作就是如:新增文件、删除文件、新增目录、列出目录内容、重命名、获取路径…
- IOException:IO中常见的异常。
- 注意字符串数组打印方法:Arrays.toString(字符串数组名)
- 文件类型是啥?
——准确来说,文件类型是与文件内部的数据格式相关联的,与文件的后缀名无关。- 针对文件内容操作,涉及到的关键操作是:读文件和写文件。
- 注:删除目录也用delete
标准库中给这些父类提供了各种子类的实现来适应不同场景下的读写
子类举例:
① InputStream的子类:FileInputStream
② OutputStream的子类 FileOutputStream
③ Reader的子类:FileReader
④ Writer的子类:FileWriter
InputStream是一个抽象类,不能实例化。
要想读写文件,就要在操作之前打开文件(创建实例),操作结束之后关闭文件(close)。
一定要关闭文件!否则可能会造成资源泄露,导致后续打不开该文件。
InputStream的相关方法:

FileInputStream的构造方法:

参考代码:Demo7字节流

字符流文件的读取:Reader、Scanner, 字符流文件的写入:Writer、PrintWriter。
针对文本文件,使用字符流的时候,其实还可以使用Scanner进行读取。
进行文件操作的时候,必须要进行close!
理由:1)每个进程都对应着PCB(可能是多个),PCB里面有一个字段叫“文件描述符表”(同一个进程里多个PCB共同使用同一份文件描述符表)。
2)“文件描述符表”就相当于是一个数组/顺序表,进程每次打开一个文件就会在该表中创建一个项,这个项就表示一个文件。如果关闭文件,就会将表里对应的项给释放掉;如果不关闭,意味着这个表项就一直在该表中占着位置;如果是持续打开文件且从不进行关闭,此时表项就会被耗尽(因为存储的最大长度是有上限的),导致后续再打开文件就会打开失败。——这种行为称为:文件资源泄露,这是一个非常严重的问题,比程序崩溃还严重。
补充:JVM是有自动释放的策略的:对应的流对象如果被GC销毁了,是会关闭对应的文件(释放文件描述符表的元素/表项)。但是“对应的流对象被GC销毁”是不一定会发生的,如果我们写的代码有问题,但仍然保持了流对象的引用,此时就不会触发GC,文件就始终保持打开的状态,文件描述符表中的表项也不会被释放。
客户端一般不怕资源泄露问题,因为客户端一般运行一段时间后就会关闭;
服务器端是害怕资源泄露问题的,因为服务器要长期运行。
(ps. 建议使用hasNext与next,而不是hasNextLine与nextLine,后面这两个方法有很多坑)
如何确保文件一定执行到close呢?
① 使用try…finally {file.close(); }
② 使用 try with resources:把要关闭的对象写到try()中(也就是说直接在()中new实例对象!),当try结束后就会自动调用到对应对象的close方法!而且支持一个()中放多个对象, 多个对象的创建之间使用分号; 进行分割就ok了。
① 先让用户输入:要扫描的路径+你要查找的词/指定字符
② 遍历目录,找到名字匹配的文件遍历目录需要借助核心方法listFiles():能够把当前目录里的文件和子目录列举出来,但是这个方法只能列出一层,没法列出子目录中的内容。
解决方法:遍历listFiles的结果,针对每个元素进行判定,看它是一个普通文件还是一个目录,如果是普通文件就直接判定是否包含了要查的关键词,如果是目录就递归的调用listFiles。
(目录结构本质上是“树”,树的遍历使用递归) ③ 询问用户是否删除
注意:String类型比较相等如果使用==,则会一直false,因为String是引用类型,会new对象,比较的是地址!!
引用类型比较大小使用equals!!
把第一个文件打开,将里面的内容逐个字节的读取出来写到第二个文件中即可。
“逐个字节”就是要使用字节流来进行操作,字节流是可以用来拷贝文本文件的。
① 输入源文件、目标文件 + 判断是否存在该文件(路径是否正确)② 源文件按字节读出InputStream,目标文件按字节写入OutStream
try with resources的使用,可以在try之后自动关闭文件(此时针对的是两个对象)
① 输入扫描的路径和要查询的关键词 + 判断文件是否存在
② 递归的扫描目录
③查看是否包含关键字:
检查文件名中是否包含关键字;
检查内容中是否包含关键字(读取内容InputStream:同样使用try with source 避免忘记close文件—打开文件之后使用Scanner进行读文件:按行读取)
(注意:StringBuilder的拼接+按行读取+使用indexOf判定关键词是否存在!)
参考代码:Demo1-3
缓冲区buffer 和缓存cache是不一样的。