NIO中Buffer的重要属性关系解析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: NIO中Buffer的重要属性关系解析

Buffer 是java NIO中三个核心概念之一 缓存, 在java的实现体系中Buffer作为顶级抽象类存在


简单说,Buffer在做什么?#


我们知道,在java IO中体系中, 因为InputStream和OutputStream是抽象类,而java又不可以多重继承,于是任何一个流要么只读,要么只写.而无法完成同时读写的工作

于是: Buffer来了


NIO中,对数据的读写,都是在Buffer中完成的,也就是说,同一个buffer我们可以先读后写, 它底层维护着一个数组,这个数组被三个重要的属性控制,有机的工作结合,使buffer可读可写;


此外,Buffer是线程不安全的,并发访问需要同步


三个重要属性:#


  • capacity: 容量


  • Buffer中元素的个数
  • 永远不能为负数
  • 永远不会变化


  • limit: 限制


  • 实际上它是Buffer所维护的那个数组中的一个下标
  • limit是第一个不能被读,或者第一个不能被写的元素的index
  • limit永远不会是负数
  • 永远不会超过capacity


  • Position: 定位


  • 指数组中下一个将要被读或者将要被写的元素的索引


图解,Buffer是如何维护的数组#



一开始: 我们初始化它的大小为6 初始状态,Capacity和Limit都在最后一个不能被读或者不能被写的位置上



接着我们读入两个数据.position跳转到下一个将被读的index


接下来,准备写把buffer中的数据写出去两个, 于是我们需要反转数值


buffer.flip();


反转的逻辑:


limit=position;
position=0;


于是从0写 ,写到哪个位置? 写到limi前


#



写完毕后:如下图:


网络异常,图片无法展示
|


现在可以看到,pisition == limit

如果再想读入新的数据,同样需要反转数据flip()


但是此时,limit仍然是刚刚position的所在的最后的位置#


Buffer架构体系:#



模拟Buffer实现一个相同的继承体系,进一步了解成员变量在他们之间是怎么维护的

首先,和Buufer等级一样的顶级父类


public abstract class ParentSupper {
    private int position;
    private int capacity;
    ParentSupper(int position,int capacity){
        this.position=position;
        this.capacity=capacity;
        System.out.println("ParentSupper 的构造方法执行了... ");
    }
    final int nextPutIndex(){
        if (position>=capacity){
             throw new RuntimeException("索引异常");
        }
        return position++;
    }
 int i=0;
    final int nextGetIndex(){
        if (i >=position){
           throw new RuntimeException("索引异常");
        }
        return i++;
    }
}


接着是它的实现类, 和IntBuffer 等级一样


public abstract class Parent extends ParentSupper {
    // 抽象类中的成员变量,必须放在构造方法中
    final int[] arr1;
    int tag;
    // todo 执行父类的构造方法
    Parent(int a, int[] arr1) {
        // 调用父类的构造函数
        super(0,a);
        this.arr1 = arr1;
        this.tag = a;
    }
    // 构造器
    public static Parent allocate(int capacity) {
        return new Child(capacity);
    }
    // 抽象的方法
    public abstract int get();
    public abstract void put(int number);
}


作为抽象类的它,有自己的抽象方法, get put, 同时它里面维护着 核心数组, final不可变类型的, 抽象类中的变量不能单独存在,必须依附构造函数,于是我们添加它的构造函数


再就是它的实现类:


class Child extends Parent {
    public Child(int capacity) {
        super(capacity,new int[capacity]);
    }
    // todo 重写父类的构造方法
    @Override
    public int get() {
        return arr1[nextGetIndex()];
    }
    @Override
    public void put(int num) {
        arr1[nextPutIndex()]=num;
    }
}


注意,Child的类上并没有public 修饰,意味着他只是可以包内访问


下面测试:


public class text {
    public static void main(String[] args) {
        // 初始化
        Parent allocate = Parent.allocate(9);
         for (int i=0;i<9;i++){
             allocate.put(i);
         }
        for (int i=0;i<=8;i++){
            System.out.println( allocate.get());
        }
    }
}


运行流程是怎样的呢?

当我们使用

Parent.allocate(10);创建对象时,底层确实 new Child(int i), 同时把传递给Child, 而在Child()相应的构造函数中,接着调用的是spuer()方法,同时把10 传递给super也就是Parent,同时实例化了Parent的 数组, 在Supper的构造方法中,把0,和传递进来的10 传递给了SuperParent, 让他维护两个值

