Java NIO 常用歸納

前言: 之前的文章《Java文件IO常用歸納》主要寫了Java 標(biāo)準(zhǔn)IO要注意的細(xì)節(jié)和技巧,由于網(wǎng)上各種學(xué)習(xí)途徑,所以并沒有詳細(xì)示例等。本文主要簡單看看java的NIO庫的用法,并做個小歸納,可以對比標(biāo)準(zhǔn)IO參考一下。

NIO概述


(一)背景

NIO(New IO),在Java 1.4引入的一個新的IO API。【可替代標(biāo)準(zhǔn)IO API】

(二)工作方式

  • Channels and Buffers(通道和緩沖區(qū)):標(biāo)準(zhǔn)的IO基于字節(jié)流和字符流進(jìn)行操作的,而NIO是基于通道(Channel)和緩沖區(qū)(Buffer)進(jìn)行操作,數(shù)據(jù)總是從通道讀取到緩沖區(qū)中,或者從緩沖區(qū)寫入到通道中。
  • Asynchronous IO(異步IO):Java NIO可以讓你異步的使用IO,例如:當(dāng)線程從通道讀取數(shù)據(jù)到緩沖區(qū)時,線程還是可以進(jìn)行其他事情。當(dāng)數(shù)據(jù)被寫入到緩沖區(qū)時,線程可以繼續(xù)處理它。從緩沖區(qū)寫入通道也類似。
  • Selectors(選擇器):Java NIO引入了選擇器的概念,選擇器用于監(jiān)聽多個通道的事件(比如:連接打開,數(shù)據(jù)到達(dá))。因此,單個的線程可以監(jiān)聽多個數(shù)據(jù)通道

NIO 與 標(biāo)準(zhǔn)IO 的區(qū)別


  1. IO面向Stream(流),NIO面向Buffer(緩存)
  • 面向Stream:每次從流中讀取一個或多個字節(jié),直到讀取所有字節(jié),并沒有緩存字節(jié)的地方。不能前后移動流中的數(shù)據(jù)(因?yàn)槿绻昂笠苿訌牧髦凶x取的數(shù)據(jù),就需先將其緩存到一個緩存區(qū)中)。
  • 面向Buffer【更靈活】:數(shù)據(jù)讀取到一個稍后處理的緩沖區(qū),需要時即可在緩沖區(qū)中前后移動(注意:移動前首先需要檢查是否該緩沖區(qū)中包含你需要處理的數(shù)據(jù))。需要確保當(dāng)更多數(shù)據(jù)讀入緩沖區(qū)時,不會覆蓋掉區(qū)中原有的尚未處理的數(shù)據(jù)。
  1. IO流都是阻塞的,而NIO有非阻塞模式
  • IO的流:當(dāng)一個線程threadA使用IO調(diào)用read()/write()操作時,threadA被阻塞,直到一些數(shù)據(jù)被讀取或?qū)懭胪瓿桑诉^程中threadA不能做任何事。
  • NIO的非阻塞模式:
    • 【非阻塞讀】線程threadA從某channel發(fā)送請求讀取數(shù)據(jù)時,threadA僅能得到目前可用的數(shù)據(jù),若目前沒有可用數(shù)據(jù),那么threadA不會獲取任何數(shù)據(jù)并可以先做別的事情,而不是保持阻塞,直到有可用數(shù)據(jù)在這個通道出現(xiàn)。
    • 【非阻塞寫】一個線程請求寫入一些數(shù)據(jù)到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。
    • 【應(yīng)用】線程通常將非阻塞IO的空閑時間用于在其它通道上執(zhí)行IO操作,所以一個單獨(dú)的線程現(xiàn)在可以管理多個輸入和輸出channels。
  1. NIO獨(dú)有選擇器(Selector)

常用API總結(jié)


(一)核心接口與類關(guān)系圖解與分析

