Java网络编程的Java流介绍

前言

网络程序所作的很大一部分工做都是简单的输入输出:将数据字节从一个系统移动到另外一个系统。Java的I/O创建于流(stream)之上。输入流读取数据,输出流写入数据。过滤器流(filter)流能够串联到输入或输出流上。读写数据时过滤器能够修改数据(加密或压缩),或者只是提供额外的方法,将读/写的数据转换为其余格式。阅读器(reader)和书写器(writer)能够串链到输入流和输出流上,容许程序读/写文本而不是字节。java

输出流

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;测试

这个类提供了将数据读取为原始字节所须要的基本方法。以下:this

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()方法。

过滤器经过其构造函数与流链接。

FileInputStream iin = new FileInputStream("test.txt");
BufferedInputStream bin = new BufferedInputStream(iin);

这种状况下若是混合调用链接到同一个源的不一样流,这可能会违反过滤器流的一些隐含约定。大多数状况下应当只使用链中最后一个过滤器进行实际的读/写。

能够用以下方式:

InputStream iin = new FileInputStream("test.txt");
iin = new BufferedInputStream(iin);

缓冲流

BufferedOutputStream类将写入的数据存储在缓冲区中,直到缓冲区满了或者执行了flush方法。而后将数据一次所有写入底层输出流。在网络链接中,缓冲网络输出一般会带来巨大的性能提高。

BufferedInputStream类也有一个做为缓冲区的保护字节数组,当调用某个流的read()方法时,它首先尝试从缓冲区得到请求的数据。当缓冲区没有数据时,流才从底层的源中读取数据。这时,它会读取尽量多的数据存入缓冲区,而不论是否立刻须要全部这些数据。不会当即用到的数据能够在之后调用read()时读取。当从本地磁盘中读取文件时,从底层流中读取几百字节的数据与读取1字节数据几乎同样快。所以,缓冲能够显著提高性能。

BufferedOutputStream有两个构造函数,BufferedInputStream也是有两个构造函数:

public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in, int size) 

public BufferedOutputStream(OutputStream out)
public BufferedOutputStream(OutputStream out, int size) 

PrintStream

PrintStream类是大多数程序员都会遇到的第一个过滤器输出流,由于System.out就是一个PrintStream。还可使用下面两个构造函数将其余输出流串链到打印流:

public PrintStream(OutputStream out)
public PrintStream(OutputStream out, boolean autoFlush)

若是autoFlush参数为true,那么每次写入1字节数组或换行,或者调用println()方法时,都会刷新输出流。除了日常的write()、flush()和close()方法,PrintStream还有9个重载的print()方法和10个重载的println方法:

public void print(boolean b)
public void print(char c)
public void print(int i)
public void print(long l)
public void print(float f)
public void print(double d)
public void print(char s[])
public void print(String s) 
public void print(Object obj)
public void println()
public void println(boolean x)
public void println(char x) 
public void println(int x)
public void println(long x) 
public void println(float x) 
public void println(double x) 
public void println(char x[])
public void println(String x)
public void println(Object x) 

每一个print()方法都将其参数以可见的方式转换为一个字符串,再用默认的编码方式把字符串写入底层输出流。println()方法也完成相同操做,但会在所写的行末尾追加一个与平台有关的行分隔符。

在网络编程中应尽可能避免使用PrintStream。

PrintStream第一个问题,println()输出是与平台有关的。

PrintStream第二个问题,会假定使用所在平台的默认编码方式。

PrintStream第三个问题,会吞掉了全部异常。

数据流

DataInputStream和DataOutputStream类提供了一些能够用二进制格式读/写Java的基本数据类型和字符串。

DataOutputStream类提供下面11种方法,能够写入特定的Java数据类型。

