JavaNIO-創(chuàng)新互聯(lián)

Java NIO

Java NIO簡介

Java NIO ( New IO )是從 Java 1.4 版本開始引入的一個新的 IO API ,
可以替代標準的 Java IO API 。NIO 與原來的 IO 有同樣的作用和目的,但是使用的方式完全不同, NIO 支持面向緩沖區(qū)的、基于通道的 IO 操作。 NIO 將以更加高效的方式進行文件的讀寫操作.

讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:主機域名、虛擬主機、營銷軟件、網(wǎng)站建設(shè)、泉州網(wǎng)站維護、網(wǎng)站推廣。

Java IO 與 NIO 的區(qū)別

IONIO
面向流(StreamOriented)面向緩沖區(qū)(BufferOriented)
阻塞IO(BlockingIO)非阻塞IO(NonBlockingIO)
(無)選擇器(Selectors)

通道(Channel)與緩沖區(qū)(Buffer)

Java NIO 系統(tǒng)的核心在于:通道 (Channel) 和緩沖區(qū)(Buffer) 。
通道表示打開到 IO 設(shè)備 ( 例如:文件、套接字 ) 的連接。若需要使用 NIO 系統(tǒng),需要獲取用于連接 IO 設(shè)備的通道以及用于容納數(shù)據(jù)的緩沖區(qū)。然后操作緩沖區(qū),對數(shù)據(jù)進行處理。
簡而言之, Channel 負責(zé)傳輸, Buffer 負責(zé)存儲

緩沖區(qū)(Buffer)

緩沖區(qū)( Buffer ):一個用于特定基本數(shù)據(jù)類型的容器。由 java.nio 包定義的,所有緩沖區(qū)都是 Buffer 抽象類的子類。
Java NIO 中的 Buffer 主要用于與 NIO 通道進行交互,數(shù)據(jù)是從通道讀入緩沖區(qū),從緩沖區(qū)寫入通道中的。
Buffer 就像一個數(shù)組,可以保存多個相同類型的數(shù)據(jù)。
根據(jù)數(shù)據(jù)類型不同 (boolean 除外 ) ,有以下 Buffer 常用子類:
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
上述 Buffer 類 他們都采用相似的方法進行管理數(shù)據(jù),只是各自
管理的數(shù)據(jù)類型不同而已。都是通過如下方法獲取一個 Buffer對象
static XxxBuffer allocate(int capacity) : 創(chuàng)建一個容量為 capacity 的 XxxBuffer 對象

緩沖區(qū)的基本屬性

Buffer 中的重要概念:

? 容量 (capacity) : 表示 Buffer 大數(shù)據(jù)容量,緩沖區(qū)容量不能為負,并且創(chuàng)
建后不能更改。

? 限制 (limit) : 第一個不應(yīng)該讀取或?qū)懭氲臄?shù)據(jù)的索引,即位于 limit 后的數(shù)據(jù)
不可讀寫。緩沖區(qū)的限制不能為負,并且不能大于其容量。

? 位置 (position) : 下一個要讀取或?qū)懭氲臄?shù)據(jù)的索引。緩沖區(qū)的位置不能為
負,并且不能大于其限制

? 標記 (mark) 與重置 (reset) : 標記是一個索引,通過 Buffer 中的 mark() 方法
指定 Buffer 中一個特定的 position ,之后可以通過調(diào)用 reset() 方法恢復(fù)到這
個 position.
標記、位置、限制、容量遵守以下不變式:
0 <= mark <= position <= limit <= capacity

緩沖區(qū)的常用方法

