1. 程式人生 > >java nio系列教程(2)---channel(通道介紹)和使用

java nio系列教程(2)---channel(通道介紹)和使用

大家推薦個靠譜的公眾號程式設計師探索之路,大家一起加油https://img-blog.csdnimg.cn/20181129224604602.png ​  

package com.zzh.buffer;

import com.google.common.collect.Lists;
import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;

/**
 * 一.通道(channel):用於源節點與目標節點的連線.在java nio中負責緩衝區中資料的傳輸.channel本身不儲存資料,因此需要配合緩衝區進行傳輸.
 * <p>
 * 二.通道的主要實現類
 * java.nio.channels.Channel介面:
 * |--FileChannel
 * |--SocketChannel
 * |--ServerSocketChannel
 * |--DatagramChannel
 * 三.獲取通道
 * 1.java 針對支援通道的類提供了getChannel()方法
 * 本地IO:
 * FileInputStream/FileOutputStream
 * RandomAccessFile
 * <p>
 * 網路IO
 * Socket
 * ServerSocket
 * DatagramSocket
 * 2. 1.7之後 針對各個通道提供了靜態方法open()
 * 3. 1.7後, Files工具類的newByteChannel
 */
public class TestChannel {

    public static void main(String[] args) {
        System.currentTimeMillis();

    }

