一句話簡介:相對於傳統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();
}
}
}
}
}