思考, 各个部分之间的作用


  • Buffer : 维护着当前数据的 position limit 和 capaciy值,这是数组的下标值,但是Buffer却没有数组
  • Buffer的直接实现类,如IntBuffer , 依然是抽象类, 它有数组作为缓存, 数组的维护它交给了它的父类Buffer, 针对数组的初始化,读写,他交给了自己的实现类


数组的维护工作是一样的,所以抽象成Buffer

  • HeapIntBuffer: 它继承了IntBuffer, 同时把它的父类的数组进行了实例化,并且重写了父类的get put方法

不同类型的数据,具体的读写是有区别的,所有抽象成不同的子类


在回去看,allocate(); 显然我们得到的是最外层的子类对象,这也就意味着他是最强的那个对象,它拥有父类的数据,并且这个数据的读写由它的爷爷替自己把关,这就是Buffer的设计模式


Buffer中常见的API#


Buffer的所有子类全部定义了两套关于get()put()的方法#


  • Relative: 相对的读写, 相对的读写都是从当前的position的位置开始,每次的get/put都会是position的位置发生改变
  • Avsolute: 绝对的读写 , 用户指定索引, 从指定的索引里面get,或者直接往指定的索引里面put


clear()#


将buffer置为初始状态的值,实际上就是让新读入buffer中的值,覆盖掉原来的值


position=0;
limit=capacity;


flip()#

反转buffer


limit = position;
positon=0;


isReadOnly()#

判断是否是只读的buffer


分片Buffer#


//限制前后 准备切片
byteBuffer.position(2);
byteBuffer.limit(6);
// 切片
ByteBuffer slice = byteBuffer.slice();


新得到的buffer和原buffer共享内存空间


类型化的Buffer#


ByteBuffer allocate = ByteBuffer.allocate(123);
    allocate.putInt(1);
    allocate.putChar('你');
    allocate.putDouble(123.123123);
    allocate.putShort((short) 2);
    allocate.putLong(3L);
    allocate.flip();
    System.out.println(allocate.getInt());
    System.out.println(allocate.getChar());
    System.out.println(allocate.getDouble());
    System.out.println(allocate.getShort());
    System.out.println(allocate.getLong());


类型化 的put和get 但是呢!!! 怎么存进去的 就得怎么取出来 , 否者会出现乱码


NIO拷贝文件#


// 获取输入流
        FileInputStream fileInputStream = new FileInputStream("123.txt");
        FileOutputStream fileOutputStream = new FileOutputStream("output123.txt");
        // 获取一个通道,关联上数据
        FileChannel channel = fileInputStream.getChannel();
        FileChannel outputStreamChannel = fileOutputStream.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(128);
        while (true) {
            // todo 每次读满一次缓存后, 重新初始化buffer
            byteBuffer.clear();
            // 将数据读入 缓存
            int read = channel.read(byteBuffer);
            System.out.println("read == "+read);
            if (read == -1) {
                break;
            }
            // 反转   limit=position   position=0
            byteBuffer.flip();
            // 通过channel 往输出流中写入缓存的数据
            outputStreamChannel.write(byteBuffer);
        }
        // 释放资源
        fileInputStream.close();
        fileInputStream.close();
        System.out.println("结束...");
    }


每次循环一开始,都要重置buffer,否则第一轮读取结束之后, limit==position 从而channel里面新的数据读不进去,而 limit==position也不会引发异常,随后的flip()将buffer反转,position为0, 使得当前buffer中的数据被循环写到文件中,成为死循环

