ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java进阶之NIO&AIO

2021-05-04 17:58:45  阅读:199  来源: 互联网

标签:Java 进阶 buffer put AIO limit ByteBuffer 缓冲区 byte


Java进阶之NIO&AIO

一、简单了解各种IO

之前在网络编程的学习中,服务器调用accept方法时会一直等待客户端访问,如果没有客户端访问,服务器程序会一直停留在这个阶段,这种数据传输方式就被称为BIO。

除了BIO之外,还有两种IO,分别是NIO和AIO:

  • BIO:同步阻塞IO
  • NIO:同步非阻塞IO
  • AIO:异步非阻塞IO

举例来说,去银行排队叫号和写作业三种IO不同的执行过程:
在这里插入图片描述

二、NIO

NIO有三个重要的组成部分:BufferChannelSelector

2.1 Buffer

Buffer是缓冲区,本质就是由数组组成的,在NIO中,数据都是要在缓冲区进行操作的。

常见的缓冲区:

  • ByteBuffer(常用)
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

2.1.1 ByteBuffer的创建

ByteBuffer创建三种方式:

  • static ByteBuffer allocate(int capacity):创建一个字节缓冲区并返回,参数是缓冲区的长度(间接缓冲区)
  • static ByteBuffer allocateDirect(int capacity):创建一个字节缓冲区并返回,参数是缓冲区的长度(直接缓冲区)
  • static ByteBuffer wrap(byte[] array):根据字节数组创建字节缓冲区并返回(间接缓冲区)

缓冲区:

  • 间接缓冲区:在Java的内存中创建的缓冲区
  • 直接缓冲区:在系统内存中创建的缓冲区

间接缓冲区的创建和销毁效率比直接缓冲区要高,但是工作效率比直接缓冲区要低。

public class Demo01Buffer {
    public static void main(String[] args) {
        // static ByteBuffer allocate(int capacity):创建一个字节缓冲区并返回,参数是缓冲区的长度(间接缓冲区)
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //将ByteBuffer转成数组,然后借助工具类Arrays.toString输出
        System.out.println(Arrays.toString(buffer.array()));

        //static ByteBuffer allocateDirect(int capacity):创建一个字节缓冲区并返回,参数是缓冲区的长度(直接缓冲区)
        ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
        System.out.println(buffer2);//java.nio.DirectByteBuffer[pos=0 lim=10 cap=10]

        //static ByteBuffer wrap(byte[] array):根据字节数组创建字节缓冲区并返回(间接缓冲区)
        ByteBuffer buffer3 = ByteBuffer.wrap("hello".getBytes());
        System.out.println(Arrays.toString(buffer3.array()));//[104, 101, 108, 108, 111]
    }
}

2.1.2 ByteBuffer的put方法

在ByteBuffer中有一些方法叫做put,可以向缓冲区中添加元素:

  • ByteBuffer put(byte b):向当前位置添加一个字节
  • ByteBuffer put(byte[] src):向当前位置添加一个字节数组
  • ByteBuffer put(byte[] src, int offset, int length):添加字节数组的一部分。参数offset是数组起始索引,参数length是元素个数
public class Demo02BufferPut {
    public static void main(String[] args) {
        //获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //ByteBuffer put(byte b):向当前位置添加一个字节。
        buffer.put((byte) 100);
        buffer.put((byte) 101);
        buffer.put((byte) 102);
        //ByteBuffer put(byte[] src):向当前位置添加一个字节数组。
        byte[] bArr = {90, 91, 92, 93, 94};
        //buffer.put(bArr);

        //ByteBuffer put(byte[] src, int offset, int length):添加字节数组的一部分。参数offset是数组起始索引,参数length是元素个数
        buffer.put(bArr, 1, 3);

        //输出
        System.out.println(Arrays.toString(buffer.array()));

    }
}

2.1.3 ByteBuffer的capacity方法

在ByteBuffer中有一个方法叫做capacity,可以获取到缓冲区的容量:

  • int capacity():返回缓冲区的容量
