目录标题

  • 在学习IO流 过程中难免会跟文件或者目录打交道 这里 目录就是文件夹
  • 文件和目录
  • 这里 Java将它们抽象为File类 也就是说 File类既可以表示文件 也可以表示目录
  • 当你传入一个文件路径给它时 他就是代表一个文件 当你传入一个目录路径给它时 它就代表一个目录
  • File方法分类
  • File类有四十多个方法 其中与查询相关的操作有20多个 与修改相关的操作有10多个 与批量操作相关的有 6 个
  • 但是在这些方法里面 只有十一个方法是常用的
  • File
  • 接下来 我们学习 在日常开发中的应用
  • 创建 删除 重命名 文件
  • 首先是 创建 删除 重命名 文件 先创建一个File对象 并传入你要创建的文件完整路径 然后判断文件是否存在 当文件不存在时 调用createNewFile方法创建新文件 该方法有异常抛出 使用try-catch将其捕获 输出创建结果 例子编写完成
  • 练习
  • 接下来 重命名刚刚创建的新文件 当文件存在时 调用renameTo方法 重命名此文件 该方法需要传入一个File对象 File路径为新名称的路径 接收方法返回值 输出重命名结果 例子编写完成
  • 注意(此处的创建是代替原来名字相同的文件 不是重新创建一个) !!! 观察执行结果
  • 接下来 删除刚刚重命名的目录 当目录存在时 调用delete方法删除此目录 输出删除结果 例子编写完成 执行程序
  • 重点! 从执行结果来看目录删除成功 但是注意 刚刚删除的目录是一个空目录 如果目录下还有文件或者目录的话 那么该目录是无法被删除的 想要被删除的话 必须将目录下所有文件(包括目录)清空
  • 列出路径所有文件或目录
  • 对于目录嵌套目录的情况 建议使用递归进行删除 方法参数只有一个File 方法返回Boolean类型 删除成功返回true 否则返回false
  • 首先判断File是否为目录 当为目录时 列出目录下所有文件(包括目录)
  • 当 File数组不为空时 遍历它 然后调用recursionDelete方法进行递归删除 目录下的文件(或者目录) 最后 无论是文件 还是目录 都将调用delete方法删除 到此 递归方法编写完成
  • 练习
  • 接下来 我们试试该方法 创建File对象 传入要删除的目录路径 调用recursionDelete方法进行删除 输出删除结果 到此 例子编写完成
  • 编写代码 执行结果来看
  • 创建多级目录
  • 方法 还是先创建一个File对象 传入多级目录路径 当目录不存在时 调用mkdirs方法创建多级目录 输出创建结果 例子编写完成 观察执行结果
  • 总结本节内容
  • 介绍了File类的基本操作 和使用递归删除目录嵌套目录的情况
  • File类方法繁多 在实际开发中 它常用的也就那几个
  • 字符是如何存储在电脑上的
  • 在学习IO前 我们先要了解 字符是如何存储在电脑上的
  • 首先先来了解 文本.text 上的字符 是如何存储在电脑上的 它们需要先根据字符编码° 找到对应的码值 例如这里的ASCII码 字符“a”所对应的码值是97 "b" 是 98 "c"是99 剩下的以此类推
  • 找到对应的码值后 再将码值 转化为字节 存储在电脑上
  • 读文件的时候 再将这个过程反过来即可 总结: 本节介绍了 字符是根据字符编码 找到对应的码值 然后再将码值 转换为 字节 存储在电脑上
  • 字节输入流 InputStream
  • 读取文件中的内容
  • 我们可以借助InputStream旗下的子类 它和它的子类们都属于字节流 也就是按字节为单位读取数据的流 刚刚我们要读取的是文件数据 所以 选择FileInputStream 文件字节输入流
  • 所以可以看出 它用于读取文本、图片、音乐、视频等文件数据
  • 它一共有三个构造方法 你可以传一个文件路径 也可以传一个表示文件的File对象 这两个方法用得最多 也可以传一个文件描述符 这个构造方法几乎用不到 因为文件描述符 只能通过已经创建的流对象来获取
  • 通过其read方法 每次只读一个字节 直到最后没有可读的字节 返回 -1 作为结束标识 read方法返回的类型 并不是byte 而是int
  • 至于为什么 看下一个知识点 直接输出这些int值 得到的只是一些数字 这些数字就是字符所对应的字节值 对于字符而言 这些字节值就是码值 如果想要输出字符的话 需将其转换为char类型 然后就可以得到原来的字符
  • 练习
  • 编码练习 首先创建文件字节输入流 并传入文件路径 构造方法有异常抛出 使用try-catch将其捕获 然后调用read方法读取单个字节
  • read方法有异常抛出 使用catch将其捕获 接着输出刚刚读取到的字节值 这里输出的是一个数字 不便于观看 我们可以将其转换为char类型 以字符形式输出
  • 从执行结果来看 "a”是我们读取到的第一个字符 也是第一个字节 一行读一个 这种写法太低效 可以采用循环的方式来读 那循环什么时候结束呢
  • 当我们读到字节值为-1的时候结束
  • 这个程序还有更优的写法 可以将字节值定义在循环外 将读取字节的操作 和判断字节值是否为-1 写在循环条件中 这样的写法在实际开发中更为常见 从执行结果来看 文件中的每一个字节都读出来了
  • 总结
  • 本节介绍了输入流体系 及其读取字节的操作
  • 在实际开发中 FileInputStream 方法用的最多
  • read方法读的是字节为什么返回int
  • 在上一讲中 我们可以看到 read方法读的是字节返回的而是int 为什么read方法 返回的不是byte 而是int呢 这个和它的结束标识有关 也就是和这个 -1 有关
  • 在Java中 一个数字的二进制位 最高位是符号位 0 代表的是正数 1 代表的是负数 但一个负数 远不是将最高位置于1这么简单 它是其正数的原码 取反得到反码 再 +1 得到补码 最后的补码 才是一个负数的二进制表现形式 这是 -1 的二进制
  • 假设我们要读的文件中 正好有 -1 时 就无法判断 这是数据已经读完了 还是数据本身 就是 -1
  • 因此设计者 将read方法 的返回值类型 设置为int 四个字节 16 位 目的就是为了 防止直接读到 -1
  • 上方的字节值 是 255 想要原生的字节值时 只需再把它转为byte即可 高位全部去掉 只保留低 8 位 这样我们就得到了 原生数据 -1 以上就是 为什么read方法 返回int 的原因
  • 总结
  • 介绍了 read方法为什么返回int的原因 在面试中有可能会被问到 要重点理解掌握
  • 数据读取完以后为什么要关闭流?
  • 数据读完以后 调用close方法关闭流
  • 为什么一定要关闭流呢
  • 例如: 例如 在删除电脑文件时 如果这个文件正在打开或者正在使用 电脑是不会执行这个步骤的 提示 该文件正在使用无法删除的情况 那么这个时候 就需要关闭正在使用的文件 释放资源 然后才可以删除 这个和我们关闭流是一个道理的
  • 关闭流是一定要关闭的 但不是写在try代码块中 而是写在finally代码块中 这样 即使操作流的过程中发生异常 也会执行关闭流的操作 将创建流的操作 拆分为声明流 和初始化流 两部分 然后将关闭流的操作 移动到finally代码块中 在关闭流之前 先判断流是否为空 当流不为空时 再关闭流 close方法有异常抛出 使用try-catch将其捕获 到此 完整的输入流读取数据的书写格式就是下图三格式
  • 重点! 一共有四步 第一步 : 声明流 第二部:初始化流 第三步: 读取数据 第四步:关闭流 从执行结果来看:改写后的程序没有任何问题 流关闭以后 就不能再读取数据了 否则就会发生io异常
  • 总结:
  • 本节介绍了 为什么数据读完以后要关闭流 以及日常开发流的书写格式
  • 如何更高效的读取字节
  • 除了每次读取单个字节以外 我们还可以每次读取多个字节 以此来提升效率
  • 批量读取的字节 都装在一个容器里 这个容器就是字节数组 数组大小可以自己拟定 如此以来 数组有多大 每次就能读多少
  • 但是需要注意的是 数组是重复使用的 新读入的字节 会覆盖掉原来位置上的字节 这样就会产生 没覆盖全的情况 数组中还存在之前读入的字节 最后输出的结果 就会存在多余的字符 这个现象一会演示
  • 下面演示一个批量读取字节的示例 按照上一节讲解的代码书写格式中 编写好基本代码 读取数据的部分 目前还没写 创建一个长度为4 的字节数组 循环批量读取字节 读到 -1 时结束 每次都以字符串形式输出 长度为 4 的数组 刚刚好 分两次读完 没有多余的字节
  • 接下来 我们来演示 有多余字节的问题 基本代码一样! 只需将数组长度 由4 改为 3 这样就会出现多余的字符 多输出 一个 “f” 产生问题的原因 前面的内容已经讲解过了 问题也很好解决!!! 我们只需记录一下 read方法实际读入的字节数 在输出的时候 从下标 0 开始输出 输出的长度为实际读入的字节数 注意重点 : ((length = in.read(bytes)) != -1) 这样的话 便没有多余的字符了
  • 总结:
  • 本节介绍了 如何批量读取字节 在实际开发中 这种方式更加常用 这一节的批量读取字节的书写格式如下
  • 什么是字节输出流OutputStream
  • 它和输入流一样 也有一套体系 包括将数据输出到文件FileOutputStream 带缓冲区的 BufferedOutputStream 等其他一些输出流 其中最为常用的 就是 FileOutputStream
  • 借助 FileOutputStream 我们可以向文件中写入数据 完成复制文件等操作
  • FileOutputStream 一共有五个构造方法 其功能如下图所示 第一个参数 String name 创建文件输出字节流 并指定文件路径 默认覆盖文件 第二个参数 boolean append 创建文件输出字节流 并指定文件路径 是否追加文件 默认覆盖文件
  • 下面 通过将一个字符串写入到文件中 来学习 输出流的基本知识 而这个过程 正好和读取字节相反
  • 先根据编码表 找到字符所对应的十进制码值 再将码值 转换为 字节 转换为字节以后 再调用输出流的write方法 依次写入文件中
  • 写入的方式有两种 一种是: 每次写入单个字节 还有一种是: 每次写入一个字节数组 后者效率更高
  • 练习
  • 下面我们就采用后者 来编写一份示例 首先声明一个输出流 然后初始化它 并传入你要输出的文件路径中
  • 创建FileOutputStream时有异常抛出 使用try-catch 将其捕获 写上finally 代码块
  • 在finally代码块中 调用close方法关闭流 在关闭流之前 先判断流是否为空 当流不为空时 再关闭流
  • close方法有异常抛出 使用 try-catch 将其捕获
  • 练习
  • 重点!!! 最后 我们来编写 写入数据的部分 创建你要写入的数据 比如这里的 "二哥一直坚持abcdefgh" 然后调用其 getBytes方法 转为字节数组
  • 因为写入数据必须按字节为单位 所以这里要转为 字节数组 接着 调用输出流的write方法 将字节数组 中的数据 写到文件中
  • write方法有异常抛出 使用catch将其捕获 至此 写入数据的部分编写完成 整个例子也编写完成 执行程序观看
  • 一般来说 程序没有报错 就说明 写入成功 再来查看文件 文件可以看出 程序运行成功!!!
  • 注意点: 当我们初始化输出流的时候 这里的输出文件 是我本身 事先创建好了的!!! 如果文件没有事先创建好 还需要使用 file对象 来进行 创建!!!
  • 在创建之前 无论文件是否存在 都要进行判断一下 文件的存在情况
  • 养成习惯 因为在实际开发中 不可能还去看看 文件到底创建了没有 一个提示 重点注意
  • 总结
  • 本节介绍了 输出流体系 及其 基本操作 在实际开发中 FileOutputStream 用的比较多


