NIO - FileChannel

深入理解Java NIO中的FileChannel API
本文详细解析Java NIO中FileChannel的核心特性,包括异步I/O、通道位置管理、文件空洞概念及force()方法的作用,强调FileChannel在文件I/O操作中的高效与灵活性。
[color=#345286] 对于文件 I/O,最强大之处在于异步 I/O(asynchronous I/O),它允许一个进程可以从操作系统请求一个或多个 I/O 操作而不必等待这些操作的完成。发起请求的进程之后会收到它请求的 I/O 操作已完成的通知。异步 I/O 是一种高级性能,当前的很多操作系统都还不具备。[b]因此,文件通道在多数情况下来说总是阻塞式的,因此不能被置于非阻塞模式。[/b][/color]

[color=#345286] FileChannel 对象不能直接创建。一个 FileChannel 实例只能通过在一个打开的 File 对象(RandomAccessFile、FileInputStream 或 FileOutputStream)上调用 getChannel() 方法获取。调用 getChannel() 方法会返回一个连接到相同文件的 FileChannel 对象且该 FileChannel 对象具有与 File 对象相同的访问权限,然后你就可以[/color][color=#008000][b]使用通道对象来利用强大的 FileChannel API了:[/b][/color]

package java.nio.channels;

public abstract class FileChannel extends AbstractChannel implements ByteChannel, GatheringByteChannel, ScatteringByteChannel {
// This is a partial API listing
// All methods listed here can throw java.io.IOException

public abstract int read(ByteBuffer dst, long position);
public abstract int write(ByteBuffer src, long position);
public abstract long size();
// 返回当前文件的 position 值。返回值是一个长整型(long),表示文件中的当前字节位置。
public abstract long position();
// 将通道的 position 设置为指定值。负值,将异常伺候;值可以超过文件尾,这会导致文件空洞。
public abstract void position(long newPosition);
public abstract void truncate(long size);
public abstract void force(boolean metaData);
public final FileLock lock();
public abstract FileLock lock(long position, long size, boolean shared);
public final FileLock tryLock();
public abstract FileLock tryLock(long position, long size, boolean shared);
public abstract MappedByteBuffer map(MapMode mode, long position, long size);

public abstract long transferTo(long position, long count, WritableByteChannel target);
public abstract long transferFrom(ReadableByteChannel src, long position, long count);

public static class MapMode {
public static final MapMode READ_ONLY;
public static final MapMode READ_WRITE;
public static final MapMode PRIVATE;
}
}

[color=#345286] [b]同大多数通道一样,只要有可能,FileChannel 都会尝试使用本地 I/O 服务。[/b]FileChannel 类本身是抽象的,你从 getChannel() 方法获取的实际对象是一个具体子类(subclass)的一个实例(instance),该子类可能使用本地代码来实现以上 API 方法中的一些或全部。[/color]

[color=#345286] [b]FileChannel 对象是线程安全(thread-safe)的。[/b]多个进程可以在同一个实例上并发调用方法而不会引起任何问题,不过并非所有的操作都是多线程的(multithreaded)。[b]影响通道位置或者影响文件大小的操作都是单线程的(single-threaded)。[/b]如果有一个线程已经在执行会影响通道位置或文件大小的操作,那么其他尝试进行此类操作之一的线程必须等待。并发行为也会受到底层的操作系统或文件系统影响。[/color]

[color=#FF0000] [b][size=medium]◇ 访问文件[/size][/b][/color]
[color=#345286] 每个 FileChannel 对象都同一个文件描述符(file descriptor)有一对一的关系,所以上面列出的 API 方法与在你最喜欢的 POSIX(可移植操作系统接口)兼容的操作系统上的常用文件 I/O 系统调用紧密对应也就不足为怪了。[/color]

[color=#345286] 本质上讲,RandomAccessFile 类提供的是同样的抽象内容。在通道出现之前,底层的文件操作都是通过 RandomAccessFile 类的方法来实现的。FileChannel 模拟同样的 I/O 服务,因此它的 API 自然也是很相似的。[/color]

[img]http://dl.iteye.com/upload/attachment/0065/7713/11ff9d9e-962a-3d4a-a4aa-f0e5ad6400e4.png[/img]
[color=#008000] [b]File I/O API 比较[/b][/color]

[color=#345286] 同底层的文件描述符一样,每个 FileChannel 都有一个叫 “file position”的概念。这个 position 值决定文件中哪一处的数据接下来将被读或者写。因此,FileChannel 位置(position)是从底层的文件描述符获得的,该 position 同时被作为通道引用获取来源的文件对象共享。这也就意味着[/color][color=#008000][b]一个对象对该 position 的更新可以被另一个对象看到:[/b][/color]

RandomAccessFile randomAccessFile = new RandomAccessFile ("filename", "r");
// Set the file position
randomAccessFile.seek (1000);

// Create a channel from the file
FileChannel fileChannel = randomAccessFile.getChannel( );

// This will print "1000"
System.out.println ("file pos: " + fileChannel.position( ));

// Change the position using the RandomAccessFile object
randomAccessFile.seek (500);

// This will print "500"
System.out.println ("file pos: " + fileChannel.position( ));

// Change the position using the FileChannel object
fileChannel.position (200);

// This will print "200"
System.out.println ("file pos: " + randomAccessFile.getFilePointer( ));

[color=#345286] 尝试在文件末尾之外的 position 进行一个绝对 read() 操作,size() 方法会返回一个 end-of-file。在超出文件大小的 position 上做一个绝对 write() 会导致文件增加以容纳正在被写入的新字节。[b]文件中位于之前 end-of-file 位置和新添加的字节起始位置之间区域的字节的值不是由 FileChannel 类指定,而是在大多数情况下反映底层文件系统的语义。不过,大部分情况下,这些空洞会被填 0。[/b][/color]

[color=#345286] 当需要减少一个文件的 size 时,truncate() 方法会砍掉您所指定的新 size 值之外的所有数据。如果当前 size 大于新 size,超出新 size 的所有字节都会被悄悄地丢弃。如果提供的新 size 值大于或等于当前的文件 size 值,该文件不会被修改。这两种情况下,truncate() 都会产生副作用:文件的 position 会被设置为所提供的新 size 值。[/color]

[color=#345286] 上面列出的最后一个 API 是 force()。该方法告诉通道强制将全部待定的修改都应用到磁盘的文件上。所有的现代文件系统都会缓存数据和延迟磁盘文件更新以提高性能。调用 force() 方法要求文件的所有待定修改立即同步到磁盘。[/color]

[color=#FF0000] [b][size=medium]◇ 文件空洞[/size][/b][/color]
[color=#345286] 文件位移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将延长该文件,并在文件中构成一个空洞,这一点是允许的。位于文件中但没有写过的字节都被设为 0。[/color]

[color=#345286] 如果 offset 比文件的当前长度更大,下一个写操作就会把文件“撑大(extend)”。这就是所谓的在文件里创造“空洞(hole)”。没有被实际写入文件的所有字节由重复的 0 表示。[b]空洞是否占用硬盘空间由文件系统(file system)决定。[/b][/color]
### 回答1: FileChannelJava NIO中的一个类,它用于从文件中读取数据或向文件写入数据。FileChannel是非阻塞的,这意味着当调用FileChannel的读写方法时,线程不会被阻塞。相反,它会立即返回一个Future对象,你可以在后面调用这个对象的get()方法来获取实际读写操作的结果。 例如,你可以使用FileChannel来读取文件的内容,代码如下: ``` Path path = Paths.get("file.txt"); FileChannel channel = FileChannel.open(path, StandardOpenOption.READ); ByteBuffer buffer = ByteBuffer.allocate(1024); Future<Integer> result = channel.read(buffer, 0); int bytesRead = result.get(); ``` 你也可以使用FileChannel向文件写入数据,代码如下: ``` Path path = Paths.get("file.txt"); FileChannel channel = FileChannel.open(path, StandardOpenOption.WRITE); ByteBuffer buffer = ByteBuffer.wrap("Hello, World!".getBytes()); Future<Integer> result = channel.write(buffer, 0); int bytesWritten = result.get(); ``` 需要注意的是,FileChannel只能用于文件,如果你想要从网络或其他非文件源读取数据,你需要使用Java NIO中的其他类,例如SocketChannel或ServerSocketChannel。 ### 回答2: FileChannelJava NIO中表示文件的通道。它是对文件进行读取和写入的通道,类似于传统IO中的InputStream和OutputStream。 通过FileChannel,我们可以使用缓冲区(Buffer)来读取和写入文件的数据。我们可以创建一个FileChannel对象来操作文件,这可以通过FileInputStream、FileOutputStream或RandomAccessFile等类的getChannel()方法来获取。 FileChannel支持阻塞式和非阻塞式的IO操作。我们可以使用read()方法从通道中读取数据,使用write()方法将数据写入通道。还可以使用transferTo()和transferFrom()方法在通道之间直接传输数据,避免了传统IO中需要使用缓冲区来进行数据复制的过程。 FileChannel还提供了其他的一些有用的方法,比如position()和position(long newPosition)用于获取和设置文件指针的位置;size()方法用于获取文件的大小;truncate(long size)用于截断文件等。 在操作文件时,通常需要首先打开一个FileChannel,然后创建一个缓冲区来进行数据的读取和写入。一般的操作流程是,首先从通道中读取数据到缓冲区中,然后进行相应的处理,最后将数据写入到文件中。 总之,FileChannelJava NIO中用于操作文件的通道,它提供了更加灵活和高效的文件操作方式。通过FileChannel,我们可以使用缓冲区来读取和写入文件的数据,还可以直接在通道之间传输数据。同时,FileChannel还提供了一些方便的方法来操作文件的指针和大小等属性。使用FileChannel可以提高文件IO操作的效率和灵活性。 ### 回答3: NIO(New Input/Output)中的FileChannel是用于文件的通道类,它提供了一种在文件中读取、写入和操作数据的方式。 FileChannel可以通过Java程序对文件进行各种操作,包括读取文件内容、写入数据到文件,以及对文件指定位置进行读写操作等。 FileChannel的一些主要功能包括: 1. 读取文件内容:可以使用FileChannel的read()方法来读取文件中的数据,可以一次读取一定数量的字节,也可以读取整个文件的内容。 2. 写入文件内容:使用FileChannel的write()方法,可以将数据写入文件,可以一次写入一定数量的字节,也可以覆盖原有内容或者追加到文件末尾。 3. 文件位置操作:FileChannel的position()方法可以获取当前位置,也可以通过position(long newPosition)方法设置新的位置。可以根据实际需要在文件中进行定位,进行读写操作。 4. 通道之间的传输:FileChannel提供了transferFrom()和transferTo()方法用于直接在通道之间传输数据,可以高效地传输大量数据。 5. 文件的锁定:可以使用FileChannel的lock()和tryLock()方法对文件进行锁定,确保多个线程或进程之间对同一个文件的读写操作的互斥性。 总之,FileChannelNIO中用于处理文件的通道,它提供了一系列的API方法,可以方便地读取、写入和操作文件中的数据,同时还提供了一些文件的操作特性,如文件位置操作、通道之间的数据传输以及文件的锁定等。使用FileChannel可以更灵活地对文件进行操作,并且相比于传统的IO操作,它在性能上可以得到一定的提升。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值