public class Demo03Capacity {
    public static void main(String[] args) {
        //获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //输出容量
        System.out.println("容量:" + buffer.capacity());
    }
}

2.1.4 ByteBuffer的limit方法

在ByteBuffer中,有一个方法叫做limit,可以对缓冲区进行限制(比如限制缓冲区中只能使用前5个元素)

  • int limit():获取缓冲区的限制
  • Buffer limit(int newLimit):设置缓冲区的限制。 参数表示新的限制,比如参数是5,就表示只能使用5个元素
public class Demo04Limit {
    public static void main(String[] args) {
        //获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //输出
        System.out.println("容量:" + buffer.capacity() + ",限制:" + buffer.limit());
        //设置limit
        buffer.limit(2);
        System.out.println("容量:" + buffer.capacity() + ",限制:" + buffer.limit());

        //添加元素
        buffer.put((byte) 100);
        buffer.put((byte) 101);
        //限制了只能使用前两个元素,如果添加第三个,就会报错
        buffer.put((byte) 102);//BufferOverflowException
    }
}

2.1.5 ByteBuffer的position方法

在ByteBuffer中有一个方法叫做Position,可以获取以及设置缓冲区的光标位置

  • int position():获取缓冲区的光标位置
  • Buffer position(int newPosition):设置缓冲区的光标位置,参数表示新设置的位置
public class Demo05Position {
    public static void main(String[] args) {
        //获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //输出缓冲区的信息
        System.out.println("位置:" + buffer.position() + ", 限制:" + buffer.limit() + ", 容量:" + buffer.capacity());
        //添加
        buffer.put((byte) 100);
        buffer.put((byte) 101);
        buffer.put((byte) 102);
        //输出缓冲区的信息
        System.out.println("位置:" + buffer.position() + ", 限制:" + buffer.limit() + ", 容量:" + buffer.capacity());
        //设置缓冲区的位置
        buffer.position(0);
        System.out.println("位置:" + buffer.position() + ", 限制:" + buffer.limit() + ", 容量:" + buffer.capacity());
        buffer.put((byte) 50);
        buffer.put((byte) 51);
        //输出
        System.out.println(Arrays.toString(buffer.array()));
    }
}

2.1.6 ByteBuffer的mark方法

在ByteBuffer中有一个方法叫做mark,可以设置缓冲区的标记:

  • Buffer mark():设置缓冲区的标记
  • Buffer reset():恢复之前的标记

调用mark方法时position位置是多少,那么调用reset方法后恢复的position就是多少。

public class Deo06Mark {
    public static void main(String[] args) {
        //获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //输出缓冲区的信息
        System.out.println("位置:" + buffer.position() + ", 限制:" + buffer.limit() + ", 容量:" + buffer.capacity());
        //添加
        buffer.put((byte) 100);
        buffer.put((byte) 101);
        buffer.put((byte) 102);
        System.out.println("位置:" + buffer.position() + ", 限制:" + buffer.limit() + ", 容量:" + buffer.capacity());
        //设置标记
        buffer.mark();
        //添加
        buffer.put((byte) 103);
        buffer.put((byte) 104);
        System.out.println("位置:" + buffer.position() + ", 限制:" + buffer.limit() + ", 容量:" + buffer.capacity());
        //调用reset,恢复之间做标记是的位置.
        buffer.reset();
        System.out.println("位置:" + buffer.position() + ", 限制:" + buffer.limit() + ", 容量:" + buffer.capacity());
        buffer.put((byte) 10);
        buffer.put((byte) 11);
        //输出元素
        System.out.println(Arrays.toString(buffer.array()));
    }
}

2.1.7 ByteBuffer中的其他的方法

  • Buffer flip():缩小limit的范围
    a. 将limit设置到position位置。
    b. 将position设置为0
    c. 丢弃标记

  • Buffer clear():还原缓冲区的状态。
    a. 将limit设置到capacity
    b. 将position设置为0
    c. 丢弃标记

