java流的基本原理和结构
1.java流的基本原理和结构
1.1采用装饰者模式的java流设计
java流的设计采用装饰器模式的方式,装饰者模式采用共同抽象类或者接口A,被装饰者的组件B直接继承于抽象类,而所有的装饰者也有一个共同的抽象类或者接口C,而这个装饰者们的公共抽象类和接口C也是继承或者实现了A。下面是一张装饰者模式的通用原理图。
- 而在装饰者模式的所有类中实现了相同的方法,这样就可以通过嵌套的方式组合出不同的方法,在运行的过程中动态的添加功能到原先的对象上,而且可以自定义额外的功能。
- 在使用的过程中,可以使用多层嵌套成链的方式来简单的使用,通常只需要保留最后一个类对象的引用,而减少需要管理的对象数量,当然如果你不慎使用了中间的对象,就无法达到预先设想的数据处理流程,因此在java的实现中常常使用匿名的方式嵌套流对象例如:
DataOutputStream dout = new DataOutputStream(
new BufferOuputStream(
new FileOutPutStream("data.txt");
当然在某些特殊情况下需要保留对底层流的引用,这里不再深究。
1.2java流的分类
java流的设计也参考了这里的方法简单的说。java流大体上可以分为以下2中分类方式,分别对应于四个大类。
分类 | 字符 | 字节 |
---|---|---|
输入 | Reader | InputStream |
输出 | Writer | OuputStream |
这四个流即为四个最顶层的抽象流将java分成了四大类。通常也会被人称为底层流,之后将一一介绍这四大类java流。
1.3同步的基本流操作
另外需要说明的一点是流是同步的,当程序请求一个流或者写入一段数据的时候,必须要等到这段流完成操作之后才能进行其他操作。非阻塞的流操作可能会比较复杂一些,这里只介绍基本的流操作。
2.四大类流的基本方法
2.1OutputStream流
首先需要明确的是所有的OutputStream子类都实现或覆盖了这几个抽象方法:
void write(int b)
void write(byte[] b)
void write(byte[] b, int off, int len)
void flush()
void close()
这里不会对所有的方法进行详细的介绍,但是需要做几点说明。
- OutputStream是面向字节的操作的流,这里Write方法传入的是int但是写入的是无符号int,所有需要进行转换,如果传入一个超过0-255范围的int将只写入一个最低字节,
- 当然一个一个操作字节的效率是很低的,所有通常来说会采用BufferedOutputStream来装饰这个OutputStream,这样可以提高效率,但是有时也会需要强制缓冲流刷新的方法flush()。合适需要刷新缓冲流是一个需要讨论的问题,如果不是考虑的效率问题应该建议刷新缓冲流,否则可能会导致一些比较复杂的问题。
- 当流操作结束的时候需要调用close()方法来释放与这个流相关联的所有资源,如文件句柄或端口,如果是在网络编程中也会终止这个链接,如果 再次写入会导致IOXException异常。除少量特例之外建议及时关闭,否则可能会泄漏资源。
- 关闭资源应采用释放模式,或者带资源的try。
- 就个人来看,OutputStream可能是早期的流方法,使用的方式和C\C++中的比较类似,操作起来比较机械,如果涉及到编码还是应该采用Writer类操作比较方便。
2.2InputStream流
抽象方法:
abstract int read()
int read(byte[] b)
int read(byte[] b, int off, int len)
int available()
void close()
long skip(long n)
void mark(int readlimit)
void reset()
boolean markSupported()
- 最基本的当然是read()方法,它读入1字节,然后返回一个带符号的int,所有如果你需要对应的0-255无符号int许你自己进行转换。
- int read(byte[] b)和int read(byte[] b, int off, int len)这两个方法byte[]表示希望填入的数组,而返回的int则表示实际读取的字节数。你可以利用这一点重复读取数据,直到读取了需要数量的字节数。
- 如果返回的int为-1则表示流结束了,则在下次缓冲器刷新之前,任何这个read,方法之后的read都将返回-1,以免重复读取。
这里提供一个比较简装的read方法:
int bytesRead = 0;
int bytesToRead = 1024;
byte[] input = new byte[bytesToRead];
while(bytesRead<bytesToRead){
int result = in.read(input,bytesRead,bytesToRead-bytesRead);
if(result == -1) break;
bytesRead +=result;
}
- available()用来确定不阻塞的情况下有多少字节可以读取。他会返回可以读取的最小字节数。实际上还能读取更多的字节,但是至少可以读取这么多字节。
int bytesAvaliable = in.avaliable();
byte[] input = new byte[bytesAvailable];
int bytesRead = in.read(input,0,bytesAvailable);
- skip方法用来跳过一部分数据,
- close方法用关闭流,并释放资源。
- 另外三个mark(),rest(),用来标记流的对应位置并通过重置来回到mark标记的位置进行读取,但是并不是所有的流都支持这样的操作所有需要用marksupported方法来检测。用的很少。
2.3过滤器流
filterStream非常丰富,除了io包内的意外还有各种第三方包或者官方的子类,但是这里就仅仅介绍一下java.io包内的几个子类。
2.3.1BufferedInput(Output)Stream
在底层流的基础上加入缓冲流没什么需要解释的。
2.3.1PrintStream
PrintStream,System.out使用的流,默认情况下显式刷新输出,如果autoFlush为true。在每次写入字节数字或者换行的时候或者调用println的时候刷新输出。
但是这个方法有3个很严重的问题,尽可能不用
- println的输出和平台实现相关
- PrintStream采用所在平台默认的编码方式,而不可指定
- PrintStream吞噬所有异常。
2.3.1DataInput(Output)Stream
用二进制格式读取java基本数据类型和字符串。通常来说成对使用比较好,编码方式有一些特殊性,如果不成对使用会比较麻烦。
2.4阅读器和书写器
2.4.1简述
首先需要说明的是java内置字符集是UTF-16编码。Reader和Writer包含8个子类:
- InpuStreamReader
- OutputStreamWriter
- FileReader
- FileWriter
- CharArrayReader
- CharArrayWriter
这几个原始的阅读器和书写器都没有底层流而可以直接读取或写入。后四个是java内部用的。
2.4.2书写器
void write(int c)
void write(char[] cbuf)
abstract void write(char[] cbuf, int off, int len)
void write(String str)
void write(String str, int off, int len)
abstract void flush()
abstract void close()
Writer append(char c)
Writer append(CharSequence csq)
Writer append(CharSequence csq, int start, int end)
void write(char[] cbuf, int off, int len)
是基础方法,其他根据四个根据这个方法实现- 写入多少字节及有什么编码方式通常由子类来决定。不同编码写入方式也有所不同。
2.4.3OutputStreamWriter
OutputStreamWriter是Writer最重要的子类,因为它通过构造函数指定了使用的编码的方式。
public OutputStreamWriter(OutputStream out, String encoding) throws UnsupporteEncodingException
- 如果没有指定编码就采用平台默认编码
Window -根据国家变化。中国的一般是GB2312
Linux - 通常是UTF-8,不过也可能根据不同发行版有所不同
MAC- UTF-8
采用默认编码可能会产生意想不到的结果尽可能指定编码。
2.4.4阅读器
int read()
int read(char[] cbuf)
abstract int read(char[] cbuf, int off, int len)
int read(CharBuffer target)
boolean ready()
long skip(long n)
abstract void close()
void mark(int readAheadLimit)
void reset()
boolean markSupported()
abstract int read(char[] cbuf, int off, int len)
是基础方法。- read()方法返回一个Unicode字符可以是0-65535之间的任意数,或者在流结束的时候返回-1,理论上说返回的是UTF-16码点。其他内容和InputStrem类似。
- ready()方法和InputStream的available相同,但是语义不同,它只能解释是否可读,而不能指示读取长度,因为有些字符编码方式UTF-8之类对于不同的字符会有不同数量的字节。
2.4.5InputStreamReader
public InputStreamReader(InputStream in)
public InputStreamReader(InputStream in,String encoding) throws UnsupportedEncodingException
2.4.6阅读器和书写器的过滤器
- BufferedReader
- BufferedWriter
- LineNumberReader
- PushbackReader
- PrintWriter
需要注意的由一下几点: - Buffered的默认字符大小为8192字符,
- BufferedReader中还有一个readLine()方法用来代替已经废弃的DataInputStream方法。
- BufferedWriter中有个新方法,命名为newLine();