Java NIO(一)FileChannel 詳解
阿新 • • 發佈:2019-02-14
@個人筆記整理,個人理解整理,請勿輕信!請勿輕信!
目錄
系列目錄
FileChannel概述
FileChannel
是面向檔案的通道,可以從FileInputStream
、FileOutputStream
和RandomAccessFile
中通過getChannel()
方法獲得。FileChannel
只能以阻塞方式工作。
FileChannel
的類圖:
FileChannel
可以從IO流中直接獲得。可以獲得FileChannel
的IO流有FileInputStream,FileOutputStream,RandomAccessFile
。
RandomAccessFile
getChannel
方法原始碼:
/**
* 返回一個獨一無二的與該檔案相關聯的FileChannel。
* Channel的position與返回Channel時該檔案物件的檔案指標偏移量相等。
* 改變檔案指標偏移量同時也會改變position,反之亦然。改變檔案的長度也會改變Channel檔案長度,反之亦然。
* FileChannel是一個抽象類,此處返回的channel是一個指向FileChannelImpl.open方法所返回的具體實現類的引用。
*/
public final FileChannel getChannel () {
synchronized (this) {
if (channel == null) {
channel = FileChannelImpl.open(fd, path, true, rw, this);
}
return channel;
}
}
自頂向下進行原始碼分析
Channel
介面的原始碼
/**
* 一個為IO操作而生的連線。
* 一個通道(channel)表示一個面向實體(硬體裝置,檔案,網路socket,程式元件)的連線,
* 通道能夠指向一個或多個IO操作,比如讀或寫。
* 一個通道(channel)要麼是開啟狀態,要麼是關閉狀態。通道在建立時開啟,關閉之後將維持關閉狀態。
* 可以呼叫`isOpen`方法來檢測通道是否開啟。
* 通常來說,就如同規範中描述的,實現了該介面或者繼承了該介面並實現的類在多執行緒訪問中是安全的(因為是阻塞的)。
*/
public interface Channel extends Closeable {
public boolean isOpen();
/**
* 關閉一個通道(channel),重複呼叫不會也額外效果,
*
* 如果有執行緒執行了關閉,那麼其他執行緒對通道(channel)的關閉操作將會阻塞
* ,直到第一個呼叫結束,之後其他的呼叫會直接返回.
*
*/
public void close() throws IOException;
}
ReadableByteChannel
介面原始碼
/**
* 繼承自Channel介面,附加了可以讀位元組的功能。
* 在同一時間,僅僅有一個讀操作可以在可讀通道(channel)上執行。
*
* 如果一個執行緒在一個通道上發起讀操作,則其他嘗試發起讀操作的執行緒將會阻塞,
* 直到第一個讀操作完成。是否其他型別的IO操作可以與讀操作同時進行取決於通道(channel)型別。
*
*/
public interface ReadableByteChannel extends Channel {
/**
* 從通道讀入位元組序列到指定的buffer中。
*
* 該方法將會嘗試從通道(channel)中讀入多達r位元組的資料到buffer中,
* 其中r是buffer的剩餘空閒容量,也就是在方法dst.remaining()呼叫時返回的值。
*
* 假設讀入長度為n(0<=n<=r)的位元組序列,這些位元組序列會被轉移到buffer中,
* 如果位元組序列的第一個位元組處於下標p處,則最後一個位元組處於下標p+n-1處,
* p就是該方法呼叫時buffer的position,呼叫該方法後,
* 返回給buffer的position將會是p+n,buffer的limit不會改變。
*
* 一個讀操作可能不會填滿緩衝器,事實上有可能啥也不讀。
* 是否會這樣取決於緩衝區的性質和狀態。
* 處於非阻塞模式下的套接字通道(socket channel)讀入的位元組最多
* 也不會超過當前套接字輸入緩衝中可獲得的位元組數。
* 相似的,一個檔案通道(file channel)不可能讀入比檔案中剩餘位元組更多的位元組數。
* 但是可以保證的是,如果一個通道處於阻塞模式,並且緩衝區(buffer)中至少有一個位元組,
* 則該方法將阻塞直到至少有一個位元組被讀入。
* 這個方法可以隨時被呼叫,如果一個執行緒已經發起了該通道上的讀操作,
* 則其他執行緒對剛方法的呼叫將阻塞直到第一個操作執行完畢。
* @param dst
* 位元組將會被轉移往dst buffer。
*
* @return 讀入到buffer中位元組的數量,可能為0,當返回-1時,表示已經到了流的末尾。
*
* @throws NonReadableChannelException
* 如果通道沒有被開啟以供讀入。
*
* @throws ClosedChannelException
* 如果通道時關閉著的。
*
* @throws AsynchronousCloseException
* 當讀操作進行中,如果有一個執行緒關閉了該通道。
*
* @throws ClosedByInterruptException
* If another thread interrupts the current thread
* while the read operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
* 如果另一個執行緒中斷了當前正在執行讀操作的執行緒,從而會關閉該通道,並且設定當前執行緒的中斷標誌位。
* @throws IOException
* If some other I/O error occurs
*/
public int read(ByteBuffer dst) throws IOException;
}
WritableByteChannel
介面原始碼
/**
* 給通道(channel)附加寫位元組的功能。
* 在同一時間,僅僅有一個寫操作可以在可寫通道(channel)上執行。
* 如果一個執行緒在一個通道上發起寫操作,則其他嘗試發起寫操作的執行緒將會阻塞,直到第一個寫操作完成。
* 是否其他型別的IO操作可以與寫操作同時進行取決於通道(channel)型別。
*
*/
public interface WritableByteChannel
extends Channel
{
/**
* 從指定的buffer寫入位元組序列到通道(channel)中。
*
* 會嘗試寫入到通道中多達r位元組資料,其中r是buffer中剩餘位元組數,也是src.remaining()方法呼叫時的返回值。
*
* 假設n(0<=n<=r)位元組序列將被寫到通道(channel)中,這個位元組序列從buffer下標p處轉移,
* p是該方法呼叫時buffer的position,最後一個將被寫入的位元組下標將會是p+n-1。
* 該方法呼叫後返回的buffer的position將會是p+n,limit不會改變。
*
* 除非指明特殊情況,寫操作會在寫入所有的需要的r位元組後返回。
* 一些型別的通道,寫入的數量取決於它們的狀態,可能寫入一部分位元組,也可能不寫入。
* 工作在非阻塞模式下的套接字通道不能寫入比套接字輸出緩衝區空閒空間更多的位元組數。
*
* 該方法能在任何時刻呼叫。如果一個執行緒已經發起了在該通道上的寫操作,
* 則其他呼叫該方法的執行緒將會阻塞,直到第一個操作完成。
* @param src
* The buffer from which bytes are to be retrieved
*
* @return The number of bytes written, possibly zero
*
* @throws NonWritableChannelException
* If this channel was not opened for writing
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws AsynchronousCloseException
* If another thread closes this channel
* while the write operation is in progress
*
* @throws ClosedByInterruptException
* If another thread interrupts the current thread
* while the write operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws IOException
* If some other I/O error occurs
*/
public int write(ByteBuffer src) throws IOException;
}
InterruptibleChannel
介面原始碼
/**
* 賦予Channel可非同步中斷或關閉的能力。
*
* 一個實現了該介面的通道能夠非同步關閉:如果一個執行緒在一個可中斷通道上因IO操作而阻塞,
* 則有可能其他執行緒會呼叫通道的close方法,這將會造成阻塞的執行緒接受一個AsynchronousCloseException。
*
* 一個實現了該介面的通道也是能夠中斷的:如果一個執行緒在一個可中斷通道上因IO操作而阻塞,
* 其他執行緒可能會呼叫阻塞執行緒的interrupt()方法。這將會造成通道關閉,
* 阻塞的執行緒接收ClosedByInterruptException,並且會設定阻塞執行緒的中斷標誌位。
*
* 如果執行緒的中斷標誌位已經被設定,如果它在通道上呼叫阻塞IO操作,
* 則這個通道會被關閉,執行緒會立即收到ClosedByInterruptException,執行緒的中斷狀態不會改變。
*
* 一個通道支援非同步關閉或中斷當且僅當實現了該介面。
*/
public interface InterruptibleChannel
extends Channel
{
/**
* 關閉這個通道。
* 任意一個當前因IO操作阻塞在通道上的執行緒都會收到一個AsynchronousCloseException。
* 該方法在其他方面表現的跟Channel介面中的close介面一模一樣。
*/
public void close() throws IOException;
}
AbstractInterruptibleChannel
抽象類原始碼
/**
*
* 可中斷通道的基礎實現類。
*
* 該類封裝了實現非同步關閉通道和中斷通道的底層機制。具體實現類必須在呼叫可能無限阻塞IO操作之前呼叫begin方法,
* 在呼叫可能無線阻塞操作之後呼叫end方法。為了確保end方法總是會被呼叫,這些方法應該在try...finally塊中使用。
* 如下:
* boolean completed = false;
* try {
* begin();
* completed = ...; // 執行IO操作
* return ...; // 返回結果
* } finally {
* end(completed);
* }
*
*
* end方法的completed引數說明IO操作是否已經完成,該引數是否有效對呼叫者是可見的。
* 在讀取位元組的操作中,當且僅當一些位元組確實已經轉移到呼叫者指定的目標緩衝區,該引數為true。
*
* 一個具體通道類必須實現implCloseChannel方法,如果一個執行緒的IO操作已經在通道上阻塞了,
* 那麼該方法呼叫將會直接返回,不管是使用丟擲異常還是普通返回。
* 如果已經中斷的執行緒或者已經在通道上阻塞的執行緒被非同步關閉,那麼通道的end方法將會丟擲合適的異常。
*/
public abstract class AbstractInterruptibleChannel
implements Channel, InterruptibleChannel
{
private final Object closeLock = new Object();
private volatile boolean open = true;
/**
* 初始化該類的一個例項。
*/
protected AbstractInterruptibleChannel() { }
/**
* 關閉通道
*
* 如果通道已經關閉,則直接返回。否則標記該通道需要關閉,呼叫implCloseChannel方法來具體完成關閉操作。
* @throws IOException
* If an I/O error occurs
*/
public final void close() throws IOException {
synchronized (closeLock) {
if (!open)
return;
open = false; //標記通道需要關閉。
implCloseChannel();
}
}
/**
* 關閉通道的具體實現方法。
*
* 該方法被close方法呼叫來完成真正的關閉通道操作。該方法僅當通道沒有被關閉時呼叫,而且最多會被呼叫一次。
*
* 該方法的實現必須安排任意一個在該通道上因IO操作阻塞的執行緒立即返回,不管是通過丟擲異常還是正常返回的方式。
* @throws IOException
* If an I/O error occurs while closing the channel
*/
protected abstract void implCloseChannel() throws IOException;
public final boolean isOpen() {
return open;
}
// --中斷機制 --
private Interruptible interruptor;
private volatile Thread interrupted;
/**
*
* 標記可能永久阻塞IO操作的起始操作。
*
* 該方法應該通過使用try...finally與end方法串接呼叫,該方法的目的是實現通道的非同步關閉和中斷。
*/
protected final void begin() {
if (interruptor == null) {
interruptor = new Interruptible() { //建立一箇中斷器(自己起的名字)
public void interrupt(Thread target) { //實現中斷器的中斷方法
synchronized (closeLock) {
if (!open)
return;
open = false;
//如果執行緒中斷則關閉通道,將interrupted賦值為引數target。
try {
AbstractInterruptibleChannel.this.implCloseChannel();
} catch (IOException x) { }
}
}};
}
blockedOn(interruptor);
Thread me = Thread.currentThread();
if (me.isInterrupted()) //判斷執行緒是否中斷,如果中斷了,則執行中斷器的中斷方法,該方法實際上是關閉通道。
interruptor.interrupt(me); //該方法呼叫會設定interrupted為當前執行緒。
}
/**
*
* 標記可能永久阻塞IO操作之後的操作。
*
* @param completed
*
* true,當且僅當IO操作成功完成,也就是說對IO操作的呼叫者是可見的。
*
* @throws AsynchronousCloseException
* If the channel was asynchronously closed
*
* @throws ClosedByInterruptException
* If the thread blocked in the I/O operation was interrupted
*/
protected final void end(boolean completed)
throws AsynchronousCloseException
{
blockedOn(null);
Thread interrupted = this.interrupted;
if (interrupted != null && interrupted == Thread.currentThread()) {
interrupted = null;
throw new ClosedByInterruptException();
//如果interrupted是當前執行緒,則說明上面執行緒被中斷(在begin方法中,檢測了執行緒是否中斷,如果中斷
//則設定了interrupted為當前執行緒),並且通道已經被關閉了。
}
if (!completed && !open)
throw new AsynchronousCloseException();
//如果IO操作沒有完成,但是通道已經關閉,說明是該通道已經被非同步關閉了。
}
// -- sun.misc.SharedSecrets --
static void blockedOn(Interruptible intr) { // package-private
sun.misc.SharedSecrets.getJavaLangAccess().blockedOn(Thread.currentThread(),
intr);
}
}
ScatteringByteChannel
介面原始碼
/**
* A channel that can read bytes into a sequence of buffers.
* 為通道附加了能夠讀入位元組到一系列緩衝區的功能。
*
* 在一次單獨呼叫中,一個分散的讀操作讀入位元組序列到一個或多個指定的緩衝區中。
* 分散讀在實現一些網路協議或檔案格式時非常有用,比如由一個或多個固定長度的報文頭部,緊跟著一個位置長度的報文體。
*
*/
public interface ScatteringByteChannel
extends ReadableByteChannel
{
/**
*
* 從通道讀取位元組序列到指定緩衝區的子集。
*
* 呼叫該方法將嘗試從通道讀入多達r位元組資料,其中r時指定緩衝區陣列子集的剩餘空閒容量。也就是
*
* dsts[offset].remaining()
* + dsts[offset+1].remaining()
* + ... + dsts[offset+length-1].remaining()
*
* 方法呼叫時的值。
*
* 假設讀入的位元組序列長度為n(0<=n<=r)。
* 長達dsts[offset].remaining()位元組序列將會被轉移到緩衝區dsts[offset]中,
* 多達下一個緩衝區dsts[offset+1].remaining()位元組序列轉移到緩衝區dsts[offset+1]中,
* 以此類推,直到整個位元組序列都被轉移到指定的緩衝區中。
* 儘可能多的位元組會被一次轉移到每個緩衝區中,因此除了最後一個緩衝區,其他的緩衝區最新的position一定等於limit。
*
* 該方法可以在任意時刻呼叫,如果有一個執行緒已經在該通道上發起了讀操作,
* 則該方法的呼叫會一直阻塞到第一個執行緒的操作完成。
* @param dsts
* The buffers into which bytes are to be transferred
*
* @param offset
* The offset within the buffer array of the first buffer into
* which bytes are to be transferred; must be non-negative and no
* larger than <tt>dsts.length</tt>
*
* @param length
* The maximum number of buffers to be accessed; must be
* non-negative and no larger than
* <tt>dsts.length</tt> - <tt>offset</tt>
*
* @return The number of bytes read, possibly zero,
* or <tt>-1</tt> if the channel has reached end-of-stream
*
* @throws IndexOutOfBoundsException
* If the preconditions on the <tt>offset</tt> and <tt>length</tt>
* parameters do not hold
*
* @throws NonReadableChannelException
* If this channel was not opened for reading
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws AsynchronousCloseException
* If another thread closes this channel
* while the read operation is in progress
*
* @throws ClosedByInterruptException
* If another thread interrupts the current thread
* while the read operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws IOException
* If some other I/O error occurs
*/
public long read(ByteBuffer[] dsts, int offset, int length)
throws IOException;
/**
* Reads a sequence of bytes from this channel into the given buffers.
*
* <p> An invocation of this method of the form <tt>c.read(dsts)</tt>
* behaves in exactly the same manner as the invocation
*
* <blockquote><pre>
* c.read(dsts, 0, dsts.length);</pre></blockquote>
*
* @param dsts
* The buffers into which bytes are to be transferred
*
* @return The number of bytes read, possibly zero,
* or <tt>-1</tt> if the channel has reached end-of-stream
*
* @throws NonReadableChannelException
* If this channel was not opened for reading
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws AsynchronousCloseException
* If another thread closes this channel
* while the read operation is in progress
*
* @throws ClosedByInterruptException
* If another thread interrupts the current thread
* while the read operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws IOException
* If some other I/O error occurs
*/
public long read(ByteBuffer[] dsts) throws IOException;
}
GatheringByteChannel
介面原始碼
/**
*
* 給通道附加可以從多個緩衝區寫的功能。
*
* 在一次單獨呼叫中,一個聚集的寫操作從一個或多個指定的緩衝區中向通道寫入位元組序列。
* 聚集寫在實現一些網路協議或檔案格式時非常有用,比如由一個或多個固定長度的報文頭部,緊跟著一個位置長度的報文體。
*
*/
public interface GatheringByteChannel
extends WritableByteChannel
{
/**
*
* 從多個指定的緩衝區中寫入位元組序列到通道。
*
* 該方法嘗試寫入多達r位元組資料到通道中,其中r時指定的緩衝區子集的所有剩餘容量,也就是
*
* srcs[offset].remaining()
* + srcs[offset+1].remaining()
* + ... + srcs[offset+length-1].remaining()
*
* 方法呼叫時返回的值。
*
*
* 假設n(0<=n<=r)位元組序列需要寫,
* 先嚐試從srcs[offset]緩衝區中寫入src[offset].remaining()個位元組到通道,
* 緊接著嘗試從srcs[offset+1]緩衝區中寫入src[offset+1].remaining()個位元組到通道,
* 以此類推,直到整個位元組序列全部寫入通道。該方法會盡可能多的從緩衝區寫入通道,
* 因此除了最後一個要寫入的緩衝區,其他緩衝區最新的position與limit相等。
*
* 除非其他說明,一個寫操作僅當r位元組資料寫完後才會返回。一些型別的通道,
* 根據它們的狀態,可能會寫入一部分資料或者不寫。比如在非阻塞模式下的套接字通道,
* 寫入的資料不可能多餘套接字輸出緩衝區可獲得的位元組數。
*
* 該方法可以在任何時刻呼叫。如果一個執行緒已經在通道上發起了寫操作,
* 那麼該方法的呼叫會一直阻塞,直到第一個執行緒的操作完成。
* @param srcs
* The buffers from which bytes are to be retrieved
*
* @param offset
* The offset within the buffer array of the first buffer from
* which bytes are to be retrieved; must be non-negative and no
* larger than <tt>srcs.length</tt>
*
* @param length
* The maximum number of buffers to be accessed; must be
* non-negative and no larger than
* <tt>srcs.length</tt> - <tt>offset</tt>
*
* @return The number of bytes written, possibly zero
*
* @throws IndexOutOfBoundsException
* If the preconditions on the <tt>offset</tt> and <tt>length</tt>
* parameters do not hold
*
* @throws NonWritableChannelException
* If this channel was not opened for writing
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws AsynchronousCloseException
* If another thread closes this channel
* while the write operation is in progress
*
* @throws ClosedByInterruptException
* If another thread interrupts the current thread
* while the write operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws IOException
* If some other I/O error occurs
*/
public long write(ByteBuffer[] srcs, int offset, int length)
throws IOException;
/**
* Writes a sequence of bytes to this channel from the given buffers.
*
* <p> An invocation of this method of the form <tt>c.write(srcs)</tt>
* behaves in exactly the same manner as the invocation
*
* <blockquote><pre>
* c.write(srcs, 0, srcs.length);</pre></blockquote>
*
* @param srcs
* The buffers from which bytes are to be retrieved
*
* @return The number of bytes written, possibly zero
*
* @throws NonWritableChannelException
* If this channel was not opened for writing
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws AsynchronousCloseException
* If another thread closes this channel
* while the write operation is in progress
*
* @throws ClosedByInterruptException
* If another thread interrupts the current thread
* while the write operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws IOException
* If some other I/O error occurs
*/
public long write(ByteBuffer[] srcs) throws IOException;
}
ByteChannel
介面原始碼
/**
* 為通道附加了可以讀寫位元組的功能。這個介面只是簡單的繼承了ReadableByteChannel和WritableByteChannel介面。
*
*/
public interface ByteChannel
extends ReadableByteChannel, WritableByteChannel
{
}
SeekableByteChannel
介面原始碼
/**
*
* 為可讀寫位元組的通道維護一個當前位置(position),並且允許改變position。
*
* 一個可尋位元組通道連線到一個實體,典型的比如檔案,該實體包含可讀寫的長度不定的位元組序列。
* 當前的位置(position)能夠通過position()方法查詢,也可以通過position(long)方法修改。
* 該通道同時也提供了對當前通道連線的實體的當前大小(size)的訪問。
* 當位元組寫入超過當前size時,size會增長。當通道是truncated時,size會減小。
* <p> The {@link #position(long) position} and {@link #truncate truncate} methods
* which do not otherwise have a value to return are specified to return the
* channel upon which they are invoked. This allows method invocations to be
* chained. Implementations of this interface should specialize the return type
* so that method invocations on the implementation class can be chained.
* 沒有返回值的position和truncate方法被指定為返回呼叫它們的通道。
* 這允許連結方法呼叫。 此介面的實現應特化返回型別,以便可以連結實現類上的方法呼叫。(google翻譯,沒看懂)
* @since 1.7
* @see java.nio.file.Files#newByteChannel
*/
public interface SeekableByteChannel
extends ByteChannel
{
/**
*
* 從通道中讀入位元組序列到指定的緩衝區中。
*
* 通道會從當前的positon開始讀入位元組,當讀入一定數量的位元組時,position也會被更新到正確的值。
*/
@Override
int read(ByteBuffer dst) throws IOException;
/**
* Writes a sequence of bytes to this channel from the given buffer.
* 從指定緩衝區向通道寫入位元組序列。
*
* 該方法將會在通道當前的position開始寫入位元組,除非該通道連線的實體比如檔案是以
* java.nio.file.StandardOpenOption APPEND選項開啟的,
* 在這種情況下position會先到達末尾位置,為了容納寫入的位元組,position會根據實際寫入的位元組來更新。
*/
@Override
int write(ByteBuffer src) throws IOException;
/**
* Returns this channel's position.
*
* @return This channel's position,
* a non-negative integer counting the number of bytes
* from the beginning of the entity to the current position
*
* @throws ClosedChannelException
* If this channel is closed
* @throws IOException
* If some other I/O error occurs
*/
long position() throws IOException;
/**
*
* 設定通道的position。
*
* 設定比當前size更大的position是合法的,但是不會改變實體的size。
* 之後在此時的position上的讀操作將會立即返回EOF,
* 而寫操作會造成實體增長以滿足寫入的新位元組。
* 在之前EOF位置和新的開始寫入位元組位置之間的空白位元組是沒有定義的,也就是垃圾值。
*
* 在以java.nio.file.StandardOpenOption APPEND選項開啟的通道上更改position是不建議的。
* 當開啟通道附加內容時,寫入之前position會被設定到末尾。
* @param newPosition
* The new position, a non-negative integer counting
* the number of bytes from the beginning of the entity
*
* @return This channel
*
* @throws ClosedChannelException
* If this channel is closed
* @throws IllegalArgumentException
* If the new position is negative
* @throws IOException
* If some other I/O error occurs
*/
SeekableByteChannel position(long newPosition) throws IOException;
/**
*
* 返回當前通道連線的實體的大小。
* @return The current size, measured in bytes
*
* @throws ClosedChannelException
* If this channel is closed
* @throws IOException
* If some other I/O error occurs
*/
long size() throws IOException;
/**
*
* 縮減通道連線的實體到指定大小(size)。
*
* 如果引數size比當前實體的大小小,則實體縮減,在新的末尾之後的位元組將會被忽略。
* 如果引數size大於或等於當前實體的大小,則不改變。
* 在其他情況下,如果當前的position比引數size大,則position會被設定成size。
*
* 當通道連線的實體比如檔案是以java.nio.file.StandardOpenOption APPEND選修開啟的,則不允許該操作。
* @param size
* The new size, a non-negative byte count
*
* @return This channel
*
* @throws NonWritableChannelException
* If this channel was not opened for writing
* @throws ClosedChannelException
* If this channel is closed
* @throws IllegalArgumentException
* If the new size is negative
* @throws IOException
* If some other I/O error occurs
*/
SeekableByteChannel truncate(long size) throws IOException;
}
FileChannel
抽象類原始碼
/**
*
* 面向檔案可讀可寫可對映可操作的通道。
*
* 檔案通道是一個連線到檔案的SeekableByteChannel。
* 檔案通道具有一個當前position能夠通過position()方法查詢,也能狗通過position(long)修改。
* 檔案本身包含了能被讀寫的可變長度的位元組序列,當前大小可通過size查詢。
* 當位元組寫入到當前size之外時,檔案的size會增長。當檔案truncate後,檔案的size會減小。
* 檔案本身可能包含一些元資料,比如訪問許可權,檔案型別和最近修改實際。該類不提供訪問元資料的方法。
*
* 除了跟byte channel相似的讀、寫和關閉操作外,該類還定義了以下檔案操作:
*
*
*
* 位元組可能通過read(ByteBuffer,long)或者write(ByteBuffer,long)在檔案的絕對位置上進行讀寫,
* 該操作不會影響通道的當前position。
*
* 檔案的部分可能直接對映到記憶體中,對於大型檔案往往要比read和write方法更有效率。
*
* 對檔案的更新可能會被強制退出到底層儲存裝置,確保在系統崩壞的情況下資料不會丟失。
*
* 位元組可以從檔案直接轉移到其他通道,反之亦然,
* 該方法被很多作業系統組織成一種非常快速的在檔案系統快取中直接轉移的方式。
*
* 檔案的一部分可以被鎖住以阻止其他程式的訪問。
*
* FileChannel可以被多執行緒安全使用。close方法可以在任何時候呼叫,就像在Channel介面實現一樣。
* 在任一時間,僅僅有一個涉及檔案position和改變檔案大小的操作執行,當嘗試發起第二個上述操作時,
* 會阻塞直到第一個操作執行完畢。其他的操作,比如獲取positon的操作,可以併發執行,
* 這些操作到底是不是這樣執行取決與依賴實現,所有不加說明。
*
* 在同一個程式中,由該類例項提供的檔案的檢視與其他該類例項提供的試圖一致。
* 由於快取機制或者網路延遲,併發執行的該類的例項所提供的檔案檢視可能不一致。
* 無論程式是哪種語言寫的,也無論程式是跑在同一個或者不同的機器上,具體的性質是系統決定的,所以也不會說明。
*
* 檔案通道可以通過呼叫該類的open方法來建立。
* 檔案通道也可以由一個已經存在的FileInputStream,FileOutputStream或者
* RandomAccessFile呼叫getChannel方法獲得,
* getChannel方法會返回一個連線到相同依賴檔案的檔案通道。
* 從上述三個類獲得的檔案通道的狀態與初始物件的狀態一直,並且兩者會互相影響,
* 比如改變了通道的position,流中的檔案指標也會改變。
*
* 在不同的點上,該類指定需要“開啟以供閱讀”,“開啟以進行寫入”或“開啟以進行讀寫”的例項。
* 通過FileInputStream例項的getChannel方法獲得的通道將開啟以供讀取。
* 通過FileOutputStream例項的getChannel方法獲得的通道將開啟以進行寫入。
* 最後,如果例項是使用模式“r”建立的,
* 則通過RandomAccessFile例項的getChannel方法獲得的通道將開啟以供讀取,
* 如果例項是使用模式“rw”建立的,則將開啟以進行讀取和寫入。(google翻譯,此處沒看懂,不是說通道時雙向的嗎?)
*
* 一個檔案通道可能以附加寫模式開啟,
* 例如從一個由FileOutputStream(File,boolean)建構函式建立的輸出流中獲得的通道,
* 在該模式下呼叫相關的讀操作會首先使position到達檔案的末尾,
* 在這之後寫入需要的資料。修改position和寫入資料是不是原子操作依賴於系統。
*
*/
public abstract class FileChannel
extends AbstractInterruptibleChannel
implements SeekableByteChannel, GatheringByteChannel, ScatteringByteChannel
{
/**
* 初始化該類的一個例項。
*/
protected FileChannel() { }
/**
*
* 開啟或建立一個檔案,返回一個檔案通道來訪問檔案。
*
* options引數決定了檔案如何開啟,READ和WRITE選項決定了檔案是否開啟以供讀或者(和)寫。
* 如果沒有選項引數(或者引數是APPEND)則檔案開啟以供讀,預設讀寫操作在檔案的開始處。
*
* 除了READ和WRITE,下面的選項可供選擇:
*
* 1.APPEND
* 如果該引數出現,則檔案開啟以供讀,並且每一個通道的寫方法將首先修改position到檔案的末尾,
* 然後寫需要的資料。修改position和寫資料是否是原子操作取決與系統。
* 此選項不能和READ和TRUNCATE_EXISTING選項一起使用。
*
* 2.TRUNCATE_EXISTING
* 如果該選項出現,則已經存在的檔案將被縮減到大小為0的狀態。當檔案開啟以供讀時該選項會被忽略。
*
* 3.CREATE_NEW
* 如果該引數出現,則一個新的檔案將會被建立,如果檔案存在則操作失敗。
* 建立檔案時檢查檔案是否存在和檢查到檔案不存在時建立檔案相對於其他檔案系統操作是原子的。當檔案開啟僅供讀時該選項會被忽略。
*
* 4.CREATE
* 當該引數出現時,已經存在的檔案被開啟,不存在則建立。
* 建立檔案時檢查檔案是否存在和檢查到檔案不存在時建立檔案相對於其他檔案系統操作是原子的,
* 當引數CREATE_NEW存在或者檔案開啟僅供讀時該引數被忽略。
*
* 5.DELETE_ON_CLOSE
* 當該引數出現時,該實現會盡可能在close方法呼叫時刪除檔案。
* 如果close方法沒有呼叫,則儘可能在虛擬機器終止時刪除檔案。
*
* 6.SPARSE
* 建立新檔案時該選項提示檔案將會是稀疏檔案(https://zh.wikipedia.org/wiki/稀疏檔案)。
* 不建立新檔案時,該選項忽略。
*
* 7.SYNC
* 要求所有對檔案內容或元資料的更新必須同步寫入底層儲存裝置。
*
* 8.DSYNC
* 要求所有對檔案內容的更新同步寫入到底層儲存裝置。
*
* 實現也會提供額外的選項。
*
* attrs引數是一個可選的屬性陣列當建立檔案時以原子方式設定。
*
* 新的通道由建立了Path的provider的newFileChannel方法呼叫建立
* @param path
* 開啟或建立檔案的路徑
* @param options
* 選項說明了檔案如何開啟
* @param attrs
* 建立檔案時原子設定的可選屬性列表
* @return A new file channel
*
* @throws IllegalArgumentException
* If the set contains an invalid combination of options
* @throws UnsupportedOperationException
* If the {@code path} is associated with a provider that does not
* support creating file channels, or an unsupported open option is
* specified, or the array contains an attribute that cannot be set
* atomically when creating the file
* @throws IOException
* If an I/O error occurs
* @throws SecurityException
* If a security manager is installed and it denies an
* unspecified permission required by the implementation.
* In the case of the default provider, the {@link
* SecurityManager#checkRead(String)} method is invoked to check
* read access if the file is opened for reading. The {@link
* SecurityManager#checkWrite(String)} method is invoked to check
* write access if the file is opened for writing
*/
public static FileChannel open(Path path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
FileSystemProvider provider = path.getFileSystem().provider();
return provider.newFileChannel(path, options, attrs);
}
@SuppressWarnings({"unchecked", "rawtypes"}) // generic array construction
private static final FileAttribute<?>[] NO_ATTRIBUTES = new FileAttribute[0];
public static FileChannel open(Path path, OpenOption... options)
throws IOException
{
Set<OpenOption> set = new HashSet<OpenOption>(options.length);
Collections.addAll(set, options);
return open(path, set, NO_ATTRIBUTES);
}
// -- Channel operations --
/**
*
* 從通道中讀入位元組序列到指定緩衝區。
*
* 位元組會從通道的當前position開始讀,position會隨著讀入一些資料而更新。
*/
public abstract int read(ByteBuffer dst) throws IOException;
/**
*
* 從通道中讀入位元組序列到一些指定子緩衝區中。
*
* 位元組會從通道的當前position開始讀,position會隨著讀入一些資料而更新。
* 除此之外,該方法跟ScatteringByteChannel介面一致。
*/
public abstract long read(ByteBuffer[] dsts, int offset, int length)
throws IOException;
/**
*
* 從通道中讀入位元組序列到一些緩衝區中。
*
* 位元組會從通道的當前position開始讀,position會隨著讀入一些資料而更新。
* 除此之外,該方法跟ScatteringByteChannel介面一致。
*/
public final long read(ByteBuffer[] dsts) throws IOException {
return read(dsts, 0, dsts.length);
}
/**
*
* 從指定緩衝區中向通道寫入位元組序列。
*
* 除非通道是append模式(position會首先提升到檔案末尾),位元組將會從通道當前position開始寫入。
* 為了容納寫入的位元組,檔案可能會變大,position也會實時根據具體寫入位元組數更新。
* 除此之外,該方法與WritableByteChannel介面一致。
*/
public abstract int write(ByteBuffer src) throws IOException;
/**
*
* 從指定的一些子緩衝區中寫入位元組序列到通道。
*
* 除非通道是append模式(position會首先提升到檔案末尾),位元組將會從通道當前position開始寫入。
* 為了容納寫入的位元組,檔案可能會變大,position也會實時根據具體寫入位元組數更新。
* 除此之外,該方法與GatheringByteChannel介面一致。
*/
public abstract long write(ByteBuffer[] srcs, int offset, int length)
throws IOException;
/**
*
* 從指定的一些緩衝區中寫入位元組序列到通道中。
*
* 除非通道是append模式(position會首先提升到檔案末尾),位元組將會從通道當前position開始寫入。
* 為了容納寫入的位元組,檔案可能會變大,position也會實時根據具體寫入位元組數更新。
* 除此之外,該方法與GatheringByteChannel介面一致。
*/
*/
public final long write(ByteBuffer[] srcs) throws IOException {
return write(srcs, 0, srcs.length);
}
// -- Other operations --
/**
* 返回通道的檔案position。
*
* @return This channel's file position,
* a non-negative integer counting the number of bytes
* from the beginning of the file to the current position
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract long position() throws IOException;
/**
*
* 設定通道的position。
*
* 設定比當前size更大的position是合法的,但是不會改變實體的size。
* 之後在此時的position上的讀操作將會立即返回EOF,而寫操作會造成實體增長以滿足寫入的新位元組。
* 在之前EOF位置和新的開始寫入位元組位置之間的空白位元組是沒有定義的,也就是垃圾值。
* @param newPosition
* The new position, a non-negative integer counting
* the number of bytes from the beginning of the file
*
* @return This file channel
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws IllegalArgumentException
* If the new position is negative
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract FileChannel position(long newPosition) throws IOException;
/**
*
* 返回當前通道的檔案的當前大小。
* @return The current size of this channel's file,
* measured in bytes
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract long size() throws IOException;
/**
*
* 縮減檔案大小到給定值。
*
* 如果引數size比當前的檔案大小小,則檔案縮減,在新的末尾之後的位元組將會被忽略。
* 如果引數size大於或等於當前