IO與NIO 接口與類關(guān)系圖.png
  • 首先,圖中有顏色背景的,是NIO中最重要的幾個概念:Selector選擇器、Channel通道、Buffer緩沖區(qū)、Pipe管道,他們幾個的良好分工合作,才有了NIO。
  • 這四者之間的關(guān)系:
    1. Channel就是管理文件/數(shù)據(jù)傳輸出入口的地方【水龍頭 / 百姓家】。文件數(shù)據(jù)/客戶端數(shù)據(jù)/服務(wù)端數(shù)據(jù)【水】 在需要開始傳遞時【水要從水管出來】,就先通過Channel【水龍頭打開】;
    2. Buffer用于暫存從Channel傳來的數(shù)據(jù)【水廠】,從而提供外部選擇和調(diào)整數(shù)據(jù)集合的能力【管理桶裝水】。當(dāng)數(shù)據(jù)量大于Buffer的最大裝載量時,Buffer中原有的數(shù)據(jù)將被覆蓋。Channel的實(shí)現(xiàn)類通過read(Buffer)等方法將數(shù)據(jù)保存到Buffer中,又通過write(Buffer)將Buffer中存儲的數(shù)據(jù)寫入到通道中【桶裝水最終會按需派送到各個百姓家中】。
    3. Selector 用于控制和管理一或多個Channel的數(shù)據(jù)流動【桶裝水管理搬運(yùn)工,負(fù)責(zé)自己管理的若干水龍頭,哪里有山泉水要出了,就關(guān)好別的水龍頭,拿水桶去裝水并打上備注即立刻回去管理水龍頭,待會桶裝水會被同事送到水廠】。
    4. Pipe 用于在兩個線程之間傳輸數(shù)據(jù)。其內(nèi)部依賴著兩個Channel對象,分別用于數(shù)據(jù)的收和發(fā)。
  • Channel的相關(guān)實(shí)現(xiàn)類:FileChannel、SocketChannel與ServerSocketChannel、DatagramChannel,分別對應(yīng):“文件操作通道”、“TCP通信操作通道”、“UDP通信操作通道”。這幾個實(shí)現(xiàn)類中,除了FileChannel不能進(jìn)入非阻塞狀態(tài),其他實(shí)現(xiàn)類都可以進(jìn)入非阻塞狀態(tài)。
  • 所謂 阻塞狀態(tài)(BIO)和非阻塞狀態(tài)(NBIO),上述已說。
  • 圖中的“分散”和“聚集”,分別指:
    1. 分散(scatter):【讀】從channel中讀取數(shù)據(jù)到buffer時,一個channel可以將數(shù)據(jù)緩存到多個buffer中。(read(Buffer[]);:開始存儲數(shù)據(jù)到第一個buffer中,當(dāng)一個buffer被寫滿后,channel緊接著向下一個buffer中寫(按照緩沖區(qū)數(shù)組的排列順序))
    2. 聚合(gather):【寫】將buffer中的數(shù)據(jù)寫入到channel中時,可以連續(xù)將多個buffer中的數(shù)據(jù)依次寫入。
  • Buffer的相關(guān)實(shí)現(xiàn)類:
    1. 各種基本數(shù)據(jù)類型的Buffer,如:ByteBuffer、IntBuffer、ShortBuffer、LongBuffer、DoubleBuffer等,對應(yīng)初始化時設(shè)定的capacity(緩沖區(qū)大小)即:最多同時緩存XX個byte、int、short、long、double數(shù)據(jù)。
    2. MappedByteBuffer:表示內(nèi)存映射文件
  • Channel的原理:打開文件并構(gòu)建符合NIO讀寫規(guī)則的通信橋梁口,對于網(wǎng)絡(luò)TCP和UDP連接則是構(gòu)建一個連接到特定IP特定端口的橋梁,并準(zhǔn)備數(shù)據(jù)發(fā)送與數(shù)據(jù)接收。Channel與Stream(流)不同的是,Stream是單向傳遞數(shù)據(jù)的,而Channel是可讀取并可寫入的,具有雙向性,并且更容易配合緩沖區(qū)來靈活獲取數(shù)據(jù)。
  • Buffer的原理:Buffer 實(shí)際上是指向一個占N個單位的內(nèi)存空間的對象,它本身就代表了一塊內(nèi)存區(qū)域。Buffer有兩個模式:讀模式和寫模式(初始模式),通過silp()方法可以切換狀態(tài)。而Buffer內(nèi)部有三個成員屬性用于共同維護(hù)這塊內(nèi)存區(qū)域,他們分別是:capacity【buffer總大小】、position【寫模式:下一個可插入數(shù)據(jù)的位置,初始為0,最大是cap-1;讀模式:下一個可讀取數(shù)據(jù)的位置,初始為0,最大cap-1】、limit【讀模式:最多可寫入limit=cap個數(shù)據(jù);寫模式:可讀取第limit=position(之前寫入)的所有數(shù)據(jù)】。

