InputStream、OutputStream、Buffer
I/O
- 输出流:程序(内存)---> 外界设备
- 输 ⼊流:外界设备---> 程序(内存)
处理数据类型分类
- 字符流:处理字符相关,如处理 ⽂本数据(如 txt ⽂件), Reader/Writer
- 字节流: 处理字节相关,如声 ⾳或者图 ⽚等 ⼆进制,InputStream/OutputStream
字符流和字节流区别
- 字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,⼀次可能读多个字节。
- 字节流可以处理 ⼏乎所有 ⽂件,字符流只能处理字符类型的数据。
InputStream
InputStream 是输 ⼊字节流的 ⽗类,它是 ⼀个抽象类(⼀般 ⽤他的 ⼦类)
常见方法
1//讲解:从输⼊流中读取单个字节,返回0到255范围内的int字节值,字节数据可直接转换为int类 型, 如果已经到达流末尾⽽没有可⽤的字节,则返回-1
2int read()
3
4//从输⼊流中读取⼀定数量的字节,并将其存储在缓冲区数组buf中, 返回实际读取的字节 数,如果已经到达流末尾⽽没有可⽤的字节,则返回-1
5int read(byte[] buf)
6
7//从输⼊流中跳过并丢弃 n 个字节的数据。
8long skip(long n)
9
10//返回这个流中有多少个字节数,可以把buf数组⻓度定为这个
11int available()
12
13//关闭输⼊流并释放与该流关联的系统资源
14void close() throws IOException
常见子类 FileInputStream
⽂件字节输 ⼊流, 对 ⽂件数据以字节的形式进 ⾏读取操作。
1@Test
2 public void ioStreamTesting() throws IOException {
3 //传入文件路径
4 FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1\\1.txt");
5
6 //传入File对象
7 String dir = "C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1";
8 String name = "1.txt";
9 File file = new File(dir, name);
10 FileInputStream inputStream = new FileInputStream(file);
11
12 //对于汉字(两个字节) unicoe种的字符不能正常读取,因为每次只会读取一个字节,所以会显示乱码。
13 int read = inputStream.read();
14 System.out.println(read); //49 ASCII
15 System.out.println((char) read);//1
16
17 //跳过 2个字节
18 long skipSize = inputStream.skip(2);
19 System.out.println(skipSize);
20 int read2 = inputStream.read();
21 System.out.println(read2);
22
23 //字节数组:如果Buf的长度为0,则不读取任何字节并返回0,每次读取的字节数最多等于buf的长度。
24 //byte[] buf = new byte[1024];
25 byte[] buf = new byte[inputStream.available()];//流中的长度,作为字节数组的长度
26 int length;//用于判断读取时候是否返回 -1,代表读取到了尽头
27 while ((length = inputStream.read(buf)) != -1) { //不等于-1代表还可以继续读,返回值是读取到的字节数
28 System.out.println(new String(buf,0,length,"UTF-8")); //中文乱码问题可以换成 GBK 或 UTF-8 GBK中英文占用2个字节,UTF-8中文使用了3个字节。
29 }
30 }
OutputStream
常见方法
1//将指定的字节写⼊输出流
2void write(int b)
3
4//将b.length个字节的byte数组写⼊当前输出流
5void write(byte[] b)throws IOException
6
7//关闭输⼊流并释放与该流关联的系统资源
8void close() throws IOException
常见子类 FileOutputStream
1@Test
2 public void outputStreamTesting() throws IOException {
3 //传入文件路径(会自动创建文件,但是必须父目录存在,否在不会自动创建多级目录)
4 FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1\\1.txt");
5 FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1\\2.txt");
6 //不覆盖文件,追加数据
7 //FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1\\2.txt",true);
8
9 //单个字节读取,中文会出现乱码
10// int value = 0;
11// while (value!=-1){
12// value = fileInputStream.read();
13// fileOutputStream.write(value);
14// }
15
16 //使用字节数组读取
17 byte[] buf = new byte[1024];
18 int length;
19 while ( (length = fileInputStream.read(buf))!=-1){
20 fileOutputStream.write(buf,0,length);
21 }
22
23 //关闭流
24 fileInputStream.close();
25 fileOutputStream.close();
26 }
Buffer 缓冲区
它是内存空间的 ⼀部分,在内存空间中预留了 ⼀定的存储空间,这些存储空间 ⽤来缓冲输 ⼊或输出的数据,这部分空间就叫做缓冲区,缓冲区是具有 ⼀定 ⼤ ⼩的。
缓冲区的意义
- 缓和冲击,例如操作磁盘 ⽐内存慢的很多,所以不 ⽤缓冲区效率很低
- 数据传输速度和数据处理的速度存在不平衡,⽐如你每秒要写 100 次硬盘,对系统冲击很 ⼤, 浪费了 ⼤量时间在忙着处理开始写和结束写这两个事件,⽤ buffer 暂存起来,变成每 10 秒写 ⼀次硬盘,数据可以直接送往缓冲区,⾼速设备不 ⽤再等待低速设备,对系统的冲击就很 ⼩,写 ⼊效率 ⾼了
Java IO 包里面的两个缓冲类(高级流)
- BufferInputStream 和 BufferOutputStream
- 采 ⽤包装设计模式
BufferInputStream 缓冲字节输入流
BufferedInputStream 通过预先读 ⼊ ⼀整段原始输 ⼊流数据 ⾄缓冲区中,⽽外界对 BufferedInputStream 的读取操作实际上是在缓冲区上进 ⾏,如果读取的数据超过了缓冲区 的范围,那么 BufferedInputStream 负责重新从原始输 ⼊流中载 ⼊下 ⼀截数据填充缓冲区, 然后外界继续通过缓冲区进 ⾏数据读取。
- 避免了 ⼤量的磁盘 IO,原始的 InputStream 类实现的 read 是即时读取的,每 ⼀次读取 都会是 ⼀次磁盘 IO 操作(哪怕只读取了 1 个字节的数据),如果数据量巨 ⼤,这样的磁盘消耗 ⾮常可怕。
- 缓冲区的实现: 读取可以读取缓冲区中的内容,当读取超过缓冲区的内容后再进 ⾏ ⼀次磁盘 IO,载 ⼊ ⼀段数据填充缓冲,下 ⼀次读取 ⼀般情况就直接可以从缓冲区读取,减少了磁盘 IO。
- 默认缓冲区 ⼤ ⼩是 8k, int DEFAULT_BUFFER_SIZE = 8192;
常见构造函数
1//对输⼊流进⾏包装,⾥⾯默认的缓冲区是8k
2public BufferedInputStream(InputStream in);
3
4//对输⼊流进⾏包装,指定创建具有指定缓冲区⼤⼩的
5public BufferedInputStream(InputStream in,int size);
常用方法
1//从输⼊流中读取⼀个字节
2public int read();
3
4//从字节输⼊流中给定偏移量处开始将各字节读取到指定的 byte 数组中。
5public int read(byte[] buf,int off,int len);
6
7//关闭释放资源,关闭的时候这个流即可,InputStream会在⾥⾯被关闭
8void close();
BufferOutputStream 缓冲字节输出流
构造函数
1//对输出流进⾏包装,⾥⾯默认的缓冲区是8k
2public BufferedOutputStream(OutputStream out);
3
4//对输出流进⾏包装,指定创建具有指定缓冲区⼤⼩的
5public BufferedOutputStream(OutputStream out,int size);
常用方法
1//向输出流中输出⼀个字节
2public void write(int b);
3
4//将指定 byte 数组中从偏移量 off 开始的 len 个字节写⼊缓冲的输出流。
5public void write(byte[] buf,int off,int len);
6
7//刷新此缓冲的输出流,强制使所有缓冲的输出字节被写出到底层输出流中。
8public void flush();
9
10//关闭释放资源,关闭的时候这个流即可,OutputStream会在⾥⾯被关闭, JDK7新特性try(在这 ⾥声明的会⾃动关闭){}
11void close();
练习
文件拷贝
1@Test
2 public void copyFileTesting() throws IOException {
3 //文件路径
4 String sourceFilePath = "C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1\\1.txt";
5 String targetFilePath = "C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1\\2.txt";
6
7 //用buf包装FileInputStream
8 BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(sourceFilePath));
9
10 //用buf包装FileOutputStream
11 BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(targetFilePath));
12
13 //临时空间
14 int size;
15 byte[] buf = new byte[1024];
16
17 //从bis中读取数据 写入bos中
18 while ((size = bufferedInputStream.read(buf)) != -1) { //读取到bis缓冲区尽头才会返回-1
19 bufferedOutputStream.write(buf,0,size);
20 }
21
22 //刷新数据(没有填满BOS不会触发自动刷新,需要手动刷新)
23 bufferedOutputStream.flush();
24
25 //关闭资源(buf类会自动关闭 bis、bos) 如果A依赖于B,则先关闭B
26 bufferedInputStream.close();
27 bufferedOutputStream.close();
28
29 //触发GC
30 buf = null;
31 }
将一个目录下的所有图片,拷贝到另外一个目录
1@Test
2 public void copyPictruePractice() throws IOException {
3 //目录
4 String sourceDir = "C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1";
5 String targetDir = "C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\2";
6
7 //遍历源目录中所有文件
8 File file = new File(sourceDir);
9 String[] listFile = file.list();
10 for (String s : listFile) {
11 String sourceNamePath = sourceDir + File.separator + s;
12 String targetNamePath = targetDir + File.separator + s;
13
14 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceNamePath));
15 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetNamePath));
16
17 //临时数组
18 int size;
19 byte[] buffer = new byte[1024];
20
21 //从bis中读取到bos
22 while ((size = bis.read(buffer)) != -1) {
23 bos.write(buffer,0,size);
24 }
25
26 //bos缓冲区未满(不会自动触发刷新,需要手动刷新)
27 bos.flush();
28
29 //关闭资源
30 bis.close();
31 bos.flush();
32
33 //触发gc
34 buffer = null;
35 }
36 }