JAVA基础-IO流复习笔记
一、什么是io流
- "i"指的是input;"o"指的是output;
- “流”是一种抽象概念,就像一个数据通道。
- io流就是数据输入输出的通道。
- 输入输出是相对于计算机内存而言的。数据从内存到外部设备是输出,反之是输入。
- java提供了一系列的API来实现“流”。
二、流的分类
1,按数据流向分类:
- 输入流
- 输出流
2,按一次文件交互操作的数据单位分类:
-
字节流
-
字符流
???再确认一下
-
字符输入流会首先创建默认的缓冲区,长度为8192的字节数组,程序每次(一次读取的单位是一个字符)读取数据时先在缓冲区查看有无数据,有就读缓冲区,没有就会读文件,使数据尽量装满缓冲区以后,再读缓冲区。
-
字符输出流同样有缓冲区,以下情况会将数据正式写到外部文件中:
- 缓冲区装满了。
- 调用flush()方法,手动刷新数据到文件。
- 释放资源,close()。不过流一旦关闭,就不能再继续写数据了
-
3,按功能分类:
- 节点流:直接操作数据读写的流(java中的类)。
- 处理流(或者叫过滤流):处理流相当于修饰节点流的类,它的参数就是节点流。处理流最终还是使用的节点流的功能。底层使用修饰器模式实现。常用处理流:缓冲流。
三、相关的API
1、四个抽象类基类
2,字节输入流(常用)
3、字节输出流(常用)
4,字符输入流(常用)
5,字符输出流(常用)
6,File 文件对象
文件对象,表示一个抽象的文件,可能实际存在,也可不存在;可以是文件,也可以是文件夹。
常用方法
- 构造方法
new File(String path); //1
new File(String parent,String child); //2
new File(File parent,String child); //3
new File(URI uri); //4
-
其他方法
- 获取文件名、各种路径、文件长度(单位:字节)、修改时间(lastModified);
- 获取获取所有的根目录文件对象列表(静态方法);
- 获取目录下的所有文件名或文件对象列表;
方法 说明 String[] list(FileNameFilter filter) 这个方法可以传入一个过滤器对象,添加一些过滤条件。 String[] listFiles(FileNameFilter filter) 这个方法可以传入一个过滤器对象,添加一些过滤条件。 - 判断文件或文件夹是否存在;
- 判断是否为文件、是否为文件夹;
- 判断是否是隐藏的文件或目录;
- 判断该抽象路径是否是绝对路径;
- 判断是否可读、是否可写;
- 创建文件、文件夹、多级文件夹;
- 重命名和剪切文件;
方法 说明 boolean renameTo(File file); 将file1对应的文件截切到file2文件位置,并且名字改为file2的。file2对应的文件必须是不存在的。文件夹同理。 - 删除文件或文件夹;
方法 说明 boolean delete() 删除文件或空文件夹。不能删除非空文件夹。 void deleteOnExit() jvm退出的时候删除文件或文件夹。常用于删除临时文件,无返回值;
四、FileInputStream和FileOutputStream
1、FileInputStream
万能的文件字节输入流。节点流。任何文件都可以用这个流读取。
- 构造方法
方法 | 说明 |
---|---|
FileInputStream(String name) | |
FileInputStream(File file) |
- 其他方法
方法 | 说明 |
---|---|
int read(); | 读一个字节,然后指针后移。返回值为该字节ASCII码。 |
int read(byte[] buffer); | 读一个字节数组的数据。返回值为读到的字节个数。 |
int read(byte[] buffer,int off,int len); | 读len个字节的数据,从buffer数组下标为off的位置存入数据。 |
int available(); | 返回文件的有效字节数。 |
long skip(long n); | 跳过n个字节; |
2、FileOutputStream
文件字节输出流。节点流。
- 构造方法
方法 | 说明 |
---|---|
FileOutputStream(String name) | |
FileOutputStream(File file) |
- 其他方法
方法 | 说明 |
---|---|
void write(int b); | 将一个字节的数据写到文件中; |
void write(byte[] buffer); | 将一个字节数组的数据写到文件中; |
void write(byte[] buffer,int off,int len); | 将字节数组从off位置,长度为len的数据写到文件中; |
void fiush(); | 刷新。 |
五、FileReader和FileWriter
1、FileReader
字符输入流。节点流。用于读取普通文本。
- 构造方法
和字节流的构造类似。
- 其他方法(略)
写数据的方法,和字节流类似。不同点在于,操作数据的单位是字符。
2、FileWritert
字符输出流。节点流。
- 构造方法
和字节流的构造类似。注意第二个boolean类型的参数表示是否是在文件末尾追加数据。默认值是false,表示清空重写。
- 其他方法(略)
写数据的方法,可以使用int(写入对应的ASCll码的数据)、char[]、String类型的数据写入文件。
六、缓冲区
1,缓冲区是什么?
缓冲区 (buffer)就是内存中预留的一段内存空间。
2,缓冲区的作用:
- 防止低速设备限制高速设备。数据直接送往缓冲区,高速设备不用再等待低速设备。 例如:使用打印机打印文档,由于打印机的打印速度相对较慢,可以先把文档输出到打印机相应的缓冲区,打印机再自行逐步打印,这时CPU可以处理别的事情。
- 提高单次数据读写的数据量,减少数据的读写次数。将数据送往缓冲区,待缓冲区满后再进行传输,这样可以节省很多时间。例如:我们想将数据写入到磁盘中,不是立马将数据写到磁盘中,而是先输入缓冲区中,当缓冲区满了以后,再将数据写入到磁盘中,这样就可以减少磁盘的读写次数。
3,注意
字符流自带缓冲区,所以字符流缓冲流效率的提升没有字节缓冲流明显。
七、BufferedInputStream
字节输入缓冲流。处理流。
- 构造方法
方法 | 说明 |
---|---|
BufferedInputStream(OutputStream out); | |
BufferedInputStream(OutputStream out,int size); | size可以指定缓冲区大小 |
- 其他方法(略)
主要有读数据、刷新流、关闭流的方法。
八、BufferedOutputStream
字节输出缓冲流。处理流。
- 构造方法(略)
- 其他方法(略)
九、缓冲流读写数据实践
- 源文件:“C:\Users\86152\Desktop\src.txt”, 大小:40kb
1、字节输入流 + 字节输出流
private void NoBuffer() throws IOException {
//字节输出流
FileOutputStream os = new FileOutputStream("C:\\Users\\86152\\Desktop\\NoBuffer.txt");
//字节输入流
FileInputStream is = new FileInputStream("C:\\Users\\86152\\Desktop\\src.txt");
int i = -2;
while ((i=is.read())!=-1){
os.write(i);
}
is.close();
os.close();
}
@Test
public void t1() throws Exception {
long startTime1 = System.currentTimeMillis();
NoBuffer();
long endTime1 = System.currentTimeMillis();
long time = endTime1-startTime1;
System.out.println("Time:" + time);
}
大约耗时:560
2、字节缓冲输入流 + 字节输出流
private void Buffer() throws IOException {
//字节输入
FileInputStream is = new FileInputStream("C:\\Users\\86152\\Desktop\\src.txt");
//字节输出
FileOutputStream os = new FileOutputStream("C:\\Users\\86152\\Desktop\\Buffer.txt");
//缓冲输入
BufferedInputStream bis = new BufferedInputStream(is);
int i = -2;
while ((i=bis.read())!=-1){
os.write(i);
}
bis.close();
os.close();
}
@Test
public void t1() throws Exception {
long startTime1 = System.currentTimeMillis();
Buffer();
long endTime1 = System.currentTimeMillis();
long time = endTime1-startTime1;
System.out.println("Time:" + time);
}
大约耗时:490
3、字节输入流 + 字节缓冲输出流
private void Buffer() throws IOException {
//字节输入
FileInputStream is = new FileInputStream("C:\\Users\\86152\\Desktop\\src.txt");
//字节输出
FileOutputStream os = new FileOutputStream("C:\\Users\\86152\\Desktop\\Buffer.txt");
//缓冲输出
BufferedOutputStream bos = new BufferedOutputStream(os);
int i = -2;
while ((i=is.read())!=-1){
bos.write(i);
}
is.close();
bos.close();
}
@Test
public void t1() throws Exception {
long startTime1 = System.currentTimeMillis();
Buffer();
long endTime1 = System.currentTimeMillis();
long time = endTime1-startTime1;
System.out.println("Time:" + time);
}
大约耗时:70
4、字节缓冲输入流 + 字节缓冲输出流
private void Buffer() throws IOException {
//字节输入
FileInputStream is = new FileInputStream("C:\\Users\\86152\\Desktop\\src.txt");
//字节输出
FileOutputStream os = new FileOutputStream("C:\\Users\\86152\\Desktop\\Buffer.txt");
//缓冲输入
BufferedInputStream bis = new BufferedInputStream(is);
//缓冲输出
BufferedOutputStream bos = new BufferedOutputStream(os);
int i = -2;
while ((i=bis.read())!=-1){
bos.write(i);
}
bis.close();
bos.close();
}
大约耗时:3
5、其他
以前一直有个问题:为什么使用缓冲流的时候,同样是在循环中一次读一个字节、写一个字节,相比于不用缓冲流时,效率高很多?
以下做了一个小实验,已知缓冲流在底层默认创建了一个长度为8192的字节数组作为缓冲区,循环读写一个缓冲区大小的数据前后,打开文件看看其中是否有内容。
1、首先不使用缓冲流,读写过程中打开文件查看是否有内容
private void NoBuffer() throws IOException {
//字节输入
FileInputStream is = new FileInputStream("C:\\Users\\86152\\Desktop\\src.txt");
//字节输出
FileOutputStream os = new FileOutputStream("C:\\Users\\86152\\Desktop\\Buffer.txt");
int i = -2;
int count = 0; //计数,表示写到第几个字节了。
while ((i=is.read())!=-1){
os.write(i); //这里设置断点
count++;
}
is.close();
os.close();
}
@Test
public void test() throws Exception {
NoBuffer();
}
- debug运行test(),分别在读取1、2、3个字节时打开文件,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S8dw39iw-1673330982822)(https://gitee.com/lu-yunji/image/raw/master/image-20230109234953188.png)]
- 如上所示:读一个写一个,效率不高。
2、使用缓冲区
private void Buffer() throws IOException {
//字节输入
FileInputStream is = new FileInputStream("C:\\Users\\86152\\Desktop\\src.txt");
//字节输出
FileOutputStream os = new FileOutputStream("C:\\Users\\86152\\Desktop\\Buffer.txt");
//缓冲输出
BufferedOutputStream bos = new BufferedOutputStream(os);
int i = -2;
int count = 0; //计数,用于表示读到第几个字节了。
while ((i=is.read())!=-1){
count++;
bos.write(i);
if (count==8192){
System.out.println("读取的数据未满一个缓冲区"); //这里设置断点1
}
if (count==8193){
System.out.println("读取的数据刚好一个缓冲区"); //这里设置断点2
}
}
is.close();
bos.close();
}
@Test
public void test() throws Exception {
Buffer();
}
- debug运行test(),断点1(未满一个缓冲区时)的时候打开文件Buffer.txt,如下所示,没有内容:
- 继续运行,断点2(读满一个缓冲区时)的时候打开文件Buffer.txt,如下所示,一次直接写入了一个缓冲区大小的数据:
- 结论:以上实验说明,虽然在代码的循环中,看似是读一个字节(调用read方法)即写一个字节(调用write方法),但是读到的数据并未在输出流调用write方法时立即写到文件上,而是暂存在了内存中的缓冲区里,等到缓冲区满时才真正写到文件上。
九、BufferedReader
字符输入缓冲流。处理流。
- 构造方法(略)
- 其他方法(略)
- 特有成员方法
方法 | 说明 |
---|---|
void readline() | 读一个文本行 |
十、BufferedWriter
字符输出缓冲流。处理流。
- 构造方法(略)
- 其他方法(略)
- 特有成员方法
方法 | 说明 |
---|---|
void newline() | 写一个文本行 |
十一、序列化流
1、什么是序列化
- Java 提供了一种将对象持久化的方式。用一个字节序列表示一个对象,而字节序列可以写出到文件中。
- 反之,字节序列可以从文件中读取到内存,重构对象,对它进行反序列化。
2、前言
- 参与序列化和反序列化的对象,必须实现 Serializable 接口。这是一个标记接口,不需要重写任何方法。
- jvm对实现了Serializable接口的类会自动生成一个序列化版本号。用于区别不同的类。
- 一旦修改了类,就会重新生成版本号,这在实际应用中是不合适的,所以对于实现了Serializable接口的类,我们都会手动给一个固定的版本号。
- 对于类中一些我们不愿意序列化的属性,可以使用transient关键字修饰,这样这个属性的数据就不会写道字节序列中去了。
3、ObjectOutputStream
处理流。
- 构造方法
方法 | 说明 |
---|---|
ObjectOutputStream(OutputStream out); |
- 常用方法
方法 | 说明 |
---|---|
writeObject(Object obj); | 序列化对象 |
4、ObjectInputStream
处理流。
- 构造方法
方法 | 说明 |
---|---|
ObjectInputStream(InputStream in); |
- 常用方法
方法 | 说明 |
---|---|
writeObject(Object obj); | 反序列化对象 |
十二、DataInputStream和DataOutputStream
处理流。数据字节输入流和数据字节输出流。对于Java中每一种类型的数据,它都有对应的方法读写。
-
继承结构
- DataOutputStream
- DataInputStream
- 对于写出数据,它会生成一个特定类型的文件;
- 对于写入数据,你必须记得文件中的数据顺序,不然无法正确的读出数据;
十三、标准输出流
1、PrintStream
处理流。标准字节输出流,用于写数据的方法参数类型随意,输入什么写到文件就是什么。默认打印输出到控制台。
常用的 System.out.println(); 就是封装的这种流。
@Test
public void t7() throws FileNotFoundException {
//一、使用默认的输出,效果是直接输出到控制台。
System.out.println("输出到控制台");
//二、创建自定义的输出流
PrintStream ps = new PrintStream("C:\\Users\\86152\\Desktop\\printStream.txt");
//设置使用自定义的输出流
System.setOut(ps);
//这时候就会输出到文件
System.out.println("输出到文件");
}
- 继承结构
- 构造方法
方法 | 说明 |
---|---|
PrintStream(File file) | |
PrintStream(OutputStream out) | |
PrintStream(String fileName) |
- 常用方法
方法 | 说明 |
---|---|
println(类型不定 x) | 输出完换行 |
print(类型不定 x) | 输出完不换行 |
2、PrintWriter
处理流。标准字符输出流。和PrintOutputStream类似。只不过PrintWriter继承自字符流,有字符流的特性;PrintOutputStream继承自字节流,有字节流的特性。
- 继承结构
见目录第三大点。
tln(“输出到文件”);
}
- 继承结构
[外链图片转存中...(img-j8ODaQlo-1673330982824)]
- 构造方法
| 方法 | 说明 |
| ----------------------------- | ---- |
| PrintStream(File file) | |
| PrintStream(OutputStream out) | |
| PrintStream(String fileName) | |
- 常用方法
| 方法 | 说明 |
| ------------------- | ------------ |
| println(类型不定 x) | 输出完换行 |
| print(类型不定 x) | 输出完不换行 |
### 2、PrintWriter
处理流。标准字符输出流。和PrintOutputStream类似。只不过PrintWriter继承自字符流,有字符流的特性;PrintOutputStream继承自字节流,有字节流的特性。
- 继承结构
见目录第三大点。