NIO的了解

1、流一旦打开,就必须关闭,使用close方法,

放入finally语句块中(finally语句块一定会执行)

多个流互相调用,只关闭最外层的流。

2、InputStreamReader的reader方法,返回所读取的字节的int类型(0-255),

read(byte[] data)将读取的字节,存储在这个数组里,返回传入数组参数个数

3、write(byte b[], int off, int len)  将指定字节传入数据源,off是第一个字节的位置,len是长度

4、JAVA序列化

是一种用来处理对象流的机制,将对象的内容进行流化。可以对流化后的对象进行读写操作,可以将流化后的对象传输于网络之间。

实现serializable接口,使用ObjectOutPutStream类的writeObject()方法

2023/4/1

Java-NIO则是JDK1.4中新引入的API,它在BIO功能的基础上实现了非阻塞式的特性,其所有实现都位于java.nio包下。NIO是一种基于通道、面向缓冲区IO操作,相较BIO而言,它能够更为高效的对数据进行读写操作,同时与原先的BIO使用方式也大有不同。

   Java-NIO是基于多路复用模型实现的,其中存在三大核心理念:Buffer(缓冲区)、Channel(通道)、Selector(选择器),与BIO还有一点不同在于:由于BIO模型中数据传输是阻塞式的,因此必须得有一条线程维护对应的Socket连接,在此期间如若未读取到数据,该线程就会一直阻塞下去。而NIO中则可以用一条线程来处理多个Socket连接,不需要为每个连接都创建一条对应的线程维护。

Buffer缓冲区

缓冲区其实本质上就是一块支持读/写操作的内存,底层是由多个内存页组成的数组,我们可以将其称之为内存块,在Java中这块内存则被封装成了Buffer对象,需要使用可直接通过已提供的API对这块内存进行操作和管理。

// 缓冲区抽象类

public abstract class Buffer {

  • // 标记位,与mark()、reset()方法配合使用,
  • // 可通过mark()标记一个索引位置,后续可随时调用reset()恢复到该位置
  • private int mark = -1;
  • // 操作位,下一个要读取或写入的数据索引
  • private int position = 0;
  • // 限制位,表示缓冲区中可允许操作的容量,超出限制后的位置不能操作
  • private int limit;
  • // 缓冲区的容量,类似于声明数组时的容量
  • private int capacity; long address;
  • // 清空缓冲区数据并返回对缓冲区的引用指针
  • // (其实调用该方法后缓冲区中的数据依然存在,只是处于不可访问状态)
  • // 该方法还有个作用:就是调用该方法后会从读模式切换回写模式
  • public final Buffer clear();
  • // 调用该方法后会将缓冲区从写模式切换为读模式
  • public final Buffer flip();
  • // 获取缓冲区的容量大小
  • public final int capacity();
  • // 判断缓冲区中是否还有数据
  • public final boolean hasRemaining();
  • // 获取缓冲区的界限大小
  • public final int limit();
  • // 设置缓冲区的界限大小
  • public final Buffer limit(int n);
  • // 对缓冲区设置标记位
  • public final Buffer mark();
  • // 返回缓冲区当前的操作索引位置
  • public final int position();
  • // 更改缓冲区当前的操作索引位置
  • public final Buffer position(int n);
  • // 获取当前索引位与界限之间的元素数量
  • public final int remaining();
  • // 将当前索引转到之前标记的索引位置
  • public final Buffer reset();
  • // 重置操作索引位并清空之前的标记
  • public final Buffer rewind();
  • // 省略其他不常用的方法.....

}

对于Java中缓冲区的定义,首先要明白,当缓冲区被创建出来后,同一时刻只能处于读/写中的一个状态,同一时间内不存在即可读也可写的情况。理解这点后再来看看它的成员变量,重点理解下述三个成员:

  • pasition:表示当前操作的索引位置(下一个要读/写数据的下标)。
  • capacity:表示当前缓冲区的容量大小。
  • limit:表示当前可允许操作的最大元素位置(不是下标,是正常数字)。

pasition是变化的,每次都会记录着下一个要操作的索引下标,当发生模式切换时,操作位会置零,因为模式切换代表新的开始。

