一句话简介:相对于传统IO,NIO(New IO)新IO的读写是基于buffer的。对于网络编程来说,NIO通过了选择器实现了非阻塞编程。
从Java1.4开始就有了NIO,随着程序对性能的要求越来越高,NIO得到了广泛使用。IO与NIO的区别如下:
IO | NIO |
面向流 | 面向缓冲区 |
阻塞 | 非阻塞 |
/ | 有选择器 |
第一点不同:面向缓冲区。
以下代码分为两个部分,第一部分为Buffer的日常调用。第二部分为使用Buffer进行IO操作,其中穿插讲了编码及解码。
Buffer常用API:
package com.example.nio;
import org.junit.Test;
import java.nio.Buffer;
import java.nio.ByteBuffer;
/**
* 一、缓冲区(buffer):在Java NIO中负责数据的存取,缓冲区就是数组,用于存储不同数据类型的数据
* <p>
* 根据数据类型的不同(boolean 除外),提供了相应类型的缓冲区:
* ByteBuffer
* IntBuffer
* LongBuffer
* ShortBuffer
* FloatBuffer
* DoiubleBuffer
* CharBuffer
* <p>
* 上述缓冲区的管理方式几乎一致,通过allocate*()获取缓冲区
* <p>
* 二、缓冲区存取数据的两个核心方法
* put():存入数据到缓冲区
* get():获取缓冲区数据。
* <p>
* 三、缓冲区中的四个核心属性、
* capacity:容量,
* limit:界限,表示缓冲区中可以操作数据的大小,(limit后的数据不能进行读写)
* position:位置,表示缓冲区中正在操作数据的位置
* mark:标记,表示记录当前position的位置,可以通过reset()使position恢复到mark位置。
* 0 <= mark <= position <= limit <=capacity
*/
public class TestBuffer {
@Test
public void test2() {
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("abcde".getBytes());
System.out.println(buffer.position());
buffer.flip();
byte[] dts = new byte[buffer.limit()];
buffer.get(dts, 0, 2);
System.out.println(new String(dts,0,2));
System.out.println(buffer.position());
//mark标记一下
buffer.mark();
buffer.get(dts, 2, 2);
System.out.println(new String(dts,2,2));
System.out.println(buffer.position());
//恢复position到mark标记处
buffer.reset();
buffer.get(dts, 4, 1);
System.out.println(new String(dts,4,1));
System.out.println(buffer.position());
System.out.println(new String(dts));
//判断缓冲区是否还有剩余数据
if (buffer.hasRemaining()){
//获取缓冲区中剩余数据数量
System.out.println(buffer.remaining());
}
}
@Test
public void test() {
//1、分配一个制定大小的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
System.out.println("---------------------allocate()------------------------------");
System.out.println(buffer.position());
System.out.println(buffer.limit());
System.out.println(buffer.capacity());
//2、使用put()存入数据到缓冲区中
String string = "abcde";
buffer.put(string.getBytes());
System.out.println("---------------------put()------------------------------");
System.out.println(buffer.position());
System.out.println(buffer.limit());
System.out.println(buffer.capacity());
//3、切换读取数据模式
buffer.flip();
System.out.println("---------------------flip()------------------------------");
System.out.println(buffer.position());
System.out.println(buffer.limit());
System.out.println(buffer.capacity());
//4、读取数据
byte[] dst = new byte[buffer.limit()];
buffer.get(dst);
System.out.println(new String(dst, 0, dst.length));
System.out.println("---------------------get()------------------------------");
System.out.println(buffer.position());
System.out.println(buffer.limit());
System.out.println(buffer.capacity());
//5、rewind():可重复度
buffer.rewind();
System.out.println("---------------------rewid()------------------------------");
System.out.println(buffer.position());
System.out.println(buffer.limit());
System.out.println(buffer.capacity());
//6、clear(),清空缓冲区,缓冲区中的数据还存在, 但是出于“被遗忘”状态。
buffer.clear();
buffer.rewind();
System.out.println("---------------------clear()------------------------------");
System.out.println(buffer.position());
System.out.println(buffer.limit());
System.out.println(buffer.capacity());
System.out.println((char) buffer.get());
}
}
Channel常用API
package com.example.nio;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Set;
/**
* 一、通道(Channel):用于原节点与目标节点的连接。在Java NIO中负责缓冲区中数据的传输。
* Channel本身不存储数据,需要配合缓冲区buffer进行传输。
* <p>
* 二、通道的主要实现类
* Java.nio.channels.Channel 接口
* |--FileChannel
* |--SocketChannel
* |--ServerSocketChannel
* |--DatagramChannel
* <p>
* 三、获取通道
* 1.Java 针对支持通道的类提供了getChannel()方法
* 本地IO:
* FileInputStream/FileOutputStream
* RandomAccessFile
* <p>
* 网络IO:
* Socket
* ServerSocket
* DatagramSocket
* <p>
* 2.在JDK 1.7中NIO.2 针对各个通道提供了静态方法 open()
* <p>
* 3.在JDK1.7中NIO.2的Files 工具类的 newByteChannel()
* <p>
* 四、.通道之间的数据传输
* transferFrom()
* transferTo()
* <p>
* 五、分散(Scatter)与聚集(Gather)
* 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
* 聚集写入(Gather Writers):将多个缓冲区中的数据聚集到一个通道中
*
* 六、字符集:Charsert
* 编码:字符串-> 字节数组
* 解码:字节数组-> 字符串
*
*/
public class TestChannel {
@Test
//字节编码、解码
public void test6() throws CharacterCodingException {
Charset cs1 = Charset.forName("GBK");
//获取编码器
CharsetEncoder ce = cs1.newEncoder();
//获取解码器
CharsetDecoder cd = cs1.newDecoder();
CharBuffer buf = CharBuffer.allocate(1024);
buf.put("架构师之路");
buf.flip();
//编码
ByteBuffer bBuf = ce.encode(buf);
for (int i = 0; i < bBuf.limit(); i++) {
System.out.println(bBuf.get());
}
bBuf.flip();
//解码
CharBuffer cbuf = cd.decode(bBuf);
System.out.println(cbuf.toString());
}
@Test
//看下提供哪些中编码解码类型。
public void test5(){
Map<String, Charset> map = Charset.availableCharsets();
Set<Map.Entry<String, Charset>> entries = map.entrySet();
for (Map.Entry<String,Charset> entry: entries) {
System.out.println(entry.getValue() + "==" + entry.getValue());
}
}
@Test
//分散读取与聚集写入
public void test4() throws IOException {
RandomAccessFile raf1 = new RandomAccessFile("1.txt", "rw");
//1.获取通道
FileChannel channel1 = raf1.getChannel();
//2.分配制定大小的缓冲区
ByteBuffer buf1 = ByteBuffer.allocate(100);
ByteBuffer buf2 = ByteBuffer.allocate(1024);
//3.分散读取
ByteBuffer[] bufs = {buf1, buf2};
channel1.read(bufs);
for (ByteBuffer byteBuffer : bufs) {
byteBuffer.flip();
}
System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
System.out.println("-----------------------------------------------------");
System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
//聚集写入
RandomAccessFile raf2 = new RandomAccessFile("2.txt","rw");
FileChannel channel2 = raf2.getChannel();
channel2.write(bufs);
channel1.close();
channel2.close();
}
//通道之间的数据传输(直接缓冲区)
@Test
public void test3() throws IOException {
FileChannel inChannel = FileChannel.open(Paths.get("1.txt"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("2.txt"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
// inChannel.transferTo(0,inChannel.size(),outChannel);
outChannel.transferFrom(inChannel, 0, inChannel.size());
inChannel.close();
outChannel.close();
}
//快速直接缓冲区完成文件的复制(内存映射文件)
@Test
public void test2() throws IOException {
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 inMapperBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMapperBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
//直接对缓冲区进行数据的读写操作
byte[] dts = new byte[inMapperBuf.limit()];
inMapperBuf.get(dts);
outMapperBuf.put(dts);
inChannel.close();
outChannel.close();
}
@Test
//利用通道完成文件复制(非直接缓冲区)
public void test() {
FileInputStream fis = null;
FileOutputStream fos = null;
FileChannel inChannel = null;
FileChannel outChannel = null;
try {
fis = new FileInputStream("1.jpg");
fos = new FileOutputStream("2.jpg");
//获取通道
inChannel = fis.getChannel();
outChannel = fos.getChannel();
//分配指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
//将input通道的数据放入缓冲区中
while (inChannel.read(buf) != -1) {
buf.flip();//切换读取数据的模式
//将缓冲区的数据写入output通道
outChannel.write(buf);
buf.clear();//清空缓冲区
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inChannel != null) {
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outChannel != null) {
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}