一.应用程序操作IO的流程
- 当进程需要进行IO读写的时候,通过一个特殊的指令,进程进入内核空间, 读取IO到内核空间
- 进程再将IO拷贝到用户空间
二.InputStream
- 所有字节输入流的父类
- read()操作会一直等待IO堵塞,除非到达流末尾或者抛出异常
常用的字节输入流及其继承关系
/**
* 抽象输入流
* 所有字节输入流的父类
* 定义了输入流的通用方法
*/
public abstract class InputStream implements Closeable {
//最大可跳过的字节数
//私有常量
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
/**
* 抽象方法,每个子类都需要实现该方法,
* 从输入流中读取一个字节,
* 所以返回值的范围是[0,255],
* 除了ASCII码,其余打印字符都会乱码,
* 该方法效率较为低下
* @return 返回的是无符号字节数值,
* 当读取到文件结尾,那么返回-1
*/
public abstract int read() throws IOException;
/**
* 从流中读取数组b长度的字节,
* 然后放到字节数组中
* 注意:
* 当返回值小于b.length的时候,
* 说明b数组没有被填满,
* 其中[0,返回值]才是真正读取的数据,
* 而[返回值,b.length]是数组中原有的数据
*
* @param b 保存读取数据的字节数组
* @return 实际读取数据的长度
* 如果返回-1,说明到了流的末尾
*/
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
/**
* 从流中读取len长度的字节数据,
* 然后将数据放到数组b中,放入的位置从off开始,
* 注意:
* 当返回值小于len的时候,
* 说明b数组没有被填满,
* 其中[off,返回值]才是真正读取的数据,
* 而[返回值,len]是数组中原有的数据
*
* @param b 保存读取流中数据的字节数组
* @param off 字节数组的写入数据索引的偏移
* @param len 写入字节数组数据的长度
* @return 实际读取数据的长度
* 如果返回-1,说明到了流的末尾
*/
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) {
//如果到了流的末尾,那么返回-1
return -1;
}
//将读取到的数据,缓存到b数组中
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
//循环读取数据,一直读到len长度
c = read();
if (c == -1) {
//到了流的尾部,那么结束循环
break;
}
//将数据保存到b数组中
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
//返回读取的长度
return i;
}
/**
* 跳过并丢弃n个字节,
* 之后的读取操作从n字节之后继续进行
* @return 返回的是实际跳过的字节数
*/
public long skip(long n) throws IOException {
long remaining = n;
int nr;
if (n <= 0) {
return 0;
}
//跳过的最大长度为MAX_SKIP_BUFFER_SIZE
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
byte[] skipBuffer = new byte[size];
while (remaining > 0) {
//跳过操作是通过read()方法来实现的
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
if (nr < 0) {
break;
}
remaining -= nr;
}
return n - remaining;
}
/**
* 可用字节数
*/
public int available() throws IOException {
return 0;
}
/**
* 关闭流
*/
public void close() throws IOException {}
/**
* 标记流
* 作用是记录当前流读取到的位置,
* 当后续调用reset()方法时, 可以回到mark标记的位置
*
* 默认不支持标记
* 所以没有做任何操作
*/
public synchronized void mark(int readlimit) {}
/**
* 重置标记
* 将流重新定位到上一次调用mark方法时的位置
*
* 默认不支持标记重置
*/
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
/**
* 测试输入流是否支持mark和reset方法
* 默认不支持标记
*/
public boolean markSupported() {
return false;
}
}
三.OutputStream
- 所有字节输出流的父类
- write()操作会一直等待IO堵塞,除非到达流末尾或者抛出异常
常用的字节输入流及其继承关系
/**
* 抽象输出流
* 所有字节输出流的父类
* 定义了通用的字节输出流的通用方法
*/
public abstract class OutputStream implements Closeable, Flushable {
/**
* 将一个字节,写入到输出流中
* 抽象方法,所有的子类都必须实现该方法
* 该方法效率较低
*/
public abstract void write(int b) throws IOException;
/**
* 将b中的数据,全部写入到输出流中
*/
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
/**
* 从字节数组b的off位置开始,取出len长度的数据
* 写入到输出流中
* @param b 取出数据的字节数组
* @param off 从b中取出数据的起始位置
* @param len 从b中取出数据的长度
*/
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()方法实现
write(b[off + i]);
}
}
/**
* 刷新数据
* 如果流中使用了byte数组缓存
* 调用该方法会将缓存的byte数组更新到流里面
*/
public void flush() throws IOException {
}
/**
* 关闭流
*/
public void close() throws IOException {
}
}
网友评论