相关文章
|
3月前
|
缓存 NoSQL Java
千万级电商线上无阻塞双buffer缓冲优化ID生成机制深度解析
【11月更文挑战第30天】在千万级电商系统中,ID生成机制是核心基础设施之一。一个高效、可靠的ID生成系统对于保障系统的稳定性和性能至关重要。本文将深入探讨一种在千万级电商线上广泛应用的ID生成机制——无阻塞双buffer缓冲优化方案。本文从概述、功能点、背景、业务点、底层原理等多个维度进行解析,并通过Java语言实现多个示例,指出各自实践的优缺点。希望给需要的同学提供一些参考。
69 8
|
4月前
|
人工智能 前端开发 JavaScript
拿下奇怪的前端报错(一):报错信息是一个看不懂的数字数组Buffer(475) [Uint8Array],让AI大模型帮忙解析
本文介绍了前端开发中遇到的奇怪报错问题,特别是当错误信息不明确时的处理方法。作者分享了自己通过还原代码、试错等方式解决问题的经验,并以一个Vue3+TypeScript项目的构建失败为例,详细解析了如何从错误信息中定位问题,最终通过解读错误信息中的ASCII码找到了具体的错误文件。文章强调了基础知识的重要性,并鼓励读者遇到类似问题时不要慌张,耐心分析。
|
4月前
|
Java
让星星⭐月亮告诉你,Java NIO之Buffer详解 属性capacity/position/limit/mark 方法put(X)/get()/flip()/compact()/clear()
这段代码演示了Java NIO中`ByteBuffer`的基本操作,包括分配、写入、翻转、读取、压缩和清空缓冲区。通过示例展示了`position`、`limit`和`mark`属性的变化过程,帮助理解缓冲区的工作原理。
63 2
|
5月前
|
索引 Python
XPath解析之获取属性
XPath解析(三)
91 10
|
6月前
|
数据采集 API 开发工具
淘系商品详情数据解析(属性youhui券sku详情图等)API接口开发系列
在电商领域,特别是像淘宝(淘系)这样的平台,商品详情数据对于商家、开发者以及数据分析师来说至关重要。这些数据包括但不限于商品属性、优惠券信息、SKU(Stock Keeping Unit)详情、商品图片、售后保障等。然而,直接访问淘宝的内部API接口通常需要特定的权限和认证,这通常只对淘宝的合作伙伴或内部开发者开放。 不过,对于需要这些数据的第三方开发者或商家,有几种方式可以间接获取或解析淘系商品详情数据: ——在成长的路上,我们都是同行者。这篇关于商品详情API接口的文章,希望能帮助到您。期待与您继续分享更多API接口的知识,请记得关注Anzexi58哦!
|
6月前
|
JavaScript 前端开发 算法
【Vue秘籍揭秘】:掌握这一个技巧,让你的列表渲染速度飙升!——深度解析`key`属性如何成为性能优化的秘密武器
【8月更文挑战第20天】Vue.js是一款流行前端框架,通过简洁API和高效虚拟DOM更新机制简化响应式Web界面开发。其中,`key`属性在列表渲染中至关重要。本文从`key`基本概念出发,解析其实现原理及最佳实践。使用`key`帮助Vue更准确地识别列表变动,优化DOM更新过程,确保组件状态正确维护,提升应用性能。通过示例展示有无`key`的区别,强调合理使用`key`的重要性。
79 3
|
6月前
|
JSON 前端开发 API
【淘系】商品详情属性解析(属性规格详情图sku等json数据示例返回参考),淘系API接口系列
在淘宝(或天猫)平台上,商品详情属性(如属性规格、详情图、SKU等)是商家在发布商品时设置的,用于描述商品的详细信息和不同规格选项。这些信息对于消费者了解商品特性、进行购买决策至关重要。然而,直接通过前端页面获取这些信息的结构化数据(如JSON格式)并非直接暴露给普通用户或开发者,因为这涉及到平台的商业机密和数据安全。 不过,淘宝平台提供了丰富的API接口(如淘宝开放平台API),允许有资质的开发者或合作伙伴通过编程方式获取商品信息。这些API接口通常需要注册开发者账号、申请应用密钥(App Key)和秘钥(App Secret),并遵守淘宝的API使用协议。
|
6月前
|
数据采集 JSON API
淘系商品详情图属性sku价格解析,API接口系列
淘宝(Taobao)作为阿里巴巴集团旗下的电商平台,其商品详情图、属性、SKU和价格的采集通常不直接通过公开的API接口来实现,因为淘宝的API主要面向商家和开发者提供店铺管理、订单处理、物流查询等功能,并不直接提供商品详情页的完整数据抓取接口
|
6月前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
213 0
|
6月前
|
存储 网络协议 Java
【Netty 神奇之旅】Java NIO 基础全解析:从零开始玩转高效网络编程!
【8月更文挑战第24天】本文介绍了Java NIO,一种非阻塞I/O模型,极大提升了Java应用程序在网络通信中的性能。核心组件包括Buffer、Channel、Selector和SocketChannel。通过示例代码展示了如何使用Java NIO进行服务器与客户端通信。此外,还介绍了基于Java NIO的高性能网络框架Netty,以及如何用Netty构建TCP服务器和客户端。熟悉这些技术和概念对于开发高并发网络应用至关重要。
115 0

热门文章

最新文章

推荐镜像

更多