java NIO

目录

NIO

NIO基础

NIO和多路复用的区别

Buffer的使用

写文件操作 

 读文件操作

 Buffer类的常用方法

 Channel类的常用方法

 写入数据

 selector

基本用法

Chatset字符集

Charset类 


NIO


 从JDK1.4开始Java引入了一系列改进的输入/输出处理的新功能,统称为NIO,即新IO,新增了许多用于 处理输入输出的类,新IO采用内存映射文件的方式处理输入输出,新IO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件,这种方式进行输入输出比传统的输入输出快的多

NIO基础

Channel通道和Buffer缓冲是NIO中的两个核心对象

  • Chanel是对传统输入输出系统的模拟,通过map方法可以将一块数据映射到内存中
  • Buffer本质是一个数组,发送到Channel中的所有对象都必须先放到Buffer中,而从Channel中读 取的数据必须先放到Buffer中

NIO和多路复用的区别

IO模型

  • 同步阻塞IO(Blocking IO):即传统的IO模型
  • 同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置 为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库
  • 多路复用IO(IO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的 Selector和Linux中的epoll都是这种模型
  • 异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。

java NIO就是采用多路复用IO模式

在多路复用IO模型中,会有一个线程(Java中的Selector)不断去轮询多个socket的状态,只有当 socket真正有读写事件时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个 线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只 有在真正有socket读写事件进行时,才会使用IO资源,所以它大大减少了资源占用。

Buffer的使用

Buffer是一个抽象类,主要作用是用于装入数据,然后输出数据

  • 最常见的子类ByteBuffer可以在底层字节数组上进行get/set操作
  • 其它基本数据类型都有对应的Buffer类:CharBuffer、 ShortBuffer、 IntBuffer、 LongBuffer、 FloatBuffer、 DoubleBuffer

静态方法static XxxBuffer allocate(int capacity)创建一个容量为capacity的XxxBuffer对象 Buffer中有3个重要概念:容量capacity、界限limit和位置position

  • 容量capacity表示该Buffer的最大数据容量,创建后则不能改变
  • 界限limit,位于limit后的数据既不可被读,也不可被写
  • 位置position用于指明下一个可以被读出的或者写入缓冲区的位置索引
  • 标记mark位置

Buffer的position为0,limit为capacity,程序可以通过put方法向Buffer写入一些数据,每放入一些数 据,Buffer的position响应的向后移动。

写文件操作 

String fileName = "nioFile";
try (FileOutputStream fos = new FileOutputStream(new File(fileName))) {
    FileChannel channel = fos.getChannel();
    ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode("你好你好..");
    int length = 0;
    while ((length = channel.write(byteBuffer)) != 0) {
        System.out.println("写入长度:" + length);
    }
} catch (IOException e) {
    e.printStackTrace();
}

 读文件操作

String fileName = "C:\IODemo\nioFile";
try (
    FileInputStream fis = new FileInputStream(new File(fileName));
    FileChannel channel = fis.getChannel()) {
        int capacity = 100;
        ByteBuffer byteBuffer = ByteBuffer.allocate(capacity);
        int length = -1;
        while ((length = channel.read(byteBuffer)) != -1) {
            byteBuffer.clear();
            byte[] array = byteBuffer.array();
            System.out.write(array, 0, length);
            System.out.println();
        }
} catch (FileNotFoundException e) {
    e.printStackTrace();
}

 Buffer类的常用方法

  • capacity():int返回Buffer的容量大小
  • hasRemaining():boolean判断是否还有元素可以进行处理
  • remaining():int返回当前位置和界限之间的元素个数
  • position():int返回当前操作的位置
  • mark():Buffer设置Buffer的标记位置,只能在0和position之间做标记
  • reset():Buffer将位置position转到mark所在的位置
  • rewind():Buffer将位置position设置到0,取消设置的mark
  • put(obj)用于向Buffer中放入数据
  • get()用于从Buffer中取出数据
// 创建对应的buffer对象,其中最大容积值为10
CharBuffer buffer = CharBuffer.allocate(10);
System.out.println(buffer.capacity()); // 获取buffer的容积值
capacity=10,position=0,limit=10
System.out.println(buffer.position());
System.out.println(buffer.limit());
buffer.put('中');
System.out.println(buffer.capacity()); // 10
System.out.println(buffer.position()); // 1
System.out.println(buffer.limit()); // 10
String ss="中国人民解放军";
for(int i=0;i<ss.length();i++)
buffer.put(ss.charAt(i));
buffer.flip(); // 将limit设置到position,并且把position设置为0。相当于是将buffer中
没有数据的存储位置封印起来,从而避免读取时读到不合法的数据
System.out.println(buffer.capacity()); // 10
System.out.println(buffer.position()); // 0
System.out.println(buffer.limit()); //7
char cc=buffer.get();//获取position对应的字符
System.out.println(cc); //中
System.out.println(buffer.capacity()); // 10
System.out.println(buffer.position()); // 1
System.out.println(buffer.limit()); //7
buffer.clear(); //清空buffer中的数据,并重置position和limit
System.out.println(buffer.capacity()); // 10
System.out.println(buffer.position()); // 0
System.out.println(buffer.limit()); //10
ss="中国人民解放军";
for(int i=0;i<ss.length();i++)
buffer.put(ss.charAt(i));
cc=buffer.get(2); //按照下标位置获取对应的数据,并不会操作position
System.out.println(cc);
System.out.println(buffer.position()); //7

 Channel类的常用方法

Channel可以直接将文件的部分或者全部直接映射成Buffer

注意:不能直接访问Channel中的数据,包括读取、写入都不行。Channel只能与Buffer进行交互

  • 所有Channel不应该通过构造器来直接创建,而是通过传统的节点InputStream、OutputStream 的getChannel方法来返回对应的Channel
  • 常用的是FileInputStream、FileOutputStream的getChannel()返回的FileChannel

Channel中最常用的三个方法是map()、read()和write()

File file = new File("T1.java");
FileChannel inChannel = new FileInputStream(file).getChannel();
ByteBuffer buffer = ByteBuffer.allocate(256);
while (inChannel.read(buffer) != -1) {//多次读取数据的方式从文件中获取内容
    buffer.flip(); //将没有数据的区域封印起来
    Charset charset = Charset.forName("GBK");//使用GBK的字符集创建解码器
    CharsetDecoder decoder = charset.newDecoder();
    CharBuffer cb = decoder.decode(buffer);//使用解码器将ByteBuffer转换为
CharBuffer
    System.out.println(cb);
    buffer.clear();//将position设置为0,为下一次读取数据做准备
}

 异常:java.nio.charset.MalformedInputException一般是编码转换时由于编码字符集错误导致的,可 以修改Charset中编码字符集名称解决。例如GBK、UTF-8等

map()方法将Channel对应的部分或全部数据映射成ByteBuffer 

File f = new File("T1.java");
FileChannel in = new FileInputStream(f).getChannel();
FileChannel out = new FileOutputStream("a.txt").getChannel();
MappedByteBuffer buffer = in.map(FileChannel.MapMode.READ_ONLY, 0,
f.length());//参数1为执行映射时的模式,有只读、读写模式;参数2和3用于设置哪些数据执行映
射。可以将FileChannel中全部数据映射为ByteBuffer
out.write(buffer);
buffer.clear();
Charset charset = Charset.forName("GBK");
CharsetDecoder decoder = charset.newDecoder();
CharBuffer cb = decoder.decode(buffer);
System.out.println(cb);

 写入数据

File file = new File("data/a1.txt");
int len=(int)file.length();
RandomAccessFile raf = new RandomAccessFile(file, "rw");
FileChannel channel = raf.getChannel();// 返回的channel是只读还是读写,取决于
RandomAccessFile文件对象的打开模式
ByteBuffer buffer = channel.map(MapMode.READ_ONLY, 0, len);//buffer支持可读,将
文件中的所有内容映射到buffer中
channel.position(len); //移动指针到内容末尾
channel.write(buffer); //重新写出buffer中的内容,实际上就是将文件内容拷贝

 selector

是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel通道的状态是否处于可读、可写。如 此可以实现单线程管理多个channels也就是可以管理多个网络链接。

使用Selector的好处在于: 使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上 下文切换带来的开销

  • FileChannel不能切换为非阻塞模式,更准确的来说是因为FileChannel没有继承 SelectableChannel
  • 多用于网络应用编程中

基本用法

1. Selector的创建。通过调用Selector.open()方法创建一个Selector对象 

Selector selector = Selector.open();

2. 注册Channel到Selector

channel.configureBlocking(false); SelectionKey key = channel.register(selector, Selectionkey.OP_READ); 

Channel必须是非阻塞的。

3. 轮询方式获取选择器上的状态值 

while(selector.select()>0){
    Iterator<SelectionKey> it=selector.selectedKeys().iterator();
    ...
}

Chatset字符集

所有文件在底层都是二进制文件,字符文件是系统将底层的二进制序列转换为字符,这里会涉及编码 Encoder和解码Decoder

  • 将明文的字符序列转换为计算机理解的二进制序列称为编码
  • 将二进制序列转换为明文字符串称为解码

Charset类 

availableCharsets():SortedMap 获取当前JDK所支持的所有字符集

字符串别名

  • GBK简体中文
  • ISO-8859-1拉丁文
  • UTF-8是8位UCS转换格式 

Charset c=Charset.forName("GBK"); 

Java7新增StandardCharsets类,其中包含了ISO_8859_1、UTF_8、UTF-16等类变量,这些类变量 代表了最常见的字符集对应的Charset对象

  • newDecoder():CharsetDecoder获取该编码字符集对应的解码器

        decode(ByteBuffer):CharBuffer方法可以将字节序列ByteBuffer转换为CharBuffer字符序列

  • newEncoder():CharsetEncoder获取该编码字符集对应的编码器

        encode(CharBuffer):ByteBuffer方法可以将字符序列CharBuffer转换为ByteBuffer字节序列 

Charset c1 = Charset.forName("GBK");
CharsetEncoder encoder = c1.newEncoder();
CharsetDecoder decoder = c1.newDecoder();
CharBuffer cb = CharBuffer.allocate(8);
cb.put('孙');
cb.put('误');
cb.put('空');
cb.flip();
ByteBuffer bb=encoder.encode(cb);//将CharBuffer转换为ByteBuffer
for(int i=0;i<6;i++) {
    System.out.println(bb.get(i)+" ");
}
System.out.println("====================");
System.out.println(decoder.decode(bb));//将byteBuffer转换为charBuffer

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值