public class Demo07OtherMethod {
    public static void main(String[] args) {
        //获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //输出缓冲区的信息
        System.out.println("位置:" + buffer.position() + ", 限制:" + buffer.limit() + ", 容量:" + buffer.capacity());
        //添加
        buffer.put((byte) 100);
        buffer.put((byte) 101);
        buffer.put((byte) 102);
        System.out.println("位置:" + buffer.position() + ", 限制:" + buffer.limit() + ", 容量:" + buffer.capacity());
        //缩小limit范围
        buffer.flip();
        System.out.println("位置:" + buffer.position() + ", 限制:" + buffer.limit() + ", 容量:" + buffer.capacity());
        //还原缓冲区状态
        buffer.clear();
        System.out.println("位置:" + buffer.position() + ", 限制:" + buffer.limit() + ", 容量:" + buffer.capacity());
    }
}

2.2 Channel

Channel表示通道,在NIO中数据的读写都是使用通道完成的,可以将通道看成之前的流,只不过流是单向的,通道是双向的,通道既有读取的方法,也有写的方法。

常见的通道:

  • FileChannel:从文件读取数据的
  • DatagramChannel:读写UDP网络协议数据
  • SocketChannel:读写TCP网络协议数据
  • ServerSocketChannel:可以监听TCP连接

2.2.1 FileChannel复制文件

通过NIO的方式复制文件,如果要对文件读写,需要使用FileChannel

通过文件字节流获取FileChannel:

  • FileChannel getChannel():获取通道

在通道(Channel)中还有用于读写的方法

  • int write(ByteBuffer src):写数据,参数是缓冲区。
  • int read(ByteBuffer dst):读取数据,参数是缓冲区
public class Demo01Channel {
    public static void main(String[] args) throws IOException {
        //创建字节流
        FileInputStream fis = new FileInputStream("d:\\aa.jpg");
        FileOutputStream fos = new FileOutputStream("d:\\bb.jpg");
        //获取通道
        FileChannel inChannel = fis.getChannel();
        FileChannel outChannel = fos.getChannel();
        //先定义缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);//
        //定义变量,表示读取到的字节个数
        int len;
        //开始循环
        while ((len = inChannel.read(buffer)) != -1) {
            //如果条件成立,就表示读取到了数据,那么就进行处理。
            //缩小limit范围(将limit设置到position位置),读取到几个,就让缓冲区中的几个元素是有效的。
            buffer.flip();
            //将读取到的数据写到目的地文件了
            outChannel.write(buffer);
            //重置缓冲区(将position设置为0,将limit设置到capacity,丢弃标记)
            buffer.clear();
        }
        //释放资源
        fos.close();
        fis.close();

    }
}

2.2.2 RandomAccessFile复制文件

上面复制文件的案例直接使用FileChannel结合ByteBuffer实现channel读写,但并不能提高文件的读写效率。

ByteBuffer有个子类:MappedByteBuffer,可以创建一个“直接缓冲区”,并可以将文件直接映射至内存,可以提高大文件的读写效率。

RandomAccessFile类(是一个可以设置读写模式的IO流类)

  • RandomAccessFile(String name, String mode): 第一个参数是字符串的文件路径,第二个参数是模式(举例:"r"表示只读;"rw"表示读写)

RandomAccessFile其他的方法:

  • FileChannel getChannel():获取通道

FileChannel获取MappedByteBuffer方法

  • MappedByteBuffer map(FileChannel.MapMode mode, long position, long size):获取直接缓冲区
    参数mode:表示模式
    参数position:表示起始位置
    参数size:映射的大小

注意:通过RandomAccessFile复制文件的方式文件不能超过2G。