public final void writeBoolean(boolean v) throws IOException 
public final void writeByte(int v) throws IOException
public final void writeShort(int v) throws IOException
public final void writeChar(int v) throws IOException
public final void writeInt(int v) throws IOException
public final void writeLong(long v) throws IOException
public final void writeFloat(float v) throws IOException
public final void writeDouble(double v) throws IOException 
public final void writeBytes(String s) throws IOException
public final void writeChars(String s) throws IOException 
public final void writeUTF(String str) throws IOException

前面的8个方法,都按照实际参数的类型长度来写数据的,最后三个方法有些特别,writeChars()方法只是对String参数迭代处理,将各个字符按顺序写为一个2字节的big-endian Unicode字符。writeBytes()方法迭代处理String参数,但只是写入每一个字符的低字节。

writeChars和writeBytes都不会对输出流的字符串的长度编码。所以,你没法真正区分原始字符和做为字符串一部分的字符。writeUTF()方法则包括了字符串的长度。它将字符串自己用Unicode UTF-8编码的一个变体进行编码。

除了这些写入二进制数字和字符串的方法,DataOutputStream固然还有全部OutputStream类都有的日常的write()、flush()、和close()方法。

DataInputStream与DataOutputStream是互补的。DataOutputStream写入的每一种数据格式,DataInputStream均可以读取。此外DataInputStream还有一般read()、available()、skip()和close()方法,以及读取整个字节数组和文本行的方法。因此DataInputStream的内容就不写了。

书写器

Writer是以字符流的方式书写数据,它是一个抽象类,有两个保护类型的构造函数。与OutputStream相似,Writer类从不直接使用;相反,会经过他的某个子类以多态方式使用。它有5个write()方法,另外还有flush()和close()方法。

protected Writer()
protected Writer(Object lock) 
abstract public void write(char cbuf[], int off, int len) throws IOException
public void write(int c) throws IOException
public void write(char cbuf[]) throws IOException 
public void write(String str) throws IOException 
public void write(String str, int off, int len) throws IOException 
bstract public void flush() throws IOException
abstract public void close() throws IOException

abstract public void write(char cbuf[], int off, int len)方法是基础方法,其余四个write()都是根据它实现的。子类至少覆盖整个方法以及flush()和close(),
可是为了提供更高效的实现方法,大多数子类还覆盖了其余一些write()方法。

例如:给定一个Writer对象w,能够这样写入字符串“Jimoer”:

char[] jimoer = {'J','i','m','o','e','r'};
w.write(jimoer,0,jiomer.length);

也能够用其余write()方法完成一样的任务:

Writer w = new OutputStreamWriter(out);
w.write(jiomer);
for(int i=0;i<jimoer.length;i++){
     w.write(jiomer[i]);
}
w.write("Jimoer");
w.write("Jimoer",0,5);

全部这些例子表述都是一样的事情,只不过方式有所不一样。

书写器能够缓冲,有可能直接串链到BufferedWriter,也有可能直接链入。为了强制将一个写入提交给输出介质,须要调用flush()方法。

OutputStreamWriter

OutputStreamWriter是Writer的最重要的具体子类。OutputStreamWriter会从Java程序中接收字符。会根据指定的编码方式将这些字符转换为直接,并写入底层输出流。

构造函数指定了要写入的输出流和使用的编码方式:

public OutputStreamWriter(OutputStream out, String charsetName)
        throws UnsupportedEncodingException
    {
        super(out);
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        se = StreamEncoder.forOutputStreamWriter(out, this, charsetName);
    }

除了构造函数,OutputStream只有一般的Writer方法,还有一个返回对象编码方式的方法:

public String getEncoding()

阅读器

Reader是一个抽象类,从不直接使用,只经过子类来使用。有三个read()方法,另外还有skip()、close()、ready()、mark()、reset()和markSupported()方法:

protected Reader()
protected Reader(Object lock) 
abstract public int read(char cbuf[], int off, int len) throws IOException
public int read() throws IOException 
public int read(char cbuf[]) throws IOException
public long skip(long n) throws IOException
public boolean ready() throws IOException
public boolean markSupported()
public void mark(int readAheadLimit) throws IOException 
public void reset() throws IOException 
abstract public void close() throws IOException

