文章目录
- 前言
- 一、File 类
- 1, 构造方法
- 2, 成员方法
- 二、字节流输入输出
- 1, 字节流输入 InputStream
- 1.1, 每次输入一个字节
- 1.2, 每次输入多个字节
- 2, 字节流输出 OutputStream
- 2.1, 每次输出一个字节
- 2.2, 每次输出多个字节
- 总结
前言
各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你:
📕 JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等
📗 Java数据结构: 顺序表, 链表, 堆, 二叉树, 二叉搜索树, 哈希表等
📘 JavaEE初阶: 多线程, 网络编程, TCP/IP协议, HTTP协议, Tomcat, Servlet, Linux, JVM等(正在持续更新)
之前几篇文章陆续介绍了 Java 多线程的相关知识, 本篇继续介绍文件操作和 IO 流相关的基础内容
文件, 狭义上讲, 电脑中 C 盘 D 盘中的文件, 广义上讲, 很多操作系统为了实现接口的统一性,将所有的 IO 设备都抽象成了文件
IO 是指 : Input输入 和 Output输出
IO 设备就是 : 可以把数据输入到计算机, 或者把计算机中的数据输出的外部设备
提示:是正在努力进步的小菜鸟一只,如有大佬发现文章欠佳之处欢迎批评指点~ 废话不多说,直接上干货!
识狭义上的文件 : 针对硬盘这种持久化存储的 I/O 设备,当我们想要进行数据保存时,往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概念,就类似办公桌上的一份份真实的文件一般。
狭义上的文件包括文件夹和普通文件, 例如下图 : D 盘就是一个文件夹, 打开之后包含若干个文件夹和普通文件. 这些文件夹再打开可能还包含若干文件夹和普通文件
可以发现, 文件的存储结构实际上就是树形结构, 文件夹都是"非叶子节点", 普通文件都是"叶子节点" , 文件夹的专业术语是目录
一、File 类
Java 标准库封装了一个 File 类来操作文件, 如果把电脑上的文件当作电视机, File 类的对象就是遥控器, 通过路径把 File 对象实例出来(于遥控器和电视机连接上了), 就可以利用 File 对象(遥控器)来操作文件(电视机)
不完全准确的类比, 但是意思差不多
注意 : 实例化出来 File 对象, 并不代表电脑上就真实存在这个文件
1, 构造方法
File file = new File("D:\\Program Files\\Tencent\\QQ\\Bin\\test.txt");
构造方法的参数是该目标文件的路径, 这个路径可以是绝对路径, 也可以是相对路径, 都是字符串
绝对路径 : 从盘符开始, 到目标文件的完整路径
相对路径 : 从某个目录(工作目录)开始, 到目标文件的路径, 相对路径中, ".
"表示当前目录, "..
"表示上一级目录
如果以当前 Java 项目所在的目录为工作目录, 在这个目录下创建test.txt这个文件 相对路径就可以表示成 : ./test.txt
以相对路径的方式创建 file 对象的代码: File file = new File(“./test.txt”);
2, 成员方法
返回值 | 方法名 | 说明 |
---|---|---|
String | getParent() | 返回 File 对象的父目录文件路径 |
String | getName() | 返回 FIle 对象的纯文件名称 |
String | getPath() | 返回 File 对象的文件路径 |
String | getAbsolutePath() | 返回 File 对象的绝对路径 |
String | getCanonicalPath() | 返回 File 对象的修饰过的绝对路径 |
System.out.println(file.getName() + " --> 文件名");
System.out.println(file.getParent() + " --> 父目录文件路径");
System.out.println(file.getPath() + " --> 路径");
System.out.println(file.getAbsolutePath() + " --> 绝对路径");
System.out.println(file.getCanonicalFile() + " --> 修饰后的绝对路径");
运行结果 :
下面三行输出看起来没有什么区别, 但是如果把构造方法里的路径换成相对路径, 区别就很明显了, 各位读者可以自行尝试
返回值 | 方法名 | 说明 |
---|---|---|
boolean | exists() | 判断 File 对象描述的文件是否真实存在 |
boolean | isDirectory() | 判断 File 对象代表的文件是否是一个目录 |
boolean | isFile() | 判断 File 对象代表的文件是否是一个普通文件 |
boolean | createNewFile() | 根据 File 对象,自动创建一个空文件。成功创建后返回 true |
boolean | delete() | 根据 File 对象,删除该文件。成功删除后返回 true |
void | deleteOnExit() | 根据 File 对象,标注文件将被删除,删除动作会到 JVM 运行结束时才会进行 |
System.out.println(file.exists() + " --> 是否存在");
System.out.println(file.createNewFile() + " --> 是否成功创建(第一次)");
System.out.println(file.createNewFile() + " --> 是否成功创建(第二次)");
System.out.println(file.isDirectory() + " --> 是否为目录");
System.out.println(file.isFile() + " --> 是否为普通文件");
System.out.println(file.delete() + " --> 是否成功删除(第一次)");
System.out.println(file.delete() + " --> 是否成功删除(第二次)");
运行结果 :
下面是创建目录的方法
返回值 | 方法名 | 说明 |
---|---|---|
boolean | mkdir() | 创建 File 对象代表的目录 |
File dir = new File("D:\\Program Files\\Tencent\\QQ\\Bin\\test");
System.out.println(dir.exists() + " --> 是否存在");
System.out.println(dir.isDirectory() + " --> 是否为目录");
System.out.println(dir.isFile() + " --> 是否为普通文件");
System.out.println(dir.mkdir() + " --> 是否为成功创建");
System.out.println(dir.delete() + " --> 是否为成功删除");
System.out.println(dir.exists() + " --> 是否存在");
运行结果 :
下面是创建多级目录的方法
返回值 | 方法名 | 说明 |
---|---|---|
boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目录 |
File dir = new File("D:\\Program Files\\Tencent\\QQ\\Bin\\test2/aaa/bbb");
由于本机没有test2 和 aaa 这两个文件, 这个方法会创建出来从 test2 到 bbb 这三层目录, 我们可以用以下两个方法观察
返回值 | 方法名 | 说明 |
---|---|---|
String[] | list() | 返回 File 对象代表的目录下的所有文件名 |
File[] | listFiles() | 返回 File 对象代表的目录下的所有文件,以 File 对象表示 |
File dir = new File("D:\\Program Files\\Tencent\\QQ\\Bin\\test2/aaa/bbb");
System.out.println(dir.mkdirs() + " --> 是否为成功创建");
File dir2 = new File("D:\\Program Files\\Tencent\\QQ\\Bin\\test2");
String[] results1 = dir2.list();
File[] results2 = dir2.listFiles();
System.out.println(Arrays.toString(results1));
System.out.println(Arrays.toString(results2));
运行结果 :
以上就是 File 类的常用方法介绍, 下面介绍字节流的 IO 操作
-
明确"流"的概念
“流”, 可以联想到"水流", 把水龙头打开就会有水流出来, 并且可以控制流出来的水的快慢
水流的最小单位是水滴, 而字节流 IO 的最小单位就是一个字节 -
明确"输入"和"输出"的方向
输入输出都是以 CPU 的视角来看的, CPU 运行时是从内存中读取数据, 而文件在硬盘(外存)上,
文件输入, 是硬盘 --> 内存 --> CPU , 数据流入到 CPU(读取文件中的数据)
文件输出, 是 CPU --> 内存 --> 硬盘, 数据从 CPU 流出(把数据写入到文件) -
明确对应的类
字节流 IO 操作使用的类是 : InputStream 和 OutputStream
二、字节流输入输出
1, 字节流输入 InputStream
1.1, 每次输入一个字节
InputStream 这个类是一个抽象类, 不能被直接实例化, 应该实例化它的子类
文件只是 IO 设备的其中一种, 还有其他 IO 设备(比如网卡…), 所以针对文件的 IO 操作
的类是 FileInputStream, 需要在构造方法中指定路径来表示操作的具体文件是哪个(或者是 File 对象)
注意 :
实例化出来对象之后就相当于文件被打开, (产生了文件描述符), 使用完文件之后需要调用 close 方法关闭文件, 否则可能会产生文件资源泄露的严重bug
由于字节流字符流的 IO 操作的类都继承了 Closeable 这个接口, 所以有了一种新的语法 :
try(InputStream inputStream = new FileInputStream("D:/hello.txt")) {
}
}
try(实例化对象) { }, 当大括号中的代码执行完后, 就会自动的调用 close 方法
调用 inputStream.read() 就可以读取一个字节的数据, 如果读到文件末尾, 返回值为 -1, 我们不知道文件中有多少字节的数据, 所以利用 while 循环 :
// 输入流 从硬盘读到内存
try(InputStream inputStream = new FileInputStream("D:/hello.txt")) {
while(true) {
int ret = inputStream.read();
if(ret == -1) {
// 说明读完了
break;
}
System.out.println(ret);
}
}
如果文件中的数据是字母和标点符号, 根据数字查询 ASCII 码表即可, 如果是汉字, 需要把数字转换成十六进制再查询 UTF-8 码表
1.2, 每次输入多个字节
要想一次输入多个字节, 就创建一个字节数组, 调用 inputStream.read(字节数组), 最多读取数组长度大小个字节, 返回数组长度, 读完了返回 -1
// 输入流 从硬盘读到内存
try(InputStream inputStream = new FileInputStream("D:/hello.txt")) {
byte[] array = new byte[1024];
while(true) {
int ret = inputStream.read(array);
if(ret == -1) {
// 说明读完了
break;
}
for(int i = 0; i < ret; i++) {
System.out.println(array[i]);
}
}
}
这种方式从文件读取数据的次数更少, 性能要更好
2, 字节流输出 OutputStream
2.1, 每次输出一个字节
和 InputStream 类似, 针对文件的输出对应的类是 FileOutputStream
调用 outputStream.write(整形), 可以每次输出一个字节
try(OutputStream outputStream = new FileOutputStream("D:/hello.txt")) {
outputStream.write(97);
outputStream.write(98);
outputStream.write(99);
}
这样就可以在文件中依次写入"abc", 如图 :
接着再写入一些数据到文件
try(OutputStream outputStream = new FileOutputStream("D:/hello.txt")) {
outputStream.write(100);
outputStream.write(101);
outputStream.write(102);
}
查看文件中, 如图 :
发现原本的"abc" 三个字符没有了, 这是因为第二次写入的数据把之前的数据覆盖了, 只需要在实例化 outputStream 的构造方法中多给一个参数 “true”, 表示在之前的数据末尾 append 要写入的数据
// 输出流 从内存写到硬盘
try(OutputStream outputStream = new FileOutputStream("D:/hello.txt",true)) {
outputStream.write(100);
outputStream.write(101);
outputStream.write(102);
}
查看文件中的数据如图 :
2.2, 每次输出多个字节
和 InputStream 类似, 要想每次输出多个字节, 需要创建一个字符数组, 调用 outputStream.write(字符数组), 就可以把字符数组中的所有数据写入到文件
// 输出流 从内存写到硬盘
try(OutputStream outputStream = new FileOutputStream("D:/hello.txt", true)) {
byte[] array = {103, 104, 105};
outputStream.write(array);
}
查看文件中的数据, 如图 :
总结
以上就是本篇的全部内容, 都相对比较简单, 只是方法比较繁多, 需要小伙伴们多加练习
如果本篇对你有帮助,请点赞收藏支持一下,小手一抖就是对作者莫大的鼓励啦😋😋😋~
上山总比下山辛苦
下篇文章见