Java在1.5之后提供了新的IO通信框架呀洲,NIO和普通IO的區(qū)別是NIO是基于channel和Buffer來進行操作的兼贡,這和傳統(tǒng)的IO是有一些區(qū)別的,傳統(tǒng)的IO是基于管道流的方式進行數(shù)據(jù)傳輸吼具,而NIO的數(shù)據(jù)首先需要添加到buffer中赡勘,之后通過channel來進行傳輸。
Buffer緩沖區(qū)
首先要學會Buffer緩沖區(qū)的基本用法楼熄,不同的數(shù)據(jù)類型都是自己的緩沖區(qū)忆绰,但是在NIO中比較通用的是ByteBuffer,通過allocate和allocateDirect來創(chuàng)建緩沖區(qū)可岂,第一種緩沖區(qū)是在堆中創(chuàng)建错敢,第二種緩沖區(qū)會在操作系統(tǒng)的內(nèi)存中創(chuàng)建,第二種緩沖區(qū)占用的是系統(tǒng)的內(nèi)存資源缕粹,創(chuàng)建和銷毀都需要一定的開銷稚茅,在使用channel的時候能夠提高一定的效率纸淮。這里先介紹Buffer的用法
//創(chuàng)建了一個8個字節(jié)的緩沖區(qū)
ByteBuffer buffer = ByteBuffer.allocate(8);
緩沖區(qū)中有四個比較有用的屬性
- capacity:緩沖區(qū)的大小,此時是8個字節(jié)
- limit:緩沖區(qū)的目前的可存儲量亚享,如果不做任何操作咽块,該值用來表示緩沖區(qū)當前的存儲位置,如果存儲之后超過這個值就不能再存儲欺税。
- position:即將被讀寫的緩沖區(qū)的位置的索引侈沪。
- mark:一個游標對象,使用mark()方法可以將mark設置到position的位置峭竣,使用reset()方法可以將position設置為mark的值晃虫。
下面通過代碼來了解緩沖區(qū)
//此時capacity是8,limit是8,position是0
System.out.println(buffer.capacity()+","+buffer.limit()+","+buffer.position());
沒有任何數(shù)據(jù)的時候capactiy是8,說明是緩沖區(qū)的總大小哲银,limit是8扛吞,表示可以存儲到8個字節(jié)的位置,position表示緩沖區(qū)當前可以直接寫入的索引荆责,下面寫一些數(shù)據(jù)看看
//寫入hello
buffer.put("hello".getBytes());
//此時capacity是8,limit是8,position是5
System.out.println(buffer.capacity()+","+buffer.limit()+","+buffer.position());
//還存在3個元素的數(shù)量
System.out.println(buffer.remaining());
//存儲空間不夠,報錯
buffer.put("this".getBytes());
添加了數(shù)據(jù)之后limit的值不變做院,position的位置調(diào)整了,使用remaining可以獲取還能存儲的數(shù)量键耕。下面我們來看limit的作用寺滚,limit主要是在讀取的時候有用,要讀取信息屈雄,可以將position設置到0,之后通過get方法來讀取酒奶。nio中提供了flip()方法來反轉緩沖區(qū),反轉之后limit會指向當前緩沖區(qū)的最大值杠氢。
buffer.flip();
//此時capacity是8,limit是5,position是0
System.out.println(buffer.capacity()+","+buffer.limit()+","+buffer.position());
byte[] buf = new byte[buffer.limit()];
buffer.get(buf,0,buffer.limit());
System.out.println(new String(buf));
//讀完之后position的值和limit一樣
System.out.println(buffer.capacity()+","+buffer.limit()+","+buffer.position());
//再寫入一個值另伍,由于已經(jīng)到了limit的位置,就不能再寫入了,寫入就會報錯
buffer.put((byte)('A'));
此時通過clear()可以重置position為0愕宋,而且設置limit為capacity中贝。
buffer.clear();
//capactiy為8,limit為8邻寿,position為0
System.out.println(buffer.capacity()+","+buffer.limit()+","+buffer.position());
//可以讀取值,說明clear并不會清空緩沖區(qū)
System.out.println((char)buffer.get());
buffer.clear();//清空
最后看看mark和reset,當在某個位置是調(diào)用mark()方法誊涯,會將mark設置為當前的position蒜撮,此時可以通過reset回復到原來的mark的位置
buffer.put("abc".getBytes());
buffer.mark();//設置mark的標記,此時mark這個變量為position
buffer.put("yes".getBytes());//在abc之后加入yes
buffer.reset();//此時position會設置到mark
System.out.println((char)buffer.get());////讀取的值是y
Channel的講解
NIO中是通過Channel來傳輸buffer的數(shù)據(jù)段磨,操作和IO類似,但多了將Buffer添加到Channel的步驟
public class TestChannel {
public static void main(String[] args) {
FileChannel fc = null;
try {
//創(chuàng)建文件管道
fc = FileChannel.open(Paths.get("d:/test/01.txt"), StandardOpenOption.READ);
//創(chuàng)建buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = 0;
byte[] buf = new byte[1024];
//只要能夠從緩沖區(qū)中讀取數(shù)據(jù)
while((len=fc.read(buffer))>0) {
//重置緩沖區(qū)
buffer.flip();
//從緩沖區(qū)讀取到字節(jié)數(shù)組中
buffer.get(buf,0,len);
//輸出字節(jié)數(shù)組的值
System.out.println(new String(buf,0,len));
//重置緩沖區(qū)
buffer.clear();
}
fc.read(buffer);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fc!=null) fc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
以上程序實現(xiàn)了通過一個文件管道讀數(shù)據(jù),此時沒有輸入和輸出概念债蜜,如果希望把一個文件讀到另外一個文件中,需要再創(chuàng)建一個通道來寫文件儒洛。
public class TestChannel02 {
public static void main(String[] args) {
FileChannel fin = null;
FileChannel fout = null;
ByteBuffer buf = null;
try {
//創(chuàng)建兩個通道特姐,一個讀數(shù)據(jù)黍氮,一個寫數(shù)據(jù)
fin = FileChannel.open(Paths.get("d:/test/01.jpg"), StandardOpenOption.READ);
//設置文件如果不存在就創(chuàng)建,并且可以進行寫操作
fout = FileChannel.open(Paths.get("d:/test/02.jpg"),StandardOpenOption.CREATE,StandardOpenOption.WRITE);
//創(chuàng)建緩沖區(qū)來作為數(shù)據(jù)的中轉
buf = ByteBuffer.allocate(1024);
while((fin.read(buf))>0) {
buf.flip();
fout.write(buf);
buf.clear();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fin!=null) fin.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fout!=null) fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
在NIO中提供了transferTo方法來快速將兩個通道進行轉換
public class TestChannel03 {
public static void main(String[] args) {
FileChannel fin = null;
FileChannel fout = null;
try {
fin = FileChannel.open(Paths.get("d:/test/01.jpg"), StandardOpenOption.READ);
fout = FileChannel.open(Paths.get("d:/test/03.jpg"), StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE);
//通過transferTo可以將兩個通道對接
fin.transferTo(0, fin.size(), fout);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fin!=null) fin.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fout!=null) fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
以上就是NIO的基本操作,難度不大专执,NIO目前比較的優(yōu)勢在于網(wǎng)絡通信中,下一部分將會講解基于網(wǎng)絡通信的NIO操作攀痊。