- 1,NIO的简介
- 2,NIO和IO的区别
- 3,缓冲区(Buffer)的数据存取
- 4,通道(Channel)的原理与获取
- 5,分散(Scatter)读取与聚集(Gather)写入
- 6,字符集:Charset
NIO的简介
- Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。
NIO和IO的区别
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200305212314870.jpg)
缓冲区(Buffer)的数据存取
- 在Java NIO中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据
- 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
上述缓冲区的管理方式几乎一致,通过allocate()获取缓冲区
- 缓冲区存取数据的两个核心方法:
put():存入数据到缓冲区中
get():获取数据到缓冲区中
- 缓冲区中的四个核心属性:
①,capacity:容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变
②,limit:界限,表示缓冲区中可以操作数据的大小。(limit 后数据不能进行读写)
③,position:位置,表示缓冲区中正在操作的位置。
④,mark:标记,表示记录当前position的位置。可以通过reset()恢复到mark的位置
0 <= position <= limit <= capacity
- 缓冲区的基本属性
- Buffer的常用方法
代码如下:
@Test
public void test01(){
String str = "abcde";
//1,分配一个指定大小的缓存区
ByteBuffer buffer = ByteBuffer.allocate(1024);
System.out.println(buffer.position());//0
System.out.println(buffer.limit());//1024
System.out.println(buffer.capacity());//1024
//2,利用put()存入数据到缓冲区中
buffer.put(str.getBytes());
//3,利用flip()读取数据模式
buffer.flip();
//4,利用get()读取缓冲区中的数据
byte[] dst = new byte[buffer.limit()];
buffer.get(dst);
System.out.println(new String(dst, 0, dst.length));
//5,rewid():可重复度
buffer.rewind();
//6,clear():清空缓冲区,但是缓冲区中的数据依然存在,但处于“被遗忘”的状态,被遗忘--里面的界限,位置都变成了最初状态,不能正确读取数据
buffer.clear();
}
- 直接缓冲区和非直接缓冲区
①,非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中。如下图:
②,直接缓冲区:通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中,可以提高效率。如下图:
通道(Channel)的原理与获取
- 通道(Channel):用与源节点与目标节点的连接。在javaNIO中负责缓冲区中的数据的传输。Channel本身不存储数据,因此需要配合缓冲区进行传输
- Java 为 为 Channel 接口提供的最主要实现类如下:
①,FileChannel:用于读取、写入、映射和操作文件的通道。
②,DatagramChannel:通过 UDP 读写网络中的数据通道。
③,SocketChannel:通过 TCP 读写网络中的数据。
④,ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
- 获取通道
①,Java针对通道的类提供了getChannel() 方法。如下:
本地IO:
FileInputStream
FileOutputStream
RandomAccessFile
网络IO:
DatagramSocket
Socket
ServerSocket
②,在JDK1.7中的NIO.2针对各个通道提供了静态方法open()
③,在JDK1.7中的NIO.2的Files工具类的newByteChannel()
- 利用通道完成文件的复制(非直接缓冲区)。代码如下
@Test
public void test01() throws Exception{
FileInputStream fis = new FileInputStream("1.jpg");
FileOutputStream fos = new FileOutputStream("2.jpg");
//1,获取通道
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel();
//2,分配指定大小的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//3,将通道中的数据存入缓冲区中
while (inChannel.read(buffer) != -1){
buffer.flip();//切换读取数据库模式
//4,将缓冲区中的数据写入通道中
outChannel.write(buffer);
buffer.clear();//清空缓冲区
}
outChannel.close();
inChannel.close();
fis.close();
fos.close();
}
- 使用直接缓冲区完成文件的复制(内存映射文件)。代码如下:
@Test
public void test02() throws Exception {
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("3.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
//内存映射文件
MappedByteBuffer inMapped = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMapped = outChannel.map(FileChannel.MapMode.READ_WRITE,0,inChannel.size());
//直接对缓冲区进行数据的读写操作
byte[] bytes = new byte[inMapped.limit()];
inMapped.get(bytes);
outMapped.put(bytes);
inChannel.close();
outChannel.close();
}
- 通道之间的数据传输。代码如下:
//(直接缓冲区)
@Test
public void test03() throws Exception {
FileChannel inChannel = FileChannel.open(Paths.get("D://1.mkv"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("D://2.mkv"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
// inChannel.transferTo(0,inChannel.size(),outChannel);
outChannel.transferFrom(inChannel,0, inChannel.size());
inChannel.close();
outChannel.close();
}
分散(Scatter)读取与聚集(Gather)写入
-
分散读取(Scattering Reads) 是指从 Channel 中读取的数据“分散”到多个 Buffer 中。
注意:按照缓冲区的顺序,从 Channel 中读取的数据依次将 Buffer 填满。
-
聚集写入(Gathering Writes) 是指将多个 Buffer 中的数据“聚集”到 Channel。
注意:按照缓冲区的顺序,写入 position 和 limit 之间的数据到 Channel 。代码如下:
//分散和聚集
@Test
public void test04() throws Exception {
RandomAccessFile raf1 = new RandomAccessFile("1.txt","rw");
//1,获取通道
FileChannel channel = raf1.getChannel();
//2,分配指定大小的缓冲区
ByteBuffer buffer1 = ByteBuffer.allocate(100);
ByteBuffer buffer2 = ByteBuffer.allocate(1024);
//3,分散读取
ByteBuffer[] buffers = {buffer1,buffer2};
channel.read(buffers);
for (ByteBuffer byteBuffer : buffers){
byteBuffer.flip();
}
System.out.println(new String(buffers[0].array(),0, buffers[0].limit()));
System.out.println(new String(buffers[1].array(), 0, buffers[1].limit()));
//聚集写入
RandomAccessFile raf2 = new RandomAccessFile("2.txt","rw");
FileChannel channel2 = raf2.getChannel();
channel2.write(buffers);
}
字符集:Charset
- 编码:字符串 --> 字节数组
- 解码:字节数组 --> 字符串
编码和解码支持的字符集。代码如下:
//编码和解码支持的字符集
@Test
public void test06() throws Exception {
Map<String, Charset> map = Charset.availableCharsets();
Set<Map.Entry<String, Charset>> set = map.entrySet();
for (Map.Entry<String, Charset> entry : set) {
System.out.println(entry.getKey()+ "=" + entry.getValue());
}
}
编码和解码。代码如下:
//编码和解码
@Test
public void test05() throws Exception {
Charset gbk = Charset.forName("GBK");
//获取编码器
CharsetEncoder encoder = gbk.newEncoder();
//获取解码器
CharsetDecoder decoder = gbk.newDecoder();
CharBuffer charBuffer = CharBuffer.allocate(1024);
charBuffer.put("Tommey周");
charBuffer.flip();
//编码
ByteBuffer encodeBuffer = encoder.encode(charBuffer);
for (int i = 0; i < 8; i++) {
System.out.println(encodeBuffer.get());
}
//解码
encodeBuffer.flip();
CharBuffer decodeBuffer = decoder.decode(encodeBuffer);
System.out.println(decodeBuffer.toString());
//以GBK编码UTF-8解码 出现乱码
Charset utf8 = Charset.forName("UTF-8");
encodeBuffer.flip();
CharBuffer decodeBuffer1 = utf8.decode(encodeBuffer);
System.out.println(decodeBuffer1.toString());
}