4. IO Stream
文章目录
- 一、相对论理解IO流
- 二、汉语文学理解流
- 三、图解IO流
- 四、俩亲爹InputStream和OutputStream
- 五、FileInputStream字节流读取文件
- 六、FileOutputStream字节流写入文件
- 七、buff缓冲复制文件
- 1. 例一(无buff)
- 2. 例二(有buff)
- 八、buffered字节缓冲流和装饰设计模式
- 九、FileReader和FileWriter,俩专门来搞txt文件的
- 十、BufferedReader和BufferedWriter
- 十一、一次性讲解剩余的N个流(扩展课)Java里那些极其骚的IO流
- 十二、Apache Common IO
- 1. 例一
- 2. 例二(arrayList的妙用)
- 十三、补充
- 十四、内容出处
一、相对论理解IO流
官方文档
不仅是水,数据也是如此。例如,你发了一个视频给你的朋友,数据就从你的电脑上流通到了ta的电脑上,这个过程就叫数据流。
二、汉语文学理解流
为什么中国人用”流"这个字,而外国人用"stream"来代替?
这个字的偏旁是三点水,所以古人说的流一定跟水有关。可以引申为数据在流动时也像水一样流动
三、图解IO流
官方文档
当我们打开一个记事本读取里面的内容时,数据从本地存储设备流入记事本程序。因此,相对于记事本程序而言,数据是输入的;相对于本地存储设备而言,数据是输出的。
四、俩亲爹InputStream和OutputStream
Package java.io
重点关注这俩:
filechannel(文件通道):并发编程 现在可以先忽略
五、FileInputStream字节流读取文件
第一部分代码出处
菜鸟教程
① 字节流:以字节为单位去读和写,好处在于可以读写任何文件(图片、.mp3、.mp4等都可以化为字节)
② FileInputStream又称为字节流的原因:它是按照字节来读的
package com.practice.demo3;import org.junit.Test;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;public class TestIo {private final static int SIZE = 4096;@Testpublic void inputFile() throws IOException {FileInputStream fileInputStream = new FileInputStream("file\\1.txt");
// System.out.println("------------该部分代码来自上述网页--------------------------");/*System.out.println("按字节读取, read()返回读取到的字节");int by = 0;// 如果读到文件末尾, 则会返回-1while ((by = fileInputStream.read()) != -1){System.out.println(by);}*//*System.out.println("read(byte[] b)返回读取到的字节个数");byte[] buf = new byte[30];int len = fileInputStream.read(buf);System.out.println(len);System.out.println(Arrays.toString(buf));System.out.println(new String(buf, 0, len));*//*System.out.println("创建一个字节数组");int len = 0;byte[] buf = new byte[SIZE];while((len = fileInputStream.read(buf))!= -1){System.out.println(new String(buf, 0, len));}*/
// System.out.println("---------------------------------------------------");System.out.println("结合一下菜鸟教程的案例:");int by;while ((by = fileInputStream.read()) != -1){System.out.print((char)by);}fileInputStream.close(); // 不用的话就把管子切断,节省内存// 以上写法其实都更倾向于读取一个字节数组}
}
六、FileOutputStream字节流写入文件
菜鸟教程
执行结果:
@Testpublic void outputFile() throws IOException {FileOutputStream fileOutputStream = new FileOutputStream("file\\2.txt");// getBytes()作用:把字符串转换成字符数组byte[] bytes = "Hello World!".getBytes();
// for(int i = 0; i < bytes.length; ++i){
// System.out.print((char)bytes[i]);
// }// 第一次执行上述命令后2.txt就会被创建出来, 删除方式:右击2.txt -> Delete -> OK -> 左下角Do Refactor// 把字符数组里的内容写入文件for(int i = 0; i < bytes.length; ++i){fileOutputStream.write(bytes[i]);}fileOutputStream.close();}
七、buff缓冲复制文件
文件输入输出流还有一些用途,比如说像:文件拷贝(文本文件(.txt)、媒体文件(.mp3、mp4、图片)均可)
1. 例一(无buff)
① 图片大的话可能会卡
② 此时是一个字节一个字节地从源文件读取,并且一个字节一个字节地写入到目标文件中。这种方式由于每次只处理一个字节,导致频繁地进行磁盘I/O操作,效率非常低。
@Testpublic void copyFileBase() throws IOException{FileInputStream fileInputStream = new FileInputStream("file\\1.jpg");FileOutputStream fileOutputStream = new FileOutputStream("file\\target.jpg");int by;while((by = fileInputStream.read()) != -1){fileOutputStream.write(by);}fileInputStream.close();fileOutputStream.close();}
2. 例二(有buff)
代码使用了缓冲区(buffer),这是一个字节容器,用于在内存中暂存数据。缓冲区的大小在这里被设置为1024字节。这样,每次从源文件读取和写入目标文件时,都是按照缓冲区的大小来进行的,即每次操作都是1024字节。这大大减少了磁盘I/O操作的次数,因为每次操作都处理更多的数据,从而显著提高了文件复制的速度。
当调用 fileInputStream.read(buff) 方法时,它会尝试填充整个缓冲区(最多1024字节),然后返回实际读取的字节数。这个字节数可能小于1024,比如在文件的最后部分。然后,fileOutputStream.write(buff, 0, by) 方法将这些字节写入目标文件。这个过程会一直重复,直到文件结束(即 read 方法返回-1)。
可以理解为搬运箱子,之前是一个人搬一个箱子,突然来了个大力士,一次性可以搬10个箱子。
总结来说,使用缓冲区是处理I/O操作时提高效率的常见做法,这在处理大文件时尤其重要。
@Testpublic void copyFileBase2() throws IOException{FileInputStream fileInputStream = new FileInputStream("File\\1.jpg");FileOutputStream fileOutputStream = new FileOutputStream("File\\target2.jpg");// 缓冲区// 1024单位:字节byte[] buff = new byte[1024];int by;while((by=fileInputStream.read(buff)) != -1){fileOutputStream.write(buff, 0, by);}fileInputStream.close();fileOutputStream.close();}
八、buffered字节缓冲流和装饰设计模式
BufferedInputStream
① 之前用的是普通的字节流,然后手动创建缓冲区;现在直接用缓冲流,好处在于不需要自己手写缓冲区了。
② 装饰设计模式:把一个类当作一个函数的参数传进去
③ 这里默认的缓冲区大小是8192字节
@Testpublic void bufferFileBase() throws IOException{BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("file\\1.txt"));BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("file\\3.txt"));int by;while((by = bufferedInputStream.read()) != -1){bufferedOutputStream.write(by);}bufferedInputStream.close();bufferedOutputStream.close();}
九、FileReader和FileWriter,俩专门来搞txt文件的
①
② FileReader
FileWriter
③ 用文件写学生管理系统,别再用FileInputStream和FileOutputStream了,应该用FileReader和FileWriter(它们针对于记事本这种输入输出设备文件)
④
但是我们可以从源码发现,FileReader和FileWriter的原理其实还是FileInputStream和FileOutputStream,只不过它俩可以使代码看起来更简洁一点。
@Testpublic void fileReaderTest() throws IOException{FileReader fileReader = new FileReader("file\\1.txt");int ch;while((ch = fileReader.read()) != -1){// 可以把ln删掉System.out.print((char)ch);}fileReader.close();}@Testpublic void fileWriterTest() throws IOException{FileWriter fileWriter = new FileWriter("file\\4.txt");fileWriter.write("hello world!");fileWriter.close();}
十、BufferedReader和BufferedWriter
BufferedReader
BufferedWriter
① 进一步补充:用文件写学生管理系统,BufferedReader和BufferedWriter比FileReader和FileWriter速度快。
② 除此之外,BufferedWriter里有一个newLine()方法,可以根据不同的系统生成换行符(因为不同的系统换行符可能不同),所以更推荐用它去写入文件;BuffertedReader里有一个readLine()方法,可以按行读取字符
③ 但实际工作可能不太用这些
@Testpublic void bufferedReaderTest() throws IOException{BufferedReader bufferedReader = new BufferedReader(new FileReader("file\\2.txt"));// 因为是字符流所以用String// 之前用int那种实际是有问题的String str;while ((str = bufferedReader.readLine()) != null){// 这个时候就可以加ln了System.out.println(str);}bufferedReader.close();}@Testpublic void bufferedWriterTest() throws IOException{BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("file\\5.txt"));bufferedWriter.write("hhhhhhhh");bufferedWriter.newLine();bufferedWriter.write("ffffffff");bufferedWriter.close();}
十一、一次性讲解剩余的N个流(扩展课)Java里那些极其骚的IO流
① 借助它们可以用字节流操作字符流。但是要注意文件得是文本文件(因为涉及到操作字符流)
转换流InputStreamReader
转换流OutputStreamWriter
② 对象输入输出流:把一个对象写入文件,传的参数是一个Object对象
③ 存取成员变量(成员数据)
④ 打印流:存一个小的东西作为字符串输出
⑤ 输入输出流:System.out.println
在上述PrintStream里
⑥ 控制台
⑦ 多线程管道流(写底层用的,平时用得不多):多线程用的,多线程管道输入。这个流需要一个线程控制它去读,一个线程控制它去写,跟打电话一样。
Channel就是一个信道,和管道有区别。它们都跟并发有关,性能很高。
⑧
⑨
⑩ 可以理解为装货卸货。货多的时候就算用缓冲流也依然很慢。ByteArrayOutputStream这个类在创建时会自动创建一个Byte[]类型的缓冲区,写入的时候先把所有东西丢到缓冲区,最后再把所有东西一次性扔到另一个文件里
⑪ 跟上面的类似,只不过这里存的是char
⑫ 网上那种多线程下载跟这个类似,它会把两个东西进行合并。例如创建2个文件input流时,它可以把2个流合并,变成一个流。
另一个构造方法用到泛型(写框架会用到),可以提供多个流进行合并
⑬ I/O流都是从前向后,先后顺序读取。要想从文件任意处进行读写操作,字符流和字节流都做不到,只有RandomAccessFile可以实现。它虽然在java.io包里,但是它不属于流。
它可以用指针表明开始位置
或者直接用随机戳生成pos随机数
十二、Apache Common IO
apache公司自己写的io,可以帮助我们简化开发流程,非常实用
2.18jar包下载
2.18API文档
这里面有很多有意思的代码,例如:FileUtils。上节课手写的一些东西,这里几乎都有封装好可以直接拿来用的方法。
例子
1. 例一
@Testpublic void writeTest() throws IOException{File file = new File("file\\6.txt");if(!file.exists()){file.createNewFile();}FileUtils.writeLines(file, new ArrayList(Collections.singleton(("春风很柔,夏风很烈,秋风清爽,东风凛冽,你就是春夏秋冬的风"))), true);}
2. 例二(arrayList的妙用)
需要往文件里写很多东西时就可以用它
writeLines方法涉及到泛型
package com.practice.demo3;public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "name='" + name + '\'' +", age=" + age;}
}
@Testpublic void writeTest2() throws IOException{File file = new File("file\\7.txt");if(!file.exists()){file.createNewFile();}ArrayList<Student> arrayList = new ArrayList<>();// 第一个学生arrayList.add(new Student("Tom", 12));// 第二个学生arrayList.add(new Student("Jerry", 14));FileUtils.writeLines(file, arrayList, true);}
十三、补充
① File和IO Stream配合使用非常强
② 文件复制时也可以用这个类里的方法(这个类里有很多跟文件有关的方法,我们还可以用文件的方式去创建一个流甚至Channel),没必要使用上面说的字节流的方式
jdk文档
③ 复制文件时,我们可以使用字节流的方式,然后手动创建缓冲区;也可以用apache io 包里的方法;还可以用java.nio.file.Files类里的方法。
综上,java中每种功能可能有多种实现方式,要依据当前的项目选择合适的实现方式。
④ c语言中的eof表示文件结束并已读取到最后一个字符。
十四、内容出处
java