目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

InputStream、OutputStream、Buffer

I/O

  • 输出流:程序(内存)---> 外界设备
  • 输 ⼊流:外界设备---> 程序(内存)

处理数据类型分类

  • 字符流:处理字符相关,如处理 ⽂本数据(如 txt ⽂件), Reader/Writer
  • 字节流: 处理字节相关,如声 ⾳或者图 ⽚等 ⼆进制,InputStream/OutputStream

字符流和字节流区别

  •  字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,⼀次可能读多个字节。
  • 字节流可以处理 ⼏乎所有 ⽂件,字符流只能处理字符类型的数据。

IO 流相关类体系

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    }

作者:Soulboy