緩沖區(qū)存取數(shù)據(jù)的兩個核心方法:

  • put() : 存入數(shù)據(jù)到緩沖區(qū)中
  • get() : 獲取緩沖區(qū)中的數(shù)據(jù)
  • flip(); 切換讀取數(shù)據(jù)模式
  • rewind() : 可重復(fù)讀
  • clear() : 清空緩沖區(qū). 但是緩沖區(qū)中的數(shù)據(jù)依然存在,但是處于“被遺忘”狀態(tài)
  • mark() : 標記是一個索引,通過 Buffer 中的 mark() 方法
    指定 Buffer 中一個特定的 position ,之后可以通過調(diào)用 reset() 方法恢復(fù)到這
    個 position.
    Java NIO

    列:

    public void test1(){
    String str = "abcde";

    //1. 分配一個指定大小的緩沖區(qū)
    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() 存入數(shù)據(jù)到緩沖區(qū)中
    buf.put(str.getBytes());
    
    System.out.println("-----------------put()----------------");
    System.out.println(buf.position());
    System.out.println(buf.limit());
    System.out.println(buf.capacity());
    
    //3. 切換讀取數(shù)據(jù)模式
    buf.flip();
    
    System.out.println("-----------------flip()----------------");
    System.out.println(buf.position());
    System.out.println(buf.limit());
    System.out.println(buf.capacity());
    
    //4. 利用 get() 讀取緩沖區(qū)中的數(shù)據(jù)
    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() : 可重復(fù)讀
    buf.rewind();
    
    System.out.println("-----------------rewind()----------------");
    System.out.println(buf.position());
    System.out.println(buf.limit());
    System.out.println(buf.capacity());
    
    //6. clear() : 清空緩沖區(qū). 但是緩沖區(qū)中的數(shù)據(jù)依然存在,但是處于“被遺忘”狀態(tài)
    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());

    }

    Buffer的常用方法

方法描述
Buffer clear()清空緩沖區(qū)并返回對緩沖區(qū)的引用
Buffer flip()將緩沖區(qū)的界限設(shè)置為當(dāng)前位置,并將當(dāng)前位置充值為0
int capacity()返回Buffer的capacity大小
boolean hasRemaining()判斷緩沖區(qū)中是否還有元素
int limit()返回Buffer的界限(limit)的位置
Buffer limit(intn)將設(shè)置緩沖區(qū)界限為n,并返回一個具有新limit的緩沖區(qū)對象
Buffer mark()對緩沖區(qū)設(shè)置標記
int position()返回緩沖區(qū)的當(dāng)前位置position
Buffer position(int n)將設(shè)置緩沖區(qū)的當(dāng)前位置為n,并返回修改后的Buffer對象
int remaining()返回position和limit之間的元素個數(shù)
Buffer reset()將位置position轉(zhuǎn)到以前設(shè)置的mark所在的位置
Buffer rewind()將位置設(shè)為為0,取消設(shè)置的mark

緩沖區(qū)的數(shù)據(jù)操作

Buffer 所有子類提供了兩個用于數(shù)據(jù)操作的方法 get() 與 put() 方法

? 獲取 Buffer 中的數(shù)據(jù)
get() :讀取單個字節(jié)
get(byte[] dst) :批量讀取多個字節(jié)到 dst 中
get(int index) :讀取指定索引位置的字節(jié) ( 不會移動 position)
放入數(shù)據(jù)到 Buffer 中
put(byte b) :將給定單個字節(jié)寫入緩沖區(qū)的當(dāng)前位置
put(byte[] src) :將 src 中的字節(jié)寫入緩沖區(qū)的當(dāng)前位置
put(int index, byte b) :將指定字節(jié)寫入緩沖區(qū)的索引位置 ( 不會移動 position)

直接與非直接緩沖區(qū)

字節(jié)緩沖區(qū)要么是直接的,要么是非直接的。如果為直接字節(jié)緩沖區(qū),則 Java 虛擬機會盡大努力直接在
此緩沖區(qū)上執(zhí)行本機 I/O 操作。也就是說,在每次調(diào)用基礎(chǔ)操作系統(tǒng)的一個本機 I/O 操作之前(或之后),
虛擬機都會盡量避免將緩沖區(qū)的內(nèi)容復(fù)制到中間緩沖區(qū)中(或從中間緩沖區(qū)中復(fù)制內(nèi)容)。

直接字節(jié)緩沖區(qū)可以通過調(diào)用此類的 allocateDirect() 工廠方法 來創(chuàng)建。此方法返回的 緩沖區(qū)進行分配和取消
分配所需成本通常高于非直接緩沖區(qū) 。直接緩沖區(qū)的內(nèi)容可以駐留在常規(guī)的垃圾回收堆之外,因此,它們對
應(yīng)用程序的內(nèi)存需求量造成的影響可能并不明顯。所以,建議將直接緩沖區(qū)主要分配給那些易受基礎(chǔ)系統(tǒng)的
本機 I/O 操作影響的大型、持久的緩沖區(qū)。一般情況下,最好僅在直接緩沖區(qū)能在程序性能方面帶來明顯好
處時分配它們。