这里面的方法大多数都与Writer中方法能对应上,read()方法以int型(从0到65,535)返回一个单一的Unicode字符或读到流结束时返回-1。

InputStreamReader是Reader的最重要的具体子类。InputStreamReader从其底层输入流中读取字节。而后根据指定的编码发那个仍是将字节转为字符,并返回这些字符。

构造函数以下:

public InputStreamReader(InputStream in) 
public InputStreamReader(InputStream in, String charsetName)
        throws UnsupportedEncodingException
public InputStreamReader(InputStream in, Charset cs) 
public InputStreamReader(InputStream in, CharsetDecoder dec)

若是没有指定编码方式,就使用平台的默认编码方式。若是指定了一个位置的编码方式,会抛出UnsupportedEncodingException异常。

过滤阅读器和书写器

InputStreamReader和OutputStreamWriter类就至关于输入和输出流之上的装饰器,把面向字节的接口改成面向字符的接口。完成以后就能够将其余面向字符的过滤器放在使用java.io.FilterReader和java.io.FilterWriter类的阅读器或书写器上。

BufferedReader和BufferedWriter也有与阅读器和书写器关联的经常使用方法,如read()、ready()、write()和close()。这两个类都有两个构造函数,能够将BufferedReader或BufferedWriter串链到一个底层阅读器或书写器,并设置缓冲区的大小。若是没有设置大小,则使用默认的大小8192字符:

public BufferedReader(Reader in, int sz) 
public BufferedReader(Reader in)
public BufferedWriter(Writer out)
public BufferedWriter(Writer out, int sz) 

BufferedReader类还有一个readLine()方法,它读取一行文本,并做为一个字符串返回:

public String readLine() throws IOExceptioin

这个方法能够替代DataInputStream中国已经废弃的readLine()方法,它与该方法的行为基本相同。主要区别在于,经过BufferedReader串链到InputStreamReader,能够用正确的字符集读取行,而不是采用平台的默认编码方式。

BufferedWriter类增长了一个其超类所没有的新方法,名为newLine(),也用于写入一行:

public void newLine() throws IOException

这个方法向输出插入一个与平台有关的行分隔符字符串。

PrintWriter

PrintWriter类用户取代Java1.0的PrintStream类,它能正确地处理多字节字符集和国际化文本。除了构造函数,PrintWriter类也有与PrintStream几乎相同的方法集。

public PrintWriter (Writer out)
public PrintWriter(Writer out,boolean autoFlush) 
public PrintWriter(OutputStream out)
public PrintWriter(OutputStream out, boolean autoFlush) 
public PrintWriter(String fileName) throws FileNotFoundException 
private PrintWriter(Charset charset, File file) throws FileNotFoundException
public PrintWriter(String fileName, String csn)
        throws FileNotFoundException, UnsupportedEncodingException
public PrintWriter(File file) throws FileNotFoundException 
public PrintWriter(File file, String csn)
        throws FileNotFoundException, UnsupportedEncodingException
public void flush() 
public void close()
public boolean checkError() 
public void write(int c) 
public void write(char buf[], int off, int len)
public void write(char buf[])
public void write(String s, int off, int len) 
public void write(String s)
public void print(boolean b)
public void print(char c)
public void print(int i)
public void print(long l)
public void print(float f)
public void print(double d)
public void print(char s[])
public void print(String s)
public void print(Object obj)
public void println()
public void println(boolean x)
public void println(char x)
public void println(int x) 
public void println(long x) 
public void println(float x) 
public void println(double x) 
public void println(char x[])
public void println(String x)
public void println(Object x) 
public PrintWriter printf(String format, Object ... args) 

这些方法的行为大多数与PrintStream中相同。只有4个write()方法有所例外,它们写入字符而不是字节。

相关文章
相关标签/搜索