java io有什么模式 java io流的典型使用方式有几种
大家好,今天给各位分享java io有什么模式的一些知识,其中也会对java io流的典型使用方式有几种进行解释,文章篇幅可能偏长,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在就马上开始吧!
Java NIO和IO的区别
JavaNIO和IO之间的主要差别,我会更详细地描述表中每部分的差异。
IONIO
面向流面向缓冲
阻塞IO非阻塞IO
无选择器
面向流与面向缓冲
JavaNIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。JavaIO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。JavaNIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
阻塞与非阻塞IO
JavaIO的各种流是阻塞的。这意味着,当一个线程调用read()或write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。JavaNIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
选择器(Selectors)
JavaNIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来逗选择地通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
NIO和IO如何影响应用程序的设计
无论您选择IO或NIO工具箱,可能会影响您应用程序设计的以下几个方面:
1.对NIO或IO类的API调用。
2.数据处理。
3.用来处理数据的线程数。
API调用
当然,使用NIO的API调用时看起来与使用IO时有所不同,但这并不意外,因为并不是仅从一个InputStream逐字节读取,而是数据必须先读入缓冲区再处理。
数据处理
使用纯粹的NIO设计相较IO设计,数据处理也受到影响。
在IO设计中,我们从InputStream或Reader逐字节读取数据。假设你正在处理一基于行的文本数据流,例如:
Name:Anna
Age:25
Email:anna@mailserver.com
Phone:1234567890
该文本行的流可以这样处理:
BufferedReaderreader=newBufferedReader(newInputStreamReader(input));
StringnameLine=reader.readLine();
StringageLine=reader.readLine();
StringemailLine=reader.readLine();
StringphoneLine=reader.readLine();
请注意处理状态由程序执行多久决定。换句话说,一旦reader.readLine()方法返回,你就知道肯定文本行就已读完,readline()阻塞直到整行读完,这就是原因。你也知道此行包含名称;同样,第二个readline()调用返回的时候,你知道这行包含年龄等。正如你可以看到,该处理程序仅在有新数据读入时运行,并知道每步的数据是什么。一旦正在运行的线程已处理过读入的某些数据,该线程不会再回退数据(大多如此)。下图也说明了这条原则:
(JavaIO:从一个阻塞的流中读数据)而一个NIO的实现会有所不同,下面是一个简单的例子:
ByteBufferbuffer=ByteBuffer.allocate(48);
intbytesRead=inChannel.read(buffer);
注意第二行,从通道读取字节到ByteBuffer。当这个方法调用返回时,你不知道你所需的所有数据是否在缓冲区内。你所知道的是,该缓冲区包含一些字节,这使得处理有点困难。
假设第一次read(buffer)调用后,读入缓冲区的数据只有半行,例如,逗Name:An地,你能处理数据吗看显然不能,需要等待,直到整行数据读入缓存,在此之前,对数据的任何处理毫无意义。
所以,你怎么知道是否该缓冲区包含足够的数据可以处理呢看好了,你不知道。发现的方法只能查看缓冲区中的数据。其结果是,在你知道所有数据都在缓冲区里之前,你必须检查几次缓冲区的数据。这不仅效率低下,而且可以使程序设计方案杂乱不堪。例如:
ByteBufferbuffer=ByteBuffer.allocate(48);
intbytesRead=inChannel.read(buffer);
while(!bufferFull(bytesRead)){
bytesRead=inChannel.read(buffer);
}
bufferFull()方法必须跟踪有多少数据读入缓冲区,并返回真或假,这取决于缓冲区是否已满。换句话说,如果缓冲区准备好被处理,那么表示缓冲区满了。
bufferFull()方法扫描缓冲区,但必须保持在bufferFull()方法被调用之前状态相同。如果没有,下一个读入缓冲区的数据可能无法读到正确的位置。这是不可能的,但却是需要注意的又一问题。
如果缓冲区已满,它可以被处理。如果它不满,并且在你的实际案例中有意义,你或许能处理其中的部分数据。但是许多情况下并非如此。下图展示了逗缓冲区数据循环就绪地:
3)用来处理数据的线程数
NIO可让您只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。
如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,实现NIO的服务器可能是一个优势。同样,如果你需要维持许多打开的连接到其他计算机上,如P2P网络中,使用一个单独的线程来管理你所有出站连接,可能是一个优势。一个线程多个连接的设计方案如
JavaNIO:单线程管理多个连接
如果你有少量的连接使用非常高的带宽,一次发送大量的数据,也许典型的IO服务器实现可能非常契合。下图说明了一个典型的IO服务器设计:
JavaIO:一个典型的IO服务器设计-一个连接通过一个线程处理
java io流的典型使用方式有几种
Java中IO流分成两大类,
一种是输入流,所有的输入流都直接或间接继承自InputStream抽象类,输入流作为数据的来源,我们可以通过输入流的read方法读取字节数据;
另一种是输出流,所有的输出流都直接或间接继承自OutputStream抽象类,输出流接收数据,可以通过write方法写入字节数据。
Java的IO流类中,大部分的输入流和输出流都是成对存在的,即如果存在XXXInputStream,那么就存在XXXOutputStream,反之亦然。SequenceInputStream和StringBufferInputStream是特例,没有对应的SequenceOutputStream类和StringBufferOutputStream类,许多IO操作都可能会抛出IOException异常,比如read、write、close操作。
以下是Java的IO流中常见的输入流,由于每个输入流都有其对应的输出流,所以此处就不再列出输出流的继承结构图。
1、ByteArrayInputStream& ByteArrayOutputStream:
ByteArrayInputStream构造函数中需要传入一个byte数组作为数据源,当执行read操作时,就会从该数组中读取数据,正如其名,是一种基于字节数组实现的一种简单输入流,显而易见的是,如果在构造函数中传入了null作为字节数据,那么在执行read操作时就会出现NullPointerException异常,但是在构造函数初始化阶段不会抛出异常;与之相对应的是ByteArrayOutputStream,其内部也有一个字节数组用于存储write操作时写入的数据,在构造函数中可以传入一个size指定其内部的byte数组的大小,如果不指定,那么默认它会将byte数组初始化为32字节,当持续通过write向ByteArrayOutputStream中写入数据时,如果其内部的byte数组的剩余空间不能够存储需要写入的数据,那么那么它会通过调用内部的ensureCapacity
方法对其内部维护的byte数组进行扩容以存储所有要写入的数据,所以不必担心其内部的byte数组太小导致的IndexOutOfBoundsException之类的异常。
2、FileInputStream& FileOutputStream
FileInputStream能够将文件作为数据源,读取文件中的流,通过File对象或文件路径等初始化,在其构造函数中,如果传入的File对象(或与其相对应的文件路径所表示的File对象)不存在或是一个目录而不是文件或者由于其他原因无法打开读取数据,都会导致在初始化阶段导致抛出FileNotFoundException异常;与FileInputStream相对应的是FileOutputStream,可以通过FileOutputStream向文件中写入数据,也需要通过File对象或文件路径对其初始化,如同FileInputStream,如果传入的File对象(或与其相对应的文件路径所表示的File对象)是一个目录而不是文件或者由于其他原因无法创建该文件写入数据,都会导致在初始化阶段抛出FileNotFoundException异常。
3、PipedInputStream& PipedOutputStream
PipedInputStream和PipedOutputStream一般是结合使用的,这两个类用于在两个线程间进行管道通信,一般在一个线程中执行PipedOutputStream的write操作,而在另一个线程中执行PipedInputStream的read操作。可以在构造函数中传入相关的流将PipedInputStream和PipedOutputStream绑定起来,也可以通过二者的connect方法将二者绑定起来,一旦二者进进行了绑定,那么PipedInputStream的read方法就会自动读取PipedOutputStream写入的数据。PipedInputStream的read操作是阻塞式的,当执行PipedOutputStream的write操作时,PipedInputStream会在另一个线程中自动读取PipedOutputStream写入的内容,如果PipedOutputStream一直没有执行write操作写入数据,那么PipedInputStream的read方法会一直阻塞PipedInputStream的read方法所运行的线程直至读到数据。单独使用PipedInputStream或单独使用PipedOutputStream时没有任何意义的,必须将二者通过connect方法(或在构造函数中传入对应的流)进行连接绑定,如果单独使用其中的某一个类,就会触发IOException: Pipe Not Connected.
4、ObjectInputStream& ObjectOutputStream
ObjectOutputStream具有一系列writeXXX方法,在其构造函数中可以掺入一个OutputStream,可以方便的向指定的输出流中写入基本类型数据以及String,比如writeBoolean、writeChar、writeInt、writeLong、writeFloat、writeDouble、writeCharts、writeUTF等,除此之外,ObjectOutputStream还具有writeObject方法。writeObject方法中传入的类型必须实现了Serializable接口,从而在执行writeObject操作时将对象进行序列化成流,并将其写入指定的输出流中。与ObjectOutputStream相对应的是ObjectInputStream,ObjectInputStream有与OutputStream中的writeXXX系列方法完全对应的readXXX系列方法,专门用于读取OutputStream通过writeXXX写入的数据。
5、SequenceInputStream
SequenceInputStream主要是将两个(或多个)InputStream在逻辑上合并为一个InputStream,比如在构造函数中传入两个InputStream,分别为in1和in2,那么SequenceInputStream在读取操作时会先读取in1,如果in1读取完毕,就会接着读取in2。在我们理解了SequenceInputStream的作用是将两个输入流合并为一个输入流之后,我们就能理解为什么不存在对应的SequenceOutputStream类了,因为将一个输出流拆分为多个输出流是没有意义的。
6、StringBufferInputStream
StringBufferInputStream允许通过在构造函数中传入字符串以读取字节,在读取时内部主要调用了String的charAt方法。与SequenceInputStream类似,StringBufferInputStream也没有对应的OutputStream,即不存在StringBufferOutputStream类。Java没有设计StringBufferOutputStream类的理由也很简单,我们假设StringBufferOutputStream存在,那么StringBufferOutputStream应该是内部通过执行write操作写入数据更新其内部的String对象,比如有可能是通过StringBuilder来实现,但是这样做毫无意义,因为一旦我们String的构造函数中可以直接传入字节数组构建字符串,简单明了,所以设计StringBufferOutputStream就没有太大的必要了。StringBufferInputStream这个类本身存在一点问题,它不能很好地将字符数组转换为字节数组,所以该类被Java标记为废弃的(Deprecated),其官方推荐使用StringReader作为代替。
7、FilterInputStream& FilterOutputStream
FilterInputStream包含了其他的输入流,说具体点就是在其构造函数中需要传入一个InputStream并将其保存在其名为in的字段中,FilterInputStream只是简单的覆盖了所有的方法,之所说是简单覆盖是因为在每个覆盖函数中,它只是调用内部的保存在in字段中的InputStream所对应的方法,比如在其覆盖read方法时,内部只是简单调用了in.read()方法。FilterInputStream的子类可以进一步覆盖某些方法以保持接口不变的情况下实现某一特性(比如其子类有的可以通过使用缓存优化读取的效率)或者提供一些其他额外的实用方法。所以在使用时FilterInputStream可以让传入的InputStream具有一些额外的特性,即对构造函数传入的InputStream进行了一层包裹,使用了典型的装饰着模式,如果只看FilterInputStream本身这一个类的话,则该类自己本身意义不大,因为其只是通过内部的字段in简单覆写某些方法。但是如果将FilterInputStream和其子类结合起来使用话,那么就很有用了。比如FilterInputStream有两个子类BufferedInputStream和DataInputStream,这两个类在下面还会详细介绍。BufferedInputStream对read操作做了优化,每次读操作时都读取一大块数据,然后将其放入内部维护的一个字节数组缓冲区中。当外面调用BufferedInputStream的read方法时,首先去该缓冲区中读取数据,这样就避免了频繁的实际的读操作,BufferedInputStream对外没有暴露额外的其他方法,但是其内部的read方法已经经过优化了,所以在执行读操作的时候效率更高。DataInputStream与ObjectInputStream有点类似,可以通过一些readXXX方法读取基本类型的数据,这是非常有用的一些方法。
8、BufferedInputStream& BufferedOutputStream
如上面所介绍的那样,在BufferedInputStream的构造函数中需要传入一个InputStream, BufferedInputStream内部有一个字节数组缓冲区,每次执行read操作的时候就从这buf中读取数据,从buf中读取数据没有多大的开销。如果buf中已经没有了要读取的数据,那么就去执行其内部绑定的InputStream的read方法,而且是一次性读取很大一块数据,以便填充满buf缓冲区。缓冲区buf的默认大小是8192字节,也就是8K,在构造函数中我们也可以自己传入一个size指定缓冲区的大小。由于我们在执行BufferedInputStream的read操作的时候,很多时候都是从缓冲区中读取的数据,这样就大大减少了实际执行其指定的InputStream的read操作的次数,也就提高了读取的效率。与BufferedInputStream相对的是BufferedOutputStream。在BufferedOutputStream的构造函数中我们需要传入一个OutputStream,这样就将BufferedOutputStream与该OutputStream绑定在了一起。BufferedOutputStream内部有一个字节缓冲区buf,在执行write操作时,将要写入的数据先一起缓存在一起,将其存入字节缓冲区buf中,buf是有限定大小的,默认的大小是8192字节,即8KB,当然也可以在构造函数中传入size指定buf的大小。该buf只要被指定了大小之后就不会自动扩容,所以其是有限定大小的,既然有限定大小,就会有被填充完的时刻,当buf被填充完毕的时候会调用BufferedOutputStream的flushBuffer方法,该方法会通过调用其绑定的OutputStream的write方法将buf中的数据进行实际的写入操作并将buf的指向归零(可以看做是将buf中的数据清空)。如果想让缓存区buf中的数据理解真的被写入OutputStream中,可以调用flush方法,flush方法内部会调用flushBuffer方法。由于buf的存在,会大大减少实际执行OutputStream的write操作的次数,优化了写的效率。
Java中nio与普通io有什么优势
面向流与面向缓冲
Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
阻塞与非阻塞IO
Java IO的各种流是阻塞的。这意味着,当一个线程调用read()或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
选择器(Selectors)
Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
NIO和IO如何影响应用程序的设计
无论您选择IO或NIO工具箱,可能会影响您应用程序设计的以下几个方面:
1.对NIO或IO类的API调用。
2.数据处理。
3.用来处理数据的线程数。
API调用
当然,使用NIO的API调用时看起来与使用IO时有所不同,但这并不意外,因为并不是仅从一个InputStream逐字节读取,而是数据必须先读入缓冲区再处理。
数据处理
使用纯粹的NIO设计相较IO设计,数据处理也受到影响。
关于java io有什么模式和java io流的典型使用方式有几种的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。