Java IO相关知识

java IO

Java中使用流来处理程序的输入和输出操作,流是一个抽象的概念,封装了程序数据于输入输出设备交换的底层细节。JavaIO中又将流分为字节流和字符流,字节流主要用于处理诸如图像,音频视频等二进制格式数据,而字符流主要用于处理文本字符等类型的输入输出。
1.字节输入流InputStream
输入流InputStream负责从各种数据/文件源产生输入,输入源包括:数组,字符串,文件,管道,一系列其他类型的流,以及网络连接产生的流等等。
常用字节输入流的主要类型:
(1).ByteArrayInputStream字节数组输入流
主要功能:允许内存缓存作为输入流。
ByteArrayInputStream包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪read()方法要提供的下一个字节。
注意:关闭ByteArrayInputStream无效,该类中的方法在关闭此流之后仍可被调用,而不会产生任何的IOException。
(2).FileInputStream文件输入流
主要功能:从文件系统中的某个文件获得输入字节,用于读取诸如图像数据子类的原始字节流。若要读取字符流,请使用FileReader。
(3).PipedInputStream管道输入流
主要功能:和管道输出流一起构成一个输入输出的管道,是管道的数据输入端。
管道输入流应该连接到管道输出流,管道输入流提供要写入管道输出流的所有数据字节。通常,这些数据有某个线程从PipedInputStream对象中读取,并有其他线程将其写入到相应的PipedOutputStream对象中。
注意:不建议PipedInputStream和PipedOutputStream对象使用单线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可以在缓冲区限定范围内将读操作和写操作分离开,如果先连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。
(4).SequenceInputStream顺序输入流
重要功能:将两个或多个输入流对象转换为一个单个输入流对象。
SequenceInputStream表示其他输入流的逻辑串联关系,它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,以此类推,直到到达包含的最后一个输入流的文件末尾为止。
(5).FilterInputStream过滤输入流
主要功能:包含其他一些输入流,将这些被包含的输入流用作其基本数据源,它可以直接传输数据或者提供一些额外的功能
常用的FilterInputStream是DataInputStream数据输入流,主要用于允许程序以与机器无关的方式从底层输入流中读取java基本数据类型。其常用的方法有readInt(),readBoolean(),readChar()等等。
2.字节输出流OutputStream:
和字节输入流相对应,字节输出流负责字节类型数据想目标文件或设备的输出。常见的字节输出流如下:
(1).ByteArrayOutputStream字节数组输出流:
**主要功能:在内存中创建一个缓冲区,将接收到的数据放入该内存缓冲区中。
ByteArrayOutputStream实现了一个输出流,其中的数据被写入一个byte数组中。缓冲区会随着数据的不断写入而自动增长,可使用toByteArray()和toString()方法获取数据。**
注意:和ByteArrayInputStream类似,关闭ByteArrayOutputStream也是无效的,此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException。
(2).FileOutputStream文件输出流:
主要功能:将数据写入到指定文件中。
文件输出流是用于将数据写入File或FIleDescriptor的输出流,用于写入诸如图像数据之类的原始字节流,若要写入字符流,请使用FileWriter。
(3).PipedOutputStream管道输出流:
主要功能:连接管道输入流用来创建通信管道,管道输出流是管道数据输出端。
(4).FilterOutputStream过滤输出流:
主要功能:用于将已存在的输出流作为其基本数据接收器,可以直接传输数据或提供一些额外的处理。
常用的FIlterOutputStream是DataOutputStream数据输出流,它允许程序以适当的方式将java基本数据类型写入输出流中。其常用方法有writeInt(intV),writeChar(int v),writeByte(String s)等等。
3.字符流:
Java中得字节流只能针对字节类型数据,即支持处理8位的数据类型,由于java中的是Unicode码,即两个字节代表一个字符,于是在JDK1.1之后提供了字符流Reader和Writer。
字符流相关常用类如下:
(1).Reader:
用于读取字符串流的抽象类,子类必须实现的方法只有reader(char[],int, int)和close()。
(2).InputStreamReader:
是将字节输入流转换为字符输入流的转换器,它使用指定的字符集读取字节并将其解码为字符。即:字节——>字符。
它使用的字符集可以有名称指定或显式给定,也可以使用平台默认的字符集。
(3).Writer:
用于写入字符流的抽象类,子类必须实现的方法只有write(char[],int, int)和close()。
(4).OutputStreamWriter:
是将字符输出流转换为字节输出流的转换器,它使用指定的字符集将要写入流的字符编码成字节。即:字符——>字节。
4.综合使用Java IO各种流:
Java IO中的各种流,很少单独使用,经常结合起来综合使用,既可以满足特定需求,又搞效。例子如下:

