Java-IO流

一、IO概念

  • I/O就是input和output的缩写,是计算机调度把各个储存中(包括内存和外部储存)的数据写入写出的过程
  • 所谓的外部设备可以包括硬盘文件, 网络设备, 另个程序等. 也就是当前程序之外的数据设备
  • java中用流(stream)来抽象表示这么一个写入写出的功能,封装成一个类,都放在java.io这个包里面

二、流(stream)的概念

  • stream(流)是从起源(source)到接收(sink)的有序数据。
  • 如果把输入输出源比作“水桶”的话,“流”就相当于是连接他们的“管道”,这个管道的粗细、单向性等各种属性就是区分不同流的特性,流中的各种方法就是管道上的按钮,用于操作数据的流通。
    在这里插入图片描述

三、IO流的分类

1、按照流的方向

  • 输入流:只能从中读取数据(主要由InputStream和Reader作为基类)
  • 输出流:只能向其写入数据(主要由OutputStream和Writer作为基类)

2、按照流的操作颗粒度划分

  • 字节流:以字节为单位,可操作任何数据(主要以InputStream和OutputStream作为基类)
  • 字符流:以字符为单位,只能操作纯字符数据,比较方便(主要以Reader和Writer为基类)

3、按照流的角色划分

  • 节点流:可以从/向一个特定的IO设备(如磁盘,网络)读/写数据的流,也叫低级流。
  • 处理流:用于对一个已经存在的流进行连接和封装,通过封装后的流实现数据的读/写功能,也叫高级流。

InputStream

InputStream是Java标准库提供的最基本的输入流。InputStream是一个抽象类,是所有输入类的超类。这个抽象类定义的最重要的方法就是int read()这样的一个方法

public abstract int read() throws IOException;
这个方法会读取输入流的下一个字节,并返回字节表示的int值。如果己经> 读到末尾,返回-1,表示不能继续读取了

FileInputStream是InputStream的一个子类,可以用来从文件中获得数据,下面的代码演示了如何完整读取一个文件的字节:

public static void main(String[] args) throws IOException {
      InputStream inputStream=new FileInputStream("src/sample.txt");
        for(;;){
            int n=inputStream.read();
            if(n==-1){
                break;
            }
            System.out.println(n);
        }
        inputStream.close();
    }

注意:在计算机中,类似文件,网络端口这些资源,都是由操作系统统一管理,应用程序在运行的时候,如果打开了一个文件进行读写,完成后要及时关闭,以便操作系统把资源释放掉,否则,应用程序占用的资源会越来越多,不但白白占用内存还会影响其他应用程序的运行。

1.缓冲

在读取流的时候,一次读取一个字节并不是最高效的方法。很多流支持一次性读取多个字节到缓冲区,对于文件和网络流来说,利用缓冲区一次性读取多个字节的效率要高很多。InputStream提供两个重载方法来支持读取多个字节:

int read(byte[] b): 读取若干字节填充到byte[]数组,返回读取的字节数
int read(byte[] b,int off,int len): 指定byte[]数组的偏移量和最大填充数

利用上述方法读取多个字节时需要先定义一个byte[]数组作为缓冲区,read()会尽可能多的读取字节到缓冲区,但不会超过缓冲区容量的大小,read()的返回值变为实际读取的字节数。若返回-1,则表示没有更多数据了。
栗子:

public static void main(String[] args) throws IOException{
      InputStream inputStream=new FileInputStream("src/sample.txt");
        byte[] butter=new byte[5];
        int n;
        while((n=inputStream.read(butter))!=-1){
            System.out.println("read"+n+"bytes\n");
        }
        inputStream.close();
    }

2.阻塞

在调用InputStream的read()方法时,我们说它是阻塞的

int n;
n=inputStream.read();
m=n;

执行到第二行时,必须等read()方法返回后才可以继续。因为读取IO流比普通代码速度要慢很多,无法确定read()方法到底要花费多长时间,所以它是阻塞的。

OutputStream

和InputStream相反,OutputStream是Java库中提供的最基本的输出流。
和InputStream类似,OutputStream也是抽象类,它是所有输出流的超类。这个抽象类定义的最重要的方法是void write(int b):

public abstract void write(int b) throws IOException;

这个方法会写入一个字节到输出流。要注意,虽然传入的是int类型的参数,但只会写入一个字节,即只写入int最低8位表示字节的部分。

  • 和InputStream一样,也有close()方法,write()也是阻塞的
  • OutputStream还提供了一个flush()方法,它的作用是将缓冲区的内容真正输入到目的地。

为什么要有flush()方法?因为向网络、磁盘写入数据的时候,出于效率考虑,操作系统并不是输出一个字节就立马写入到文件或发送到网络,而是把输出的字节先放到内存的一个缓冲区(本质上也是一个byte[]数组),等到缓冲区写满,在一次性发送。对于很多IO设备,一次性写一个字节和一次性写1000个字节花费的时间几乎是一样的,所以这个flush方法的存在,就是强制把缓冲区的flush输出。

以FileOutputStream为例,演示将若干字节写入文件流:

public static void main(String[] args) throws IOException {
 OutputStream outputStream = new FileOutputStream("src/sample.txt");
        outputStream.write(108);
    }

Filter模式

  • 如果我们要给一个FileInputStream添加缓冲功能,则可以从FileInputStream派生出一个类

BufferedFileInputStream extends FileInputstream

  • 如果我们要给一个FileInputStream添加计算签名功能,则可以派生一个类

DigestFileInputStream extends FileInputstream

  • 若要添加加密解密功能,还可以派生一个类

CipherFileInputStream extends FileInputstream

如果要给一个FileInputStream添加以上三种功能,用继承的方法实现的话,子类会比较多,实现的功能越多,子类就越复杂。因此直接使用继承的方法,为InputStream添加很多功能,根本无法控制代码的复杂度。

为了解决依赖继承导致子类数量失控的问题,JDK首先将InputStream分成两大类:

  1. 直接提供数据的基础InputStream,例如
  • FileInputStream
  • ByteArrayInputStream
  1. 提供额外附加功能的InputStream。例如
  • BufferedInputStream
  • DigestInputStream

当我们给一个基础的的InputStream附加各种功能时,我们先确定这个提供数据源的InputStream,即第一类Inputstream
例如:

Inputstream file= new FileInputStream(“test.gz”);

紧接着我们希望FileInputStream能提供缓冲的功能来提高读取效率,因此我们可以用BufferedInputStream来包装这个InputStream,得到的包装类型是BufferedInputStream,他仍可视为InputStream:

InputStream buffered= new BufferedInputStream(file);

无论包装了多少次,得到的对象始终是InputStream,我们zhijieyongInputStream来引用它,皆可以正常读取。

同理,OutputStream也是如此,这样的一种通过一个基础组件再叠加各种“附加”功能组件的模式,就叫做Filter模式。

Reader

Reader是Java IO库提供的另一个输入流接口。和InputStream的区别是,Reader是一个字符流,以char为单位读取,而后者是一个字节流,以byte为单位。

InputStreamReader
字节流,byte字符流,char
读取字节(-1,0~255):int read()读取字符(-1,0~65535):int read()

Reader定义了所有字符输入流的超类:

  • FileReader实现了文字字符流的输入,使用时注意制定编码
  • CharArrayReader和StringReader可以在内存中模拟一个字符流输入

Reader 是基于inputStream构造的:可以通过InPutStreanReader在制定编码的同时讲一个InputStream转化为Reader。

Reader reader=new InputStreamReader(new FileInPutstream("文件名路径”,“编码”));

Writer

Writer和OutputStream的联系与上面Reader差不多,不再赘述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值