直接字節(jié)緩沖區(qū)還可以通過 FileChannel 的 map() 方法 將文件區(qū)域直接映射到內(nèi)存中來創(chuàng)建。該方法返回
MappedByteBuffer 。 Java 平臺的實現(xiàn)有助于通過 JNI 從本機代碼創(chuàng)建直接字節(jié)緩沖區(qū)。如果以上這些緩沖區(qū)中的某個緩沖區(qū)實例指的是不可訪問的內(nèi)存區(qū)域,則試圖訪問該區(qū)域不會更改該緩沖區(qū)的內(nèi)容,并且將會在
訪問期間或稍后的某個時間導(dǎo)致拋出不確定的異常。

字節(jié)緩沖區(qū)是直接緩沖區(qū)還是非直接緩沖區(qū)可通過調(diào)用其 isDirect() 方法來確定。提供此方法是為了能夠在
性能關(guān)鍵型代碼中執(zhí)行顯式緩沖區(qū)管理。

內(nèi)存映射文件為什么效率高

文件i/o的讀操作,會先向文件設(shè)備發(fā)起讀請求,然后驅(qū)動把請求要讀的數(shù)據(jù)讀取到文件的緩沖區(qū)中,這個緩沖區(qū)位于內(nèi)核,然后再把這個緩沖區(qū)中的數(shù)據(jù)復(fù)制到程序虛擬地址空間中的一塊區(qū)域中。

文件i/o的寫操作,會向文件設(shè)備發(fā)起寫請求,驅(qū)動把要寫入的數(shù)據(jù)復(fù)制到程序的緩沖區(qū)中,位于用戶空間,然后再把這個緩沖區(qū)的數(shù)據(jù)復(fù)制到文件的緩沖區(qū)中。

內(nèi)存映射文件,是把位于硬盤中的文件看做是程序地址空間中一塊區(qū)域?qū)?yīng)的物理存儲器,文件的數(shù)據(jù)就是這塊區(qū)域內(nèi)存中對應(yīng)的數(shù)據(jù),讀寫文件中的數(shù)據(jù),直接對這塊區(qū)域的地址操作,就可以,減少了內(nèi)存復(fù)制的環(huán)節(jié)。

所以說,內(nèi)存映射文件比起文件I/O操作,效率要高,而且文件越大,體現(xiàn)出來的差距越大。
Java NIO
Java NIO

通道(Channel)

通道( Channel ):由 java.nio.channels 包定義的。 Channel 表示 IO 源與目標打開的連接。
Channel 類似于傳統(tǒng)的“流”。只不過 Channel本身不能直接訪問數(shù)據(jù), Channel 只能與Buffer 進行交互。
Java 為 Channel 接口提供的最主要實現(xiàn)類如下
本地文件傳輸通道
FileChannel :用于讀取、寫入、映射和操作文件的通道

網(wǎng)絡(luò)數(shù)據(jù)傳輸?shù)耐ǖ?br/>DatagramChannel :通過 UDP 讀寫網(wǎng)絡(luò)中的數(shù)據(jù)通道
SocketChannel :通過 TCP 讀寫網(wǎng)絡(luò)中的數(shù)據(jù)。
ServerSocketChannel :可以監(jiān)聽新進來的 TCP 連接,對每一個新進來的連接都會創(chuàng)建一個 SocketChannel
Java NIO
Java NIO
Java NIO

獲取通道

獲取通道的一種方式是對支持通道的對象調(diào)用
getChannel() 方法。支持通道的類如下:
本地I/O
FileInputStream
FileOutputStream
RandomAccessFile

網(wǎng)絡(luò) I/O
DatagramSocket
Socket
ServerSocket

