Java 对于文件操作 的API接口
1.针对文件系统的操作
比如:创建、删除、重命名、列出目录内容()等等
类似于右键一个文件可以实现的部分功能
2.针对文件内容的操作
比如:读文件,写文件
输出文件中的内容
修改文件中的内容
下面是关于这两个方面(文件系统、文件内容)操作 的类
一、文件系统
使用Java.io 包中的 File类 来进行
File
1.属性
pathSeparator
//路径分隔符,主流采用 /
Windows 系统 一般是采用 \ ,同时也支持 /
Linux系统 采用 /
2.构造方法
最主要的是中间的,传入一个路径,可以是绝对路径或相对路径
!!!相对路径注意点:
(1)在idea中执行这个构造方法,则此时这个项目所在的目录(的路径),就是这个相对路径的起点
(2)如果是将代码打包成Jar包执行,jar包所在的目录(的路径),就是相对路径的起点
这里的路径可以是真实存在的,也可以是虚假的
3.类方法
(1)getName()
//获取file 构造方法中 路径目标的文件名字
(2)getParent()
//返回从盘符开始到 目标文件 bless.txt位置之前的路径
(3)getPath()
//获取file 构造方法中 路径目标的文件的整个地址(构造函数中传入的路径)
(4)getAbsolutePath()
//获取file 构造方法中 路径目标的文件从盘符开始到目标文件的路径
(5) getCanonicalPath()
//获取file 构造方法中 修饰(去除过多于描述)的路径
2345 这四种方分两种情况讨论有什么区别:
①构造函数传入的是 绝对路径
如果传入的是绝对路径则,输出结果一致
②构造函数传入的是 相对路径
可以看到
getParent() 输出的是bless.txt 上一级的目录路径
而我上一篇文章JavaEE初阶-IO的一些前置知识说过关于点代表的就是目标文件路径的上一级的绝对路径,此时点所代表的就是当前idea项目所在的路径
getPath() 输出的是传入构造函数中的路径
!!!最重要的是后面这两个的区别
一个是绝对路径 getAbsolutePath
一个是修饰过后的绝对路径 getCanonicalPath
两者区别如上图可以看出修饰过后的路径少了个 .
而上面又说过, 点 . 代表的是上级目录的路径
所以这里的点属于是多余了,修饰过后的路径就把这个 点 . 去除掉了
从这里往后的方法都是字面意思我会附上简单的列子图片:
(6) exists()
//判断构造函数中 传入的路径目标是否存在
(7) isDirectory()
//是否为目录(简单来说就是打开后,还存有其他的普通文件或文件夹)
(8) isFile()
//是否为文件(普通文件,可执行的文件)
(9) createNewFile()
//在构造方法中的目标路径,创建目标文件
(10)delete()
//删除构造方法中的路径目标文件
(11)deleteOnExit()
//在代码结束时,删除构造方法中的路径目标文件
!!!这种文件被称为 临时文件
平常我们写word的时候他会自动进行备份,以防电脑断电死机导致文件内容丢失
写的我头都大了最后7个,写完下班
(12) list()
//直接上列子
(13) listFiles()
//同上,只是两种方法返回值不同
(14) mkdir()
//创建单个目录
(15) mkdirs()
//创建多级目录
(16) renameTo(File dest)
//重命名
除了重命名,还有一个移动文件的作用
(17) canRead()
//表示用户对于这个文件的权限(可读-能不能打开看)
(18)canWirte
//可写-能不能修改文件的名字、删除文件、创建文件等等操作
骗你的怎么可能下班,别忘了还有一个针对文件内容的操作
简单来说,针对文件系统的操作就是, 打开文件之前的操作
针对文件内容的操作就是, 打开文件之后的操作
二、文件内容
针对文件内容的操作
采用 流 这个概念
把数据分成合理的小段来读写,有以下两大流派
1.字节流
以字节为单位,对数据进行读写
2.字符流
以字符为单位,对数据进行读写
而这里的字符单位是根据字符编码来决定的
比如汉字采用 UTF8 则三个字节代表一个汉字
则每次读写都得以 3个字节(一个汉字)为单位读写字符
累了下班明天再写先发布了骗点浏览量
满血复活上班
3.下面介绍两大流派的类方法
① 字节流的两个代表:
inputStream OutputStream
//注意这里的 输出和输入以CPU为主视角
- 数据从硬盘流入CPU叫输入
- 数据从CPU流到硬盘叫输出
字节流最小读写单位 为一个字节
文件内容的操作无非就四种:
- 打开文件
- 关闭文件
- 读文件
- 写文件
inputStream 123
outputStream 124
一个读 一个写
inputStream
可以看到他是一个抽象类,抽象类本身不能实例化,标准库给我们提供了其他inputStream的子类
抽象类的目的:可以让编译器检查一些你不希望实例化的类
实例化一个FileInputStream相当于打开文件的过程
此时这里会抛出一个 FileNotFoundException(文件不存在异常)
打开一个文件,就需要在不使用的时候关闭
此时就会抛出另一个异常IOException 而 FileNotFoundException 是 IOException的一个子类
所以 它会被 IOException 替换掉
!!!下面讲讲为什么要关闭文件
进程 由 N个PCB,PCB中的进程控制块,有一些属性来描述当前进程相关的信息
其中有一个属性就是:文件描述符表(顺序表或数组构成)
//记录了当前进程打开了哪些文件
数组/顺序表 中的每个元素 都是一个结构体 里面记录了 你所打开的文件的时间、文件的路径、大小等等
每次打开文件都需要在文件描述符表中占据一个位置,如果不主动关闭,一直打开文件
文件描述符表就会爆满.
那他为什么不会像我们学的数据结构那样自动扩容呢?
因为 扩容的代价是很大的(更多的时间,和更多的空间),他需要把原来的数据复制到另一个更大的空间当中
文件描述符表所属的PCB 是属于操作系统中的,而操作系统是整个电脑的总管
所以他要求性能优先,如果为了扩容导致计算机系统负责调配进程的最重要的部分造成卡顿
那就不单单是一点空间的问题了
所以所以,文件描述符表的大小是固定有限的
文件描述符表资源被耗,就无法打开新的文件
说了这么多,那有什么方法能帮助我们自动关闭呢?
当我们尝试放到 try 和finally 中的时候发现
finally 中 他不认识try中的对象
也就是 try中的代码是个局部变量生命周期只要出了try就结束了
此时我们还可以用try的另一种写法把 对象当作参数传给try
一旦inputStream出了try代码块,try会帮我们自动调用close
而try里面传入的类,需要实现Closeable 接口
说了半天终于说完 打开和 关闭了
接下来就是 读
说之前先说说,无参版本read() 方法的返回值
虽然明面上写的是 int ,但实际上的大小只需要比1个字节大
一个字节:-128~127 ,代码逻辑需要返回值被转成整数变成了 0 ~ 255
总大小不变的情况下,需要一个值表示 文件读取结束
那就是 -1 ,所以实际是-1 和 0 ~ 255
超过了byte能表示的最大的值,所以规定返回值是int
至于为毛不用short,基本没用过,不常用
不吧啦了回到正题:
1.无参的版本:
//每次只读一个字节
2.传入一个“输出型数组”
//一次读若干个字节
//这里的byte[] 是引用类型的(在方法内部对这个数组内容进行修改,方法外部也能生效)
类似C语言说的的实参,反过来就是形参(方法中改了,方法外没变)
相当于,读一个 字节 并且把他的 值 传入 数组中填充
这里就是上面说的输出型参数,提供一个空的数组,随后返回一个修改好的数组
3.三种参数的版本
//一次读若干个字节,放到指定位置,并且每次最大放len个
往数组的off位置,开始填充,最多填充len个
三种read的简单代码列子
无参版本:
汉字在utf8中,一个汉字是三个字节, 所以 e4 ba ba代表 人 在UTF-8编码表中的 编码16进制
//如果文档中的内容换成 英文 则这里打印的数据就是纯数字的类型
//因为英文的字符编码是ascii码表,ASCII码表用数字代表英文
当b == -1 说明文件中的内容 读取完了
后两个带参数版本:
这里我感觉就不需要加while循环了,因为他是一次读完所有的内容存放到数组中
如果想根据读出来的字节转换成,她所对应的字符
OutputStream
构造方法
!!! OutputStream 的写操作,不是在原本文件内容的末尾上写,而是先清空,再重头开始写
造成清空的原因不是因为调用了Write方法,而是在构造方法时就已经清空了,上图:
调用前:
调用后:
如果你不想把原有的内容清空,可以在把参数append设为 true
append:追加
(1) void Write(int b)
(2) void Write(byte[] b)
//把数组中的所有元素全部写人文件中
(3) void Write(byte[] b,int off,int len)
//把数组中的一部分元素写入文件中
② 字符流的两个代表:
Reader Writer
字符流最小读写单位 为一个字符
一个字符,可以由多个字节组成(有该字符的字符编码确定)
终于到最后了头都大了
Reader
构造方法:
和字节流一样,不过参数时以char为单位
第三个其实也是字符数组,只不过是通过一个类封装过后的字符数组
!!!我知道了为什么要加上 while:
因为当你数组太小不能一次性装下整个文件的内容时,可以通过反复循环读取文件内容,并每次打印存到数组内的内容 上图!!!:
这里还有一个小小的问题,那就是char 占两个字节,而中文占 3 个字节
那是如何把 3 个字节的汉字 存入 2个字节的char当中的呢???
先试着把char数组当中的每个 元素打印出来看看是什么情况:
累了睡觉明天再写
Writer
基本同上
Writer.write(String s)
可以传入一个字符串,就方便了我们之间修改内容