流的概念

  JAVA程序通过流来完成输入/输出。流是生产或消费信息的抽象,流通过JAVA的输入输出与物理设备链接,尽管与它们链接的物理设备不尽相同,所有流的行为具有相同的方式。这样就意味一个输入流能够抽象多种不同类型的输入:从磁盘文件、从键盘或从网络套接字;同样,一个输出流可以输出到控制台、磁盘文件或相连的网络。

  在我们平时接触的输入/输出流中,有这样一个概念必须要弄明白,何谓输入、何谓输出?讨论这个问题的前提是要知道以什么为参考物,这个参考物就是程序或者内存。输入:就是从磁盘文件或者网络等外部的数据流向程序或者内存。输出就是相反的过程。其中这个“外部”可以是很多种介质:

1、本地磁盘文件、远程磁盘文件。

2、数据库链接

3、TCP、UDP、HTTP网络通信

4、进程间通信

5、硬件设备(键盘、串口)

流的分类

1、从功能上:输入流、输出流

2、从结构上:字节流、字符流

3、从来源上:节点流、过滤流

  其中InputStream/OutputStream是为字节流而设计的,Reader/Writer是为字符流而设计的。处理字节或者二进制对象使用字节流,处理字符或者字符串使用字符流。

在最底层,所有的输入/输出都是字节形式的,基于字符的流只在处理字符的时候提供方便有效的方法。

  节点流是从特定的地方读写的流,例如磁盘或者内存空间,也就是这种流是直接对接目标的。

  过滤流是使用节点流作为输入输出的,就是在节点流的基础上进行包装,新增一些特定的功能。

InputStream

其中有底色标注的为节点流,无底色标注的为过滤流,其中FilterInputStream在JDK中的定义为:包含其他一些输入流,它将这些流用作其基本数据源,可以直接传输数据或提供一些额外的功能,这个类本身并不经常被我们使用,常用的是它的子类。

定义了字节输入模式的抽象类,该类提供了三个重载的read方法:

我们可以看到,三个read方法中,其中有一个是抽象的。那在这里思考这样一个问题:为什么只有第一个是抽象的, 其他两个是具体的?

因为后面两个方法内部最终会去调用第一个方法,所以在InputStream派生类中只需要重写第一个方法就可以了。在这里可以看到第一个read方法是与具体的I/O设备相关的,需要子类去实现。

读写数据的逻辑步骤为:

open a stream

while more information

read/write information

close the stream

一、FileInputStream

 1 public static void main(String[] args) throws IOException
2 {
3 InputStream is = new FileInputStream("c:/test.txt");
4 int length = 0;
5 byte[] buffer = new byte[20];
6 while(-1 != (length = is.read(buffer,0,20)))
7 {
8 String str = new String(buffer,0,length);
9 System.out.print(str);
10 }
11 is.close();
12 }

执行结果:

中国移动基地咪咕阅读
中国移动基地咪咕音乐
a

1、首先我们看下读取文件test的内容:

2、在这里我们使用的是输入流,读取的是我本机C盘中名为test文件中的内容。

3、每次读取的内容存放在buffer这个字节数组中,然后转换成String字符串打印在控制台中。

4、我们来看下第6行代码:因为一个文件内容有多少我们事先并不知道,所以在这里只能分批次读取,每次最多读取20个字节(即buffer数组定义的长度)。

5、length的作用是表示在最后一次读取的时候,读取的长度小于等于buffer数组的长度,while循环体执行结束后,下一次再来读取已经没有内容了,read方法在这个时候会返    回-1,然后跳出循环。

6、对于流的操作,最后一步永远是调用close方法,释放资源。

总结:文本中内容故意定义为41个字节长度,然后打印的时候用了换行。从执行结果可以看到,前两次读取的长度都是20个字节,第三次是1个字节,最后一次没有内容返回-1,然后就跳出循环体了。这个DEMO虽然很简单,但是也是IO种最基本最需要掌握的一部分。

二、FileOutputStream

这个类与FileInputStream是成对的,用法也类似:

 public static void main(String[] args) throws IOException
{
OutputStream os = new FileOutputStream("c:/test1.txt");
String str = "中国移动手机阅读";
byte[] b = str.getBytes();
os.write(b);
os.close();
}

执行结果:

1、这个DEMO主要是将字符串写入磁盘文件中。

2、在第3行的构造函数处要注意下,这个方法中如果指定的文件不存在,则会创建一个新的;如果指定的文件存在,在后面的写入操作会覆盖原有的内容。