public class Demo02FastCopy {
    public static void main(String[] args) throws IOException {
        //创建RandomAccessFile对象
        //创建的RandomAccessFile,绑定了源文件,模式只读
        RandomAccessFile source = new RandomAccessFile("d:\\aa.rar", "r");
        //创建的RandomAccessFile,绑定了目的地文件,模式读写
        RandomAccessFile target = new RandomAccessFile("d:\\bb.rar", "rw");
        //获取通道
        FileChannel inChannel = source.getChannel();
        FileChannel outChannel = target.getChannel();
        //记录时间
        long start = System.currentTimeMillis();
        //获取源文件大小
        long size = inChannel.size();
        //获取MappedByteBuffer缓冲区
        MappedByteBuffer mbbi = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
        MappedByteBuffer mbbo = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, size);
        //遍历mbbi,将每一个字节都放入到mbbo缓冲区中.
        for (int i = 0; i < size; i++) {
            //获取到mbbi中索引为i的字节
            byte b = mbbi.get();
            //将获取到的放入到mbbo中
            mbbo.put(b);
        }
        //记录时间
        long end = System.currentTimeMillis();
        System.out.println(end - start);
        //释放资源
        target.close();
        source.close();
    }
}

2.2.3 SocketChannel客户端

SocketChannel表示客户端通道,我们可以使用该类中表示TCP协议的客户端。

获取SocketChannel

  • static SocketChannel open():获取SocketChannel

SocketChannel方法:

  • boolean connect(SocketAddress remote):连接服务器,参数是目标服务器的IP地址以及端口号
public class Demo01Client {
    public static void main(String[] args) throws IOException {
        //获取SocketChannel对象
        SocketChannel socketChannel = SocketChannel.open();
        //连接服务器
        socketChannel.connect(new InetSocketAddress("localhost", 8888));
        //给服务器发送数据
        //将要发送的数据封装到缓冲区中
        ByteBuffer buffer = ByteBuffer.wrap("你好".getBytes());
        //将数据发送给服务器
        socketChannel.write(buffer);
        //接收服务器回复过来的数据
        //创建一个长度是1024的缓冲区,用来接收服务器回复过来的数据
        ByteBuffer buffer2 = ByteBuffer.allocate(1024);
        //接收服务器的数据
        socketChannel.read(buffer2);
        //缩小limit限制
        buffer2.flip();
        //将缓冲区的内容转成字符串输出。
        System.out.println(new String(buffer2.array(), 0, buffer2.limit()));
        //释放资源
        socketChannel.close();
    }
}

2.2.4 SocketChannel服务端

ServerSocketChannel表示服务端通道,我们可以使用该类中表示TCP协议的服务端。

获取ServerSocketChannel

  • static ServerSocketChannel open():获取ServerSocketChannel

ServerSocketChannel方法:

  • static ServerSocketChannel open():获取服务器通道
  • ServerSocketChannel bind(SocketAddress local):给服务器绑定端口号
  • SocketChannel accept():监听客户端请求
public class Demo02Server {
    public static void main(String[] args) throws IOException {
        //获取服务器通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //将服务器设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        //给服务器绑定端口
        serverSocketChannel.bind(new InetSocketAddress(8888));
        while (true) {
            //监听客户端请求
            SocketChannel socketChannel = serverSocketChannel.accept();
            if (socketChannel == null) {
                System.out.println("还没有客户端来");
            } else {
                System.out.println("有客户端来连接了");
                //获取客户端发送过来的数据
                //创建ByteBuffer缓冲区,用来保存读取到的数据
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                //通过通道接收数据
                socketChannel.read(buffer);
                //缩小limit范围
                buffer.flip();
                //输出读取到的内容
                System.out.println(new String(buffer.array(), 0, buffer.limit()));


                //给客户端回复数据
                //获取缓冲区,里面保存要回复的数据
                ByteBuffer buffer2 = ByteBuffer.wrap("收到".getBytes());
                //使用通道将数据写给客户端
                socketChannel.write(buffer2);
                //释放资源
                socketChannel.close();
            }
        }

    }
}

2.3 Selector选择器

选择器Selector是NIO中的重要技术之一,它与SelectorChannel联合使用实现了非阻塞的多路复用。