在学习IO流 过程中难免会跟文件或者目录打交道
这里 目录就是文件夹

文件和目录

java file读取 字节_文件路径

这里 Java将它们抽象为File类
也就是说
File类既可以表示文件 也可以表示目录


java file读取 字节_java_02

当你传入一个文件路径给它时
他就是代表一个文件
当你传入一个目录路径给它时
它就代表一个目录



java file读取 字节_java file读取 字节_03

File方法分类

File类有四十多个方法
其中与查询相关的操作有20多个
与修改相关的操作有10多个
与批量操作相关的有 6 个



java file读取 字节_java file读取 字节_04

但是在这些方法里面
只有十一个方法是常用的

java file读取 字节_文件路径_05

File

接下来 我们学习
在日常开发中的应用


java file读取 字节_数据_06

创建 删除 重命名 文件

首先是 创建 删除 重命名 文件
先创建一个File对象
并传入你要创建的文件完整路径
然后判断文件是否存在
当文件不存在时
调用createNewFile方法创建新文件
该方法有异常抛出
使用try-catch将其捕获
输出创建结果
例子编写完成



练习

java file读取 字节_文件路径_07

接下来
重命名刚刚创建的新文件
当文件存在时
调用renameTo方法
重命名此文件
该方法需要传入一个File对象
File路径为新名称的路径
接收方法返回值
输出重命名结果
例子编写完成



