目录

Life in Flow

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

X

InputStream、OutputStream、Buffer

I/O

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

处理数据类型分类

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

字符流和字节流区别

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

IO流相关类体系

InputStream

 InputStream 是输 ⼊字节流的 ⽗类,它是 ⼀个抽象类(⼀般 ⽤他的 ⼦类)

常见方法

//讲解:从输⼊流中读取单个字节,返回0到255范围内的int字节值,字节数据可直接转换为int类 型, 如果已经到达流末尾⽽没有可⽤的字节,则返回-1
int read()

//从输⼊流中读取⼀定数量的字节,并将其存储在缓冲区数组buf中, 返回实际读取的字节 数,如果已经到达流末尾⽽没有可⽤的字节,则返回-1
int read(byte[] buf)

//从输⼊流中跳过并丢弃 n 个字节的数据。
long skip(long n)

//返回这个流中有多少个字节数,可以把buf数组⻓度定为这个
int available()

//关闭输⼊流并释放与该流关联的系统资源
void close() throws IOException

常见子类 FileInputStream
 ⽂件字节输 ⼊流, 对 ⽂件数据以字节的形式进 ⾏读取操作。

@Test
    public void ioStreamTesting() throws IOException {
        //传入文件路径
        FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1\\1.txt");

        //传入File对象
        String dir = "C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1";
        String name = "1.txt";
        File file = new File(dir, name);
        FileInputStream inputStream = new FileInputStream(file);

        //对于汉字(两个字节)  unicoe种的字符不能正常读取,因为每次只会读取一个字节,所以会显示乱码。
        int read = inputStream.read();
        System.out.println(read);   //49     ASCII
        System.out.println((char) read);//1

        //跳过 2个字节
        long skipSize = inputStream.skip(2);
        System.out.println(skipSize);
        int read2 = inputStream.read();
        System.out.println(read2);

        //字节数组:如果Buf的长度为0,则不读取任何字节并返回0,每次读取的字节数最多等于buf的长度。
        //byte[] buf = new byte[1024];
	byte[] buf = new byte[inputStream.available()];//流中的长度,作为字节数组的长度
        int length;//用于判断读取时候是否返回 -1,代表读取到了尽头
        while ((length = inputStream.read(buf)) != -1) { //不等于-1代表还可以继续读,返回值是读取到的字节数
            System.out.println(new String(buf,0,length,"UTF-8")); //中文乱码问题可以换成 GBK 或  UTF-8     GBK中英文占用2个字节,UTF-8中文使用了3个字节。
        }
    }

OutputStream

常见方法

//将指定的字节写⼊输出流
void write(int b)

//将b.length个字节的byte数组写⼊当前输出流
void write(byte[] b)throws IOException

//关闭输⼊流并释放与该流关联的系统资源
void close() throws IOException

常见子类 FileOutputStream

@Test
    public void outputStreamTesting() throws IOException {
        //传入文件路径(会自动创建文件,但是必须父目录存在,否在不会自动创建多级目录)
        FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1\\1.txt");
        FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1\\2.txt");
        //不覆盖文件,追加数据
        //FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1\\2.txt",true);

        //单个字节读取,中文会出现乱码
//        int value = 0;
//        while (value!=-1){
//            value = fileInputStream.read();
//            fileOutputStream.write(value);
//        }

        //使用字节数组读取
        byte[] buf = new byte[1024];
        int length;
        while ( (length = fileInputStream.read(buf))!=-1){
            fileOutputStream.write(buf,0,length);
        }

        //关闭流
        fileInputStream.close();
        fileOutputStream.close();
    }

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;

常见构造函数

//对输⼊流进⾏包装,⾥⾯默认的缓冲区是8k
public BufferedInputStream(InputStream in);

//对输⼊流进⾏包装,指定创建具有指定缓冲区⼤⼩的
public BufferedInputStream(InputStream in,int size);

常用方法

//从输⼊流中读取⼀个字节
public int read();

//从字节输⼊流中给定偏移量处开始将各字节读取到指定的 byte 数组中。
public int read(byte[] buf,int off,int len);

//关闭释放资源,关闭的时候这个流即可,InputStream会在⾥⾯被关闭
void close();

BufferOutputStream 缓冲字节输出流

构造函数

//对输出流进⾏包装,⾥⾯默认的缓冲区是8k
public BufferedOutputStream(OutputStream out);

//对输出流进⾏包装,指定创建具有指定缓冲区⼤⼩的
public BufferedOutputStream(OutputStream out,int size);

常用方法

//向输出流中输出⼀个字节
public void write(int b);

//将指定 byte 数组中从偏移量 off 开始的 len 个字节写⼊缓冲的输出流。
public void write(byte[] buf,int off,int len);

//刷新此缓冲的输出流,强制使所有缓冲的输出字节被写出到底层输出流中。
public void flush();

//关闭释放资源,关闭的时候这个流即可,OutputStream会在⾥⾯被关闭, JDK7新特性try(在这 ⾥声明的会⾃动关闭){}
void close();

练习

文件拷贝

@Test
    public void copyFileTesting() throws IOException {
	//文件路径
        String sourceFilePath = "C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1\\1.txt";
        String targetFilePath = "C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1\\2.txt";

        //用buf包装FileInputStream
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(sourceFilePath));

        //用buf包装FileOutputStream
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(targetFilePath));

        //临时空间
        int size;
        byte[] buf = new byte[1024];

        //从bis中读取数据 写入bos中
        while ((size = bufferedInputStream.read(buf)) != -1) { //读取到bis缓冲区尽头才会返回-1
            bufferedOutputStream.write(buf,0,size);
        }

        //刷新数据(没有填满BOS不会触发自动刷新,需要手动刷新)
        bufferedOutputStream.flush();

        //关闭资源(buf类会自动关闭 bis、bos) 如果A依赖于B,则先关闭B
        bufferedInputStream.close();
        bufferedOutputStream.close();

        //触发GC
        buf = null;
    }

将一个目录下的所有图片,拷贝到另外一个目录

@Test
    public void copyPictruePractice() throws IOException {
        //目录
        String sourceDir = "C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\1";
        String targetDir = "C:\\Users\\Ryzen-7-3800X\\Pictures\\Camera Roll\\test\\2";

        //遍历源目录中所有文件
        File file = new File(sourceDir);
        String[] listFile = file.list();
        for (String s : listFile) {
            String sourceNamePath = sourceDir + File.separator + s;
            String targetNamePath = targetDir + File.separator + s;

            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceNamePath));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetNamePath));

            //临时数组
            int size;
            byte[] buffer = new byte[1024];

            //从bis中读取到bos
            while ((size = bis.read(buffer)) != -1) {
                bos.write(buffer,0,size);
            }

            //bos缓冲区未满(不会自动触发刷新,需要手动刷新)
            bos.flush();

            //关闭资源
            bis.close();
            bos.flush();

            //触发gc
            buffer = null;
        }
    }

作者:Soulboy