JAVA-----NIO基础与Chatset字符集

NIO

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

NIO基础

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

NIO和多路复用的区别

IO 模型
  • 同步阻塞IOBlocking IO):即传统的IO模型
  • 同步非阻塞IONon-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置NONBLOCK。注意这里所说的NIO并非JavaNIONew IO)库
  • 多路复用IOIO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IOJava中的SelectorLinux中的epoll都是这种模型
  • 异步IOAsynchronous 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 LongBufferFloatBuffer 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的标记位置,只能在0position之间做标记
  • 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不应该通过构造器来直接创建,而是通过传统的节点InputStreamOutputStreamgetChannel方法来返回对应的Channel
  • 常用的是FileInputStreamFileOutputStreamgetChannel()返回的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<String,Charset> 获取当前JDK所支持的所有字符集
字符串别名
  1. GBK简体中文
  2. ISO-8859-1拉丁文
  3. UTF-88UCS转换格式
Charset c=Charset.forName("GBK");
Java7 新增 StandardCharsets 类,其中包含了 ISO_8859_1 UTF_8 UTF-16 等类变量,这些类变量代表了最常见的字符集对应的 Charset 对象
  • newDecoder():CharsetDecoder获取该编码字符集对应的解码器
  1. decode(ByteBuffer):CharBuffer方法可以将字节序列ByteBuffer转换为CharBuffer字符序列
  • newEncoder():CharsetEncoder获取该编码字符集对应的编码器
  1. 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值