注意(此处的创建是代替原来名字相同的文件 不是重新创建一个)
!!!
观察执行结果



java file读取 字节_文件路径_08

接下来
删除刚刚重命名的目录
当目录存在时
调用delete方法删除此目录
输出删除结果
例子编写完成 执行程序



java file读取 字节_java_09

重点!
从执行结果来看目录删除成功
但是注意
刚刚删除的目录是一个空目录
如果目录下还有文件或者目录的话
那么该目录是无法被删除的
想要被删除的话
必须将目录下所有文件(包括目录)清空



列出路径所有文件或目录

对于目录嵌套目录的情况
建议使用递归进行删除
方法参数只有一个File
方法返回Boolean类型
删除成功返回true
否则返回false



java file读取 字节_输出流_10

首先判断File是否为目录
当为目录时
列出目录下所有文件(包括目录)



当 File数组不为空时
遍历它
然后调用recursionDelete方法进行递归删除
目录下的文件(或者目录)
最后
无论是文件
还是目录
都将调用delete方法删除
到此
递归方法编写完成



java file读取 字节_文件路径_11

练习

接下来
我们试试该方法
创建File对象
传入要删除的目录路径
调用recursionDelete方法进行删除
输出删除结果
到此
例子编写完成



java file读取 字节_java_12

