一、NIO简介

xiaoxiao2021-03-01  40

NIO简介 javaNIO是有java1.4之后引入的一个新的IO API,可以提完标准的IO API,NIO与IO有相同的作用和目的,但使用方式完全不一样,NIO面向缓冲区、基于通道的IO操作,将以更加高效的方式进行文件读写操作。

IO NIO 面向流 面向缓冲区 阻塞 非阻塞 无 选择器

NIO:核心在于通道(channel)和缓冲区(buffer)。

通道表示打开到io设备(例如:文件、套接字)的连接若需要使用NIO系统,需要获取用于连接设备的通道以及用于容纳数据的缓存区,然后操作缓存区,对数据进行处理。

简单说,channel负责传输,buffer负责存储

一、缓冲区(Buffer):在 Java NIO 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据

1.根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:

ByteBuffer

CharBuffer

ShortBuffer

IntBuffer

LongBuffer

FloatBuffer

DoubleBuffer

上述七种缓冲区的管理方式几乎一致,通过 allocate() 获取缓冲区

ByteBuffer buf = ByteBuffer.allocate(1024);

2.缓冲区存取数据的两个核心方法:

put() : 存入数据到缓冲区中

get() : 获取缓冲区中的数据

@Test public void test2(){ ByteBuffer bf = ByteBuffer.allocate(1024);//创建一个缓冲区 bf.put("abc".getBytes());//往缓冲区放入数据 bf.flip(); //切换操作模式 byte[] dst = new byte[bf.limit()];//创建一个以缓冲区中存放数据大小相同的字节数组 bf.get(dst,0,dst.length);//从0的位置获取的数组长度的位置数据到dst数组中 System.out.println(new String(dst));//字节数据转换为字符窜打印 //abc }

3.缓冲区中的四个核心属性:

capacity : 容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变。

limit : 界限,表示缓冲区中可以操作数据的大小。(limit 后数据不能进行读写)

position : 位置,表示缓冲区中正在操作数据的位置。

mark : 标记,表示记录当前 position 的位置。可以通过 reset() 恢复到 mark 的位置

0 <= mark <= position <= limit <= capacity

public abstract class Buffer { private int mark = -1;//标记 private int position = 0;//当前位置 private int limit; //可操作空间大小 private int capacity; //容量 }

操作演示:

@Test public void test3(){ ByteBuffer bf = ByteBuffer.allocate(1024); System.out.println("---------未放入数据时---------"); System.out.println("bf.position()="+bf.position()); System.out.println("bf.limit()="+bf.limit()); System.out.println("bf.capacity()="+bf.capacity()); System.out.println("---------放入数据abcd---------"); bf.put("abcd".getBytes()); System.out.println("bf.position()="+bf.position()); System.out.println("bf.limit()="+bf.limit()); System.out.println("bf.capacity()="+bf.capacity()); System.out.println("---------flip()切换操作模式---------"); bf.flip(); System.out.println("bf.position()="+bf.position()); System.out.println("bf.limit()="+bf.limit()); System.out.println("bf.capacity()="+bf.capacity()); System.out.println("---------mark()的同时读取两个字节的数据---------"); bf.mark(); bf.get(new byte[2],0,2); System.out.println("bf.position()="+bf.position()); System.out.println("bf.limit()="+bf.limit()); System.out.println("bf.capacity()="+bf.capacity()); System.out.println("---------reset()到上一次mark()位置---------"); bf.reset(); System.out.println("bf.position()="+bf.position()); System.out.println("bf.limit()="+bf.limit()); System.out.println("bf.capacity()="+bf.capacity()); } /** ---------未放入数据时--------- bf.position()=0 bf.limit()=1024 bf.capacity()=1024 ---------放入数据abcd--------- bf.position()=4 bf.limit()=1024 bf.capacity()=1024 ---------flip()切换操作模式--------- bf.position()=0 bf.limit()=4 bf.capacity()=1024 ---------mark()的同时读取两个字节的数据--------- bf.position()=2 bf.limit()=4 bf.capacity()=1024 ---------reset()到上一次mark()位置--------- bf.position()=0 bf.limit()=4 bf.capacity()=1024 */

4.直接缓冲区与非直接缓冲区:

非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中

直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率

@Test//判断缓冲区类型 public void test1(){ ByteBuffer buffer = ByteBuffer.allocate(1024); ByteBuffer buffer2 = ByteBuffer.allocateDirect(1024); }

5.其它方法

isDirect();//判断是否是直接缓冲区

flip();切换操作模式

reset();恢复到mark的位置

rewind() : 可重复读

clear() : 清空缓冲区. 但是缓冲区中的数据依然存在,但是处于“被遗忘”状态

@Test//判断缓冲区类型 public void test1(){ ByteBuffer buffer = ByteBuffer.allocate(1024); ByteBuffer buffer2 = ByteBuffer.allocateDirect(1024); System.out.println(buffer.isDirect());//false System.out.println(buffer2.isDirect());//true } @Test public void test1(){ String str = "abcde"; //1. 分配一个指定大小的缓冲区 ByteBuffer buf = ByteBuffer.allocate(1024); System.out.println("-----------------allocate()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //2. 利用 put() 存入数据到缓冲区中 buf.put(str.getBytes()); System.out.println("-----------------put()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //3. 切换读取数据模式 buf.flip(); System.out.println("-----------------flip()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //4. 利用 get() 读取缓冲区中的数据 byte[] dst = new byte[buf.limit()]; buf.get(dst); System.out.println(new String(dst, 0, dst.length)); System.out.println("-----------------get()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //5. rewind() : 可重复读 buf.rewind(); System.out.println("-----------------rewind()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //6. clear() : 清空缓冲区. 但是缓冲区中的数据依然存在,但是处于“被遗忘”状态 buf.clear(); System.out.println("-----------------clear()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); System.out.println((char)buf.get()); }

二、通道Channel

1.通道channel介绍

有java.nio.channels包定义的。channel表示IO源于目标打开的连接,Channel类似于传统的“流”。只不过Channel本身不能直接访问数据,Channel只能与Buffer进行交互。

2.通道的主要实现类

java.nio.channels.Channel 接口: |--FileChannel:用于读取、写入、映射和操作文件的通道,用于本地文件操作。 |--SocketChannel:通过TCP读写网络中的数据。 |--ServerSocketChannel:可以监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel。 |--DatagramChannel:通过UDP读写网络中的数据通道。

3.获取通道

3.1 Java 针对支持通道的类提供了 getChannel() 方法 本地 IO:用于本地数据操作 FileInputStream/FileOutputStream RandomAccessFile //以FileInputStream为例 FileInputStream fisChannel = new FileInputStream("需要读取的文件"); FileChannel fisChannel = fis.getChannel();//获取通道 网络IO:用于网络传输数据 Socket ServerSocket DatagramSocket 3.2使用JDK 1.7中的NIO.2 针对各个通道提供的静态方法open(); 调用示例 FileChannel fis = FileChannel.open(Paths.get("d:/tomcat.zip"), StandardOpenOption.READ); //源码:OpenOption... options--可变形参 public static FileChannel open(Path path, OpenOption... options) throws IOException { Set<OpenOption> set = new HashSet<OpenOption>(options.length); Collections.addAll(set, options); return open(path, set, NO_ATTRIBUTES); } 3.3在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel() //源码 public static SeekableByteChannel newByteChannel(Path path, OpenOption... options) throws IOException { Set<OpenOption> set = new HashSet<OpenOption>(options.length); Collections.addAll(set, options); return newByteChannel(path, set); } //FileChannel关系 public abstract class FileChannel extends AbstractInterruptibleChannel implements SeekableByteChannel, GatheringByteChannel, ScatteringByteChannel //示例:可知返回是SeekableByteChannel类型,SeekableByteChannelFileChannel的父接口 SeekableByteChannel newByteChannel = Files.newByteChannel(Paths.get("d:/tomcat.zip"), StandardOpenOption.READ);

4.通道之间的数据传输

transferFrom():从哪里来,输出管道中的数据从哪里来 transferTo():到哪里去,读取管道的数据要输出到哪里去 示例代码一会会有专门一篇记录

5.分散(Scatter)与聚集(Gather)

分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中 @SuppressWarnings("resource") @Test public void test5() throws IOException{ //1.创建操作对象,指定操作类型 RandomAccessFile ra1 = new RandomAccessFile("1.txt", "rw"); //2.获取通道 FileChannel channel = ra1.getChannel(); //3.创建缓冲区 ByteBuffer by1 = ByteBuffer.allocate(100); ByteBuffer by2 = ByteBuffer.allocate(100); ByteBuffer by3 = ByteBuffer.allocate(1024); ByteBuffer[] buf = {by1,by2,by3}; //4.将通道中的数据读入缓冲区 channel.read(buf); //5.操作指令转换,准备从0开始写数据模式 for (ByteBuffer byteBuffer : buf) { byteBuffer.flip(); } //打印每个分散数据值 System.out.println("-- "+new String(buf[0].array(),0,buf[0].limit())); System.out.println("-- "+new String(buf[1].array(),0,buf[1].limit())); System.out.println("-- "+new String(buf[2].array(),0,buf[2].limit())); //6.创建输出对象,指定输出数据位置,操作指令 RandomAccessFile ra2 = new RandomAccessFile("2.txt", "rw"); //7.获取通道 FileChannel channel2 = ra2.getChannel(); //8.写出数据 channel2.write(buf); //9.关闭通道 channel.close(); channel2.close(); }

6.字符集:Charset

编码:字符串 -> 字节数组 解码:字节数组 -> 字符串

查看NIO中支持的字符集

@Test public void test5(){ //查看NIO中支持的字符集 Map<String, Charset> map = Charset.availableCharsets(); Set<Entry<String, Charset>> set = map.entrySet(); for (Entry<String, Charset> entry : set) { System.out.println(entry.getKey() + "=" + entry.getValue()); } }

编码与解码演示

@Test public void test6() throws CharacterCodingException{ String str = "java从入门到放弃"; Charset charset = Charset.forName("GBK"); CharsetEncoder en = charset.newEncoder(); CharsetDecoder de = charset.newDecoder(); CharBuffer buffer = CharBuffer.allocate(1024); buffer.put(str); buffer.flip(); ByteBuffer encode = en.encode(buffer); for(int i = 0;i<encode.limit();i++){ //encode.get():调用一次position位置向后移动一次 System.out.println(encode.get()); } //切换指令准备解码,由此时get到末尾的position指向首位 encode.flip(); //2.同一对象,相同编码格式 CharBuffer decode = de.decode(encode); // System.out.println("同一对象解码器="+new String(decode.array(),0,decode.limit())); System.out.println("同一对象解码器="+decode.toString()); //2.不同对象,相同编码格式 Charset charset2 = Charset.forName("GBK"); CharsetDecoder de2 = charset2.newDecoder(); encode.flip(); CharBuffer decode2 = de2.decode(encode); System.out.println("不同对象,同一格式解码器="+decode2.toString()); //3.不同对象,不同编码格式 //切换position位置 encode.flip(); /* Charset charset3= Charset.forName("utf-8"); CharsetDecoder de3 = charset3.newDecoder(); CharBuffer decode3 = de3.decode(encode); System.out.println("不同象,不同格式解码器="+decode3.toString());*/ Charset charset3= Charset.forName("utf-8"); CharBuffer decode3 = charset3.decode(encode); System.out.println("不同对象,不同格式解码器="+decode3.toString()); /** 同一对象解码器=java从入门到放弃 不同对象,同一格式解码器=java从入门到放弃 不同对象,不同格式解码器=java�����ŵ����� */ }

来源于尚硅谷官网NIO视频

转载请注明原文地址: https://www.6miu.com/read-3936655.html

最新回复(0)