首先什么是IO操作呢?
IO操作其实解释操作硬盘
1. 文件系统操作
创建文件,删除文件,重命名文件,创建目录…操作
2. 文件内容操作
进行读与写操作
先来了解一下基本的文件知识方便学习接下来的IO操作
文件路径
文件路径是从数根节点触发,沿着树杈一直往下走,到达目标文件,在此期间所经过的路径
如图所示, 其中文件路径为D:\111\新建文件夹\新建文件夹
Windows 中可以将此电脑省略,直接从盘符开始表示
细节问题
1. / 与 \ 的问题
我们看前面的文件的那个路径是 D:\111\新建文件夹\新建文件夹
他是使用 \ 来进行分割, \ 只适合windows系统,代码中经常使用 / 进行分割, 如果要使用 \ 进行分割要使用转义字符进行强转写成这样 \\
2. 相对路径与绝对路径
举个例子比较好明白
比如说在这个路径下我要找到如图所示的文件
此时我在这个目录里
相对路径表示为 ./scaffolds/draft.md
绝对路径为 C:/Users/47240/Desktop/myblog/scaffolds/draft.md
. 属于特殊符号, 是表示当前目录的意思
还有一个 . .表示的是当前目录的上级目录的意思
路径是十分重要的,尤其是相对路径,绝对路径可以理解为此电脑的路径,相对路径要明确基准目录是什么
文件介绍
1. 文本文件
文本文件存储的是遵守ASCII或其他字符集编码比如utf - 8,所得到的文件,本质上存的是字符(不仅仅是所说的char)
2. 二进制文件
存储的是二进制数据,没有任何字符集限制,存什么都可以
常见的区别
文本文件: .txt .java .c 等等
二进制文件: .class .exe .jpg 等等
文件系统操作
Java标准库提供了File这个类
File是硬盘上文件的抽象表示,由于文件是存储在硬盘上的,直接操作硬盘非常不方便,所以先通过File在内存中创建一个对应的对象
通过操作内存这个对象,间接影响到硬盘中的文件情况
1. 构造File对象
import java.io.File;
public class IoDemo1 {
public static void main(String[] args) {
File file = new File("./1.jpg");
System.out.println(file.getParent());
System.out.println(file.getPath());
System.out.println(file.getAbsolutePath());
System.out.println(file.exists());
}
}
构造的时候可以使用绝对路径或者相对路径初始化
这个路径指向文件,可以是真实存在的,也可以是不存在的
我遇到的疑问
疑问一
为什么打印的不是 io 而是 .
以上是他的打印内容,我当时不清楚为什么会打印.而不是 io 发现打印父目录是根据文件来决定, 1.jpg 的父目录就是 . 所以打印 .
疑问二
为什么不存在这个文件也可以打印出绝对路径
File创建路径只是一个抽象的表示,他并不知道文件是否存在,只是基于程序运行的当前目录去表示路径 总之,
绝对路径的计算并不依赖于文件是否存在。只要提供路径, Java 就能基于当前工作目录生成一个绝对路径
以下是File的一些方法
返回类型 | 方法签名 | 说明 |
---|---|---|
String | getParent() | 返回 File 对象的父目录的路径。 |
String | getName() | 返回 File 对象的文件名称。 |
String | getPath() | 返回 File 对象的相对路径或绝对路径。 |
String | getAbsolutePath() | 返回 File 对象的绝对路径。 |
String | getCanonicalPath() | 返回 File 对象的规范路径(去除符号链接和相对路径的情况)。 |
boolean | exists() | 判断 File 对象描述的文件或目录是否存在。 |
boolean | isDirectory() | 判断 File 对象是否是一个目录。 |
boolean | isFile() | 判断 File 对象是否是一个普通文件。 |
boolean | createNewFile() | 创建一个新的空文件,成功创建返回 true。 |
boolean | delete() | 删除文件或目录,成功删除返回 true。 |
void | deleteOnExit() | 标记文件在 JVM 运行结束时删除。 |
String[] | list() | 返回 File 对象代表的目录下的所有文件名。 |
File[] | listFiles() | 返回 File 对象代表的目录下的所有文件和目录对象,以 File 对象数组形式返回。 |
boolean | mkdir() | 创建 File 对象代表的目录。 |
boolean | mkdirs() | 创建 File 对象代表的目录及所有必要的父目录。 |
boolean | renameTo(File dest) | 对文件进行重命名或剪切到新位置。 |
boolean | canRead() | 判断文件是否具有可读权限。 |
boolean | canWrite() | 判断文件是否具有可写权限。 |
2. 简单介绍几个方法
File file = new File("./helloWorld.txt");
System.out.println(file.exists());//判断是否存在
System.out.println(file.isDirectory());//是否为目录
System.out.println(file.isFile());//是否是文件
file.createNewFile();//创建一个txt文件
可以发现如果写的是相对路径,会以 io ,也就是项目所在的目录展开
mkdirs
import java.io.File;
public class IoDemo3 {
public static void main(String[] args) {
File file = new File("./text/aaa/bbb");
System.out.println(file.mkdir());
file.mkdirs();
}
}
midir 只能创建一个目录, 在以上代码会创建失败返回一个 false , 通过 midirs 创建出多级目录也是在 io 目录下展开的
文件内容操作
针对文本文件,提供一组类, 统称 ‘’ 字符流 ‘’(代表, Reader, Writer) 读写的基本单位是 字符
针对二进制文件,提供一组类, 统称 ‘’ 字节流 ‘’(代表, InputStream,OutputStream) 读写的基本单位是 字节
流对象又分为两种
输入的: Reader, InputStream
输出的: Writer, OutputStream
输入是通过内存创建的对象读取硬盘的内容输送到CPU中
输出是通过内存创建的对象将CPU的内容输出到硬盘上
介绍InputStream
InputStream的使用方法
首先
抽象类无法进行实例化
InputStream inputStream = new FileInputStream("D:/test.txt")
使用 FileInputStream 指定一个文件路径
但是最后一定要记得关闭, 关闭操作一定不能忘记
文件的资源是需要手动释放的
其中资源指的是文件描述符
回顾
进程的表示结构
- pid
- 内存指针
- 文件描述符表
其中文件描述符表就是记载当前进程都打开了哪些文件,每打开一个文件就会在符表中申请到一个位置
这个表你可以当成一个数组, 数组下标就是文件描述符
数组元素就是文件在内核的结构体表示
但是这是有限制的,一旦满了继续打开就会打开失败,发生文件资源泄露(非常严重)
有时候经常会忘记关闭
所以可以说使用try with resoures, 可以自动帮你关闭
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class IoDemo6 {
public static void main(String[] args)throws IOException {
try(InputStream inputStream = new FileInputStream("D:/test.txt")){
while(true){
int n = inputStream.read();
if(n == -1){
break;
}
System.out.println(n);
System.out.printf("%x\n",n);
}
}
}
}
比如上述代码的写法,虽然没有写close关闭文件
但是InputStream实现了一个特定的interface Closeable接口
简单介绍一下inputStream类里的方法
无参数的read()相当于每次读读取一个字节,返回一个int值,如果文件里面读完了就会返回-1
例如
我在文件里存储hello world
输出每个字符的ASCII码值
如果输入的是汉字呢
将他转化成16进制进行输出
与编码表对照
发现正好是汉字的编码但是每次读取一个字节,以字节形式输出, 输出了12个字节
当然也可以用read(bytes)来读取
但是返回值是读到的字节数
也举个代码例子
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class IoDemo8 {
public static void main(String[] args)throws IOException {
try(InputStream inputStream = new FileInputStream("D:/test.txt")){
byte[] bytes = new byte[4096];
while(true){
int n = inputStream.read(bytes);
if(n == -1){
break;
}
// System.out.println(n);
System.out.printf("%x\n",n);
}
}
}
}
如果我们输入你好世界这四个汉字,就会返回c,也就是12
因为他只能读取数据到bytes数组中,尽可能去填满,然后返回数量
介绍一下Reader类的方法,和InputStream异曲同工
Reader 类常用方法
返回类型 | 方法签名 | 说明 |
---|---|---|
int | read() | 读取单个字符并返回其 Unicode 编码,返回 -1 表示已到达流的末尾。 |
int | read(char[] cbuf) | 将字符读入指定的字符数组,返回实际读取的字符数,返回 -1 表示已到末尾。 |
int | read(char[] cbuf, int off, int len) | 将字符读入数组的部分区域,从 off 开始存储,最多读取 len 个字符。返回实际读取的字符数,返回 -1 表示到达末尾。 |
void | close() | 关闭流并释放与之关联的所有资源。 |
long | skip(long n) | 跳过输入流中的 n 个字符,返回实际跳过的字符数。 |
boolean | ready() | 如果流已经准备好读取字符,则返回 true ,否则返回 false 。 |
boolean | markSupported() | 测试此流是否支持 mark() 和 reset() 方法,返回 true 表示支持。 |
void | mark(int readAheadLimit) | 标记流中的当前位置,可以通过 reset() 方法返回到这个位置。 |
void | reset() | 将流重新定位到上次调用 mark() 所标记的位置。 |
int | read(CharBuffer target) | 将字符读入 CharBuffer 中,返回读取的字符数。 |
举个同样的例子
import java.io.*;
public class IoDemo9 {
public static void main(String[] args)throws IOException {
try(Reader reader = new FileReader("D:/test.txt")){
byte[] bytes = new byte[4096];
while(true){
int n = reader.read();
if(n == -1){
break;
}
// System.out.println(n);
System.out.printf("%x\n",n);
}
}
}
}
如果你也是读取你好世界
会发现读取到的是字符的 Unicode 编码, 因为之前是一个一个字节进行读取,但是现在时使用一个一个字符读取,会读取一整个’你’ 所以会输出Unicode编码