首页 > 代码库 > Java-IO
Java-IO
总结一些io的知识点,摘自http://ifeve.com/java-io/
1、管道和线程:
同一个JVM的两个线程可以通过Java IO的管道通信,除了管道之外,一个JVM中不同线程之间还有许多通信的方式。实际上,线程在大多数情况下会传递完整的对象信息而非原始的字节数据。但是,如果需要在线程之间传递字节数据,Java IO的管道是一个不错的选择。
当使用两个相关联的管道流时,务必将它们分配给不同的线程。read()方法和write()方法调用时会导致流阻塞,也就是在一个线程中同时进行读和写,可能会导致线程死锁。
2、不同JVM进程之间IO:
使用Java网络API建立网络连接,使用Java IO在进程之间交换数据。一个具有读取文件数据功能的组件,同样可以轻松读取网络连接中的数据。只需要保证读取数据的组件是基于InputStream而非FileInputStream即可。
3、字节/字符数组:
用ByteArrayInputStream或者CharArrayReader封装字节或者字符数组从数组中读取数据。通过这种方式字节和字符就可以以数组的形式读出了。用ByteArrayOutputStream或者CharArrayWriter,把数据写入,就像写其它的流一样。当所有的数据都写进去了以后,只要调用toByteArray()或者toCharArray,所有写入的数据就会以数组的形式返回。
4、流处理:
流和数组不一样,不能通过索引读写数据。在流中,你也不能像数组那样前后移动读取数据,除非使用RandomAccessFile 处理文件。流仅仅只是一个连续的数据流。流中的数据只能够顺序访问。当达到流末尾时,返回-1。
请注意:
InputStream的read()方法返回一个字节大小,返回值的范围在0到255之间。
Reader的read()方法返回一个字符,会根据文本的编码,一次读取一个或者多个字节,返回值的范围在0到65535之间。
read(byte[])会尝试读取与给定字节数组容量一样大的字节数,返回值int表示已经读取过的字节数。如果InputStream内可读的数据不足以填满字节数组,那么数组剩余的部分将包含本次读取之前的数据。记得检查有多少数据实际被写入到了字节数组中。
read(byte, int offset, int length)同样将数据读取到字节数组中,不同的是,该方法从数组的offset位置开始,并且最多将length个字节写入到数组中。同样地,read(byte, int offset, int length)方法返回一个int变量,表示有多少字节已经被写入到字节数组中,所以请记得在读取数据前检查上一次调用read(byte, int offset, int length)的返回值。
5、并发IO:
IO之所以使用多线程,主要原因在于socket.accept()、socket.read()、socket.write()三个主要函数都是同步阻塞的,当一个连接在处理I/O的时候,系统是阻塞的,如果是单线程的话必然就挂死在那里;但CPU是被释放出来的,开启多线程,利用多核,每个客户端连接过来后,服务端都会启动一个线程去处理该客户端的请求,就可以让CPU去处理更多的事情。其实这也是所有使用多线程的本质。
多个线程不能同时从InputStream或者Reader中读取数据,也不能同时往OutputStream或者Writer里写数据。你没有办法保证每个线程读取多少数据,以及多个线程写数据时的顺序。如果线程之间能够保证操作的顺序,它们可以使用同一个stream、reader、writer。比如,你有一个线程判断当前的输入流来自哪种类型的请求,然后将流数据传递给其他合适的线程做后续处理。当有序存取stream、reader、writer时,这种做法是可行的。请注意,在线程之间传递流数据的代码应当是同步的。
6、输出流的flush()
即使所有数据都写入到了FileOutputStream,这些数据还是有可能保留在内存的缓冲区中。通过调用flush()方法,可以把缓冲区内的数据刷新到磁盘(或者网络,以及其他任何形式的目标媒介)中。
7、RandomAccessFile:
在RandomAccessFile的某个位置读写之前,必须把文件指针指向该位置。通过seek()方法可以达到这一目标。可以通过调用getFilePointer()获得当前文件指针的位置。
read()方法返回当前RandomAccessFile实例的文件指针指向的位置中包含的字节内容。
read()方法在读取完一个字节之后,会自动把指针移动到下一个可读字节。这意味着使用者在调用完read()方法之后不需要手动移动文件指针。与read()方法类似,write()方法在调用结束之后自动移动文件指针,所以你不需要频繁地把指针移动到下一个将要写入数据的位置。
IO:
DataInputStream、DataOutputStream 提了读写java基本类型的API。
File类:
File类只能访问文件以及文件系统的元数据,不能读写文件内容。
判断文件是否为空,调用File.length()。
excel、word、pdf不属于纯文本文件,包含了很多格式信息,使用字节流读取会出现乱码,对这类文件需要使用第三方jar包。
InputStreamReader:字节流->字符流,一个字符等于两个字节,注意编码格式,默认操作系统字符集,否则乱码。
使用BufferReader包装InputStreamReader,提高效率。
OutStreamWriter:字符流->字节流,调用write()方法,使用字符编码转换器,在写入输出流之前,字节会在缓冲区缓冲,使用BufferWriter包装OutStreamWriter避免频繁调用转换器。
基本流所提供的对于输入和输出的控制比较弱。InputStream只提供了顺序读取、跳过部分字节和标记/重置的支持,而OutputStream则只能顺序输出。
如果一个方法只是作为流的使用者,就不需要考虑流的关闭问题。典型的情况是在servlet实现中并不需要关闭HttpServletResponse中的输出流。
NIO:
基于Buffer进行高效io操作,Buffer是连续的堆外空间,由native代码实现,操作系统可以直接与缓冲区交互,java程序只需要完成对缓冲区读写,后续操作交给操作系统完成。通过Channel,java更好的与操作系统IO服务结合起来,充分利用Buffer缓冲区,Channel不是纯java实现,有native代码
支持NIO操作的io类有FileInputStream、FileOutputStream、RandomAccessFile
NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入
缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象
文件读写乱码:编码、解码字符集不一致,API使用不当,因为一个中文需要两个字节,InputStream是以字节为单位读取,将中文拆成两个字节分两次读取。
注意:在Java NIO中,你可以让一个线程读写多个“channel”。
NIO利用事件模型处理I/O,解决线程池瓶颈处理海量连接,包括利用面向事件的方式编写服务端/客户端程序。
非阻塞IO处理连接的线程数和连接数没有联系,当某个连接发送请求到服务器,服务器把这个连接请求当作一个请求"事件",并把这个"事件"分配给相应的函数处理。我们可以把这个处理函数放到线程中去执行,执行完就把线程归还。这样一个线程就可以异步的处理多个事件。
本文出自 “浅谈技术” 博客,请务必保留此出处http://netpeak.blog.51cto.com/1135737/1897562
Java-IO