`

Java 网络编程---I/O部分

阅读更多
  网络程序的很大一部分是简单的输入输出,即从一个系统向另一个系统移动字节。字节就是字节,在很大程度上,读服务器发送的数据与读取文件没什么不同;向客户传送数据与写入一个文件也没有什么区别。

       Java中输入和输出组织不同于大多数其他语言。它是建立在流(stream)上。不同的基本流类(如java.io.FileInputStream和sun.net.TelnetOutputStream)用于读写特定的数据资源。但是所有的基本输出流使用同一种基本方法读数据。

       过滤器流可以连接到输入流或输出流。它可以修改已经读出或写人的数据(例如,加密或压缩数据),或者可以简单地提供附加方法将已经读出或写入的数据转化成其他格式。

       最后Reader和Writer也可以链接到输入流和输出流,从而允许程序读出和写入文本(即字符)而不是字节。如果使用正确,Reader和Writer能够处理多种类型的字符编码,包括SJIS和UTF-8等多字节字符集。



一、输出流

java的基本输出流是 java.io.OutputStream.

public abstract class OutputStream

n         public abstract void write(int b) throws IOException

n         public void write(byte[] data) throws IOException

n         public void write(byte[] data,int offset,int length) throws IOException

n         public void flush() throws IOException

n         public void close() throws IOException

OutputStream的子类使用这些方法向指定媒体写入数据。



我始终相信,我们理解了为什么它们存在,就会更好地记住它们,好,现在开始说一下OutputStream类的方法的由来

Ø         public abstract void write(int b) throws IOException

OutputStream的基本方法是write(int b)。该方法将介于0到255之间的整数看作变量,并将相应的字节写到一个输出流。该方法声明是个抽象方法,因为子类需要改变它以处理特定媒体。例如,ByteArrayOutputStream可以使用拷贝字节到其数组的纯Java代码来实现方法。但是,FileOutputStream就需要使用代码,此代码应该理解如何在主机平台上将数据写入文件。注意:尽管该方法把整形值作为变量,但是它实际上写入的是一个无符号字节。Java没有无符号字节数据类型,因此这里使用整型来代替。无符号字节和有符号字节之间的真正区别是编译器对它们的解释。二者都是由8位组成,并且当使用write(int b)将一个int写入到网络连接流时,只有8位数据传送。如果将一个超出0-255范围的int传给write(int b),则写入该数字的低位字节,而忽略余下的三个字节(大家都知道java的int是4个字节的,这里本质就是将int转换为byte)。



Ø         public void write(byte[] data) throws IOException和public void write(byte[] data,int offset,int length) throws IOException

每次写入一个字节通常效率不高。因此,大部分TCP/IP程序将数据存入一定长度的缓冲区,即在内存中累积字节,并仅当累积了一定数目字节或过了一定的时间段,才将它们发送到最终的目的地。因此write(byte[] data)和write(byte[] data,int offset,int length)就是这样产生了。



Ø         public void flush() throws IOException

我们可以在软件中或直接在Java代码中对流实施缓冲操作,也可以在网络硬件中对流实施缓冲操作。就好像BufferedOutputStream或BufferedWriter链接到底层流来实现流缓冲。因此,如果正在写入数据,则刷新输出流是相当重要的。例如,假设已经写入了一个300字节的请求给一个HTTP Keep-Alive的HTTP服务器,通常希望在发送更多数据之间等待响应。但是,如果输出流有一个1024字节的缓冲区,则该流可能在将数据发送出缓冲区之前正在等待更多的数据到达,但是这些数据似乎不会到达的,因为它们还没有发送出去,但是缓冲流不会发送数据给服务器,除非它从底层流获得更多的数据,但是底层流不会发送更多的数据,除非它从服务器获得数据,而服务器不会发送数据,除非它获得保留在缓冲区中的数据(死锁了!),flush()方法就可以解决了这个僵局,因为即使缓冲区未满,他也会强制要求实行缓冲操作的流传送数据。注意:是否对流实行了缓冲操作,这决定于你如何获得指向流的引用(例如,不论是否希望对System.out执行缓冲操作,都会对其实施缓冲)。如果刷新流需要刷新时,就必须刷新,但是如果刷新失败了就会导致不可预料、不可重复的程序挂起(flush()返回值是void啊),如果事先不了解挂起问题所在,就很难解决这个问题了。因此,在关闭所有流之前,应当立即刷新它们。否则,关闭流前,缓冲区中的剩余数据可能会丢失。



Ø         public void close() throws IOException

最后当利用完流之后,应当调用close()方法关闭流。它会释放所有与这个流相关的资源,如文件句柄或端口。一旦输出流关闭了,再向其写入数据就会触发IOException异常。但是,有些类型可能允许对对象进行一定操作。如一个已关闭的ByteArrayOutputStream仍然可以转化成一个实际的字节数组,而且一个已关闭的DigestOutputStream仍可以返回其摘要。



二、输入流

java的基本输入流是java.io.InputStream

public abstract class InputStream

n         public abstract int read() throws IOException

n         public int read(byte[] data) throws IOException

n         public int read(byte[] data,int offset,int length) throws IOException

n         public long skip(long n) throws IOException

n         public int available() throws IOException

n         public void close() throws IOException

InputStream的具体子类使用这些方法从指定媒体读取数据。但是不论读取何种资源,几乎只能使用这六种方法。有时你甚至可能不知道正在从哪种类型的流中读取数据。如隐藏在sun.net包中TelnetInputStream是一个文档没有说明的类。TelnetInputStream的实例由java.net包中的多种方法返回;如java.net.URL的openStram()方法。但是,这些方法仅声明了返回InputStream,而不是更加明确的子类TelnetInputStream,这又是多态性在起作用了。子类的实例可以作为超类的实例透明使用。

来了,又来说明方法的由来了。

Ø         public abstract void read() throws IOException

InputStream类的基本方法是没有参量的read()方法(这个与OutputStream不同了)。该方法从输入流资源读取一个单个字节数据并将数据作为0到255之间的数返回,返回-1时表示流的结尾。因为Java没有无符号字节的数据类型,所以数据以整型类型返回。Read()方法等待和阻塞该方法后人和代码的执行,直到获得数据的一个字节并准备读取该字节。因此,输入和输出可能相当慢,这时用户如果需要完成其他比较重要的任务时,最好试图将I/O放到它们自己的线程中。Read()方法被声明为抽象方法,因为子类需要改变它来处理特定媒体。给个例子

byte[] input=new byte[10];

for(int i=0;i<input.length;i++){

       int b=in.read();

       if(b==-1) break;

       input=(byte)b;

}

上面尽管read()方法仅读取字节,但是它返回的是整型值。因此在将结果存储到字节数组之前,需要一个类型转换的过程。当然,这会产生一个介于-128到127的有符号字节,而不是read()方法返回的0到255之间的一个无符号字节。但是,只要用户清楚使用的是无符号还是有符号字节就不会有很大问题。因此,我们可以把一个有符号字节转化成无符号字节(转换的原因是只有范围在0-255的整数才可以被存储在java的一个byte类型的变量中)。

int i=b>=0?b:256+b;

这里费了大篇幅,说明了read()返回的与java的byte类型的处理问题,大家可要注意阿,如果对java的原始数据类型还有兴趣,可以看一下我的原始数据类型学习笔记(未完成)。



Ø         public int read(byte[] data) throws IOException、public int read(byte[] data,int offset,int length) throws IOException

每次读取一个字节和每次写入一个字节效率都不高,因此read(byte[] data)和read(byte[] data,int offset,int length)也相应产生了。这两个方法将从流中读取的多个字节填充到一个指定的数组中。注意:这些填充到数组的操作不一定会成功的。一个很普遍的情况是一个读试图不会完全失败也不会完全成功,它可能读出请求数据的一部分字节,而不是全部字节。例如,当实际上只有512字节已经到达服务器时,用户可能会试图从一个网络流上读取1024字节,而其他字节仍然在传送中,这些字节最终会到达服务器,但到达时却已是不可以获得的。因此,多字节读取方法会返回实际读取的字节数目。给个例子

byte[] input=new byte[1024];

int bytesRead=in.read(input);

代码段试图从InputStream in读取1024字节到数组input中。但是,如果仅有512字节可以获得,则这些字节就是将要读取的全部字节,并且bytesRead值会设为512。但我们为了保证在实际上读取到所有的字节,怎么办?看

int bytesRead=0;

int byteToRead=1024;

byte[] input=new byte[byteToRead];

while(bytesRead<byteToRead){

       bytesRead+=in.read(input,bytesRead,byteToRead-bytesRead);

}

Ø         public int available() throws IOException

如果由于某种原因用户不希望读取数据,除非用户想要的全部数据可以立即得到,这时候就可以用available()方法返回的字节数是能够读取的最小字节数,而在实际上可以读取更多的字节,但是能够读取的字节数据至少与available()返回的字节数一样多。

看例子

int bytesAvailable=in.available();

byte[] input=new byte[bytesAvailable];

int byteTead=in.read(input,0,bytesAvailable);

//其他代码

这里我们可以断言bytesRead正好等于bytesAvailable,但不能断言bytesRead>0,因为available()返回0是有可能的。

流结束时:

available()返回0;

read(byte[] data,int offset,int length)通常返回-1;

       流没有结束,可读取字节数即available()得到的值为0时

read(byte[] data,int offset,int length)会忽略流的结束,返回0;



Ø         public long skip(long n) throws IOException

在极少数情况下,用户可能希望跳过数据而不去读取它们。Skip()方法就是实现这个功能的。这个方法在从文件读取数据时较为有用,而在网络连接流上则用处较小。因为网络连接流是有序的而且通常很慢,因此读取数据的耗时不会太多的超过跳过数据的耗时。文件可以随机访问,因此我们通过重定位文件指针就能简单的实现数据的跳转,而不是跳过每一个字节。



Ø         public void close() throws IOException

和输出流一样,程序利用完输入流之后,就应该调用close()方法关闭该输入流了(要记住啊)。该方法会释放与输入流有关的所有资源,如文件句柄和端口。一旦输入流关闭,再从它读取数据时会触发IOException。但是,有些类型的流可能仍允许对对象进行一定的操作。例如,用户通常不希望从java.security.DigestInputStream中获取报文摘要,除非已经读取了所有数据并且关闭了输入流。



看到这里或许你还会问怎么还有三个方法没有呢,对,还有三个不常用的方法

public void mark(int readAheadLimit)

public void reset() throws IOException

public boolean markSupported();

这些方法允许程序备份和重新读取已经读取过的数据。要实现这个功能,需要用mark()方法在输入流中的当前位置作个标记,在以后的某点可以使用reset()方法重新将流定位到标记处,随后的读取将返回从标记初开始的数据。但是,从标记处到重新将流定位点不能任意长。重新定位到标记处之前允许读取的字节数就是由mark()的变量readAheadLimit决定。多长就会触发IOException,而且任何指定时刻,输入流中只可以有一个标记,如果标记了第二个标记,就会覆盖第一个标记了。其实标记和重新设置位置都是通过存储从内部缓冲区中的标记位置读出每一字节来实现。最麻烦的状况是,并非所有输入流都支持标记和重新设置位置的。所以在设置之前要用markSupported()方法检测一下。

实际上不支持标记和重新设置位置的流多于支持它们的流。Elliotte Rusty Harold大师觉得这几个方法设计的标准不高,将功能性与一个许多甚至可能是大部分子类都不可用的抽象超类结合是一个相当拙劣的想法。最好是将这三个方法放在不同的接口中。提供类似于markSupported()方法在运行时进行功能性检测是较为传统,非面向对象的解决方法。面向对象的方法将通过接口和类把该方法嵌入到面向对象系统中,从而在编译时检测所有的流。

Java.io中总是支持标记的输入流:BufferedInputStream和ByteArrayInputStream。
分享到:
评论

相关推荐

    Java网络编程-第三版(中文版).pdf(Elliotte Rusty Harold)

    《Java网络编程》第三版是由Elliotte Rusty Harold编著的一本专业书籍,中文版为国内Java开发者提供了深入理解网络编程的宝贵资源。这本书详细介绍了如何使用Java语言进行网络应用开发,涵盖了从基础概念到高级技术...

    Java网络编程/Java网络编程实例

    Java网络编程是Java开发中的重要领域,它涵盖了网络应用程序的设计、实现和调试。在这个主题下,我们可以探讨多个关键知识点: 1. **Java Socket编程**:Java的Socket类提供了基于TCP/IP协议的网络通信能力。通过...

    java基础之I/O流

    Java中的I/O流是程序与外部数据交互的重要机制,它允许数据在程序、文件、网络等之间流动。I/O流分为两大类:字符流(Character Stream)和字节流(Byte Stream),每类又分为输入流(Input Stream)和输出流...

    Java网络编程精解(孙卫琴)电子教案

    - **Java I/O**:网络通信中数据的传输离不开I/O操作。Java提供了InputStream和OutputStream接口,以及它们的子类,用于读写数据。 2. **TCP/IP协议** - **TCP协议**:传输控制协议,提供面向连接的、可靠的、...

    java_test-////--/16

    标题“java_test-////--/16”似乎指的是一个与Java编程相关的测试项目或代码库,其中可能包含了一些特定的测试用例或者示例代码。这个名称可能表示这是一个关于Java语言的学习过程中的第16个实验或者练习。然而,...

    java网络编程

    - **NIO(非阻塞I/O)**:Java的NIO库提供了更高效的网络编程模型,适用于高并发场景。 - **异步I/O(AIO)**:Java NIO.2引入了异步I/O,允许非阻塞读写操作。 通过阅读《Java网络编程》第三版,开发者可以系统...

    Java I/O, 2nd Edition

    通过《Java I/O, 2nd Edition》这本著作,读者将能够深入理解Java的I/O系统,掌握高效、安全的I/O编程技巧,无论是处理文件、网络通信还是序列化,都能游刃有余。对于Java开发者来说,这是一本不可多得的参考资料。

    Java课程设计报告书-学生版-1_I/O流与文件课程设计_

    在Java编程语言中,I/O流(Input/Output Stream)是处理数据输入和输出的核心机制。本课程设计报告书——“Java课程设计报告书-学生版-1_I/O流与文件课程设计_”旨在帮助学生深入理解并掌握如何在Java中进行文件操作...

    Java网络编程期末考试复习题库+答案

    在Java中,网络编程主要依赖于Java的Socket编程、ServerSocket、URL类以及NIO(非阻塞I/O)等核心API。这份"Java网络编程期末考试复习题库+答案"为学生提供了全面的复习资源,涵盖了Java网络编程的主要知识点。 1. ...

    一站式学习Java网络编程 全面理解BIO:NIO:AIO1

    全面理解 Java 网络编程 - BIO、NIO、AIO 本课程旨在帮助学生全面理解 Java 网络编程中的 BIO、NIO、AIO 三剑客,掌握 RPC 编程的基础知识,并结合实战项目巩固所学。 一、网络编程三剑客 - BIO、NIO、AIO BIO...

    Java网络编程(第4版)PDF

    在Java网络编程中,首要涉及的是I/O模型。书中会介绍基础的套接字(Socket)编程,包括TCP和UDP协议的应用。TCP提供面向连接的服务,确保数据的可靠传输,而UDP则是无连接的,更注重传输效率。读者将学习如何创建和...

    java网络编程第四版pdf

    除了以上章节,书中还涵盖了套接字编程、服务器Socket、网络套接字API、URL和URLConnection类,以及高级主题如NIO(非阻塞I/O)和异步I/O。这些内容详细阐述了如何利用Java进行网络通信,包括建立连接、发送和接收...

    java网络编程实例

    Java网络编程是开发分布式应用程序的关键技术,它允许程序通过网络发送和接收数据。在这个实例中,我们将深入探讨Java网络编程的基础及其在实际应用中的实践。本文将覆盖以下几个关键知识点: 1. **Java网络编程...

    Java网络编程实例(随书源代码)

    - **网络I/O**:Java的InputStream和OutputStream类族在处理网络数据传输时起到关键作用,如SocketInputStream和SocketOutputStream。 - **缓冲和对象序列化**:BufferedInputStream和BufferedOutputStream用于...

    Java I/O 过滤流-带格式的读写操作

    在Java编程语言中,输入/输出(I/O)是处理数据传输的核心部分。过滤流(Filter Stream)是Java I/O框架中的一个重要概念,它提供了一种优雅的方式来进行数据的读写操作,同时允许我们添加额外的功能,如字符编码...

    jdk6.0从入门到精通-----chapter5网络编程 新I/O(含源码下载)

    在Java编程领域,JDK(Java Development Kit)是开发和运行Java应用程序的基础,而JDK 6.0是Oracle公司发布的较早版本之一,对于学习Java的初学者来说,它是入门的重要工具。本章节我们将深入探讨JDK 6.0中的网络...

    java网络编程精讲

    ### Java网络编程精讲知识点概览 #### 一、Java网络编程基础 1. **网络编程概念**: - 网络编程是指利用计算机网络技术进行数据交换和资源共享的一种编程方式。 - Java提供了强大的网络编程支持,使得开发者能够...

    java网络编程相关知识

    Java网络编程是开发分布式应用程序的关键领域,它涵盖了各种概念和技术,使得Java程序能够通过网络进行通信。本节将深入探讨Java网络编程的核心知识点,包括基础概念、API使用、多线程以及安全问题。 1. **网络基础...

    Java网络编程第三版.pdf

    2. **I/O与NIO**:Java的I/O流系统是网络编程的基础,包括字节流、字符流、对象流等。此外,非阻塞I/O(NIO)的引入为高性能网络应用提供了可能,如Selector和Channel的概念。 3. **多线程与并发**:在网络编程中,...

    TCP-JAVA网络编程(PPT)

    TCP-JAVA网络编程是Java开发中的重要组成部分,主要用于构建客户端-服务器应用,实现数据的可靠传输。本课程通过PPT的形式深入浅出地讲解了这一主题。以下将详细阐述TCP和Java在网络编程中的核心概念、关键技术和...

Global site tag (gtag.js) - Google Analytics