• JAVA NIO中的Channels和Buffers


    前言

    Channels和Buffers是JAVA NIO里面比较重要的两个概念,NIO正是基于Channels和Buffers进行数据操作,且数据总是从Channels读取到Buffers,或者从Buffers写入到Channels

    通道(Channel)

    NIO中的通道与IO中的流类似,不过流是单向的,而通道是双向的。例如InputStream、OutputStream等都是单向的,一个流只能进行读数据或写数据;而FileChannel、SocketChannel等是双向的,既可以从中读数据,也可以写数据。

    通道与流的区别如下:

    • 通道是双向的,可读可写,而流是单向的,只能读或只能写;
    • 通道中的数据是跟Buffer进行交互的,但在面向流的I/O中,数据是直接读/写到流中的;
    • NIO可以异步读写数据;

    以FileChannel为例,如下:

    FileChannel

    FileChannel是文件写入/读取相关的通道;

    产生FileChannel的方式:

    • 从FileOutputStream中获取;
    • 从RandomAccessFile中获取;
    • 从FileInputStream中获取;

    下面举个例子,分别从FileOutputStream、RandomAccessFile、FileInputStream中获取文件通道,进行相关写入/读取操作;

    package com.pichen.io;
    
    import java.nio.*;
    import java.nio.channels.*;
    import java.io.*;
    
    public class GetChannel {
        private static final int BSIZE = 1024;
    
        public static void main(String[] args) throws Exception {
            // 创建文件输出字节流
            FileOutputStream fos = new FileOutputStream("data.txt");
            //得到文件通道
            FileChannel fc = fos.getChannel();
            //往通道写入ByteBuffer
            fc.write(ByteBuffer.wrap("Some text ".getBytes()));
            //关闭流
            fos.close();
    
            //随机访问文件
            RandomAccessFile raf = new RandomAccessFile("data.txt", "rw");
            //得到文件通道
            fc = raf.getChannel();
            //设置通道的文件位置 为末尾
            fc.position(fc.size()); 
            //往通道写入ByteBuffer
            fc.write(ByteBuffer.wrap("Some more".getBytes()));
            //关闭
            raf.close();
            
            //创建文件输入流
            FileInputStream fs = new FileInputStream("data.txt");
            //得到文件通道
            fc = fs.getChannel();
            //分配ByteBuffer空间大小
            ByteBuffer buff = ByteBuffer.allocate(BSIZE);
            //从通道中读取ByteBuffer
            fc.read(buff);
            //调用此方法为一系列通道写入或相对获取 操作做好准备
            buff.flip();
            //从ByteBuffer从依次读取字节并打印
            while (buff.hasRemaining()){
                System.out.print((char) buff.get());
            }
            fs.close();
        }
    } 

    再举个文件复制的例子,有两种方式:

    • 打开一个FileChannel用于读,另一个用于写;
    • 直接调用transferTo或者transferFrom方法传输通道之间的数据;

    如下代码示例:

    package com.pichen.io;
    
    import java.nio.*;
    import java.nio.channels.*;
    import java.io.*;
    
    public class ChannelCopy {
        private static final int BSIZE = 1024;
    
        public static void main(String[] args) throws Exception {
            //获取文件输入输出字节流
            FileInputStream fis = new FileInputStream("C:\test.jpg");
            FileOutputStream fos = new FileOutputStream("C:\test_copy.jpg");
            
            //从文件输入输出字节流中获取通道
            FileChannel fci = fis.getChannel();
            FileChannel fco = fos.getChannel();
            //分配ByteBuffer空间大小
            ByteBuffer buffer = ByteBuffer.allocate(BSIZE);
            
            //第一种种数据拷贝方式,直接往输入通道写数据
            while (fci.read(buffer) != -1) {
                //为写入操作做准备
                buffer.flip(); 
                //往输出通道写入buffer
                fco.write(buffer);
                //清空buffer,重置内部指针
                buffer.clear(); 
            }
            
            //第二种数据拷贝方式,利用transferTo或者transferFrom方式
            FileOutputStream fos2 = new FileOutputStream("C:\test_copy2.jpg");
            FileChannel fco2 = fos.getChannel();
            fci.transferTo(0, fci.size(), fco2);
            
            
            fis.close();
            fos.close();
            fos2.close();
        }
    }

    缓冲区(Buffer)

    buffer用于与通道进行交互,举个例子如下,这里以ByteBuffer为例;

    package com.pichen.io;
    
    import java.nio.*;
    import java.nio.channels.*;
    import java.io.*;
    
    public class GetChannel {
        private static final int BSIZE = 1024;
    
        public static void main(String[] args) throws Exception {
    
            // 随机访问文件
            RandomAccessFile raf = new RandomAccessFile("C:\data.txt", "rw");
            // 得到文件通道
            FileChannel fc = raf.getChannel();
    
            ByteBuffer bf = ByteBuffer.allocate(BSIZE);
    
            int bytesRead = fc.read(bf); // read into buffer.
            while (bytesRead != -1) {
                // 通过flip()方法将Buffer从写模式切换到读模式
                bf.flip();
                while (bf.hasRemaining()) {
                    // 每次读取一个字节
                    System.out.print((char) bf.get());
    
                }
    
                // 清空缓存,准备往缓存写数据
                bf.clear();
                bytesRead = fc.read(bf);
            }
    
            // 关闭
            raf.close();
        }
    }

    Buffer基本方法

    • flip方法:从buffer读数据之前调用,从写模式切换到读模式;
    • clear:往buffer写数据之前调用,全部清除;
    • compact:往buffer写数据之前调用,只清除已读取的数据,新写入的数据会添加到未读数据末尾;

    Buffer基本属性

    • capacity:buffer空间大小;
    • position:读写数据时的指针,指向当前位置;
    • limit:读数据时,表示当前可读数据的大小;写数据时,即buffer大小=capacity;

    Buffer的类型

    • ByteBuffer
    • MappedByteBuffer
    • CharBuffer
    • DoubleBuffer
    • FloatBuffer
    • IntBuffer
    • LongBuffer
    • ShortBuffer

    Buffer中写数据

    • 从Channel写到Buffer,调用通道的read(Buffer buffer)方法
    • put()方法写到Buffer里,调用Buffer的put方法

    Buffer中读取数据

    • 从Buffer读取数据到Channel,调用通道的write(Buffer buffer)方法
    • 使用get()方法从Buffer中读取数据,调用Buffer的get方法

    参考:http://ifeve.com/java-nio-all/

  • 相关阅读:
    违反并发性: UpdateCommand 影响了预期1条记录中的0条——我的解决方案
    (转)使用DataGridView控件常见问题解答
    C#中如何去除数组中重复的项
    C#中如何去除HTML标记
    (转)中断基本概念
    JavaScript 操作 Cookie
    要有梦想创造卓越的职业生涯
    《暗时间》读书笔记与读后感
    前端攻略系列
    前端攻略系列(一) 前端各种优化(保证持续更新)
  • 原文地址:https://www.cnblogs.com/chenpi/p/5372786.html
Copyright © 2020-2023  润新知