(十三)I/O流-字节流

一、I/O的概念

I/O:Input/Output

为什么需要I/O?
在操作系统中,一切需要永久保存的数据都以文件的形式存储。需要长久保存的文件数据,存储在外部设备(硬盘)。但是,程序在运行的时候,相关的数据必须被读取到内存中才能运行,同时,内存的大小有限,远小于外部设备,因此常常需要在内存和外设之间交换数据,即I/O。

二、Java I/O流

为了在Java语言层面实现I/O功能,Java语言引入了流,基于流模型来完成I/O功能。Java语言主要通过输入流和输出流完成I/O的功能,从而实现和外设的数据交互。

流模型:
1.基于流的的数据传输,在数据传输之前,会在内存和外部设备之间建立数据传输通道(流对象来创建)。
2.在数据传输通道中,数据就类似于水流,数据传输就类似于水的流动,水的流动(内存——> 外设,外设——>内存)。

流(流对象)的分类:
1.按照数据“流动”的方向(传输的方向)
a.输入流 外设——>内存 (读操作 read)
b.输出流 内存——>外设 (写操作 write)
注意:关于输入、输出的方向问题,输入、输出都是以内存为基准来说的,数据从内存到外设是输出,数据从外设到内存是输入。

2.按照流的数据传输通道中传输的数据内容
a.字节流 传输的数据是以字节为单位的01二进制数据
b.字符流 传输的数据是字符数据

3.如何区分使用字节流还是字符流?
a.字符流专门用来传输文本数据。简单来说,文本数据就是用文本编辑器打开的时候,我们人可以看懂的数据。
b.字节流可以用来传输一切类型数据。只不过针对文本(字符)数据,找某些情况下,字节流操作起来不太方便,除了文本数据之外的其他数据,都是用字节流。
c.当我们不确定所要传输的数据类型的时候,一律使用字节流。

三、I/O流常用基类

1.字节流:
a.字节输出流 OutputStream
b.字节输入流 InputStream

2.字符流:
a.字符输出流 Writer
b.字符输入流 Reader

这四个类是都是抽象基类。
由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:InputStream的子类FileInputStream。
如:Reader的子类FileReader。

四、字节流写数据

通过OutputStream对象,完成向文本文件中输出” hello world” 。
OutputStream是抽象类,如果要使用OutputStream对象,只能使用其子类对象,完成写入功能。这里还需要注意的是,虽然写入的数据是文本数据,但是还没学字符流,所以先使用字节流。
要用字节流写数据,先创建流对象,再进行写操作。

1.OutputStream对象的创建

FileOutputStream的构造方法

  • FileOutputStream(File file)
    创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
  • FileOutputStream(String name)
    创建一个向具有指定名称的文件中写入数据的输出文件流。
 // 创建OuputStream对象(间接实例化)
 //1. FileOutputStream(String name)  以路径名字符串表示目标文件
 OutputStream os = new FileOutputStream("first.txt");

 //2. FileOutputStream(File file)
 File file = new File("first.txt");
 OutputStream os1 = new FileOutputStream(file);

2.字节流写数据的方式——写方法

  • public void write(int b)
    将指定的字节写入此输出流。write的常规协定是:向输出流写入一个字节。要写入的字节是参数 b 的八个低位。b 的24个高位将被忽略。
  • public void write(byte[] b)
    将 b.length 个字节从指定的 byte 数组写入此输出流 , 简单来说,就是把整个字节数组中的多个字节数据,一次写入流中。
  • public void write(byte[] b,int off,int len)
    将 b的len个字节,从指定的byte数组的第offset位置开始,写入此输出流 , 简单来说就是把字节数组的从offset开始的len个字节数据,写入流中。

流对象使用完毕之后要关闭,对象.close()。

