io流函数略解(java_input流)[二]
背景
在写这篇随笔之前,已经写了io流函数略解(java_File)(一),主要是总结了File的一些操作,以及一些源码介绍。
在Io实际应用中,实际上运用在如果会操作File,实际上很难写出一点能实际应用的code,因为操作文件嘛,更多的是操作流,也就是steam。
下面将简单总结一些流的概念,以及流的一些基本理论,同时也会贴出源码来略看。
实践
io之所以叫io,i的意思是input,o的意思是output,也就是一个输入一个输出,分别对应read与write。
inputsteam
inputsteam 在java 中是一个abstract class。那么它和接口是不一样的,抽象类是可以有具体方法的甚至构造函数。
inputsteam是read操作,那么看下在inputsteam有什么read的函数吧。
/** * Reads the next byte of data from the input stream. The value byte is * returned as an <code>int</code> in the range <code>0</code> to * <code>255</code>. If no byte is available because the end of the stream * has been reached, the value <code>-1</code> is returned. This method * blocks until input data is available, the end of the stream is detected, * or an exception is thrown. * * <p> A subclass must provide an implementation of this method. * * @return the next byte of data, or <code>-1</code> if the end of the * stream is reached. * @exception IOException if an I/O error occurs. */ public abstract int read() throws IOException;
read 没有实现,是一个抽象的方法。但是告诉了我们很有用的信息。
如下:
返回的是一个字节,返回是0-255。为什么是0-255呢?因为一个字节是8位,11111111不就是255嘛。
如果没有了,则返回-1,为什么会返回-1,因为-1最高效。解释起来很复杂,可以关注我后面总结的数据结构。
如果错误会返回一个IOException 异常。
同样,我找到了另外一个read
public int read(byte b[], int off, int len) throws IOException { //判断参数是否符合,比如说byte是否为空,然后off与len的一些基本要求,比如说一个正常的off肯定要>0,然后len>0,len还有大于b.length-off //在看到 b.length - off的时候就可以确定off是针对b[]的,冲off开始,给b[]写入或者替换数据。 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; } //为什么要单独写一个呢?一个是优化,不需要构造for循环,第二个是可以提前检查read错误 int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; //for 循环读取,然后read -1则说明到底了。 int i = 1; try { for (; i < len ; i++) { c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; }
这个我就不贴注释了,有源码看啥注释。一些关键点,我也给了自己的一些看法。
然后还有一个是:
public int read(byte b[]) throws IOException { return read(b, 0, b.length); }
其实就是调用read(byte b[], int off, int len);
至此,read部分就基本在这了,当然有其他函数了,不可能一一来说明,用到的时候自然就ok的。
FileInputStream
我们在inputsteam有了一个大体的框架,然而呢,read没有实现。那么来看看到底是如何读取文件的吧,FileInputStream。
依然我们来看read:
public int read() throws IOException { return read0(); } private native int read0() throws IOException;
出现了native,这表示是调用外部库。native解释起来有一丢丢麻烦,就是去调用不是java写的库了,例如调用c语言写的函数库,后面也写一片总结吧。
好吧,read只能暂时介绍到这里,操作一下吧。
try(InputStream inputStream= new FileInputStream("xxxx")) { int n; while ((n=inputStream.read())!=-1) { System.out.println(n); } }catch (Exception e) { // TODO: handle exception }
ps:
try(InputStream inputStream= new FileInputStream("xxxx"))这样写自动在finally中帮我们调用close方法,因为InputStream 继承了java.lang.AutoCloseable 接口。 为什么要close呢?因为要释放资源啊,用完就放,轻装前行。
ByteArrayInputStream
这个从字面意思是字节数组输入流?意思就是把字符数组转换成InputStream。
例如:
public void ByteArrayInputStreamTest() throws IOException { byte[] data={11,12,15,16}; try(InputStream inputStream=new ByteArrayInputStream(data)) { int n; while ((n = inputStream.read()) != -1) { } } }
来看看源码实现吧:
1.看看它的超类
ByteArrayInputStream extends InputStream
这就解释了为什么可以这样写:
InputStream inputStream=new ByteArrayInputStream(data)
2.实例化:
public ByteArrayInputStream(byte buf[]) { this.buf = buf; this.pos = 0; this.count = buf.length; }
在这里我们可以想到read(byte b[], int off, int len),其实就是模拟把文件中所有的字节都读出来了,然后给了里面的一个buf 缓存属性。
Reader
InputStream 关于字节流的,Reader 是关于字符流的。
我们知道字节是byte,字符是char,两者存在千丝万缕的关系,他们中间的桥梁是编码。编码又是一个相当难以用一两句话解释的东西了,后续会添加一篇编码的随笔。
总之,看下Reader 到底干什么的吧。
// 读取单个字符 public int read() throws IOException { char cb[] = new char[1]; if (read(cb, 0, 1) == -1) return -1; else return cb[0]; } // 抽象没得实现 abstract public int read(char cbuf[], int off, int len) throws IOException; //调用了抽象read(char cbuf[], int off, int len) public int read(char cbuf[]) throws IOException { return read(cbuf, 0, cbuf.length); }
好吧,没有什么具体的实现,那么就去看看InputStreamReader吧,它的一个实现类。
InputStreamReader
根据上文,我们迫切需要知道的是abstract public int read(char cbuf[], int off, int len) throws IOException的实现方法。
public int read(char[] cbuf, int offset, int length) throws IOException { int off = offset; int len = length; synchronized (lock) { ensureOpen(); if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } if (len == 0) return 0; int n = 0; if (haveLeftoverChar) { // Copy the leftover char into the buffer cbuf[off] = leftoverChar; off++; len--; haveLeftoverChar = false; n = 1; if ((len == 0) || !implReady()) // Return now if this is all we can produce w/o blocking return n; } if (len == 1) { // Treat single-character array reads just like read() int c = read0(); if (c == -1) return (n == 0) ? -1 : n; cbuf[off] = (char)c; return n + 1; } return n + implRead(cbuf, off, off + len); } }
关键部分:
int c = read0(); if (c == -1){ return (n == 0) ? -1 : n; } cbuf[off] = (char) c;
上文中提及到read0()是读取一个字节,然后把字节转换成字符。
ok,那么我们就知道原理了。
实践一下吧:
public void readFile() throws IOException { try (Reader reader = new FileReader("xxxx")) { char[] buffer = new char[1000]; int n; while ((n = reader.read(buffer)) != -1) { } } }
CharArrayReader与StringReader
简单说明一下他们俩吧。
char[] test={'a','b'}; try (Reader reader = new CharArrayReader(test)) { } try (Reader reader = new StringReader("xxx")) { }
就是把字符数组或者字符串专成了Reader。
以CharArrayReader为例:
1.继承:
public class CharArrayReader extends Reader
2.实例化
public CharArrayReader(char buf[]) { this.buf = buf; this.pos = 0; this.count = buf.length; }
3.读取
public int read() throws IOException { synchronized (lock) { ensureOpen(); if (pos >= count) return -1; else return buf[pos++]; } }
就是模拟了假如全部的读取文件中的所有数据,然后转换成了char[],缓存起来。
总结
1.不管是一次性读取byte[]还是一个一个读byte,原理上都是一个一个读的,只是byte[] 存储起来了。
2.读取字符流其实是在读取字节后转换的。
3.避免忘记close,推荐使用try(){}这种语法。
4.对于像ByteArrayInputStream 这样的转换,其实是假设数据全部读取出来了,然后进行操作。
- .NET架构转Java开发必须了解的历史
- Java基础—常用DOS命令
- Kafka Java 客户端开发
- JAVA基础学习20180106-继承上
- java之逗号操作符
- java功能技术记录
- JAVA -- 获取时间
- java基础(一)——变量、数据类型、运算符
- 小白学java-基础篇(super、final、抽象类、接口、多态、instanceof、包装类)
- 全排列的递归实现(Java版)
- thingking in java 读书笔记---异常处理
- Java IO之对象序列化(二)
- java实现归并算法
- Core Java Tutorial -- Threads in Java
- java生成指定范围的随机数
- java 过滤list的几种方式
- Java--环境搭建
- java语法规则
- Java 学习进度TEXT02
- 高效能Java架构师学习路线图(内附资料)