獲取通道的其他方式是使用 Files 類的靜態(tài)方法 newByteChannel() 獲取字節(jié)通道?;蛘咄ㄟ^通道的靜態(tài)方法 open() 打開并返回指定通道。
例如:
在 JDK 1.7 中的 NIO.2 針對各個通道提供了靜態(tài)方法 open()
//打開一個讀取的通道
FileChannel in = FileChannel.open(Paths.get("MyTest.java"), StandardOpenOption.READ);
//打開一個寫的通道
FileChannel out = FileChannel.open(Paths.get("MyTest.java"),StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
在 JDK 1.7 中的 NIO.2 的 Files 工具類的 newByteChannel()

通道的數(shù)據(jù)傳輸

將 Buffer 中數(shù)據(jù)寫入 Channel

例如:inChannel.read(byteBuffer)

從 Channel 讀取數(shù)據(jù)到 Buffer

例如:outChannel.write(byteBuffer);
列文件復(fù)制:
使用非直接緩沖區(qū):

public static void main(String[] args) throws IOException {
//創(chuàng)建文件輸入輸入流
FileInputStream in = new FileInputStream("短發(fā).mp3");
FileOutputStream out = new FileOutputStream("短發(fā)2.mp3");
//文件輸入輸入流的getChannel()方法獲取通道
FileChannel inChannel = in.getChannel();
FileChannel outChannel = out.getChannel();
//獲取非直接緩沖區(qū)
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//將通道中的數(shù)據(jù)放入到緩沖區(qū)中
while (inChannel.read(byteBuffer) != -1) {
//切換讀取數(shù)據(jù)的模式
byteBuffer.flip();
//將緩沖區(qū)中的數(shù)據(jù)寫入通道中
outChannel.write(byteBuffer);
//清空緩沖區(qū)
byteBuffer.clear();
}
//釋放資源
in.close();
out.close();
inChannel.close();
outChannel.close();
}

使用直接緩沖區(qū)

public static void main(String[] args) throws IOException {
//通過文件通道的靜態(tài)方法,打開讀寫通道
//參1:通過Paths獲取源文件的路徑
//參2:操作模式 StandardOpenOption.READ 讀取模式
//打開讀取文件的通道
FileChannel in = FileChannel.open(Paths.get("短發(fā).mp3"), StandardOpenOption.READ);
//打開寫入的通道 模式要讀還要寫 StandardOpenOption.CREATE 意思是文件不存在就創(chuàng)建,如果存在就覆蓋
//StandardOpenOption.CREATE_NEW 意思是文件不存在就創(chuàng)建,如果存在就報錯
FileChannel out = FileChannel.open(Paths.get("短發(fā)2.mp3"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//操作內(nèi)存映射文件(也就是這個緩沖區(qū)在物理內(nèi)存中)
MappedByteBuffer inByteBuffer = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());
MappedByteBuffer outByteBuffer = out.map(FileChannel.MapMode.READ_WRITE, 0, in.size());
//直接對緩沖區(qū)進行讀寫操作
byte[] bytes = new byte[inByteBuffer.limit()];
inByteBuffer.get(bytes);
outByteBuffer.put(bytes);
//釋放資源
in.close();
out.close();

}

通道之間的數(shù)據(jù)傳輸

通道之間的數(shù)據(jù)傳輸 用的也是直接緩沖區(qū)的方式

  • transferFrom()
  • transferTo()
    列:

public static void main(String[] args) throws IOException {
FileChannel in = FileChannel.open(Paths.get("E:\MyTest.txt"), StandardOpenOption.READ);
FileChannel out = FileChannel.open(Paths.get("D:\MyTestCopy.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//in.transferTo(0,in.size(),out);//把數(shù)據(jù)讀到 輸出通道中取 完成文件的復(fù)制
//或者使用輸出通道
out.force(false);
out.transferFrom(in,0,in.size()); //寫出數(shù)據(jù),寫出的數(shù)據(jù)從輸入通道中來
in.close();
out.close();

分散 (Scatter)和聚集(Gather)

分散讀?。?Scattering Reads )是指從 Channel 中讀取的數(shù)據(jù)“分散”到多個Buffer緩沖區(qū)中
Java NIO

注:按照緩沖區(qū)的順序,從Channel中讀取的數(shù)據(jù)依次將Buffer填滿。

聚集寫入( Gathering Writes )是指將多個 Buffer緩沖區(qū) 中的數(shù)據(jù)“聚集”到 Channel 。
Java NIO

注:按照緩沖區(qū)的順序,寫入position和limit之間的數(shù)據(jù)到Channel.
列:

public static void main(String[] args) throws IOException {
RandomAccessFile in = new RandomAccessFile("E:\demo.txt", "rw");
RandomAccessFile out = new RandomAccessFile("E:\democopy.txt", "rw");
//獲取讀取通道
FileChannel inChannel = in.getChannel();
//創(chuàng)建多個緩沖區(qū)
ByteBuffer buffer1 = ByteBuffer.allocate(100);
ByteBuffer buffer2 = ByteBuffer.allocate(1024);
//分散讀取到多個緩沖區(qū)中
ByteBuffer[] byteBuffers=new ByteBuffer[]{buffer1,buffer2};//把多個緩沖區(qū)放到一個大的數(shù)組中
long read = inChannel.read(byteBuffers);//把這個大的緩沖區(qū)傳進去

//當(dāng)然我們可以看看,每個緩沖區(qū)中讀入的數(shù)據(jù)
    //byteBuffers[0].flip(); //切換到讀取模式 看一下第一個緩沖區(qū),讀入的100個字節(jié)
    //byte[] array = byteBuffers[0].array();//把ByteBuffer轉(zhuǎn)換成字節(jié)數(shù)組
    //String s = new String(array, 0, byteBuffers[0].limit());
    //System.out.println(s);

    //把每個緩沖區(qū),切換到讀取模式
    for (ByteBuffer buffer : byteBuffers) {
        buffer.flip();
    }
    //聚集寫入
    FileChannel outChannel = out.getChannel();
    outChannel.write(byteBuffers);

    //釋放資源
    inChannel.close();
    outChannel.close();
}

FileChannel 的常用方法

方法描述
int read(ByteBufferdst**)**從Channel中讀取數(shù)據(jù)到ByteBuffer
long read(ByteBuffer**[]dsts)**將Channel中的數(shù)據(jù)“分散”到ByteBuffer[]
int write(ByteBuffersrc**)**將ByteBuffer中的數(shù)據(jù)寫入到Channel
long write(ByteBuffer**[]srcs)**將ByteBuffer[]中的數(shù)據(jù)“聚集”到Channel
long position()返回此通道的文件位置
FileChannel position(long p)設(shè)置此通道的文件位置
long size()返回此通道的文件的當(dāng)前大小
FileChannel truncate(long s)將此通道的文件截取為給定大小
void force(boolean metaData)強制將所有對此通道的文件更新寫入到存儲設(shè)備中

Files 類中復(fù)制文件的方法

static long copy(InputStream in, Path target, CopyOption... options)
將所有字節(jié)從輸入流復(fù)制到文件。

static long copy(Path source, OutputStream out)
將從文件到輸出流的所有字節(jié)復(fù)制到輸出流中。

static Path copy(Path source, Path target, CopyOption... options)
將一個文件復(fù)制到目標文件。

Files類中的常用方法

java.nio.file.Files 用于操作文件或目錄的工具類

Files 常用方法:

?    Path copy(Path src, Path dest, CopyOption … how) : 文件的復(fù)制

?    Path createDirectory(Path path, FileAttribute<?> … attr) : 創(chuàng)建一個目錄

?    Path createFile(Path path, FileAttribute<?> … arr) : 創(chuàng)建一個文件

?    void delete(Path path) : 刪除一個文件

?    Path move(Path src, Path dest, CopyOption…h(huán)ow) : 將 src 移動到 dest 位置

?    long size(Path path) : 返回 path 指定文件的大小

Files 常用方法:用于判斷
boolean exists(Path path, LinkOption … opts) : 判斷文件是否存在
boolean isDirectory(Path path, LinkOption … opts) : 判斷是否是目錄
boolean isExecutable(Path path) : 判斷是否是可執(zhí)行文件
boolean isHidden(Path path) : 判斷是否是隱藏文件
boolean isReadable(Path path) : 判斷文件是否可讀
boolean isWritable(Path path) : 判斷文件是否可寫
boolean notExists(Path path, LinkOption … opts) : 判斷文件是否不存在
public static <A extends BasicFileAttributes> A readAttributes(Path path,Class<A> type,LinkOption...
options) : 獲取與 path 指定的文件相關(guān)聯(lián)的屬性。

Files 常用方法:用于操作內(nèi)容
SeekableByteChannel newByteChannel(Path path, OpenOption…h(huán)ow) : 獲取與指定文件的連接,how 指定打開方式。
DirectoryStream newDirectoryStream(Path path) : 打開 path 指定的目錄
InputStream newInputStream(Path path, OpenOption…h(huán)ow): 獲取 InputStream 對象
OutputStream newOutputStream(Path path, OpenOption…h(huán)ow) : 獲取 OutputStream 對象

創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機房獨有T級流量清洗系統(tǒng)配攻擊溯源,準確進行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。

新聞名稱:JavaNIO-創(chuàng)新互聯(lián)
標題來源:http://muchs.cn/article44/csgiee.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站改版、服務(wù)器托管自適應(yīng)網(wǎng)站、網(wǎng)站內(nèi)鏈App設(shè)計、網(wǎng)站導(dǎo)航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護公司