[toc]
简介
流的概念和作用
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
流的分类
根据处理数据类型的不同分为: 字符流和字节流 根据数据流向不同分为: 输入流和输出流
字符流和字节流
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。 本质其实就是基于字节流读取时,去查了指定的码表。字节流和字符流的区别:
读写单位不同: 字节流以 字节(8bit)为单位,字符流以 字符为单位,根据码表映射字符,一次可能读多个字节。
处理对象不同:字节流能处理 所有类型的数据(如图片、avi等),而字符流 只能处理 字符类型的数据。
字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的;而字符流在操作的时候下后是会用到缓冲区的,是通过缓冲区来操作文件,我们将在下面验证这一点。
结论: 优先选用字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。
输入流和输出流
对输入流只能进行读操作,对输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。
包结构
InputStream主要方法
//读取一个数组这么多的字节,如果没有了返回-1 public int read(byte b[]) throws IOException { return read(b, 0, b.length); }
//从偏移量off开始读取len个字节到byte[]数组中,返回读到的所有字节 public int read(byte b[], int off, int len) throws IOException {...}
//掉过n个字节,返回跳过的字节综合 public long skip(long n) throws IOException {...}
//预计还可以读取的字节数 public int available() throws IOException {...}
// 调用mark方法会记下当前调用mark方法的时刻,InputStream被读到的位置,调用reset方法就会回到该位置 public synchronized void mark(int readlimit) {}
//将此流重新定位到对此输入流最后调用 mark 方法时的位置 public synchronized void reset() throws IOException {
//是否支持mark方法,InputStream默认不支持 public boolean markSupported() { return false; }
mark和reset方法举例
方法说明:调用mark方法会记下当前调用mark方法的时刻,InputStream被读到的位置。 调用reset方法就会回到该位置。
// 判断该输入流是否支持mark操作
if (!inputStream.markSupported()) {
System.out.println("mark/reset not supported!");
}
int ch;
boolean marked = false;
while ((ch = inputStream.read()) != -1) {
//读取一个字符输出一个字符
System.out.print((char)ch);
//读到 'e'的时候标记一下
if (((char)ch == 'e')& !marked) {
inputStream.mark(content.length()); //先不要理会mark的参数
marked = true;
}
//读到'!'的时候重新回到标记位置开始读
if ((char)ch == '!' && marked) {
inputStream.reset();
marked = false;
}
}
//程序最终输出:BoyceZhang!Zhang!
涉及的设计模式
装饰者模式
简介
InputStream的子类FilterInputStream是InputStream类的一个装饰者,譬如BufferedInputStream扩展了InputStream,该装饰者新增了缓存处理。
源码详解
/* * Creates a <code>FilterInputStream</code> * by assigning the argument <code>in</code> * to the field <code>this.in</code> so as * to remember it for later use. * * @param in the underlying input stream, or <code>null</code> if * this instance is to be created without an underlying stream. / protected FilterInputStream(InputStream in) { this.in = in; }
//读取字节 public synchronized int read() throws IOException { if (pos >= count) {//buffer中的当前位置>buffer中读取了的位置 //需要进行填充buffer fill(); if (pos >= count) return -1; } return getBufIfOpen()[pos++] & 0xff; }
private void fill() throws IOException { byte[] buffer = getBufIfOpen(); if (markpos < 0) pos = 0; / no mark: throw away the buffer / else if (pos >= buffer.length) / no room left in buffer / if (markpos > 0) { / can throw away early part of the buffer / int sz = pos - markpos; System.arraycopy(buffer, markpos, buffer, 0, sz); pos = sz; markpos = 0; } else if (buffer.length >= marklimit) { markpos = -1; / buffer got too big, invalidate mark / pos = 0; / drop buffer contents / } else { / grow buffer / //每次填充2倍 int nsz = pos * 2; if (nsz > marklimit) nsz = marklimit; byte nbuf[] = new byte[nsz]; System.arraycopy(buffer, 0, nbuf, 0, pos); if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { // Can't replace buf if there was an async close. // Note: This would need to be changed if fill() // is ever made accessible to multiple threads. // But for now, the only way CAS can fail is via close. // assert buf == null; throw new IOException("Stream closed"); } buffer = nbuf; } count = pos; int n = getInIfOpen().read(buffer, pos, buffer.length - pos); if (n > 0) count = n + pos; }
适配器模式
简介
ByteArrayInputStream是一个适配器类,ByteArrayInputStream继承了InputStream的接口,而封装了一个byte数组。换言之,它将一个byte数组的接口适配成InputStream流处理器的接口。