在《JAVA深入浅出流之一IO流》中,介绍了流安读取方式分字节流和字符流,那什么是字节流呢? 字节流的应用范围是什么?或者说什么时候用字节流?
本篇主要介绍字节流,其实如果研究字节流的类谱,它也算是个庞大家族,万事开头难,但故事总是从头说起,这里的“头”就是字节流的超类。
一:字节流(byte stream)
字节流是执行基于8位字节的输入和输出,它一次读写一字节的数据。字节流是I / O的最底层流技术,因此,如果你正在阅读或写入字符数据的最佳方法是使用字符流。其他流类型是建立在字节流之上的(如Java中的InputStream、OutputStream)。
API类关系- java.lang.Object
- java.io.InputStream
- java.lang.Object
- java.io.OutputStream
字节流类谱图:
通过上面两张关于流的类关系图,可以看到流的应用类都是由InputStream、OutputStream两类延伸(扩展)开来的。继承自InputStream/OutputStream的流都是用于向程序中输入/输出数据,且数据的单位都是字节(byte=8bit),如上图中,深色的为节点流,浅色的为处理流。(关于节点流、处理流的的介绍在《JAVA深入浅出流之一IO流》里讲过了
那就来看一下这两个超类到底是什么东东!
二:介绍InputStream、OutputStream
查看InputStream、OutputStream类的源码。
import java.io.Closeable; import java.io.IOException; public abstract class InputStream implements Closeable { // SKIP_BUFFER_SIZE is used to determine the size of skipBuffer private static final int SKIP_BUFFER_SIZE = 2048; // skipBuffer is initialized in skip(long), if needed. private static byte[] skipBuffer; /** * 方法: 从输入流中读取数据的下一个字节。返回 0到 255范围内的 int字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。 在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。 子类必须提供此方法的一个实现。 * @return 下一个数据字节;如果到达流的末尾,则返回 -1。 * @throws IOException 如果发生 I/O 错误。 */ public abstract int read() throws IOException; /** * 方法: 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。在输入数据可用、检测到文件末尾或者抛出异常前此方法一直阻塞。 如果 b 的长度为 0,则不读取任何字节并返回 0;否则,尝试读取至少一个字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1;否则,至少读取一个字节并将其存储在 b中。 将读取的第一个字节存储在元素 b[0]中,下一个存储在 b[1]中,依次类推。读取的字节数最多等于 b 的长度。设 k 为实际读取的字节数;这些字节将存储在 b[0] 到 b[k-1] 的元素中,不影响 b[k] 到 b[b.length-1]的元素。 * 类 InputStream 的 read(b) 方法的效果等同于:read(b, 0, b.length) * @param b 存储读入数据的缓冲区 * @return 读入缓冲区的总字节数;如果因为已经到达流末尾而不再有数据可用,则返回 -1。 * @throws IOException 如果不是因为流位于文件末尾而无法读取第一个字节;如果输入流已关闭;如果发生其他 I/O 错误。 */ public int read(byte b[]) throws IOException { return read(b, 0, b.length); } /** * 方法: 将输入流中最多 len个数据字节读入 byte数组。尝试读取len个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。 如果 len为 0,则不读取任何字节并返回 0;否则,尝试读取至少一个字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1;否则,至少读取一个字节并将其存储在 b 中。 将读取的第一个字节存储在元素 b[off]中,下一个存储在 b[off+1]中,依次类推。读取的字节数最多等于 len。设 k 为实际读取的字节数;这些字节将存储在 b[off]到 b[off+k-1]的元素中不影响 b[off+k]到 b[off+len-1]的元素。 在任何情况下,b[0]到 b[off]的元素以及 b[off+len]到 b[b.length-1]的元素都不会受到影响。 类 InputStream的 read(b, off, len)方法重复调用方法 read()。如果第一次这样的调用导致 IOException,则从对 read(b, off, len)方法的调用中返回该异常。如果对 read() 的任何后续调用导致 IOException,则捕获该异常并将其视为到达文件末尾,到达该点时读取的字节存储在 b 中,并返回发生异常之前读取的字节数。在已读取输入数据 len 的请求数量、检测到文件结束标记、抛出异常前此方法的默认实现将一直阻塞。建议子类提供此方法更为有效的实现。 * @param b 读入数据的缓冲区(类型为byte数组即byte[])。 * @param off 数组 b中将写入数据的初始偏移量。 * @param len 要读取的最大字节数。 * @return 读入缓冲区的总字节数;如果因为已到达流末尾而不再有数据可用,则返回 -1。 * @throws IOException */ public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) {} return i; } /** * 方法:跳过和丢弃此输入流中数据的 n 个字节。出于各种原因,skip 方法结束时跳过的字节数可能小于该数,也可能为 0。导致这种情况的原因很多,跳过 n 个字节之前已到达文件末尾只是其中一种可能。返回跳过的实际字节数。如果 n 为负,则不跳过任何字节。 此类的 skip 方法创建一个 byte 数组,然后重复将字节读入其中,直到读够 n 个字节或已到达流末尾为止。建议子类提供此方法更为 * 有效的实现。例如,可依赖搜索能力的实现。 * @param n 要跳过的字节数。 * @return 跳过的实际字节数。 * @throws IOException 如果流不支持搜索,或者发生其他 I/O 错误。 */ public long skip(long n) throws IOException { long remaining = n; int nr; if (skipBuffer == null) skipBuffer = new byte[SKIP_BUFFER_SIZE]; byte[] localSkipBuffer = skipBuffer; if (n <= 0) { return 0; } while (remaining > 0) { nr = read(localSkipBuffer, 0, (int) Math.min(SKIP_BUFFER_SIZE, remaining)); if (nr < 0) { break; } remaining -= nr; } return n - remaining; } /**方法: 返回该流在不被阻塞的情况下下一次可以读取到的数据长度。下一次调用可能是同一个线程, 也可能是另一个线程。一次读取或跳过此估计数个字节不会受阻塞,但读取或跳过的字节数可能小于该数。 * 说明: 类 InputStream 的 available 方法总是返回 0。 此方法应该由子类重写。 * @return * @throws IOException */ public int available() throws IOException { return 0; } /** 方法: 关闭此输入流并释放与该流关联的所有系统资源。 * 说明: InputStream 的 close 方法不执行任何操作。 */ public void close() throws IOException {} /** 方法:在此输入流中标记当前的位置。对 reset 方法的后续调用会在最后标记的位置重新定位此流,以便后续读取重新读取相同的字节。 * 参数:readlimit 告知此输入流在标记位置失效之前允许读取的字节数。 比如说mark(10),那么在read()10个以内的字符时,reset()操作后可以重新读取已经读出的数据,如果已经读取的数据超过10个,那reset()操作后,就不能正确读取以前的数据了, 因为此时mark标记已经失效。 */ public synchronized void mark(int readlimit) {} /**方法: 将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。 * @throws IOException */ public synchronized void reset() throws IOException { throw new IOException("mark/reset not supported"); } /**方法: 测试此输入流是否支持 mark 和 reset 方法。是否支持 mark 和 reset 是特定输入流实例的不变属性。 * @return InputStream 的 markSupported 方法返回 false。 */ public boolean markSupported() { return false; } }
package io.bytestream; import java.io.Closeable; import java.io.Flushable; import java.io.IOException; public abstract class OutputStream implements Closeable, Flushable { /** * 方法: 将指定的字节写入此输出流,write的常规协定是:向输出流写入一个字节。要写入的字节是参数 b的八个低位。b 的 24 个高位将被忽略。 * 说明: OutputStream 的子类必须提供此方法的实现。 * @param b 字节 * @exception 如果发生 I/O 错误。尤其是,如果已关闭输出流,则可能抛出 IOException */ public abstract void write(int b) throws IOException; /** * 方法: 如果发生 I/O 错误。尤其是,如果已关闭输出流,则可能抛出 IOException * @param b 数据. * @exception IOException 如果发生 I/O 错误。 */ public void write(byte b[]) throws IOException { write(b, 0, b.length); } /** * 方法: 将指定 byte数组中从偏移量 off开始的 len个字节写入此输出流。write(b, off, len)的常规协定是:将数组 b中的某些字节按顺序写入输出流;元素 b[off]是此操作写入的第一个字节,b[off+len-1]是此操作写入的最后一个字节。 * 说明: OutputStream 的 write 方法对每个要写出的字节调用一个参数的 write 方法。建议子类重写此方法并提供更有效的实现。 * @param b 数据. * @param off 数据中的初始偏移量. * @param len 要写入的字节数. * @exception 如果 b 为 null,则抛出 NullPointerException。 如果 off为负,或 len 为负,或者 off+len大于数组 b的长度 * 则抛出 IndexOutOfBoundsException */ public void write(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } for (int i = 0 ; i < len ; i++) { write(b[off + i]); } } /** * 方法: 刷新此输出流并强制写出所有缓冲的输出字节。flush 的常规协定是:如果此输出流的实现已经缓冲了以前写入的任何字节,则调用此方法指示应将这些字节立即写入它们预期的目标。 如果此流的预期目标是由基础操作系统提供的一个抽象(如一个文件),则刷新此流只能保证将以前写入到流的字节传递给操作系统进行写入,但不保证能将这些字节实际写入到物理设备(如磁盘驱动器)。 * @exception IOException - 如果发生 I/O 错误。 */ public void flush() throws IOException {} /** * 方法: 关闭此输出流并释放与此流有关的所有系统资源。close 的常规协定是:该方法将关闭输出流。关闭的流不能执行输出操作,也不能重新打开。 * @exception IOException - 如果发生 I/O 错误。 */ public void close() throws IOException { } }
总结:
(1)其中类声明部分为:
public abstract class InputStream implements Closeable public abstract class OutputStream implements Closeable, Flushable
发现都有修饰符abstract,所以都是抽象类,都有实现Closeable接口,即都有实现close()方法, 这样可以关闭此流并释放与此流关联的所有系统资源。OutputStream有实现Flushable接口,即实现了flush()方法,通过将所有已缓冲输出写入底层流来刷新此流。
(2)其中方法部分,都含有实体方法和抽象方法(方法有修饰符abstract)。其中InputStream的read()方法,OutputStream的write()方法,都是抽象方法,需要子类实现自己的方法。
(3)总的来说,InputStream、OutputStream 是把输入流、输出流的概念抽象化,充分体现了面向对象编程的“抽象”特征(过程抽象),所以可以看到两个抽象方法read()和write(),表示输入流是用来读的,输出流是用来写的。具体怎么读写,类中并没有实现,交由子类去实现。
参考资料:
http://www.cnblogs.com/flyoung2008/p/3251826.html
http://weixiaolu.iteye.com/blog/1479656
http://blog.csdn.net/haoel/article/details/2224055
http://www.seas.upenn.edu/~cis1xx/resources/java/fileIO/introToFileIO.html
http://pirate.shu.edu/~wachsmut/Teaching/CSAS2214/Virtual/Lectures/lecture16.html
http://math.hws.edu/eck/cs124/javanotes6/c11/s1.html
http://bioinfo2.ugr.es/OReillyReferenceLibrary/java/exp/ch08_01.htm
http://www.cnblogs.com/shitouer/archive/2012/12/19/2823641.html
http://docs.oracle.com/javase/tutorial/essential/io/
http://bbs.itheima.com/thread-103851-1-1.html
http://www.molotang.com/articles/754.html
相关推荐
3. **缓冲流**:为了提高性能,Java提供了缓冲流,如`BufferedReader`和`BufferedWriter`,它们在底层字节或字符流之上添加了一个缓冲区,减少了对底层资源的频繁访问。 4. **过滤流**:过滤流是在已存在的流基础上...
标题:“深入浅出液压系统技术培训” 描述:“深入浅出液压系统技术培训 很详尽的介绍了液压系统” 本文旨在全面解析液压系统的关键知识点,基于标题和描述中的内容,我们将探讨液压系统的构成、原理及其应用。 ##...
本文将重点介绍几种常见的数据流操作方式:单字节流复制、字节数组流复制以及缓冲流复制,并通过具体的代码示例来深入探讨每种方法的特点与应用场景。 #### 二、基础知识 在Java中,输入输出操作主要通过流...
在Java编程中,字节流(Byte Stream)是处理数据的基本方式,特别是在处理二进制数据,如图片、音频或视频文件时。本教程将详细讲解如何使用Java实现字节流来转换和处理图片。 首先,我们需要理解字节流的概念。在...
在Java编程语言中,输入/输出(I/O)操作是处理数据流的关键部分,而字符流与字节流则是实现这些操作的两种基本方式。理解它们的区别和应用场景对于任何Java开发者来说都是至关重要的。 ### 字节流 字节流是最基本...
### Java IO字符流和字节流详解 #### 一、引言 在Java编程中,输入输出(简称IO)操作是...以上就是关于Java IO中字节流和字符流的基本概念、API介绍及应用场景的具体分析。希望对正在学习Java IO的同学有所帮助。
本篇文章将深入探讨Java中字节流的读取方式,以及如何利用提供的BytesUtil和NumberUtil工具类进行高效操作。 首先,Java中的字节流分为两类:节点流和处理流。节点流直接连接到数据源或目的地,如文件、网络连接等...
Java中的流API是用于在内存和外部存储之间传输数据的机制。流分为两种主要类型:字节流(Byte Streams)和字符流(Character Streams),它们各自处理数据的方式不同,适用于不同的场景。 字节流是最基础的流类型,...
下面我们将深入探讨Java中的节流器及其应用。 1. **什么是节流器(Throttling)** 节流是一种流量控制策略,它限制了单位时间内允许执行的操作数量。在Java中,这通常通过使用定时器、线程池或者其他并发控制机制...
Java的输入和输出主要涉及到流的...总的来说,这个实验旨在帮助学习者理解Java中字节流和字符流的基本概念和用法,包括文件的读写、字符与字节的转换以及流的层次结构。通过实践这些练习,可以深入掌握Java的I/O操作。
本篇文章主要介绍了Java中字节流文件读取的相关知识,通过示例代码详细介绍了字节流文件读取的原理和使用场景。下面是相关知识点的总结: 1. Java中的IO流设计 Java的IO流设计用于读写文件内容,分为两大类:字节...
在Java编程语言中,字节流(Byte Stream)是处理数据的基本方式,它允许程序员以原始字节的形式读写数据。在实际应用中,我们经常需要将字节流与各种类型的数据,如图片,进行相互转换。这个"Java实现字节流与图片的...
Java中的IO流主要分为两大类:字节流和字符流,它们主要用于数据的输入和输出。字节流处理的是单个字节的数据,而字符流处理的是Unicode编码的字符。 1. 字节流: - 字节输入流:`InputStream` 是所有字节输入流的...
在这个课题中,学生将深入理解并实践流体动力学的基本原理,尤其是节流装置在工业过程控制中的应用。节流装置是一种常见的流量测量设备,它利用流体通过狭窄通道时产生的压差来推算流量,广泛应用于化工、石油、能源...
Java中的流是处理数据传输的一种抽象,它将数据视为字节序列进行操作。根据处理的数据单位,Java提供了两种主要类型的流:字符流和字节流。这两种流在处理数据时有显著的区别,尤其是在处理文本数据和二进制数据时。...
主要描述了常用字节流的用法,附上一些代码!
在 Object 基类中,有一个方法叫 clone,产生一个前期对象的克隆,克隆对象是原对象的拷贝,由于引用类型的存在,有深克隆和浅克隆之分,若克隆对象中存在引用类型的属性深克隆会将此属性完全拷贝一份,而浅克隆仅仅...
总的来说,Java中字节流到字符流的转换主要通过`InputStreamReader`完成。这个过程对于处理文本数据,特别是涉及到字符编码的问题时非常关键。转换流允许我们使用字符流的高级特性(如`readLine()`),同时保持对...
《制冷系统流路设计规范——空调毛细管节流原理》 制冷系统的设计是空调性能的关键环节,其中流路设计尤为关键,它涉及到空调的效率、稳定性和安全性。本规范主要针对一拖一房间空调器的换热器流路设计,参照了多个...