2.3.1 多路复用

"多路"是指:服务器同时监听多个"端口"的情况,每个端口都要监听多个客户端的连接。
在这里插入图片描述
如果不使用多路复用,服务端需要开很多线程处理每个端口的请求,如果在高并发环境下会造成系统性能下降。
在这里插入图片描述
使用多路复用,只需要一个线程就可以处理多个通道,降低内存占用率,减少CPU切换时间,在高并发、高频段环境下非常有优势。

Selector选择器可以实现多路复用的效果。我们可以使用一个Selector监听三个服务器的状态,哪个服务器有客户端来请求了,那么我们就可以让哪个服务器去处理客户端的请求。

获取Selector选择器:

  • static Selector open():获取一个选择器

将通道注册到选择器:

  • channel.configureBlocking(false):将通道设置为非阻塞。
  • channel.register(selector,SelectionKey.OP_ACCEPT):参数selector表示选择器;SelectionKey.OP_ACCEPT表示监听服务器接受就绪事件

Selector选择器中的方法:

  • Set<SelectionKey> keys():获取已经注册到选择器的通道(编号)并放入到Set集合中返回。 SelectionKey可以理解为通道的编号
  • Set<SelectionKey> selectedKeys(): 获取已经连接的通道(编号)并放入到Set集合中返回
  • int select():调用select方法后,程序会等着,一直到有客户端来连接