(1).使用缓冲流读取文件:
  1. import java.io.*;  
  2.   
  3. public class BufferedInputFile{  
  4.     public static String read(String filename) throws IOException{  
  5.         //缓冲字符输入流  
  6.     BufferedReader in = new BufferedReader(new FileReader(filename));  
  7.     String s;  
  8.     StringBuilder sb = new StringBuilder();  
  9.     //每次读取文件中的一行  
  10.     While((s = in.readLine()) != null){  
  11.     sb.append(s + “\n”);  
  12. }  
  13. in.close();  
  14. return sb.toString();  
  15. }  
  16. public static void main(String[] args) throws IOException{  
  17.     System.out.println(read(“BufferedInputFile.java”));  
  18. }  
  19. }
(2).读取内存中的字符串:
  1. import java.io.*;  
  2.   
  3. public class MemoryInput{  
  4.     public static void main(String[] args) throws IOException{  
  5.         //将字符串包装为字符输入流  
  6.         StringReader in = new StringReader(  
  7.   BufferedInputFile.read(“BufferedInputFile.java”));  
  8.         int c;  
  9.         //读取字符输入流中的字符  
  10.         while((c == in.read()) != -1){  
  11.     System.out.println((char)c);  
  12. }  
  13. }  
  14. }  
(3).数据输入/输出流:
  1. import java.io.*;  
  2.   
  3. public class DataInputOutput{  
  4.     public static void main(String[] args) thows IOException{  
  5.         DataOutputStream out = new DataOutputStream(new BufferedOutputStream(  
  6. new FileOutputStream(“Data.txt”)));  
  7.         out.writeDouble(3.14159);  
  8.         out.writeUTF(“That was pi”);  
  9.         out.writeDouble(1.41413);  
  10.         out.writeUTF(“Square root of 2”);  
  11.         out.close();  
  12.         DataInputStream in = new DataInputStream(new BufferedInputStream(  
  13. new FileOutputStream(“Data.txt”)));  
  14.         System.out.println(in.readDouble());  
  15.         System.out.println(in.readUTF());  
  16. System.out.println(in.readDouble());  
  17.         System.out.println(in.readUTF());  
  18. }  
  19. }  
(4).文本文件输出流:
  1. import java.io.*;  
  2.   
  3. public class TextFileOutput{  
  4.     //输出文件名  
  5.     static String file = “BasicFileOutput.out”;  
  6.     public static void main(String[] args) throws IOException{  
  7.         //将字符串先包装成字符串输入流,然后将字符串输入流再包装成缓冲字符输入流  
  8.     BufferedReader in = new BufferedReader(new StringReader  
  9. (BufferedInputFile.read(“TextFileOutput.java”)));  
  10.         //字符输出流  
  11.         PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));  
  12.         int lineCount = 1;  
  13.         String s;  
  14.         While((s = in.readLine()) != null){  
  15.     out.println(lineCount++ + “: ” + s);  
  16. }  
  17. out.close();  
  18. }  
  19. } 
(5).二进制文件读写:
  1. import java.io.*;  
  2.   
  3. public class BinaryFileOutput{  
  4.     //输出文件名  
  5.     static String file = “BinaryFileOutput.out”;  
  6.     public static void main(String[] args) throws IOException{  
  7.         //将字符串先包装成字符串输入流,然后将字符串输入流再包装成缓冲字符输入流  
  8.     BufferedInputStream in = new BufferedInputStream(  
  9. new FileInputStream(“TestFile.png”)));  
  10.         //字符输出流  
  11.         BufferedOutputStream out = new BufferedOutputStream (  
  12. new FileOutputStream(file));  
  13.         byte[] buf = new byte[1024];  
  14.         int n;  
  15.         While((n = in.read(buf)) > 0){  
  16.     out.write(buf, 0, n);  
  17. }  
  18. out.close();  
  19. }  
  20. }  

NIO(反应器设计模式)

