乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Java難點重構(gòu)-NIO

       印度阿三17 2019-09-08

      Java NIO 是從Java 1.4版本開始引入的一個新的 IO API,可以替代標(biāo)準(zhǔn)的 Java IO API。NIO與原來的 IO 有同樣的作用和目的,但是使用的方式完全不同,NIO 支持面向 緩沖區(qū) 的,基于 通道 的IO 操作,至于什么是緩沖區(qū),什么是通道,接下來我將會用大白話一一說明。總之,NIO 就是以更高效的方式進(jìn)行文件的讀寫操作。

      在學(xué)習(xí)本篇之前,首先你要對 IO 有一定的了解。當(dāng)然不了解的話,也可以看得哈哈,我會說的很通俗易懂。

      我們先看看 Java NIO 與 IO的主要區(qū)別:

      IONIO
      面向流(Stream Oriented)面向緩沖區(qū)(Buffer Oriented)
      阻塞IO(Blocking IO)非阻塞IO(Non Blocking IO)
      選擇器(Selectors)

      上面的什么 面向緩沖區(qū),又什么非阻塞IO,又是選擇器的,這些到底都啥啊,拍桌子。。。

      下面我會在本篇中對上面出現(xiàn)的概念及盲點進(jìn)行解析。

      IO與NIO的區(qū)別

      首先我們看看他們的區(qū)別

      為什么說IO是面向流,那流又是什么呢?

      在這里插入圖片描述

      我們先看上面的圖片。

      我們在學(xué)IO的時候,肯定都聽過這樣的例子,IO流就相當(dāng)于一條管道,它里面所有的操作都是單向的。如果要把文件中的數(shù)據(jù)拿到程序中,需要建立一條通道。想把程序中的數(shù)據(jù)存到文件中也需要建立一條通道。所以我們成io流是單向的。因為io管道里實際面對的是字節(jié)的流動,所以我們稱io流為面向流。

      那什么說NIO是面向緩沖區(qū)呢?

      在這里插入圖片描述

      你可以這樣想象,通道就相當(dāng)于與一條道路,緩沖區(qū)相當(dāng)于出租車,出租車上拉的是乘客,出租車可以上乘客也可以下乘客?;氐絅IO 上面,通道就是一條道路,他負(fù)責(zé)提供行駛的絕對條件,即就是有路啊,這樣出租車才能基本出行,而緩沖區(qū)在這里是出租車,出租車?yán)锩孀氖侨耍鲎廛囏?fù)責(zé)將乘客送到它要去的地方,當(dāng)然,出租車不受限制,他可以在任意地方。所以簡而言之,通道(Channel)負(fù)責(zé)傳輸,Buffer 負(fù)責(zé)存儲。

      在 NIO 里面,有兩個特別重要的東西,那就是 通道(Channel)緩沖區(qū)(Buffer)

      Java NIO系統(tǒng)的核心在于:通道 和緩沖區(qū)。通道表示 打開IO 設(shè)備(例如:文件,套接字)的鏈接。若需要使用 NIO 系統(tǒng),需要獲取用于鏈接 IO 的設(shè)備的通道以及用于容納數(shù)據(jù)的緩沖區(qū)。然后操作緩沖區(qū),對數(shù)據(jù)進(jìn)行處理。

      緩沖區(qū)(Buffer)

      /*緩沖區(qū)(Buffer):在Java Nio中負(fù)責(zé)數(shù)據(jù)的存取。緩沖區(qū)就是數(shù)組。用于存儲不同數(shù)據(jù)類型的數(shù)據(jù)
      *
      * 根據(jù)數(shù)據(jù)類型不同(boolean 除外),提供了相應(yīng)類型的緩沖區(qū)
      * ByteBuffer
      * CharBuffer
      * ...
      *
      * 上述緩沖區(qū)的管理方式幾乎一致,都是通過allocate() 獲取緩沖區(qū)
      *
      * 2/緩沖區(qū)存取數(shù)據(jù)的兩個核心方法:
      * put(): 存入數(shù)據(jù)到緩沖區(qū)中
      * get():獲取緩沖區(qū)中的數(shù)據(jù)
      *
      * 4.緩沖區(qū)中的4個核心屬性:
      * capacity:  容量,表示緩沖區(qū)中最大存儲數(shù)據(jù)的容量。一旦聲明不能改變,(底層就是數(shù)組)
      * limit:界限,表示緩沖區(qū)中可以操作數(shù)據(jù)的大小。(limit 后面的數(shù)據(jù)不能進(jìn)行讀寫)
      * position:位置,表示緩沖區(qū)中正在操作數(shù)據(jù)的位置。
      *
      * 5.直接緩沖區(qū)與非直接緩沖區(qū)
      * 非直接緩沖區(qū):通過 allocate()方法分配緩沖區(qū),將緩沖區(qū)建立在 JVM的內(nèi)存中
      * 直接緩沖區(qū):通過 allocateDirect() 方法分配直接緩沖區(qū),將緩沖區(qū)建立物理內(nèi)存中。
      *
      *  mark: 標(biāo)記,表示記錄當(dāng)前 postion 的位置,可以通過 reset() 恢復(fù)到mark 位置
      * position<=limit<=capacity
      * */
      
      public class Test {
          public static void main(String[] args) throws IOException {
              test2();
              test3();
          }
      
          private static void test1() {
              String str="Petterp";
              //1.分配一個指定大小的緩沖區(qū)
              ByteBuffer buf = ByteBuffer.allocate(1024);
      
              System.out.println("____________allocate_________");
              System.out.println(buf.position());
              System.out.println(buf.limit());
              System.out.println(buf.capacity());
      
              //2.利用 put() 存入數(shù)據(jù)到緩沖區(qū)中
              buf.put(str.getBytes());
              System.out.println("____________put_________");
              System.out.println(buf.position());
              System.out.println(buf.limit());
              System.out.println(buf.capacity());
      
              //3.切換讀取數(shù)據(jù)模式
              buf.flip();
              System.out.println("____________flip_________");
              System.out.println(buf.position());
              System.out.println(buf.limit());
              System.out.println(buf.capacity());
      
              //4.利用get() 讀取緩沖區(qū)的數(shù)據(jù)
              byte[] dst=new byte[buf.limit()];
              buf.get(dst);
              System.out.println(new String(dst,0,dst.length));
              System.out.println("____________get_________");
              System.out.println(buf.position());
              System.out.println(buf.limit());
              System.out.println(buf.capacity());
      
              //5.rewind()_可重復(fù)讀數(shù)據(jù)
              buf.rewind();
              System.out.println("____________rewind_________");
              System.out.println(buf.position());
              System.out.println(buf.limit());
              System.out.println(buf.capacity());
      
              //6.clear():  清空緩沖區(qū).(緩沖區(qū)數(shù)據(jù)還在,但是處于"被遺忘"狀態(tài)
              // 因為limit這些值全回到了初始狀態(tài),所以無法正確讀取數(shù)據(jù)。)
              buf.clear();
              System.out.println("____________clear_________");
              System.out.println(buf.position());
              System.out.println(buf.limit());
              System.out.println(buf.capacity());
          }
      
          private static void test2(){
              ByteBuffer buf =ByteBuffer.allocate(1024);
              String res="Petterp";
              buf.put(res.getBytes());
              buf.flip();
              //記錄指針位置為0
              buf.mark();
              System.out.println("____________mark記錄position位置_________");
              System.out.println(buf.position());
              byte[] bytes = new byte[buf.limit()];
              buf.get(bytes,0,3);
              System.out.println("打印get到的數(shù)據(jù)" new String(bytes,0,bytes.length));
              System.out.println("____________get之后position_________");
              System.out.println(buf.position());
      
              //會到記錄的指針位置
              buf.reset();
              System.out.println("____________reset之后position_________");
              System.out.println(buf.position());
      
              System.out.println("____________remaining判斷可操作數(shù)據(jù)長度_________");
              //判斷緩沖區(qū)是否還有剩余數(shù)據(jù)
              if (buf.hasRemaining()){
                  //獲取緩沖區(qū)中可以操作的數(shù)據(jù)長度
                  System.out.println(buf.remaining());
              }
          }
          
          private  static void test3(){
              ByteBuffer buf=ByteBuffer.allocateDirect(1024);
              //判斷是否是直接緩存區(qū)
              System.out.println(buf.isDirect());
          }
      }

      非直接緩沖區(qū)與緩沖區(qū)的區(qū)別

      非直接緩沖區(qū)在,建立在JVM內(nèi)存中,實際讀寫數(shù)據(jù)時,需要在 OS 和JVM之間進(jìn)行數(shù)據(jù)拷貝。

      img

      為什么不直接讓磁盤控制器把數(shù)據(jù)送到用戶控件的緩沖區(qū)呢?

      因為我們的硬件通常不能直接訪問用戶內(nèi)存空間。如果有一個程序需要讀寫磁盤空間,出于系統(tǒng)安全考慮,磁盤中的文件無法直接傳輸?shù)轿覀兂绦蛑?,它必須?jīng)過系統(tǒng)的內(nèi)核地址空間的緩存中,然后將內(nèi)核地址空間數(shù)據(jù)復(fù)制到用戶地址空間,這樣數(shù)據(jù)才可以傳輸?shù)轿覀兊膽?yīng)用程序。

      內(nèi)存映射空間

      直接緩沖區(qū),緩沖區(qū)建立在受操作系統(tǒng)管理的物理內(nèi)存中,OS和JVM直接通過這塊物理內(nèi)存進(jìn)行交互,沒有了中間的拷貝環(huán)節(jié)

      img

      但是直接緩沖區(qū)也有很多弊端:

      • 內(nèi)存消耗大(分配與銷毀不易控制)

      • 如果當(dāng)Java 程序?qū)?shù)據(jù)寫到物理內(nèi)存中后,這個時候我們就無法管理這塊內(nèi)存,只能由系統(tǒng)進(jìn)行控制。

      //1.利用通道完成文件的復(fù)制(非直接緩沖區(qū))
      public static void test1(){
          try{
              long l = System.currentTimeMillis();
              fis = new FileInputStream("D:1.zip");
              fos = new FileOutputStream("D:2.zip");
      
              //1.獲取通道
              inChannel = fis.getChannel();
              outChanel1 = fos.getChannel();
      
              //2.分配指定大小的緩沖區(qū)
              ByteBuffer buf = ByteBuffer.allocate(1024);
      
              //3.將通道中的數(shù)據(jù)存入緩沖中
              while (inChannel.read(buf)!=-1){
                  buf.flip();//切換讀取數(shù)據(jù)模式
                  //將緩沖區(qū)中的數(shù)據(jù)寫入通道中
                  outChanel1.write(buf);
                  buf.clear();  //清空緩沖區(qū)
              }
              long l2 = System.currentTimeMillis();
              System.out.println("時間" (l2-l));
          }catch (IOException e){
              e.printStackTrace();
          }finally {
              if (outChanel1 != null) {
                  try {
                      outChanel1.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
              if (inChannel != null) {
                  try {
                      inChannel.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
              if (fos != null) {
                  try {
                      fos.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
              if (fis != null) {
                  try {
                      fis.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
      //使用直接緩沖區(qū)完成文件的復(fù)制(內(nèi)存映射文件)
      @RequiresApi(api = Build.VERSION_CODES.O)
      public static  void test2(){
          try {
              long l = System.currentTimeMillis();
              //第一個參數(shù)是路徑,第二個參數(shù)是模式
              FileChannel inchannel=FileChannel.open(Paths.get("D:demo.txt"),StandardOpenOption.READ);
              FileChannel outChannel=FileChannel.open(Paths.get("D:demo2.txt"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
      
              //內(nèi)存映射文件
              MappedByteBuffer inMapBuf = inchannel.map(FileChannel.MapMode.READ_ONLY, 0, inchannel.size());
              MappedByteBuffer outMapBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inchannel.size());
              //直接對緩沖區(qū)進(jìn)行數(shù)據(jù)的讀寫操作
              byte[] bytes = new byte[inMapBuf.limit()];
              inMapBuf.get(bytes);
              outMapBuf.put(bytes);
      
              inchannel.close();
              outChannel.close();
              long l2 = System.currentTimeMillis();
              System.out.println("時間" (l2-l));
          } catch (IOException e) {
              e.printStackTrace();
          }
      }

      通道(Channel)

      通道(Channel) 由java.nio.channels 包定義的。Channel 表示 IO 源于目標(biāo)打開的鏈接。Channel 類似于傳統(tǒng)的流,只不過 Channel 本身不能直接訪問數(shù)據(jù),Channel 只能與 Buffer進(jìn)行交互。

      通道的主要實現(xiàn)類:
      * Java.nio/channels.Channel 接口
      *       FileChannel     本地文件傳輸
      *       SocketChannel       網(wǎng)絡(luò)傳輸
      *       ServerSocketChannel
      *       DatagramChannel
      		獲取通道
      * -1. Java 鎮(zhèn)對支持通道的類提供了 getChannel() 方法
      *       本地IO
      *       FiledInputStream/FileOutputStream
      *       RandomAccessFile
      *
      *       網(wǎng)絡(luò)IO
      *       Socket
      *       ServerSocket
      *       DatagramSocket
      * -2. 在 JDK 1.7中的 NIO.2 針對各個通道提供了靜態(tài)方法 open()
      *
      * -3. 在 jdk 1.7中的 NIO.2 的 Files 工具類 newByteChannel()
      //1.利用通道完成文件的復(fù)制(非直接緩沖區(qū))
      public static void test1(){
          try{
              long l = System.currentTimeMillis();
              fis = new FileInputStream("D:1.zip");
              fos = new FileOutputStream("D:2.zip");
      
              //1.獲取通道
              inChannel = fis.getChannel();
              outChanel1 = fos.getChannel();
      
              //2.分配指定大小的緩沖區(qū)
              ByteBuffer buf = ByteBuffer.allocate(1024);
      
              //3.將通道中的數(shù)據(jù)存入緩沖中
              while (inChannel.read(buf)!=-1){
                  buf.flip();//切換讀取數(shù)據(jù)模式
                  //將緩沖區(qū)中的數(shù)據(jù)寫入通道中
                  outChanel1.write(buf);
                  buf.clear();  //清空緩沖區(qū)
              }
              long l2 = System.currentTimeMillis();
              System.out.println("時間" (l2-l));
          }catch (IOException e){
              e.printStackTrace();
          }finally {
              if (outChanel1 != null) {
                  try {
                      outChanel1.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
              if (inChannel != null) {
                  try {
                      inChannel.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
              if (fos != null) {
                  try {
                      fos.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
              if (fis != null) {
                  try {
                      fis.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      }

      通道之間的數(shù)據(jù)傳輸

      • transferFrom()

      • transferTo()

        public static void test3(){
              try {
                  FileChannel inchannel=FileChannel.open(Paths.get("D:demo.txt"),StandardOpenOption.READ);
                  FileChannel outChannel=FileChannel.open(Paths.get("D:Demop.txt"),StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
                  if (outChannel != null) {
      //                inchannel.transferTo(0,inchannel.size(),outChannel);
                      outChannel.transferFrom(inchannel,0,inchannel.size());
                      inchannel.close();
                      outChannel.close();
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
          }

      分散(Scatter)與聚集(Gather)

      [外鏈圖片轉(zhuǎn)存失敗(img-FzYapv7x-1567945086087)(C:\Users\Pettepr\AppData\Roaming\Typora\typora-user-images\1554968002880.png)]

      • 分散讀取(Scattering Reads):將通道中的數(shù)據(jù)分散到多個緩沖區(qū)中

      • 聚集寫入(Gathering Writes):將多個緩沖區(qū)的數(shù)據(jù)聚集到通道中

        //分散和聚集
        public static void test4() throws IOException {
        RandomAccessFile rafi=new RandomAccessFile(“D:demo1.txt”,“rw”);

          //1.獲取通道
          FileChannel channel=rafi.getChannel();
          
          //2.分配指定大小的緩沖區(qū)
          ByteBuffer buf1 =ByteBuffer.allocate(100);
          ByteBuffer buf2 = ByteBuffer.allocate(1024);
          
          //3.分散讀取
          ByteBuffer[] bufs={buf1,buf2};
          channel.read(bufs);
          for (ByteBuffer byteBuffer:bufs){
              byteBuffer.flip();
          }
          System.out.println(new String(bufs[0].array(),0,bufs[0].limit()));
          System.out.println("----------");
          System.out.println(new String(bufs[1].array(),0,bufs[1].limit()));
          
          //4.聚集寫入
          RandomAccessFile raf2=new RandomAccessFile("D:demo2.txt","rw");
          FileChannel channel2=raf2.getChannel();
          
          channel2.write(bufs);

        }

      字符集:Charset

      計算機里的文件,數(shù)據(jù),圖片文件只是一種表面現(xiàn)象,所有文件在底層都是二進(jìn)制文件,即全部都是字節(jié)碼。

      對于文本文件而言,之所以可以看到一個個的字符,這完全是因為系統(tǒng)將底層的二進(jìn)制序列轉(zhuǎn)換成字符的緣故。在這個過程中涉及兩個概念:編碼(Encode) 和解碼 (Decode),通常而言,把明文的字符序列轉(zhuǎn)換成計算機理解的二進(jìn)制序列稱為編碼,把二進(jìn)制序列轉(zhuǎn)換成普通人能看懂的明文字符串稱為解碼。

      Java 默認(rèn)視同 Uniocde 字符集,但很多操作系統(tǒng)并不適用Unicode 字符集,那么當(dāng)從系統(tǒng)中讀取數(shù)據(jù)到 Java程序中時,就可能出現(xiàn)亂碼等問題。

      JDK1.4 提供了 Charset來處理字節(jié)序列和字符序列(字符串)之間的轉(zhuǎn)換關(guān)系,該類包含了用于創(chuàng)建解碼器和編碼器的方法。還提供了獲取 Charset所支持字符集的方法,Charset類是不可變的。

      • 編碼:字符串 -> 字節(jié)數(shù)組

      • 解碼:字節(jié)數(shù)組 -> 字符串

      public static void test6() throws CharacterCodingException {
      //字符集
      Charset cs1 = Charset.forName(“GBK”);

          //查看Java支持的字符集格式
          private static void test5(){
              SortedMap<String, Charset> map = Charset.availableCharsets();
      
              Set<Map.Entry<String, Charset>> set = map.entrySet();
              for (Map.Entry<String,Charset> entry: set){
                  System.out.println(entry.getKey() "=" entry.getValue());
              }
          }
      
      	//獲取編碼器
          CharsetEncoder ce = cs1.newEncoder();
      
          //獲取解碼器
          CharsetDecoder cd=cs1.newDecoder();
          CharBuffer cBuf = CharBuffer.allocate(1024);
          cBuf.put("我是Petterp");
          cBuf.flip();
      
          //編碼
          System.out.println(cBuf.limit());
          ByteBuffer bBuf = ce.encode(cBuf);
          for (int i=0;i<11;i  ){
              System.out.println(bBuf.get());
          }
      
          bBuf.flip();
          CharBuffer cBuf2 = cd.decode(bBuf);
          System.out.println(cBuf2.toString());
      
          System.out.println("__________");
          //獲得解碼器
          Charset cs2=Charset.forName("GBK");
          bBuf.flip();
          CharBuffer cBuf3 = cs2.decode(bBuf);
          System.out.println(cBuf3.toString());
      }

      二進(jìn)制序列和字符之間如何對應(yīng)呢?

      為了解決二進(jìn)制序列與字符之間的對應(yīng)關(guān)系,這就需要字符集了。所謂字符集,就是為每個字符編個號碼而已。任何人都可以制定自己獨有的字符集明知要為每個字符編個號碼即可。當(dāng)然,如果每個人都制定自己獨有的字符集,那程序就沒法交流了。

      來源:https://www./content-1-443851.html

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多