Java网络编程的Java流介绍
Posted 纪莫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java网络编程的Java流介绍相关的知识,希望对你有一定的参考价值。
前言
网络程序所做的很大一部分工作都是简单的输入输出:将数据字节从一个系统移动到另一个系统。Java的I/O建立于流(stream)之上。输入流读取数据,输出流写入数据。过滤器流(filter)流可以串联到输入或输出流上。读写数据时过滤器可以修改数据(加密或压缩),或者只是提供额外的方法,将读/写的数据转换为其他格式。阅读器(reader)和书写器(writer)可以串链到输入流和输出流上,允许程序读/写文本而不是字节。
输出流
Java的基本输出流类是:java.io.OutputStream;
这个类中提供了写入数据所需的基本方法,如下:
public abstract void write(int b) throws IOException; public void write(byte b[]) throws IOException public void write(byte b[], int off, int len) throws IOException public void flush() throws IOException public void close() throws IOException
但是我们平时使用它的子类来实现向某种特定介质写入数据。例如:FileOutputStream等,它的子类都是通过装饰模式来实现一些特定的功能的。OutputStream的基本方法是write(int b)。这个方法接受一个0到255之间的整数作为参数,将对应的字节写入到输出流中。虽然此方法接受一个int作为参数,但它实际上会写入一个无符号字节,因为java没有无符号字节数据类型,所以这要使用int来代替。无符号字节和有符号字节之间唯一的真正区别在于解释。它们都由8个二进制组成,write方法将int写入一个网络连接时,线缆上只会放8个二进制位。如果将一个超出0~255的int传入write方法,将协议这个数的最低字节,其他3个字节将被忽略。因为每次写入一个字节效率不高,所以就又提供了两个可以传入字节数组的方法,write(byte[])、write(byte b[],int off,int len)。
与网络硬件中缓存一样,流还可以在软件中得到缓冲,即直接用java代码缓存。在写入数据完成后,刷新(flush)输出流非常重要。因为flush()方法可以强迫缓冲的流发送数据,即使缓冲区还没有满,以此来打破流一直等待着缓冲区满了才会发送数据的状态。
最后,当结束一个流操作时,要通过调用它的close()方法将其关闭。关闭流会释放与整个流关联的所有资源,如果流来自网络连接,这个连接也会被关闭。长时间未关闭一个流,可能会泄漏文件句柄、网络端口和其他资源。所以在Java6以及更早的版本中,是在一个finally块中关闭流。但是Java7引入了try width resources 可以简化关闭流的操作,只需要把流定义在try的参数中即可。
如下所示:
try(OutputStream out = new FileOutputStream("D:/temp/test.txt")){ // 处理输出流 }catch (IOException e){ e.printStackTrace(); }
因为Java会对try块参数表中 声明的所有AutoCloseable对象自动调用close()。Java中的流相关的类基本上都直接或间接的实现了AutoCloseable接口。
输入流
Java的基本输出流类是:java.io.InputStream;
这个类提供了将数据读取为原始字节所需要的基本方法。如下:
public abstract int read() throws IOException; public int read(byte b[]) throws IOException public int read(byte b[], int off, int len) throws IOException public long skip(long n) throws IOException public int available() throws IOException public void close() throws IOException
InputStream的基本方法是没有参数的read()方法。此方法从输入流的源中读取1字节数据,作为一个0到255的int返回,流的结束通过返回-1来表示。read()方法会等待并阻塞其后任何代码的执行,直到有1字节的数据可供读取。输入和输出可能很慢,所以如果成行在做其他重要工作,要尽量将I/O放在单独的线程中。
一次读取1字节的效率也不高,因此,有两个重载的read()方法,可以用从流中读取的多字节的数据填充一个指定的数组:read(byte[] input)和read(byte[] input, int offset,int length)。当read的时候如果遇到IOException或网络原因只读取到了一部分,这个时候就会返回实际读取到的字节数。
例如:
int bytesRead = 0; int bytesToRead = 1024; byte[] input = new byte[bytesToRead]; while (bytesRead<bytesToRead){ bytesRead += in.read(input,bytesRead,bytesToRead - bytesRead); }
上面这段代码就是没有考虑到有可能流会中断导致读取的数据永远读不出来,所以要防止这种事情出现需要先测试read()的返回值,然后再增加到byteRead中
如下所示:
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()方法来确定不阻塞的情况下有多少字节可以读取。它会返回可读取的最少字节数。事实上还能读取更多字节,至少可以读取available()建议的字节数。
如下:
int bytesAvailable = in.available(); byte[] input = new byte[bytesAvailable]; int bytesRead = in.read(input,0,bytesAvailable); //读取到数据后,去执行其他部分
在少数情况下,你可能希望跳过数据不进行读取。skip()方法会完成这项任务。
与输出流一样,一旦结束对输入流的操作,应当调用close()方法将其关闭。这会释放这个流关联的所有资源。
InputStream类中还有3个不经常用的方法,
public synchronized void mark(int readlimit) public synchronized void reset() throws IOException public boolean markSupported()
为了重新读取数据,要用mark()方法标记流的当前位置,在以后某个时刻可以用reset()方法把流重置到之前标记的位置。在尝试使用标记和重置之前,要坚持markSupported()方法是否返回true。如果返回true,那么这个流确实支持标志和重置,否则,mark()会什么都不做,而reset()将抛出一个IOException异常。
过滤器流
过滤器由两个版本:过滤器流(filte stream)以及阅读器(reader)和书写器(writer)
每个过滤器输出流都有与java.io.OutputStream相同的write()、close()和flush()方法。每个过滤器输入流都有与java.io.InputStream相同的read()、close()和available()方法。
过滤器通过其构造函数与流连接。
以上是关于Java网络编程的Java流介绍的主要内容,如果未能解决你的问题,请参考以下文章