读书笔记-java网络编程-2java流的工作原理及应用-

46 篇文章 0 订阅
17 篇文章 0 订阅

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中分类方式,分别对应于四个大类。

分类字符字节
输入ReaderInputStream
输出WriterOuputStream

这四个流即为四个最顶层的抽象流将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();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值