public class Demo01Selector {
    public static void main(String[] args) throws IOException {
        //创建三个服务器,并将三个服务器设置为非阻塞
        ServerSocketChannel serverSocketChannelOne = ServerSocketChannel.open();
        serverSocketChannelOne.bind(new InetSocketAddress(7777));
        serverSocketChannelOne.configureBlocking(false);//设置为非阻塞

        ServerSocketChannel serverSocketChannelTwo = ServerSocketChannel.open();
        serverSocketChannelTwo.bind(new InetSocketAddress(8888));
        serverSocketChannelTwo.configureBlocking(false);//设置为非阻塞

        ServerSocketChannel serverSocketChannelThree = ServerSocketChannel.open();
        serverSocketChannelThree.bind(new InetSocketAddress(9999));
        serverSocketChannelThree.configureBlocking(false);//设置为非阻塞

        //获取Selector选择器
        Selector selector = Selector.open();

        //让上面三个服务器通道注册到Selector选择器上
        serverSocketChannelOne.register(selector, SelectionKey.OP_ACCEPT);
        serverSocketChannelTwo.register(selector, SelectionKey.OP_ACCEPT);
        serverSocketChannelThree.register(selector, SelectionKey.OP_ACCEPT);

        //死循环,让程序一直执行(选择器一直监听服务器通道的状态)
        while (true) {
            //调用选择器的select方法,等着客户端来连接服务器
            selector.select();
            //如果程序向下执行,表示有客户端来连接了。就获取已经连接的服务器通道
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            //获取迭代器
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            //遍历
            while (iterator.hasNext()) {
                //获取遍历到的元素
                SelectionKey selectionKey = iterator.next();
                //通过selectionKey获取到通道
                ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
                //让服务器监听客户端请求
                SocketChannel socketChannel = serverSocketChannel.accept();
                //获取缓冲区,用来保存接收到的数据
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                //进行读取
                socketChannel.read(buffer);
                //缩小缓冲区的limit范围
                buffer.flip();
                //输出读取到的内容
                System.out.println(new String(buffer.array(), 0, buffer.limit()));
                //客户端通道关闭
                socketChannel.close();
                //如果某个服务器处理完了客户端请求,那么就从集合中删除。
                iterator.remove();//删除遍历的元素
            }
        }
    }
}
public class Demo02Client {
    public static void main(String[] args) {
        new Thread(() -> {
            try {
                //获取SocketChannel
                SocketChannel socketChannel = SocketChannel.open();
                //连接服务器
                socketChannel.connect(new InetSocketAddress("localhost", 7777));
                //准备缓冲区,保存要发送的数据
                ByteBuffer buffer = ByteBuffer.wrap("我要连接7777".getBytes());
                //将数据发给服务器
                socketChannel.write(buffer);
                //释放资源
                socketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            try {
                //获取SocketChannel
                SocketChannel socketChannel = SocketChannel.open();
                //连接服务器
                socketChannel.connect(new InetSocketAddress("localhost", 8888));
                //准备缓冲区,保存要发送的数据
                ByteBuffer buffer = ByteBuffer.wrap("我要连接8888".getBytes());
                //将数据发给服务器
                socketChannel.write(buffer);
                //释放资源
                socketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            try {
                //获取SocketChannel
                SocketChannel socketChannel = SocketChannel.open();
                //连接服务器
                socketChannel.connect(new InetSocketAddress("localhost", 9999));
                //准备缓冲区,保存要发送的数据
                ByteBuffer buffer = ByteBuffer.wrap("我要连接9999".getBytes());
                //将数据发给服务器
                socketChannel.write(buffer);
                //释放资源
                socketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

三、AIO

AIO有关的通道

  • AsynchronousSocketChannel:TCP中的客户端异步通道
  • AsynchronousServerSocketChannel:TCP中的服务器异步通道
  • AsynchronousFileChannel:文件操作的异步通道
  • AsynchronousDatagramChannel:UDP通信异步通道
public class Demo01Server {
    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        //获取一个异步服务器通道
        AsynchronousServerSocketChannel asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();
        //绑定端口号
        asynchronousServerSocketChannel.bind(new InetSocketAddress(8888));
        //监听客户端的请求
        Future<AsynchronousSocketChannel> accept = asynchronousServerSocketChannel.accept();
        //调用get方法,获取服务器监听到的客户端通道
        AsynchronousSocketChannel asynchronousSocketChannel = accept.get();
        //创建ByteBuffer缓冲区,用来接收读取到的数据
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //调用read方法进行读取
        Future<Integer> readFuture = asynchronousSocketChannel.read(buffer);
        //判断如果read方法没有读取结束,那么就去干一些其他事情
        if (!readFuture.isDone()) {
            Thread.sleep(1000);
        }
        //缩小缓冲区limit限制
        buffer.flip();
        //输出读取到的结果
        System.out.println(new String(buffer.array(), 0, buffer.limit()));
        //释放资源
        asynchronousSocketChannel.close();
        asynchronousServerSocketChannel.close();
    }
}
/*
    AsynchronousSocketChannel:TCP中的客户端异步通道
 */
public class Demo02Client {
    public static void main(String[] args) throws IOException, InterruptedException {
        //获取客户端异步通道
        AsynchronousSocketChannel asynchronousSocketChannel = AsynchronousSocketChannel.open();
        //连接服务器
        Future<Void> future = asynchronousSocketChannel.connect(new InetSocketAddress("localhost", 8888));
        //判断如果连接没有建立成功,就做一些其他事情
        if(!future.isDone()) {
            Thread.sleep(1000);
        }
        //让客户端给服务器发送数据
        ByteBuffer buffer = ByteBuffer.wrap("你好".getBytes());
        //调用方法,发送数据
        asynchronousSocketChannel.write(buffer);
        //释放资源
        asynchronousSocketChannel.close();
    }
}

四、同步异步&阻塞非阻塞

同步和异步(线程通信的机制)

  • 同步:线程在完成某个功能时,得到结果之后才能做后面的事情【立即得到结果】
  • 异步:线程在完成功能的时候,不用得到结果也可以做后面的事情【不会立即得到结果】

阻塞和非阻塞(线程的状态)

  • 阻塞:线程在执行任务时,会挂起。
  • 非阻塞:线程在执行任务时,不会挂起,可以继续执行其他任务。

标签:Java,进阶,buffer,put,AIO,limit,ByteBuffer,缓冲区,byte
来源: https://blog.csdn.net/tysearch/article/details/116399190

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有