编写代码
执行结果来看


java file读取 字节_数据_13

创建多级目录

方法
还是先创建一个File对象
传入多级目录路径
当目录不存在时
调用mkdirs方法创建多级目录
输出创建结果
例子编写完成
观察执行结果



java file读取 字节_文件路径_14

java file读取 字节_java file读取 字节_15

总结本节内容

介绍了File类的基本操作
和使用递归删除目录嵌套目录的情况



java file读取 字节_输出流_16

File类方法繁多
在实际开发中
它常用的也就那几个



java file读取 字节_数据_17

字符是如何存储在电脑上的

在学习IO前
我们先要了解
字符是如何存储在电脑上的



首先先来了解 文本.text 上的字符 是如何存储在电脑上的
它们需要先根据字符编码°
找到对应的码值
例如这里的ASCII码
字符“a”所对应的码值是97
“b” 是 98
"c"是99
剩下的以此类推



找到对应的码值后
再将码值
转化为字节
存储在电脑上



java file读取 字节_输出流_18

读文件的时候
再将这个过程反过来即可
总结:
本节介绍了 字符是根据字符编码
找到对应的码值
然后再将码值 转换为 字节
存储在电脑上



java file读取 字节_文件路径_19

字节输入流 InputStream

读取文件中的内容



java file读取 字节_输出流_20

我们可以借助InputStream旗下的子类
它和它的子类们都属于字节流
也就是按字节为单位读取数据的流
刚刚我们要读取的是文件数据
所以
选择FileInputStream
文件字节输入流



java file读取 字节_文件路径_21

所以可以看出
它用于读取文本、图片、音乐、视频等文件数据



java file读取 字节_文件路径_22

它一共有三个构造方法
你可以传一个文件路径
也可以传一个表示文件的File对象
这两个方法用得最多
也可以传一个文件描述符
这个构造方法几乎用不到
因为文件描述符
只能通过已经创建的流对象来获取




java file读取 字节_数据_23

通过其read方法
每次只读一个字节
直到最后没有可读的字节
返回 -1 作为结束标识
read方法返回的类型
并不是byte
而是int



java file读取 字节_java_24

