java流--I/O总结

I/O 问题是整个人机交互的核心问题,因为 I/O 是机器获取和交换信息的主要渠道。这个数据大爆炸时代,I/O 问题尤其突出,很容易成为一个性能瓶颈,正因如此,所以 Java 在 I/O 上也一直在做持续的优化,如从 1.4 开始引入了 NIO,JDK1.7引入的 NIO 2.0,都一定程度上的提升了 I/O 的性能。
1.Java 的 I/O 操作类在包 java.io 下,大概有将近 80 个类,但是这些类大概可以分成四组,分别是:

   <1>基于字节操作的 I/O 接口:InputStream 和 OutputStream
   <2>基于字符操作的 I/O 接口:Writer 和 Reader
   <3>基于磁盘操作的 I/O 接口:File
   <4>基于网络操作的 I/O 接口:Socket
   前两组主要是根据传输数据的数据格式,后两组主要是根据传输数据的方式;Socket 类并不在 java.io 包下
   流最终写到什么地方必须要指定,要么是写到磁盘要么是写到网络中

2.字节流也称数据流:所有的数据通信通道

  (1)Java中每一种流的基本功能依赖于它们InputStream 用于read,OutputStream 用于write, 读和写都是相对与内存说的,
       读就是从其他地方把数据放进内存,写就是把数据从内存拿出去
  (2)这两个都是抽象类,不能直接使用

3.字符流:不管是 Writer 还是 Reader 类它们都只定义了读取或写入的数据字符的方式(怎么写或读,没有规定数据要写到哪)

  (1)基于字符的 I/O 操作接口不管是磁盘还是网络传输,最小的存储单元都是字节
  (2) I/O 操作的都是字节而不是字符
  (3)字节与字符的转化接口另外数据持久化或网络传输都是以字节进行的,所以必须要有字符到字节或字节到字符的转化
  (4)字符到字节或字节到字符需要转化:OutputStreamWriter /InputStreamReader 是字节到字符的转换桥梁

4.磁盘 I/O 也称文件流:File 可以表示文件也可以表示目录,File 类控制所有硬盘操作

 (1)该流用于从文件读取数据,它的对象可以用关键字 new 来创建
      <1>可以使用字符串类型的文件名来创建一个输入流对象来读取文件:
                 InputStream f = new FileInputStream("C:/java/hello");
      <2>也可以使用一个文件对象来创建一个输入流对象来读取文件(也可以使用一个文件对象来创建一个输入流对象来读取文件)
            File f = new File("C:/java/hello");
            InputStream out = new FileInputStream(f);
 (2)缓冲区流:在普通文件流上加了缓冲的功能
       <1>BufferedInputStream/BufferedOutputStream
       <2>缓冲流可以加快输入输出的速率
       <3>缓冲流它必须用字节流来实例化
               //创建文件输出流对象  
                FileOutputStream fos = new FileOutputStream(path);  
              //用缓冲流包装文件输出流对象  
                BufferedOutputStream bos = new BufferedOutputStream(fos);  
        <4>BufferedInputStream的默认缓冲区大小是8192字节,当每次读取数据量接近或远超这个值时,效率和FileInputStream就没有明显差别了

扩展:
1.BufferedReader和BufferedInputStream区别:

         (1)BufferedReader:针对字符流,实现时通过提供一个readLine方法,使用数组或者StringBuilder存储一行数据
                             并一次性返回(父类是Reader(父类Object))
         (2)BufferedInputStream:针对字节流,实现的时候是在自身read方法中提供缓存,是一次取1024或更多字节然后再慢慢读,
                             一个个的返回,它并没有实现读一行的方法(父类是 FilterInputStream(父类InputStream))
         (3)建议用 BufferedReader 包装所有 read() 操作(如 FileReader 和 InputStreamReader )
                 示例: BufferedReader bufferedReader =new BufferedReader( new InputStreamReader(System.in));

2.I/O与NIO(即New IO)区别:

    (1)I/O:面向流,阻塞I/O,无选择器
    (2)NIO:面向缓存,非阻塞I/O,有选择器
      <1>面向流:
           1.Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方;
           2.它不能前后移动流中的数据。
           3.如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区
      <2>面向缓存:
           1.数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动
           2.需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据
      <3> 阻塞I/O:
           1. Java IO的各种流是阻塞的
           2. 当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入
           3. 该线程在此期间不能再干任何事情了
      <4>非阻塞I/O:
           1.使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,
             而不是保持线程阻塞
           2.直至数据变的可以读取之前,该线程可以继续做其他的事情
           3. 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)
      <5>选择器(Selectors):
           1.允许一个单独的线程来监视多个输入通道
           2.这些通道里已经有可以处理的输入,或者选择已准备写入的通道
           3.这种选择机制,使得一个单独的线程很容易来管理多个通道
      <6>总结:
           1.传统阻塞式IO的瓶颈在于不能处理过多的连接
           2.阻塞式IO的线程的大部分时间都浪费在等待请求上了
           3.使用纯粹的NIO设计相较IO设计,数据处理也受到影响
           4.非阻塞IO处理连接是异步的
           5.非阻塞式IO,基于反应器模式,用于事件多路分离和分派的体系结构模式,可以利用线程池来处理。事件来了就处理,处理完了把线程归还
           6.非阻塞式IO的主要作用就是用来解决速度差异的
           7.NIO可使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂
 代码示例:
     读取数据:Name: Anna 
              Age: 25
              Email: [email protected] 
              Phone: 1234567890 
    (1)使用I/O;
           InputStream f = new FileInputStream("C:/java/hello");
           BufferedReader reader = new BufferedReader(new InputStreamReader(input));   
              String nameLine   = reader.readLine(); 
              String ageLine    = reader.readLine(); 
              String emailLine  = reader.readLine(); 
              String phoneLine  = reader.readLine(); 
    (2)使用NIO:
          ByteBuffer buffer = ByteBuffer.allocate(48);   
              int bytesRead = inChannel.read(buffer);   
              while(! bufferFull(bytesRead) ) {   
              bytesRead = inChannel.read(buffer);
     bufferFull()方法:必须跟踪有多少数据读入缓冲区,并返回真或假,这取决于缓冲区是否已满。如果缓冲区准备好被处理,那么表示缓冲区满了
     bufferFull()方法:
           1.扫描缓冲区,但必须保持在bufferFull()方法被调用之前状态相同
           2.如果没有,下一个读入缓冲区的数据可能无法读到正确的位置     
           3.如果缓冲区已满,它可以被处理。如果它不满,并且在实际案例中有意义,或许能处理其中的部分数据    

3.字节流与字符流使用场景:

(1)字节流:
             <1>可用于任何类型的对象,包括二进制对象
              <2>处理单元为1个字节, 操作字节和字节数组
              <3>不能直接处理Unicode字符,而字符流就可以
              <4>针对音频文件、图片、歌曲等用字节流
  (2)字符流:
              <1>字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串
              <2> txt、exel文件可以用字符流       

你可能感兴趣的:(java流专栏)