重点记住clear()、flip()方法,这两个方法都可以让缓冲区发生模式转换,flip()可以从写模式切换到读模式,而clear()方法本质上是清空缓冲区的意思,但清空后就代表着缓冲区回归“初始化”了,因此也可以从读模式转换到最初的写模式。

不过要注意:Buffer类仅是一个抽象类,所以并不能直接使用,因此当我们需要使用缓冲区时,需要实例化它的子类,但它的子类有几十之多,但一般较为常用的子类就只有八大基本数据类型的缓冲区,如ByteBuffer、CharBuffer、IntBuffer......

Buffer缓冲区的使用方式

当需要使用缓冲区时,都是通过xxxBuffer.allocate(n)的方式创建 

ByteBuffer buffer = ByteBuffer.allocate(10);

表示创建一个容量为10ByteBuffer缓冲区,当需要使用该缓冲区时,都是通过其提供的get/put类方法进行操作,这也是所有Buffer子类都会提供的两类方法

// 读取缓冲区中的单个元素(根据position决定读取哪个元素)
public abstract xxx get();
// 读取指定索引位置的字节(不会移动position)
public abstract xxx get(int index);
// 批量读取多个元素放入到dst数组中
public abstract xxxBuffer get(xxx[] dst);
// 根据指定的偏移量(起始下标)和长度,将对应的元素读取到dst数组中
public abstract xxxBuffer get(xxx[] dst, int offset, int length);

// 将单个元素写入缓冲区中(根据position决定写入位置)
public abstract xxxBuffer put(xxx b);
// 将多个元素写入缓冲区中(根据position决定写入位置)
public abstract xxxBuffer put(xxx[] src);
// 将另一个缓冲区写入进当前缓冲区中(根据position决定写入位置)
public abstract xxxBuffer put(xxxBuffer src);
// 向缓冲区的指定位置写入单个元素(不会移动position)
public abstract xxxBuffer put(int index, xxx b);
// 根据指定的偏移量和长度,将多个元素写入缓冲区中
public abstract xxxBuffer put(xxx[] src, int offset, int length);

一般在使用缓冲区的时候都会遵循如下步骤:

  • ①先创建对应类型的缓冲区
  • ②通过put这类方法往缓冲区中写入数据
  • ③调用flip()方法将缓冲区转换为读模式
  • ④通过get这类方法从缓冲区中读取数据
  • ⑤调用clear()、compact()方法清空缓冲区数据

Buffer缓冲区的分类

Java中的缓冲区也被分为了两大类:本地直接内存缓冲区与堆内存缓冲区,前面Buffer类的所有子实现类xxxBuffer本质上还是抽象类,每个子抽象类都会有DirectXxxBuffer、HeapXxxBuffer两个具体实现类,这两者的主要区别在于:创建缓冲区的内存是位于堆空间之内还是之外。

一般情况下,直接内存缓冲区的性能会高于堆内存缓冲区,但申请后却需要自行手动管理,不像堆内存缓冲区由于处于堆空间中,会有GC机制自动管理,所以直接内存缓冲区的安全风险要高一些。两者之间的工作原理如下:

由于堆缓冲区创建后是存在于堆空间中的,所以IO数据必须要经过一次本地内存的“转发后”才能达到堆内存,因此效率自然会低一些,同时也会占用Java堆空间。所以如若追求更好的IO性能,或IO数据过于庞大时,可通过xxxBuffer.allocateDirect()方法创建本地缓冲区使用,也可以通过isDirect()方法来判断一个缓冲区是否基于本地内存创建。

参考文档:Javadoop_首页

Java程序员的网络编程 - 竹子爱熊猫的专栏 - 掘金

        ByteBuffer allocate = ByteBuffer.allocate(1024);
        allocate.put((byte) 48);
        allocate.put((byte) 49);
        allocate.put((byte) 50);
        allocate.put((byte) 51);  //position=4;limit=1024
        allocate.flip();   //写模式转换成读模式   position=0;limit=4
        allocate.get();    //返回48 position变成1
        allocate.get();    //返回49 position变成2
        allocate.get();    //返回50 position变成3
        allocate.array(); //返回数组 position没影响
        //capacity一直1024

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值