java中io包源码详解


[toc]

简介

流的概念和作用

流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。

流的分类

根据处理数据类型的不同分为: 字符流和字节流 根据数据流向不同分为: 输入流和输出流

字符流和字节流

字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。 本质其实就是基于字节流读取时,去查了指定的码表。字节流和字符流的区别:

  1. 读写单位不同: 字节流字节(8bit)为单位,字符流以 字符为单位,根据码表映射字符,一次可能读多个字节。

  2. 处理对象不同:字节流能处理 所有类型的数据(如图片、avi等),而字符流 能处理 字符类型的数据。

  3. 字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的;而字符流在操作的时候下后是会用到缓冲区的,是通过缓冲区来操作文件,我们将在下面验证这一点。

结论: 优先选用字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。

输入流和输出流

对输入流只能进行读操作,对输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。

包结构

InputStream主要方法

//读取一个字节(8位),如果没有了返回-1
public abstract int read() throws IOException;

//读取一个数组这么多的字节,如果没有了返回-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方法就会回到该位置。

String content = "BoyceZhang!";
InputStream inputStream = new ByteArrayInputStream(content.getBytes());

// 判断该输入流是否支持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,该装饰者新增了缓存处理。

源码详解

 //在BufferedInputStream的父类FilterInputStream中关联了一个InputStream进行装饰。
 protected volatile InputStream in;

/* * 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流处理器的接口。

nemotan /
Published under (CC) BY-NC-SA in categories java  tagged with java