为了提高Java I/O的速度和效率,从JDK1.4开始引入了java.nio.*包,即java new I/O(NIO)
事实上,为了利用java nio的速度和效率优势,原来的java I/O包中相关的类已经使用java nio重新实现,因此在编程中即使没有显式地使用java nio的代码,使用传统java I/O还是利用了nio的速度和效率优势。Java nio的速度提高主要在:文件I/O和网络I/O两个方面。
Java nio的速度提升主要因为使用了类似于操作系统本身I/O的数据结构:I/O通道(Channel)和缓冲区。

selector:实现一个线程管理多个通道(采用复用和多复用实现,可以把多个流合成一个或一个分成多个流)。原理:对所有注册的channel进行轮询访问,一旦轮询到有注册事件发生,就返回一个selectionKey通知开发人员。

1.Channel通道:双向非阻塞通道,可以在两边进行数据读写。
通道表示实体,如硬件设备、文件、网络套接字或可以执行的一个或多个不同的I/O操作(如读取或写入)的程序组件的开发的链接,用于I/O操作的链接。
通道可处于打开或关闭状态。创建通道时它处于打开状态,一旦将其关闭,则保持关闭状态。一旦关闭了某个通道,试图对其调用I/O操作都会抛出ClosedChannelException异常,通过调用通道的isOpen()方法可以探测通道是否处于打开状态。一般情况下通道对于多线程的访问是安全的。
2.ByteBuffer字节缓冲区:
字节缓冲区是nio中唯一直接和通道channel打交道的缓冲区。*字节缓冲区可以存放原始字节,也可以存放java的8中基本类型数据,但是不能存放引用类型数据,String也是不能存放的。*正是由于这种底层的存放类型似的字节缓冲区可以更加高效和绝大部分操作系统的I/O进行映射。
字节缓冲区通过allocation()方法创建,此方法为该缓冲区的内容分配空间,或者通过wrapping方法将现有的字节数组包装到缓冲区中来创建。
字节缓冲区的常用操作:
(1).读写单个字节的绝对和相对get和put方法:
a. 绝对方法:
get(int index):读取指定索引处的字节。
put(int index, byte b):将字节写入指定索引处。
b.相对方法:
get():读取此缓冲区当前位置的字节,然后该位置递增。
put(byte b):将字节写入此缓冲区的当前位置,然后该位置递增。
(2).相对批量get方法:
ByteBuffer get(byte[] dst):将此缓冲区的字节传输到给定的目标数组中。
(3).相对批量put方法:
ByteBuffer put(byte[] src):将给定的源byte数组的所有内容传输到此缓冲区中。
(4).读写其他基本类型值:
getChar(), putChar(char value), getChare(int index), putChar(int index, char value).
getInt(), putInt(int value), getInt(int index), putInt(int index, int value)等等读写基本类型值得相对和绝对方法。
注意:基本类型值得相对和绝对读写方法,根据java基本类型数据底层字节数进行缓冲区移动。
(5).创建视图缓冲区:
为了访问同类二进制数据,允许将字节缓冲区视为包含它们基本类型值的缓冲区,视图缓冲区只是其内容受该字节缓冲区支持的另一种缓冲区。字节缓冲区和视图缓冲区内容更改是相互可见的。这两种缓冲区的位置、限制和标记值都是独立的。创建方法如下:
asCharBuffer(), asDoubleBuffer(), asFloatBuffer(), asIntBuffer(), asLongBuffer(), asReadOnlyBuffer(), asShortBuffer()。
与具体类型的get和put方法系列相比,视图缓冲区优势如下:
a视图缓冲区不是根据字节进行索引,而是根据其特定类型的值得大小进行索引。
b.视图缓冲区提供了相对批量get和put方法,这些方法可以在缓冲区和数组或相同类型的其他缓冲区直接传输连续序列的值。
c.视图缓冲区可能更搞效,因为当且仅当其支持的字节缓冲区为直接缓冲区时它才是直接缓冲区。
(6).缓冲区其他操作:
a.ByteBuffer compact():压缩缓冲区,从缓冲区写入数据之后调用,以防写入不完整。
b.ByteBuffer duplicate():创建共享此缓冲区内容的新的字节缓冲区,新缓冲区中的内容为此缓冲区的内容,此缓冲区和新缓冲区内容更改是相互可见的。
c.ByteBuffer slice():创建新的字节缓冲区,其内容是此缓冲区内容的共享子序列。
3.直接与非直接缓冲区:
字节缓冲区要么是直接的,要么是非直接的。
如果字节缓冲区是直接的,则JVM会尽最大努力直接在此缓冲区上执行本机的I/O操作,即在每次调用底层操作系统的一个本机I/O之前(或之后),JVM都会尽量避免将缓冲区的内容复制到中间缓冲区中(或尽量避免从中间缓冲区复制内容)。直接字节缓冲区可以通过调用allocateDirect()方法来创建,此方法返回的缓冲区进行分配和取消分配所需的成本通常高于非直接缓冲区。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此它对应用程序的内存需求量造成的影响可能并不明显,所以建议将直接缓冲区主要分配给那些容易受底层操作系统的本机I/O操作影响的大型的、持久的缓冲区
可以通过调用isDirect()来判断字节缓冲区是直接的还是非直接的。
4.文件通道:

Java I/O的FileInputStream,FileOutputStream和RandomAccessFile可以产生文件通道,例子如下:
  1. import java.nio.*;  
  2. import java.nio.channels.*;  
  3. import java.io.*;  
  4.   
  5. public class FileChannel{  
  6.     //分配字节缓冲区时指定其大小  
  7.     private static final int BSIZE = 1024;  
  8.     public static void main(String[] args) throw Exception{  
  9.         //获取FileOutputStram的文件通道  
  10.         FileChannel fc = new FileOutputStream(“data.txt”).getChannel();  
  11.         //向字节缓冲区中写入字节数组  
  12.         fc.write(ByteBuffer.wrap(“Some text”.getBytes()));  
  13.         fc.close();  
  14.         //以读写方式获取随机访问文件的文件通道  
  15.         fc = new RandomAccessFile(“data.txt”, “rw”).getChannel();  
  16.         //定位到字节缓冲区当前内容之后  
  17. fc.position(fc.size());  
  18. //向字节缓冲区中追加内容  
  19.         fc.write(ByteBuffer.wrap(“Some more”.getBytes()));  
  20.         fc.close();  
  21.         //获取FileInputStream的文件通道  
  22.         fc = new FileInputStream(“data.txt”).getChannel();  
  23.         //分配字节缓冲区  
  24.         ByteBuffer buff = ByteBuffer.allocate(BSIZE);  
  25.         //将字节数组从文件通道读入到字节缓冲区中  
  26.         fc.read(buff);  
  27.         //放置缓冲区,为缓冲区写出或相对获取做准备  
  28.         buff.flip();  
  29.         //判断缓冲区是是否还有元素  
  30.         while(buff.hasRemaining()){  
  31.             //获取字节缓冲区字节的相对方法  
  32.             System.out.println((char)buff.get());  
  33. }  
  34. }  
  35. } 
输出结果:
Some text Some more

传统java I/O中FileInputStream, FileOutputStream和RandomAccessFile三个类可以产生文件通道。
注意:Java new I/O的目标是提高I/O速度,快速移动大批量的数据,因此,字节缓冲区的大小非常重要,例子中的1K大小不一定是合适的,应用程序需要在生产环境中测试确定合适的缓冲区大小。
5.文件复制:
文件通道和字节缓冲区不但可以实现数据从文件读入和读出,还可以实现文件的复制,例子如下:

1. import java.nio.*;  
  2. import java.nio.channels/*;  
  3. import java.io.*  
  4.   
  5. public class FileCopy{  
  6.     //字节缓冲区大小  
  7.     private static final int BSIZE = 1024;  
  8.     public static void main(String[] args) throws Exception{  
  9.         //获取文件输入通道  
  10.     FileChannel in = new FileInputStream(“FileCopy.java”).getChannel();  
  11.     //获取文件输出通道  
  12.     FileChannel out = new FileOutputStream(“FileOut.txt”).getChannel();  
  13.     ByteBuffer buffer = ByteBuffer.allocate(BSIZE);  
  14.     While(in.read(buffer) != -1){  
  15.     //重置缓冲区,准备写出  
  16. buffer.flip();  
  17. //将字节缓冲区内容写出到文件输出通道  
  18. out.write(buffer);  
  19. //清除字节缓冲区  
  20. buffer.clear();  
  21. }  
  22. }  
  23. }  

6.文件通道传输:
文件通道的transferFrom(ReadableByteChannel src, long position, long count)方法将字节从给定的可读字节通道传输到此通道的文件中,transferTo(long position, long count, WritableByteChannel target)方法将此通道的文件传输到给定的可写字节通道。例子如下:

1. import java.nio.channels.*;  
  2. import java.io.*  
  3.   
  4. public class ChannelTransfer{  
  5.     public static void main(String[] args) throws Exception{  
  6.     FileChannel in = new FileInputStream(“ChannelTransfer.java”).getChannel();  
  7.     FileChannel out = new FileOutputStream(“out.txt”).getChannel();  
  8.     //将输入文件通道传输到输出文件通道  
  9.     in.transferTo(0, in.size(); out);  
  10.     //从输入文件通道传输到输出文件通道  
  11.     out.transferFrom(in, 0, in.size());  
  12. }  
  13. }  

1.数据转换:
使用字节缓冲区时,向字节缓冲区写入或者从字节缓冲区读取内容时,如果内容是字符类型,写入时指定字符编码集对内容进行编码,读取时指定字节编码集对内容解码。
每次从字节缓冲区中读取一个字节,程序中还需要对读取的字节进行类型转换才可以使用,如果使用视图缓冲区就可以直接获取所需的数据类型,例子如下

 1. import java.nio.*;  
  2. import java.nio.channels.*;  
  3. import java.nio.charset.*;  
  4. import java.io.*;  
  5.   
  6. public class BufferToText{  
  7.     private static final int BSIZE = 1024;  
  8.     public static void main(String[] args) throws Exception{  
  9.         FileChannel fc = new FileOutputStream(“data.txt”).getChannel();  
  10.         fc.write(ByteBuffer.wrap(“Some text”.getBytes()));  
  11.         fc.close();  
  12.         fc = new FileInputStream(“data.txt”).getChannel();  
  13.         ByteBuffer buffer = ByteBuffer.allocate(BSIZE);  
  14.         fc.read(buffer);  
  15.     buffer.flip();  
  16.     //创建字节缓冲区视图,作为char缓冲区,由于编码问题输出乱码  
  17.     System.out.println(buffer.asCharBuffer());  
  18.     //重绕字节缓冲区,即回到字节缓冲区开始处  
  19. buffer.rewind();  
  20. //获取系统默认字节编码  
  21. String encoding = System.getProperties(“file.encoding”);  
  22. System.out.println(“Decode using ” + encoding + “: ”   
  23. + Charset.forName(encoding).decode(buffer));  
  24.         fc = new FileOutputStream(“data2.txt”).getChannel();  
  25.         //创建字节缓冲区时指定字符集  
  26.         fc.write(ByteBuffer.wrap(“Some text”.getBytes(“UTF-16BE”)));  
  27.         fc.close();  
  28.         fc= new FileInputStream(“data2.txt”).getChannel();  
  29.         buffer.clear();  
  30.         fc.read(buffer);  
  31.         buffer.flip();  
  32.         //创建字节缓冲区的视图,将其作为char缓冲  
  33.         System.out.println(buffer.asCharBuffer());  
  34. }  
  35. }  
输出结果:
????
Decoded using Cp1252: Some text
Some text

从上面例子可以看出,使用java.nio.charset.Charset类对字符进行编码/解码之后,写入/读取字节缓冲区时,字节缓冲区中的字符内容才可以正常显示。
2.创建字节缓冲区视图:
字节缓冲区中只能存放字节内容,java 8种基本类型数据向字节缓冲区中写入,或者从字节缓冲区中读取,就需要使用字节缓冲区视图和读取8中java基本类型数据的方法,例子如下:

  1. import java.nio.*;  
  2.   
  3. public class ViewBuffer{  
  4.     private static final int BSIZE = 1024;  
  5.     public static void main(String[] args){  
  6.         ByteBuffer bb = ByteBuffer.allocate(BSIZE);  
  7.         int i = 0;  
  8.         //检查字节缓冲区的内容是否为0  
  9.         while(i++ < bb.limit()){  
  10.             if(bb.get() != 0){  
  11.                 System.out.println(“nonzero”);  
  12. }  
  13. System.out.println(“i = ” + i);  
  14. //回到字节缓冲区开始处  
  15. bb.rewind();  
  16. }  
  17.        //创建字节缓冲区视图,作为char缓冲  
  18.       bb.asCharBuffer().put(“Hello!”);  
  19.       char c;  
  20.       while((c = bb.getChar)) != 0){  
  21.           System.out.println(c + “ ”);  
  22.   }  
  23. bb.rewind();  
  24. //创建字节缓冲区视图,作为short缓冲  
  25. bb.asShortBuffer().put((Short)12390);  
  26. System.out.println(bb.getShort());  
  27. bb.rewind();  
  28. //创建字节缓冲区视图,作为int缓冲  
  29. bb.asIntBuffer().put(99471142);  
  30. System.out.println(bb.getInt());  
  31. bb.rewind();  
  32. //创建字节缓冲区视图,作为long缓冲  
  33. bb.asLongBuffer().put(99471142);  
  34. System.out.println(bb.getLong());  
  35. bb.rewind();  
  36. //创建字节缓冲区视图,作为float缓冲  
  37. bb.asFloatBuffer().put(99471142);  
  38. System.out.println(bb.getFloat());  
  39. bb.rewind();  
  40. //创建字节缓冲区视图,作为double缓冲  
  41. bb.asDoubleBuffer().put(99471142);  
  42. System.out.println(bb.getDouble());  
  43.    }  
  44. }  
输出结果:
i = 1025
H e l l o !
12390
99471142
99471142
9.9471142E7
9.9471142E7

对于刚分配的字节缓冲区来说,其所有内容都是0,所有第一次i的输出值为1025.
向字节缓冲区中写入,或者从字节缓冲区中读取8中基本类型java数据最简便的方法是使用字节缓冲区的视图缓冲区。
3.java nio相关类图:
下图是从《java编程思想第4版》中截的java nio相关类图:

从类图中可以看出,Java nio中,只有字节缓冲区ByteBuffer可以向文件通道写数据或者从文件通道中读取数据。
4.缓冲区详解:
缓冲区由:内容数据和4个索引组成。
缓冲区索引:
a. 缓冲区标记(mark):使缓冲区能够记住一个位置并在之后将其返回。并非总需要定义标记,但在定义标记时,不能将其定义为负数,且不能大于其位置。
b. 缓冲区位置(position):是缓冲区下一个要读取或写入元素的索引,位置不能为负,且不能大于其限制。
c. 缓冲区限制(limit):是第一个不应该读取或写入的元素的索引,限制不能为负,且不能大于其容量。
d. 缓冲区容量(capacity):是缓冲区所包含的元素数量,不能为负,且不能更改。
缓冲区索引遵循以下不变公式:
0 <= 标记 <= 位置 <= 限制 <= 容量
通过这些索引,缓冲区可以高效的访问和操作缓冲区中的内容数据,新创建的缓冲区总有一个0位置和一个未定义的标记,初始限制可以为0,也可以为其他值,取决于缓冲区类型和构建方式。
缓冲区的以下方法可以查询,设置和充值缓冲区的索引:
(1).capacity()方法:
返回此缓冲区的容量。
(2).clear()方法:
清除此缓冲区,将缓冲区位置设置为0,将缓冲区限制设置为容量,并丢弃标记。
(3).flip()方法:
反转此缓冲区,首先将限制设置为当前位置,然后将位置设置为0,如果已定义了标记,则丢弃该标记。
(4).limit()方法:
返回此缓冲区的限制。
(5).limit(int lim)方法:
设置此缓冲区的限制,如果位置大于新的限制,则将位置设置为新的限制,如果标记已定义且大于新限制,则丢弃该标记。
(6).mark()方法:
在此缓冲区的位置设置标记。
(7).position()方法:
返回此缓冲区的位置。
(8).position(int pos)方法:
设置此缓冲区的位置,如果标记已定义且大于新的位置,则丢弃该标记。
(9).remaining()方法:
返回当前位置与限制之间的元素个数,即limit-position。
(10).hasRemaining()方法:
判断在当前位置和限制之间是否有元素。

IO的方式通常分为几种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO
BIO是一个连接一个线程。
NIO是一个请求一个线程。
AIO是一个有效请求一个线程。

同步阻塞IO(JAVA BIO):
同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
同步非阻塞IO(Java NIO) : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问。

异步阻塞IO(Java NIO)
此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄(如果从UNP的角度看,select属于同步操作。因为select之后,进程还需要读写数据),从而提高系统的并发性!
(Java AIO(NIO.2))异步非阻塞IO:
在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。
BIO、NIO、AIO适用场景分析:
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

你可能感兴趣的:(框架,字符流,java)