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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yueloveme/article/details/84988010

大家推荐个靠谱的公众号程序员探索之路,大家一起加油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 {
        }

    }
}

猜你喜欢

转载自blog.csdn.net/yueloveme/article/details/84988010