前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java网络编程的Java流介绍

Java网络编程的Java流介绍

作者头像
纪莫
发布2018-12-19 16:39:55
8340
发布2018-12-19 16:39:55
举报
文章被收录于专栏:JimoerJimoer

前言

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

输出流

Java的基本输出流类是:java.io.OutputStream;

这个类中提供了写入数据所需的基本方法,如下:

代码语言:javascript
复制
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的参数中即可。

如下所示:

代码语言:javascript
复制
try(OutputStream out = new FileOutputStream("D:/temp/test.txt")){
     // 处理输出流

}catch (IOException e){
    e.printStackTrace();
}

因为Java会对try块参数表中 声明的所有AutoCloseable对象自动调用close()。Java中的流相关的类基本上都直接或间接的实现了AutoCloseable接口。

输入流

Java的基本输出流类是:java.io.InputStream;

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

代码语言:javascript
复制
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或网络原因只读取到了一部分,这个时候就会返回实际读取到的字节数。

例如:

代码语言:javascript
复制
int bytesRead = 0;
int bytesToRead = 1024;
byte[] input = new byte[bytesToRead];
while (bytesRead<bytesToRead){
       bytesRead += in.read(input,bytesRead,bytesToRead - bytesRead);
}

上面这段代码就是没有考虑到有可能流会中断导致读取的数据永远读不出来,所以要防止这种事情出现需要先测试read()的返回值,然后再增加到byteRead中

如下所示:

代码语言:javascript
复制
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()建议的字节数。

如下:

代码语言:javascript
复制
int bytesAvailable = in.available();
byte[] input = new byte[bytesAvailable];
int bytesRead = in.read(input,0,bytesAvailable);
//读取到数据后,去执行其他部分

在少数情况下,你可能希望跳过数据不进行读取。skip()方法会完成这项任务。

与输出流一样,一旦结束对输入流的操作,应当调用close()方法将其关闭。这会释放这个流关联的所有资源。

InputStream类中还有3个不经常用的方法,

代码语言:javascript
复制
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()方法。

过滤器通过其构造函数与流连接。

代码语言:javascript
复制
FileInputStream iin = new FileInputStream("test.txt");
BufferedInputStream bin = new BufferedInputStream(iin);

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

可以用如下方式:

代码语言:javascript
复制
InputStream iin = new FileInputStream("test.txt");
iin = new BufferedInputStream(iin);

缓冲流

BufferedOutputStream类将写入的数据存储在缓冲区中,直到缓冲区满了或者执行了flush方法。然后将数据一次全部写入底层输出流。在网络连接中,缓冲网络输出通常会带来巨大的性能提升。

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

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

代码语言:javascript
复制
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。还可以使用下面两个构造函数将其他输出流串链到打印流:

代码语言:javascript
复制
public PrintStream(OutputStream out)
public PrintStream(OutputStream out, boolean autoFlush)

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

代码语言:javascript
复制
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()方法也完成相同操作,但会在所写的行末尾追加一个与平台有关的行分隔符。

代码语言:javascript
复制
在网络编程中应尽量避免使用PrintStream。

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

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

PrintStream第三个问题时会吞掉了所有异常。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-11-28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 输出流
  • 输入流
  • 过滤器流
  • 缓冲流
  • PrintStream
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档