至于为什么
看下一个知识点
直接输出这些int值
得到的只是一些数字
这些数字就是字符所对应的字节值
对于字符而言
这些字节值就是码值
如果想要输出字符的话
需将其转换为char类型
然后就可以得到原来的字符




java file读取 字节_输出流_25

练习

编码练习
首先创建文件字节输入流
并传入文件路径
构造方法有异常抛出
使用try-catch将其捕获
然后调用read方法读取单个字节



read方法有异常抛出
使用catch将其捕获
接着输出刚刚读取到的字节值
这里输出的是一个数字
不便于观看
我们可以将其转换为char类型
以字符形式输出



从执行结果来看
"a”是我们读取到的第一个字符
也是第一个字节
一行读一个
这种写法太低效
可以采用循环的方式来读
那循环什么时候结束呢



当我们读到字节值为-1的时候结束



java file读取 字节_java_26

这个程序还有更优的写法
可以将字节值定义在循环外
将读取字节的操作
和判断字节值是否为-1
写在循环条件中
这样的写法在实际开发中更为常见
从执行结果来看
文件中的每一个字节都读出来了



java file读取 字节_输出流_27

总结

本节介绍了输入流体系
及其读取字节的操作



java file读取 字节_java_28

在实际开发中
FileInputStream 方法用的最多



java file读取 字节_java file读取 字节_29

read方法读的是字节为什么返回int

在上一讲中 我们可以看到
read方法读的是字节返回的而是int
为什么read方法 返回的不是byte 而是int呢
这个和它的结束标识有关
也就是和这个 -1 有关



java file读取 字节_输出流_30

在Java中
一个数字的二进制位
最高位是符号位
0 代表的是正数
1 代表的是负数
但一个负数
远不是将最高位置于1这么简单
它是其正数的原码
取反得到反码
再 +1
得到补码
最后的补码
才是一个负数的二进制表现形式
这是 -1 的二进制



java file读取 字节_数据_31

假设我们要读的文件中
正好有 -1 时
就无法判断
这是数据已经读完了
还是数据本身 就是 -1



java file读取 字节_java file读取 字节_32

因此设计者 将read方法 的返回值类型
设置为int
四个字节
16 位
目的就是为了 防止直接读到 -1



java file读取 字节_输出流_33

上方的字节值
是 255
想要原生的字节值时
只需再把它转为byte即可
高位全部去掉
只保留低 8 位
这样我们就得到了 原生数据 -1
以上就是 为什么read方法 返回int 的原因



java file读取 字节_java_34

总结

介绍了
read方法为什么返回int的原因
在面试中有可能会被问到
要重点理解掌握



java file读取 字节_java file读取 字节_35

数据读取完以后为什么要关闭流?

数据读完以后
调用close方法关闭流


java file读取 字节_java_36

为什么一定要关闭流呢


java file读取 字节_文件路径_37

例如:
例如 在删除电脑文件时 如果这个文件正在打开或者正在使用
电脑是不会执行这个步骤的 提示 该文件正在使用无法删除的情况
那么这个时候 就需要关闭正在使用的文件
释放资源
然后才可以删除
这个和我们关闭流是一个道理的



java file读取 字节_数据_38

关闭流是一定要关闭的
但不是写在try代码块中
而是写在finally代码块中
这样 即使操作流的过程中发生异常
也会执行关闭流的操作
将创建流的操作 拆分为声明流 和初始化流 两部分
然后将关闭流的操作
移动到finally代码块中
在关闭流之前 先判断流是否为空
当流不为空时 再关闭流
close方法有异常抛出 使用try-catch将其捕获
到此 完整的输入流读取数据的书写格式就是下图三格式



java file读取 字节_输出流_39

重点!
一共有四步
第一步 : 声明流
第二部:初始化流
第三步: 读取数据
第四步:关闭流
从执行结果来看:改写后的程序没有任何问题
流关闭以后 就不能再读取数据了
否则就会发生io异常



java file读取 字节_java file读取 字节_40

总结:

本节介绍了 为什么数据读完以后要关闭流
以及日常开发流的书写格式


java file读取 字节_java file读取 字节_41

如何更高效的读取字节

除了每次读取单个字节以外
我们还可以每次读取多个字节
以此来提升效率