(二)常用API清單

  • Channel通用方法:

    • read(Buffer):int 將數(shù)據(jù)從channel讀取到buffer中【讀channel,寫buffer】
    • read(Buffer[]):int 將數(shù)據(jù)從channel讀取到buffer數(shù)組中
    • write(Buffer):int 將數(shù)據(jù)從buffer寫入到channel中【讀buffer,寫channel】
    • write(Buffer[]):int 將數(shù)據(jù)從buffer數(shù)組寫入到channel中
  • Buffer子類(以下以ByteBuffer為例子)通用方法:

    • 構(gòu)造方法ByteBuffer(N:int) 設(shè)定緩沖區(qū)大小為N個byte大小的空間
    • get():byte[]:讀取buffer中的所有數(shù)據(jù)
    • put(byte[]):void:數(shù)據(jù)寫入buffer【功能和從channel中讀取數(shù)據(jù)到buffer中一樣】
    • filp():void:切換模式(寫模式->讀模式)
    • rewind():void:重讀buffer中的數(shù)據(jù)(position重置為0)
    • clear():void:清空。重置所有指針,不刪除數(shù)據(jù)!!(position=0,limit=capacity,重新供寫入)
    • compact():void:半清空,保留仍未讀取的數(shù)據(jù)。(position=最后一個未讀單元之后的位置,limit=cap,重新供寫入)
    • mark():標(biāo)記時刻A的當(dāng)前pos【與reset()一起用】
    • reset():回到時刻A時標(biāo)記的pos位置。
    • close():關(guān)閉并釋放channel對象。
  • FileChannel

    • 獲取FileChannel對象:
      RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw");
      FileChannel fileChannel = accessFile.getChannel();
      
  • SocketChannel

    • 創(chuàng)建SocketChannel對象:
      SocketChannel sc  = SocketChannel.open();
      
  • 設(shè)置非阻塞IO狀態(tài):

    sc.configureBlocking(false);
    
  • 開始打開連接

    sc.connect(new InetSocketAddress("http://jianshu.com", 80));
    
  • 非阻塞狀態(tài)下,成功連接前,干別的事

    sc.configureBlocking(false);
    ///...............
    while(!sc.finishConnect()){   ////do other sth.... }
    
  • 保證非阻塞IO狀態(tài)下,read()過程不會read 空數(shù)據(jù)
    sc.configureBlocking(false);
    ///...............

    while((int len = sc.read(buf))==0){   ////do other sth.... }
    
  • 保證非阻塞IO狀態(tài)下,write()過程不會write空數(shù)據(jù)
    sc.configureBlocking(false);
    ///...............

    while((int len = sc.write(buf))==0){   ////do other sth.... }
    
  • ServerSocketChannelDatagramChannelSelectorPipe的寫法與方法說明,由于篇幅原因,可以參考下文的示例。

(三)文件IO --- FileChannel

  1. 讀文件
 public static byte[] readBytes(String fileName) {
      try {
          ///獲取對應(yīng)文件的FileChannel對象
          RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw");
          FileChannel fileChannel = accessFile.getChannel();
          /// 創(chuàng)建一個緩沖區(qū)(大小為48byte)
          ByteBuffer byteBuffer = ByteBuffer.allocate(48);
          StringBuilder builder = new StringBuilder();

          int bytesRead = fileChannel.read(byteBuffer);
          while (bytesRead != -1) {
              System.out.println("Read " + bytesRead);
              ///翻轉(zhuǎn)buffer
              byteBuffer.flip();
              ///每次讀取完之后,輸出緩存中的內(nèi)容
              while (byteBuffer.hasRemaining()) {
                  System.out.println((char) byteBuffer.get());
                  builder.append((char) byteBuffer.get());
              }
              ///然后清空緩存區(qū)
              byteBuffer.clear();
              ///重新再讀數(shù)據(jù)到緩存區(qū)中
              bytesRead = fileChannel.read(byteBuffer);
          }

          accessFile.close();
          return builder.toString().getBytes();
      } catch (IOException e) {
          e.printStackTrace();
          return null;
      }
  }
  1. 寫入文件
  public static void writeBytes(String fileName, byte[] data) {
      try {
          RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw");
          FileChannel channel = accessFile.getChannel();
          ByteBuffer buffer = ByteBuffer.allocate(48);
          buffer.put(data);
          channel.write(buffer);
      } catch (FileNotFoundException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      }
  }
  1. 通道間內(nèi)容傳輸
  /**
   * channel 間的傳輸
   *
   * @param sFileName 源文件
   * @param dFileName 目標(biāo)文件
   */
  public static void channelToChannel(String sFileName, String dFileName) {
      try {
          RandomAccessFile sAccess = new RandomAccessFile(sFileName, "rw");
          RandomAccessFile dAccess = new RandomAccessFile(dFileName, "rw");
          FileChannel sChannel = sAccess.getChannel();
          FileChannel dChannel = dAccess.getChannel();

          long pos = 0;
          long sCount = sChannel.size();
          long dCount = dChannel.size();
//            dChannel.transferFrom(sChannel,pos,sCount);//dChannel 必須是FileChannel
          sChannel.transferTo(pos, dCount, dChannel);///sChannel 是FileChannel
      } catch (FileNotFoundException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      }
  }

(四)TCP通信 --- SocketChannel

  1. 基本的C/S TCP通信
  • Client客戶端寫法:

/**
* Client SocketChannel 寫法:
*/
public static void client(String fileName) {
SocketChannel sc = null;
try {
// 創(chuàng)建一個SocketChannel 通道
////TODO: FileChannel無法設(shè)置為非阻塞模式,它總是運(yùn)行在阻塞模式下
sc = SocketChannel.open();
///TODO:非阻塞IO狀態(tài)下,socketChannel就可以異步地執(zhí)行read()、write()、connect()方法了
sc.configureBlocking(false);
sc.connect(new InetSocketAddress("http://jianshu.com", 80));

        while (!sc.finishConnect()) {///保證在connect成功之前,可以做別的事情
            //做點(diǎn)別的事。。。。。
        }
        while((int len  = sc.read(xxx))==0){ ///保證NBIO下,read數(shù)據(jù)不會read空
             // 做別的事。。。
        }
        while((int len  = sc.write(xxx))==0){///保證NBIO下,write數(shù)據(jù)不會write空
            // 做別的事。。。
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (sc != null) {
                sc.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```
  • Server服務(wù)端寫法:

/**
* 關(guān)于:ServerSocketChannel
*/
public static void serverSocketChannel() {
ServerSocketChannel serverSocketChannel = null;
try {
///打開
serverSocketChannel = ServerSocketChannel.open();
///連接并開始監(jiān)聽TCP 9999端口
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
///TODO:可設(shè)置非阻塞狀態(tài)(需要檢查accept到的socketChannel是否為null)
serverSocketChannel.configureBlocking(false);
while (true) {
SocketChannel socketChannel =
serverSocketChannel.accept();
//TODO: 非阻塞時需要考慮返回的socketChannel對象是否為null
if(socketChannel != null){
//do something with socketChannel...
}
//do something with socketChannel...
}

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (serverSocketChannel != null)
            try {
                serverSocketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    }
}
```
  1. 配合Selector,簡化SocketChannel在非阻塞IO狀態(tài)下的Null情況監(jiān)測邏輯
/**
   * 關(guān)于 選擇器 和 SocketChannel 的配合使用
   */
  public static void selectorAndSocketChannel(String fileName) {
      SocketChannel sc1 = null;
      SocketChannel sc2 = null;
      SocketChannel sc3 = null;
      try {
          // 創(chuàng)建幾個SocketChannel 通道
          ////TODO: FileChannel無法設(shè)置為非阻塞模式,它總是運(yùn)行在阻塞模式下
          sc1 = SocketChannel.open();
          sc2 = SocketChannel.open();
          sc3 = SocketChannel.open();
          ///TODO:非阻塞IO狀態(tài)下,socketChannel就可以異步地執(zhí)行read()、write()、connect()方法了
          sc1.configureBlocking(false);
          sc2.configureBlocking(false);
          sc3.configureBlocking(false);
          sc1.connect(new InetSocketAddress("http://jenkov.com", 80));
          sc2.connect(new InetSocketAddress("http://jenkov.com", 80));
          sc3.connect(new InetSocketAddress("http://jenkov.com", 80));

          // 創(chuàng)建Selector
          Selector selector = Selector.open();
          // 注冊channels
          SelectionKey key1 = sc1.register(selector, SelectionKey.OP_READ);
          SelectionKey key2 = sc2.register(selector, SelectionKey.OP_READ);
          SelectionKey key3 = sc3.register(selector, SelectionKey.OP_READ);
          // 持續(xù)監(jiān)控selector的四個事件(接受、連接、讀、寫)是否就緒
          while (true) {
              int readyChannels = selector.select();
              if (readyChannels == 0) continue;
              Set selectedKeys = selector.selectedKeys();
              Iterator keyIterator = selectedKeys.iterator();
              while (keyIterator.hasNext()) {
                  SelectionKey key = (SelectionKey) keyIterator.next();
                  if (key.isAcceptable()) {
                      // a connection was accepted by a ServerSocketChannel.
                      ///我的這個連接請求被服務(wù)端接受了
                  } else if (key.isConnectable()) {
                      // a connection was established with a remote server.
                      ///已經(jīng)連接上
                  } else if (key.isReadable()) {
                      // a channel is ready for reading
                      ///可讀數(shù)據(jù)
                  } else if (key.isWritable()) {
                      // a channel is ready for writing
                      ///可寫數(shù)據(jù)
                  }
              }
              keyIterator.remove();
          }

      } catch (IOException e) {
          e.printStackTrace();
      } finally {
          try {
              if (sc1 != null) {
                  sc1.close();
              }
              if (sc2 != null) {
                  sc2.close();
              }
              if (sc3 != null) {
                  sc3.close();
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }

(五)UDP通信 --- DatagramChannel

  1. 收發(fā)UDP數(shù)據(jù)包 的簡單示例
   /**
   * 關(guān)于:DatagramChannel
   * UDP 無連接網(wǎng)絡(luò)協(xié)議
   * 發(fā)送和接收的是數(shù)據(jù)包
   */
  public static void datagramChannel() {
      DatagramChannel datagramChannel = null;
      try {
          ///打開
          datagramChannel = DatagramChannel.open();
          ///連接并開始監(jiān)聽UDP 9999端口
          datagramChannel.socket().bind(new InetSocketAddress(9999));
          // 接收數(shù)據(jù)包(receive()方法會將接收到的數(shù)據(jù)包內(nèi)容復(fù)制到指定的Buffer. 如果Buffer容不下收到的數(shù)據(jù),多出的數(shù)據(jù)將被丟棄。 )
          ByteBuffer buf = ByteBuffer.allocate(48);
          buf.clear();
          datagramChannel.receive(buf);
          // 發(fā)送數(shù)據(jù) send()
          String sendMsg = "要發(fā)送的數(shù)據(jù)";
          ByteBuffer sendBuf = ByteBuffer.allocate(48);
          sendBuf.clear();
          sendBuf.put(sendMsg.getBytes());
          sendBuf.flip();
          datagramChannel.send(sendBuf,new InetSocketAddress("xxxxx",80));

          // TODO: 連接到特定的地址(鎖住DatagramChannel ,讓其只能從特定地址收發(fā)數(shù)據(jù) 因?yàn)閁DP無連接,本身沒有真正的連接產(chǎn)出)
          datagramChannel.connect(new InetSocketAddress("jenkov.com", 80));
          ///連接后,也可以使用Channal 的read()和write()方法,就像在用傳統(tǒng)的通道一樣。只是在數(shù)據(jù)傳送方面沒有任何保證

      } catch (IOException e) {
          e.printStackTrace();
      } finally {
          if (datagramChannel != null)
              try {
                  datagramChannel.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
      }
  }

(六)NIO管道(Pipe)

首先,什么是NIO管道,下圖可以看出其內(nèi)部結(jié)構(gòu)和功能特點(diǎn):

  • NIO Pipe,是兩個線程之間的單向連接通道(讀下圖可知)
  • Pipe類內(nèi)部有兩個成員屬性,分別是:
    • Pipe.SinkChannel:數(shù)據(jù)入口通道
    • Pipe.SourceChannel:數(shù)據(jù)出口通道
  • 整體原理:ThreadA中獲取的數(shù)據(jù)通過SinkChannel傳入(寫入)管道,當(dāng)ThreadB要讀取ThreadA的數(shù)據(jù),則通過管道的SourceChannel傳出(讀取)數(shù)據(jù)。
    NIO Pipe原理圖解
  1. 示例: 管道傳輸數(shù)據(jù)
/**
   * 關(guān)于NIO管道(Pipe)
   * 定義:2個線程之間的單向數(shù)據(jù)連接
   */
  public static void aboutPipe(){
      Pipe pipe=null;
      try {
          /// 打開管道
          pipe = Pipe.open();
          ///TODO: 一、 向管道寫入數(shù)據(jù)
          /// 訪問Pipe.sinkChannel,向Pipe寫入數(shù)據(jù)
          /// 首先,獲取Pipe.sinkChannel
          Pipe.SinkChannel sinkChannel = pipe.sink();
          /// 然后,調(diào)用write(),開始寫入數(shù)據(jù)
          String newData = "New String to write to file..." + System.currentTimeMillis();
          ByteBuffer buf = ByteBuffer.allocate(48);
          buf.clear();
          buf.put(newData.getBytes());
          buf.flip();
          while(buf.hasRemaining()){
          sinkChannel.write(buf);
          }
          // TODO: 二、讀取管道中的數(shù)據(jù)
          // 首先,獲取Pipe.sourceChannel
          Pipe.SourceChannel sourceChannel = pipe.source();
          /// 讀取數(shù)據(jù)到buffer
          ByteBuffer buf2 = ByteBuffer.allocate(48);
          int bytesRead = sourceChannel.read(buf2);
      } catch (IOException e) {
          e.printStackTrace();
      }
  }

附錄:java.nio包相關(guān)接口與類圖一覽


Buffer類圖
java.nio.channels 相關(guān)接口關(guān)系圖
java.nio.channels 相關(guān)類圖
java.nio.charset 相關(guān)類圖(這個包主要做不同編碼格式的加解密等工作)

參考文章


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內(nèi)容

  • Java NIO(New IO)是從Java 1.4版本開始引入的一個新的IO API,可以替代標(biāo)準(zhǔn)的Java I...
    JackChen1024閱讀 7,564評論 1 143
  • (轉(zhuǎn)載說明:本文非原創(chuàng),轉(zhuǎn)載自http://ifeve.com/java-nio-all/) Java NIO: ...
    柳岸閱讀 825評論 0 3
  • java nio Java的IO體系:舊IO新IO:nio,用ByteBuffer和FileChannel讀寫ni...
    則不達(dá)閱讀 847評論 0 2
  • 轉(zhuǎn)自 http://www.ibm.com/developerworks/cn/education/java/j-...
    抓兔子的貓閱讀 2,329評論 0 22
  • (轉(zhuǎn)載說明:本文非原創(chuàng),轉(zhuǎn)載自http://ifeve.com/java-nio-all/) Java NIO: ...
    數(shù)獨(dú)題閱讀 814評論 0 3