3.字节流写数据常见问题:

  • 创建字节输出流到底做了哪些事情?
    1)创建FileOutputStream对象的时候,jvm首先到操作系统,查找目标文件
    a. 当发现目标文件不存在的时候, jvm会首先创建该目标文件(内容为空)
    b. 当发现目标文件存在的时候, jvm默认会首先清空目标文件内容,做好准备,让FileOutputStream从文件头开始写入数据
    2)在内存中,创建FileOutputStream对象
    3)在FileOutputStream对象和目标文件之间建立数据传输通道

  • 数据写成功后,为什么要close()?
    a. 关闭此输出流
    b. 释放与此流有关的所有系统资源。

  • 如何实现数据的换行?
    换行符,在不同的操作系统中,表示方式是不一样的:
    类unix操作系统中:’\n’
    windows默认的换行表示: ‘\r’+’\n’ 用windows自带的文本编辑器对于\n是否有换行效果,还和操作系统版本有关

  • 如何实现数据的追加写入?
    注意:一定是两次创建输出流对象,在两个不同的输出流对象上向同一个文件写入数据,实现对文件的追加写入。
    a.FileOutputStream(File file, boolean append)
    创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
    如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处.
    b.FileOutputStream(String name, boolean append)
    创建一个向具有指定 name 的文件中写入数据的输出文件流。
    如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处.

  • 给I/0流操作加上异常处理

	OutputStream out = null;
    try {
      // 1. 创建流对象
      out = new FileOutputStream("first.txt");
      // 2. 数据传输
      out.write("zhansan".getBytes());


    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      //3.关闭流,并释放系统资源
      if (out != null) {
        try {
          out.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }

    }

五、字节流读数据

上文通过OutputStream对象(FileOutputStream对象),我们成功实现了向文本输出数据的功能。现在,我们使用InputStream从该文本中,读出刚刚写入的内容,并输出在控制台。
同时,注意到InputStream是抽象类,不能直接实例化,需要使用FileInputStream子类对象来间接实例化InputStream(抽象类)。

1.InputStream对象的创建

FileInputStream的构造方法

  • FileInputStream(File file)
    创建一个从File对象指定的目标文件中,读取文件字节数据的字节输入流
  • FileInputStream(String name)
    创建一个从name路径名字符串指定的目标文件中,读取文件字节数据的字节输入流
	// FileInputStream(String name)
    InputStream in = new FileInputStream("first.txt");
    //FileInputStream(File file)
    File file = new File("first.txt");
    InputStream in2 = new FileInputStream(file);

2.字节流读取数据的方式——读方法

FileInputStream的成员方法

  • public int read()
    从此输入流中读取一个数据字节。
    返回值:
    a. 代表从流中读取到的一个字节数据
    b. 如果已到达文件末尾,则返回 -1。-1就代表一个读取结束的标志
  • public int read(byte[] b)
    从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
    返回值:
    a. 读入缓冲区的字节总数
    b. 如果因为已经到达文件末尾, 而没有更多的数据,则返回 -1。
  • int read(byte[] b, int off, int len)
    从此输入流中将最多 len 个字节的数据读入一个 byte 数组中(同时,读取到字节数据,从字节数组的第offset个位置开始存放)。
    返回值:
    a. 读入缓冲区的字节总数
    b. 如果因为已经到达文件末尾, 而没有更多的数据,则返回 -1

流对象使用完毕之后要关闭,对象.close()。

字节流复制数据两种方式比较:
一次读取一个字节
一次读取一个字节数组

从效率角度讲,哪种方式比较好呢?为什么?
第二种方式比较好
每次jvm实际执行IO都需要和操作系统交互,这意味着每一次内存和外设的数据传输都需要付出一次和操作系统的通信代价。
a.如果一次传输一个字节数据,这意味着传输一个字节就要付出一次通信代价。
b.如果一次传输一个字节数组,这意味着,传输一个字节数组的多个字节数据才付出一次通信代价。

六、字节缓冲流

字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的情况,所以提供了字节缓冲区流

字节缓冲输出流
BufferedOutputStream
构造方法:
BufferedOutputStream(OutputStream out)
创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
字节缓冲输入流
BufferedInputStream
构造方法:
BufferedInputStream(InputStream in)
创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。

这种需要,基于其他流对象来创建的这种流对象,我们称之为包装流。
关闭流的时候,我们只需要关闭包装流即可,因为包装流会负责关闭它所包装的底层流。
void flush() //刷新此输出流并强制写出所有缓冲的输出字节。

close() //关闭此输出流并释放与此流有关的所有系统资源。close 方法先调用其 flush 方法,然后在关闭流释放资源。

比较四种复制方式的效率
1.不使用缓冲流
a. 一次复制一个字节数据
b. 一次复制一个字节数组的数据
2.使用缓冲流
c. 一次复制一个字节数据
d. 一次复制一个字节数组的数据

运行时间的比较:d < c < b < a
使用缓冲流一次复制一个字节数组的数据所用时间最短,效率最高。一次复制一个字节数据所用时间最长,效率最差。
#个人学习记录,如发现有错误之处,欢迎与我交流

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值