批量读取的字节
都装在一个容器里
这个容器就是字节数组
数组大小可以自己拟定
如此以来
数组有多大
每次就能读多少



但是需要注意的是
数组是重复使用的
新读入的字节 会覆盖掉原来位置上的字节
这样就会产生 没覆盖全的情况
数组中还存在之前读入的字节
最后输出的结果 就会存在多余的字符
这个现象一会演示



java file读取 字节_java file读取 字节_42

下面演示一个批量读取字节的示例
按照上一节讲解的代码书写格式中
编写好基本代码
读取数据的部分 目前还没写
创建一个长度为4 的字节数组
循环批量读取字节 读到 -1 时结束
每次都以字符串形式输出
长度为 4 的数组 刚刚好
分两次读完
没有多余的字节



java file读取 字节_java_43

接下来 我们来演示 有多余字节的问题
基本代码一样!
只需将数组长度 由4 改为 3
这样就会出现多余的字符
多输出 一个 “f”
产生问题的原因 前面的内容已经讲解过了
问题也很好解决!!!
我们只需记录一下
read方法实际读入的字节数
在输出的时候 从下标 0 开始输出
输出的长度为实际读入的字节数
注意重点 : ((length = in.read(bytes)) != -1)
这样的话 便没有多余的字符了



java file读取 字节_文件路径_44

总结:

本节介绍了 如何批量读取字节
在实际开发中 这种方式更加常用
这一节的批量读取字节的书写格式如下



java file读取 字节_数据_45

什么是字节输出流OutputStream

它和输入流一样
也有一套体系
包括将数据输出到文件FileOutputStream
带缓冲区的 BufferedOutputStream
等其他一些输出流
其中最为常用的 就是 FileOutputStream




java file读取 字节_输出流_46

java file读取 字节_java file读取 字节_47

借助 FileOutputStream
我们可以向文件中写入数据
完成复制文件等操作



java file读取 字节_java file读取 字节_48

FileOutputStream 一共有五个构造方法
其功能如下图所示
第一个参数 String name 创建文件输出字节流 并指定文件路径 默认覆盖文件
第二个参数 boolean append 创建文件输出字节流 并指定文件路径 是否追加文件 默认覆盖文件



java file读取 字节_java file读取 字节_49

下面 通过将一个字符串写入到文件中
来学习 输出流的基本知识
而这个过程 正好和读取字节相反



先根据编码表
找到字符所对应的十进制码值
再将码值 转换为 字节
转换为字节以后 再调用输出流的write方法
依次写入文件中



写入的方式有两种
一种是: 每次写入单个字节
还有一种是: 每次写入一个字节数组
后者效率更高



java file读取 字节_数据_50

练习

下面我们就采用后者
来编写一份示例
首先声明一个输出流
然后初始化它
并传入你要输出的文件路径中



创建FileOutputStream时有异常抛出
使用try-catch 将其捕获
写上finally 代码块



在finally代码块中
调用close方法关闭流
在关闭流之前
先判断流是否为空
当流不为空时
再关闭流



close方法有异常抛出
使用 try-catch 将其捕获



java file读取 字节_文件路径_51

练习

重点!!!
最后 我们来编写 写入数据的部分
创建你要写入的数据
比如这里的 “二哥一直坚持abcdefgh”
然后调用其 getBytes方法 转为字节数组



因为写入数据必须按字节为单位
所以这里要转为 字节数组
接着 调用输出流的write方法
将字节数组 中的数据 写到文件中



write方法有异常抛出
使用catch将其捕获
至此 写入数据的部分编写完成
整个例子也编写完成
执行程序观看



一般来说 程序没有报错 就说明 写入成功
再来查看文件
文件可以看出 程序运行成功!!!



java file读取 字节_数据_52

注意点:
当我们初始化输出流的时候
这里的输出文件
是我本身 事先创建好了的!!!
如果文件没有事先创建好
还需要使用 file对象 来进行 创建!!!



在创建之前
无论文件是否存在
都要进行判断一下
文件的存在情况



养成习惯
因为在实际开发中 不可能还去看看
文件到底创建了没有 一个提示 重点注意



java file读取 字节_java_53

总结

本节介绍了
输出流体系 及其 基本操作
在实际开发中
FileOutputStream 用的比较多



java file读取 字节_文件路径_54