日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

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

 印度阿三17 2019-09-08

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

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

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

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

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

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

IO與NIO的區(qū)別

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

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

在這里插入圖片描述

我們先看上面的圖片。

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

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

在這里插入圖片描述

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

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

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

緩沖區(qū)(Buffer)

/*緩沖區(qū)(Buffer):在Java Nio中負(fù)責(zé)數(shù)據(jù)的存取。緩沖區(qū)就是數(shù)組。用于存儲(chǔ)不同數(shù)據(jù)類型的數(shù)據(jù)
*
* 根據(jù)數(shù)據(jù)類型不同(boolean 除外),提供了相應(yīng)類型的緩沖區(qū)
* ByteBuffer
* CharBuffer
* ...
*
* 上述緩沖區(qū)的管理方式幾乎一致,都是通過allocate() 獲取緩沖區(qū)
*
* 2/緩沖區(qū)存取數(shù)據(jù)的兩個(gè)核心方法:
* put(): 存入數(shù)據(jù)到緩沖區(qū)中
* get():獲取緩沖區(qū)中的數(shù)據(jù)
*
* 4.緩沖區(qū)中的4個(gè)核心屬性:
* capacity:  容量,表示緩沖區(qū)中最大存儲(chǔ)數(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.分配一個(gè)指定大小的緩沖區(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)
        // 因?yàn)閘imit這些值全回到了初始狀態(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());

        //會(huì)到記錄的指針位置
        buf.reset();
        System.out.println("____________reset之后position_________");
        System.out.println(buf.position());

        System.out.println("____________remaining判斷可操作數(shù)據(jù)長(zhǎng)度_________");
        //判斷緩沖區(qū)是否還有剩余數(shù)據(jù)
        if (buf.hasRemaining()){
            //獲取緩沖區(qū)中可以操作的數(shù)據(jù)長(zhǎng)度
            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í)際讀寫數(shù)據(jù)時(shí),需要在 OS 和JVM之間進(jìn)行數(shù)據(jù)拷貝。

img

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

因?yàn)槲覀兊挠布ǔ2荒苤苯釉L問用戶內(nèi)存空間。如果有一個(gè)程序需要讀寫磁盤空間,出于系統(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)存中后,這個(gè)時(shí)候我們就無法管理這塊內(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("時(shí)間" (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();
        //第一個(gè)參數(shù)是路徑,第二個(gè)參數(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());
        //直接對(duì)緩沖區(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("時(shí)間" (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)行交互。

通道的主要實(shí)現(xiàn)類:
* Java.nio/channels.Channel 接口
*       FileChannel     本地文件傳輸
*       SocketChannel       網(wǎng)絡(luò)傳輸
*       ServerSocketChannel
*       DatagramChannel
		獲取通道
* -1. Java 鎮(zhèn)對(duì)支持通道的類提供了 getChannel() 方法
*       本地IO
*       FiledInputStream/FileOutputStream
*       RandomAccessFile
*
*       網(wǎng)絡(luò)IO
*       Socket
*       ServerSocket
*       DatagramSocket
* -2. 在 JDK 1.7中的 NIO.2 針對(duì)各個(gè)通道提供了靜態(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("時(shí)間" (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ù)分散到多個(gè)緩沖區(qū)中

  • 聚集寫入(Gathering Writes):將多個(gè)緩沖區(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

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

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

Java 默認(rèn)視同 Uniocde 字符集,但很多操作系統(tǒng)并不適用Unicode 字符集,那么當(dāng)從系統(tǒng)中讀取數(shù)據(jù)到 Java程序中時(shí),就可能出現(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)制序列和字符之間如何對(duì)應(yīng)呢?

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

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

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

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多