这个大家会有这样一个疑问,如果我不想覆盖原有的内容,只想在后面追加内容呢?

 public static void main(String[] args) throws IOException
{
OutputStream os = new FileOutputStream("c:/test.txt",true);
String str = "注册用户6亿";
byte[] b = str.getBytes();
os.write(b);
os.close();
}

执行结果:

1、从执行结果看,这个是在原有内容后面进行追加;程序唯一的区别就在于第三行的构造函数。

OutputStream os = new FileOutputStream("c:/test.txt",true);

如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。

三、BufferedOutputStream

在前面介绍的FileOutputStream,是在程序里面读取一个字节,就往外写一个字节。在这种情况下,频繁的跟IO设备打交道,I/O的处理速度跟CPU的速度根本就不在一个量级上(I/O是一种物理设备),在信息量很多的时候,就比较消耗性能。基于这种问题,JAVA提供了缓冲字节流,通过这种流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。

 public static void main(String[] args) throws IOException
{
OutputStream os = new FileOutputStream("c:/test.txt");
OutputStream bs = new BufferedOutputStream(os);
byte[] buffer = "中国移动阅读基地".getBytes();
bs.write(buffer);
bs.close();
os.close();
}

执行结果:

缓冲输出流在输出的时候,不是直接一个字节一个字节的操作,而是先写入内存的缓冲区内。直到缓冲区满了或者我们调用close方法或flush方法,该缓冲区的内容才会写入目标。才会从内存中转移到磁盘上。下面来看看不调用close方法会出现什么情况:

 public static void main(String[] args) throws IOException
{
OutputStream os = new FileOutputStream("c:/test1.txt");
OutputStream bs = new BufferedOutputStream(os);
byte[] buffer = "中国移动阅读基地".getBytes();
bs.write(buffer);
// bs.close();
// os.close();
}

执行结果:

在这里没有调用close方法,相当于内容还在内存的缓冲区中。BufferedOutputStream本身没有close方法,调用的是父类FilterOutputStream的close方法:

  public void close() throws IOException {
try {
flush();
} catch (IOException ignored) {
}
out.close();
}

在这里可以看到这个方法的本质是在在关闭资源之前,调用的flush方法。

而flush在JDK中的定义为:刷新此缓冲的输出流。这迫使所有缓冲的输出字节被写出到底层输出流中

四、DataOutputStream

数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。

     public static void main(String[] args) throws IOException
{
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(
new FileOutputStream("c:/data.txt")));
byte b = 4;
char c = 'c';
int i = 12;
float f = 3.3f;
dos.writeByte(b);
dos.writeChar(c);
dos.writeInt(i);
dos.writeFloat(f);
dos.close();

执行结果:

打开之后,里面是乱码,程序写入之后是一个二进制文件。我们的程序中是将java的基本数据类型写入文本,注意这里不是字符串,而是基本数据类型。我们这样写入是没有意义的,下面我们用同样的方式去读取。

         DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(
new FileOutputStream("c:/data.txt")));
byte b = 4;
char c = 'c';
int i = 12;
float f = 3.3f;
dos.writeByte(b);
dos.writeChar(c);
dos.writeInt(i);
dos.writeFloat(f);
dos.close(); DataInputStream dis = new DataInputStream(new BufferedInputStream(
new FileInputStream("c:/data.txt")));
System.out.println(dis.readByte());
System.out.println(dis.readChar());
System.out.println(dis.readInt());
System.out.println(dis.readFloat());
dis.close();

执行结果:

4
c
12
3.3

这里看到,我们的数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本JAVA数据类型。

这里特别注意的地方是:读取数据类型的顺序与当初写入的数据类型的顺序要一致,否则会出现乱码或者读取的信息不准确。

这个原因我们可以这样来理解:写入的时候是不同类型的基本数据,不同的基本数据类型的字节长度不一样,如果读取时候顺序不一致,会导致字节的组合混乱,导致乱码或者走义。