    /**
     * 非直接緩衝區
     */
    @Test
    public void test1() {
        FileInputStream inputStream = null;
        FileOutputStream outputStream = null;
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            inputStream = new FileInputStream("1.jpg");
            outputStream = new FileOutputStream("2.jpg");
            //1.獲取通道
            inChannel = inputStream.getChannel();
            outChannel = outputStream.getChannel();
            //分配緩衝區
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            while (inChannel.read(byteBuffer) != -1) {
                //切換到讀資料模式
                byteBuffer.flip();
                //將緩衝區中的資料寫入通道中
                outChannel.write(byteBuffer);
                //清空緩衝區
                byteBuffer.clear();
            }
        } catch (Exception e) {

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

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

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

    /**
     * 記憶體對映檔案
     */
    @Test
    public void test2() {
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
            /**
             * 注意  StandardOpenOption.CREATE  CREATE_NEW  這兩個的區別 create 不管之前是否存在都是建立一個   create_new 如果檔案已存在就會報錯
             */
            outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
            //這個記憶體對映檔案是隻讀模式   從0開始  到inChannel.size()結束
            MappedByteBuffer inMap = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
            /**
             * 這個記憶體對映檔案是讀寫模式   從0開始  到inChannel.size()結束
             * MappedByteBuffer沒有單獨的寫模式 只有READ_WRITE讀寫模式 對應的通道StandardOpenOption 應該有讀和寫
             */
            MappedByteBuffer outMap = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

            byte[] bytes = new byte[inMap.limit()];
            inMap.get(bytes);
            outMap.put(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (outChannel != null) {
                try {
                    outChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (inChannel != null) {
                try {
                    inChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 通道之間的直接傳輸
     */
    @Test
    public void test3() {
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
            /**
             * 注意  StandardOpenOption.CREATE  CREATE_NEW  這兩個的區別 create 不管之前是否存在都是建立一個   create_new 如果檔案已存在就會報錯
             */
            outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

            /**
             * 輸入通道   從0,開始到inChannel.size()結束,目標通道outChannel
             */
//            inChannel.transferTo(0, inChannel.size(), outChannel);
            /**
             * 輸出通道  從inChannel通道來   0到inChannel.size()
             */
            outChannel.transferFrom(inChannel, 0, inChannel.size());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (outChannel != null) {
                try {
                    outChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (inChannel != null) {
                try {
                    inChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 分散(Scatter) 與 聚集(Gather)
     * 分散讀取(Scattering Reads):將通道中的資料分散到多個緩衝區中
     * 聚集寫入(Gathering Writes):將多個緩衝區的資料聚集到通道中
     */
    @Test
    public void test4() {
        RandomAccessFile randomAccessFile = null;
        RandomAccessFile outFile = null;
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            randomAccessFile = new RandomAccessFile("1.jpg", "rw");
            //獲取通道
            inChannel = randomAccessFile.getChannel();

            //分配緩衝區
            ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);
            ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024);
            ByteBuffer[] byteBuffers = {byteBuffer1, byteBuffer2};

            //分散讀取
            inChannel.read(byteBuffers);
            //切換到 讀資料模式
            for (ByteBuffer byteBuffer : byteBuffers) {
                byteBuffer.flip();
            }

            outFile = new RandomAccessFile("3.jpg", "rw");
            outChannel = outFile.getChannel();
            //聚集寫入
            outChannel.write(byteBuffers);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (randomAccessFile != null) {
                try {
                    randomAccessFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outFile != null) {
                try {
                    outFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

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

    /**
     * 嘗試根據檔案大小進行分散讀取,聚集寫入
     * @throws IOException
     */
    @Test
    public void test5() throws IOException{
        RandomAccessFile raf1 = new RandomAccessFile("1.jpg", "rw");
        //獲取通道
        FileChannel channel1 = raf1.getChannel();
        //獲取檔案大小  單位是byte
        long size = channel1.size();
        //把大小分配到16個bytebuffer中  為什麼是16個可以看下FileChannel的write方法可以發現 裡面的迴圈 小於IOV_MAX=16
        int num = ((int)size / 16) + 1;
        List<ByteBuffer> byteBufferList = Lists.newArrayList();
        for (int i = 0;i < 16;i++){
            ByteBuffer buf = ByteBuffer.allocate(num);
            byteBufferList.add(buf);
        }
        //分散讀取
        ByteBuffer[] bufs = new ByteBuffer[byteBufferList.size()];
        bufs = byteBufferList.toArray(bufs);
        channel1.read(bufs);
        //全部切換到讀資料模式
        for (ByteBuffer byteBuffer : bufs) {
            byteBuffer.flip();
        }
        //聚集寫入
        RandomAccessFile raf2 = new RandomAccessFile("5.jpg", "rw");
        FileChannel channel2 = raf2.getChannel();
        /**
         * 這裡有個坑 不是所有的bufs都會寫進去
         * 寫的時候 for迴圈最大是16 也就是 bufs的16之後的都不會寫進去
         */
        channel2.write(bufs);
    }

    /**
     * 字符集  編碼與解碼
     * 編碼:字串->位元組陣列
     * 解碼:位元組陣列->字串
     */
    @Test
    public void test6(){
        //所有字符集
//        SortedMap<String, Charset> map = Charset.availableCharsets();
//        for (Map.Entry<String, Charset> entity : map.entrySet()){
//            System.out.println(entity.getKey()+"--->"+entity.getValue());
//        }

        try {
            Charset gbk = Charset.forName("GBK");
            //獲取編碼器
            CharsetEncoder charsetEncoder = gbk.newEncoder();
            //獲取解碼器
            CharsetDecoder charsetDecoder = gbk.newDecoder();

            CharBuffer charBuffer = CharBuffer.allocate(1024);
            charBuffer.put("趙志恆");
            charBuffer.flip();
            //編碼  解出來的bytebuffer已經是 切換成讀模式了  不用在切換
            ByteBuffer encode = charsetEncoder.encode(charBuffer);
            //解碼 解出來的CharBuffer已經是 切換成讀模式了  不用在切換
            CharBuffer decode = charsetDecoder.decode(encode);
            System.out.println(decode.toString());
            Charset utf8 = Charset.forName("UTF-8");
            CharsetDecoder utf8Decoder = utf8.newDecoder();
            //encode 重置為可讀
            encode.rewind();
            //此處這樣並不能截出來 會丟擲異常  MalformedInputException(此物件表示格式錯誤的輸入錯誤)
            CharBuffer decode1 = utf8Decoder.decode(encode);
        } catch (CharacterCodingException e) {
            e.printStackTrace();
        } finally {
        }

    }
}