深入理解JAVA I/O系列二:字节流详解的更多相关文章

  1. Hexo系列(二) 配置文件详解

    Hexo 是一款优秀的博客框架,在使用 Hexo 搭建一个属于自己的博客网站后,我们还需要对其进行配置,使得 Hexo 更能满足自己的需求 这里所说的配置文件,是位于站点根目录下的 _config.y ...

  2. Java容器解析系列(11) HashMap 详解

    本篇我们来介绍一个最常用的Map结构--HashMap 关于HashMap,关于其基本原理,网上对其进行讲解的博客非常多,且很多都写的比较好,所以.... 这里直接贴上地址: 关于hash算法: Ha ...

  3. Java容器解析系列(9) PrioriyQueue详解

    PriorityQueue:优先级队列; 在介绍该类之前,我们需要先了解一种数据结构--堆,在有些书上也直接称之为优先队列: 堆(Heap)是是具有下列性质的完全二叉树:每个结点的值都 >= 其 ...

  4. Java容器解析系列(14) IdentityHashMap详解

    IdentityHashMap,使用什么的跟HashMap相同,主要不同点在于: 数据结构:使用一个数组table来存储 key:value,table[2k] 为key, table[2k + 1] ...

  5. Java容器解析系列(7) ArrayDeque 详解

    ArrayDeque,从名字上就可以看出来,其是通过数组实现的双端队列,我们先来看其源码: /** 有自动扩容机制; 不是线程安全的; 不允许添加null; 作为栈使用时比java.util.Stac ...

  6. Vue学习系列(二)——组件详解

    前言 在上一篇初识Vue核心中,我们已经熟悉了vue的两大核心,理解了Vue的构建方式,通过基本的指令控制DOM,实现提高应用开发效率和可维护性.而这一篇呢,将对Vue视图组件的核心概念进行详细说明. ...

  7. Java容器解析系列(13) WeakHashMap详解

    关于WeakHashMap其实没有太多可说的,其与HashMap大致相同,区别就在于: 对每个key的引用方式为弱引用; 关于java4种引用方式,参考java Reference 网上很多说 弱引用 ...

  8. 深入理解Java虚拟机(四)——HotSpot垃圾收集器详解

    垃圾收集器 新生代收集器 1.Serial收集器 特点: 单线程工作,收集的时候就会停止其他所有工作线程,用户不可知不可控,会使得用户界面出现停顿. 简单高效,是所有收集器中额外内存消耗最少的. 没有 ...

  9. Java并发编程系列之CyclicBarrier详解

    简介 jdk原文 A synchronization aid that allows a set of threads to all wait for each other to reach a co ...

随机推荐

  1. 用NI的数据采集卡实现简单电子测试之6——数字I/O及测试平台

    本文从本人的163博客搬迁至此. 前面几个例子介绍了NI数据采集卡的模拟输入和输出功能,本例则集中介绍USB-6009的数字输入输出功能.本例包括基本数字IO电路及在LabVIEW中控制USB-600 ...

  2. springboot-web进阶(四)——单元测试

    一.概述 基础知识,参考:https://www.cnblogs.com/ysw-go/p/5447056.html 二.springboot的单元测试 1.入门测试类 最重要的不要忘记类上面的依赖, ...

  3. JavaWeb基础—JS学习小结

    JavaScript是一种运行在浏览器中的解释型的编程语言 推荐:菜鸟教程一.简介js:javascript是基于对象[哪些基本对象呢]和和事件驱动[哪些主要事件呢]的语言,应用在客户端(注意与面向对 ...

  4. C# WPF Image控件下对于Base64的转化显示

    原文:C# WPF Image控件下对于Base64的转化显示 算作前言 本文对图片如何转化成base64不做描述,我们可以从很多途径了解到转化办法.却很少有博客提到怎么在WPF的Image控件中显示 ...

  5. 微信小程序:设置启动页面

    一.功能描述 微信小程序启动时,首先运行app.js,然后才跳转到第一个页面,也就是启动界面. 设置启动界面,只需要调整app.json的pages信息的位置,放在第一条的page记录便是启动界面

  6. JAVA中使用RSA通过秘钥文件对字符串进行加密解密

    技术交流群: 233513714 //字符串进行加密算法的名称 public static final String ALGORITHM = "RSA"; //字符串进行加密填充的 ...

  7. opencv-Getting Started with Images

    1.opencv库简单的操作图片 # coding = utf-8 # 书籍:<<学习opencv>> import cv2 from matplotlib import py ...

  8. 2_C语言中的数据类型 (七)类型限定

    1.1       类型限定 1.1.1          const const是代表一个不能改变值的常量 1.1.2          volatile 代表变量是一个可能被CPU指令之外的地方改 ...

  9. 【oracle查询】oracle查询字段显示#号 (井号)

    客户反映字段查询为井号,我自己没有遇到这种情况,于是上网百度了一下. 下面的答案很好地解决了问题,哈哈哈.

  10. java单元测试的用法及原因

    1.ctrl+n  生成  Junit Test Case 2.选择文件夹 3.superClass  继承BaseUnitTest 4.next后 打勾选择需要单元测试的方法. 5.在生成的test ...