NIO学习笔记(六)FileChannel类API的使用

NIO学习笔记(六)FileChannel类API的使用

FileChannel的主要作用是读取、写入、映射和操作文件的通道。该通道永远是阻塞操作的。

FileChannel在内部维护了当前文件的position,可以对其进行查询和修改。该文件本身包含一个可读写、长度可变的字节序列,并且可以查询该文件的当前大小。当写入的字节超出文件的当前大小,则增加文件的大小;截取该文件的时候,则减小文件的大小。文件可能还有某个相关联的元数据,如访问权限、内容类型和最后的修改时间,但此类未定义访问元数据的方法。

除了字节通道常见的读取和写入、关闭操作外,此类还定义了下列特定于文件的操作:

  1. 以不影响通道当前位置的方式,对文件中的绝对位置的字节进行读取和写入。
  2. 将文件中的某个区域直接映射到内存中。对于较大的文件,这通常比调用普通的read和write更加高效。
  3. 强制对底层的存储设备进行文件的更新,确保在系统崩溃时不丢失数据。
  4. 可以锁定某个文件区域,以阻止其他程序对其访问。
  5. 以一种可被很多操作系统优化为直接向文件系统缓存发送或从中读取的高速传输方法,将字节从文件传授到某个通道,反之亦然。

1、写操作与位置使用

int write(ByteBuffer src)方法的作用是将remaining字节序列从给定的缓冲区中写入此通道的当前位置,此方法的行为与WritableByteChannel接口所指的行为完全一样;在给定的任一时刻,一个可写入的通道上只能进行一个写入操作,那么在第一个操作完成之前,将阻塞其他试图发起另一个写入操作的线程。该方法返回值代表写入的字节数,可能为0.

这个方法有两个特点:

  1. 将一个ByteBuffer缓冲区的remaining字节序列写入通道的当前位置。
  2. write(ByteBuffer)本身是一个同步方法。

使用示例:

public class FileChannelWriteDemo {
    private static FileOutputStream fos;
    private static FileChannel fileChannel;

    public static void main(String[] args) throws IOException, InterruptedException {
        fos = new FileOutputStream(new File("C:\\Users\\dell\\Desktop\\aa.txt"));
        fileChannel = fos.getChannel();
        for(int i = 0 ; i < 10 ; i ++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    ByteBuffer buffer = ByteBuffer.wrap("abc\r\n".getBytes());
                    try {
                        fileChannel.write(buffer);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    ByteBuffer buffer = ByteBuffer.wrap("123\r\n".getBytes());
                    try {
                        fileChannel.write(buffer);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        Thread.sleep(3000);
        fileChannel.close();
        fos.close();
    }
}

2、读操作

int read(ByteBuffer src)方法的作用将字节序列从此通道的当前位置读入给定的缓冲区的位置。此方法的行为与ReadableByteChannel接口中指定的行为完全一致,在任意给定时刻,一个可读通道上只能进行一个读取操作。如果某个线程在通道上发起读取操作,那么在第一个操作完成之前,将阻塞其他所有试图发起读取操作的线程。其他类型的IO操作是否继续与读取操作并发执行,取决于该通道的类型。该方法的返回值代表读取的字节数,可能为0。如果该通道达到末尾,则返回-1。

这个方法有两个特点:

  1. 将通道当前位置的字节序列读取到一个ByteBuffer缓冲区的remaining空间中。
  2. read(ByteBuffer)本身是一个同步方法。

示例代码:

public class ReadDemo {
    private static FileInputStream fos;
    private static FileChannel fileChannel;
    public static void main(String[] args) throws IOException {
        fos = new FileInputStream(new File("C:\\Users\\dell\\Desktop\\aa.txt"));
        fileChannel = fos.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate(5);
        int readLength = fileChannel.read(buffer);
        //查看一下返回值是什么
        System.out.println(readLength);
        //如果返回值为0,因为buffer的remaining为0
        readLength = fileChannel.read(buffer);
        System.out.println(readLength);
        buffer.clear();
        readLength = fileChannel.read(buffer);
        //达到流的末尾,他的值为-1
        System.out.println(readLength);
        buffer.clear();
        fileChannel.close();
        fos.close();
    }
}

3、批量写操作

long write(ByteBuffer[] srcs)方法的作用是将每个缓冲区的remaining字节序列写入此通道的当前位置。调用此方法的形式为c.write(srcs),该调用与c.write(srcs,0,srcs.length)一样。该方法实现的是GatherByteChannel接口中的同命名方法,之前介绍过这个接口,这里我就不多提了。

该方法呢有以下特性:

  1. 讲一个ByteBuffer缓冲区中的remaining字节序列写入通道的当前位置。
  2. write(ByteBuffer)方法是同步的。

接下来我们来看一个例子,顺便验证一下他的同步性

public class SyncWriteDemo {
    private static FileOutputStream fos;
    private static FileChannel fileChannel;
    public static void main(String[] args) throws IOException, InterruptedException {
        fos = new FileOutputStream(new File("F:\\Idea-Code\\NIO-Demo\\src\\com\\xiao\\nio\\channel\\aa.txt"));
        fileChannel = fos.getChannel();

        for (int i = 0; i < 10; i++) {

            new Thread(new Runnable() {
                @Override
                public void run() {
                    ByteBuffer buffer1 = ByteBuffer.wrap("odhvuwenive".getBytes());
                    ByteBuffer buffer2 = ByteBuffer.wrap("ewhvownvebw".getBytes());
                    ByteBuffer[] bb = new ByteBuffer[]{buffer1,buffer2};
                    try {
                        fileChannel.write(bb);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    ByteBuffer buffer1 = ByteBuffer.wrap("aaaaaaaaaaa".getBytes());
                    ByteBuffer buffer2 = ByteBuffer.wrap("bbbbbbbbbbb".getBytes());
                    ByteBuffer[] bb = new ByteBuffer[]{buffer1,buffer2};
                    try {
                        fileChannel.write(bb);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        Thread.sleep(3000);
        fileChannel.close();
        fos.close();
    }
}

4、批量读操作

long read(ByteBuffer[] srcs)方法的作用是将字节序列从此通道读入给定的缓冲区数组中的第0个缓冲区的当前位置。调用此方法的形式为c.read(dsts),该调用与调用c.read(dsts,0,dsts.length)的形式完全相同。该方法的实现是ScatteringByteChannel接口中的同名方法。该方法有以下特性:

  1. 将通道当前位置的字节序列读入第一个ByteBuffer缓冲区的remaining空间中
  2. read(ByteBuffer[] srcs)方法是同步的

接下来我们来看一个例子

public class SyncReadDemo {
    private static FileInputStream fos;
    private static FileChannel fileChannel;
    public static void main(String[] args) throws IOException, InterruptedException {
        fos = new FileInputStream(new File("F:\\Idea-Code\\NIO-Demo\\src\\com\\xiao\\nio\\channel\\aa.txt"));
        fileChannel = fos.getChannel();

        for (int i = 0; i < 10; i++) {

            new Thread(new Runnable() {
                @Override
                public void run() {
                    ByteBuffer buffer1 = ByteBuffer.allocate(8);
                    ByteBuffer buffer2 = ByteBuffer.allocate(8);
                    ByteBuffer[] bb = new ByteBuffer[]{buffer1,buffer2};
                    try {
                        long readlength = fileChannel.read(bb);
                        while (readlength != -1){
                            synchronized (SyncReadDemo.class){
                                System.out.println(Arrays.toString(bb));
                            }
                            buffer1.clear();
                            buffer2.clear();
                            readlength = fileChannel.read(bb);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    ByteBuffer buffer1 = ByteBuffer.allocate(8);
                    ByteBuffer buffer2 = ByteBuffer.allocate(8);
                    ByteBuffer[] bb = new ByteBuffer[]{buffer1,buffer2};
                    try {
                        long readlength = fileChannel.read(bb);
                        while (readlength != -1){
                            synchronized (SyncReadDemo.class){
                                System.out.println(Arrays.toString(bb));
                            }
                            buffer1.clear();
                            buffer2.clear();
                            readlength = fileChannel.read(bb);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

        Thread.sleep(3000);
        fileChannel.close();
        fos.close();


    }
}

5、部分批量写操作

long wirte(ByteBuffer[] srcs,int offset , int length)方法的作用是以指定缓冲区数组的offset下标开始,向后使用length个字节缓冲区,再讲每个缓冲区的remaining剩余字节子序列写入此通道的当前位置。该方法实现的是GatheringByteChannel的接口中的同名方法,该方法有以下特性:

  1. 将1个ByteBuffer缓冲区的remaining字节序列写入通道的当前位置
  2. write方法是同步的

接下来我们来看一个例子

public class PartWriteDemo {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream(new File("F:\\Idea-Code\\NIO-Demo\\src\\com\\xiao\\nio\\channel\\aa.txt"));
        FileChannel fileChannel = fos.getChannel();

        ByteBuffer buffer1 = ByteBuffer.wrap("odhvuwenive".getBytes());
        ByteBuffer buffer2 = ByteBuffer.wrap("ewhvownvebw".getBytes());
        ByteBuffer[] bb = new ByteBuffer[]{buffer1,buffer2};

        fileChannel.write(bb,0,2);
        fileChannel.close();
        fos.close();
    }
}

6、部分批量读操作

long read(ByteBuffer[] srcs,int offset , int length)方法的作用是将通道当前位置的字节序列读入以下标为offset开始的ByteBuffer[]数组中的remianing剩余空间,并且连续写入length个ByteBuffer空间。该方法实现的是ScatteringByteChannel的接口中的同名方法,该方法有以下特性:

  1. 将通道的当前位置的字节序列读入1个ByteBuffer缓冲区的remaining
  2. write方法是同步的

接下来我们来看一个例子

public class PartReadDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream(new File("F:\\Idea-Code\\NIO-Demo\\src\\com\\xiao\\nio\\channel\\aa.txt"));
        FileChannel fileChannel = fis.getChannel();

        ByteBuffer buffer1 = ByteBuffer.allocate(2);
        ByteBuffer buffer2 = ByteBuffer.allocate(2);
        ByteBuffer[] bb = new ByteBuffer[]{buffer1,buffer2};

        long readlength = fileChannel.read(bb,0,2);
        System.out.println(readlength);
        System.out.println(bb);



        fileChannel.close();
        fis.close();
    }
}

7、向通道的指定position位置写入数据

write(ByteBuffer src , long position)方法的作用是将缓冲区src的remaining字节序列写入通道的指定位置position。除了从给定的文件位置开始写入各个字节,而不是从该通道的当前位置外此方法的执行方式与write(ByteBuffer)大体一致。

接下来我们来看一个小小例子

public class WrietPosition {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream(new File("F:\\Idea-Code\\NIO-Demo\\src\\com\\xiao\\nio\\channel\\aa.txt"));
        FileChannel fileChannel = fos.getChannel();

        ByteBuffer buffer1 = ByteBuffer.wrap("odhvuwenive".getBytes());

        fileChannel.write(buffer1,2);
        fileChannel.close();
        fos.close();
    }
}

8、读取通道指定位置的数据

read(ByteBuffer src , long position)方法的作用是将通道指定位置的字节序列读入给定的缓冲区的当前位置。

下面我们来看一个例子

public class ReadPosition {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream(new File("F:\\Idea-Code\\NIO-Demo\\src\\com\\xiao\\nio\\channel\\aa.txt"));
        FileChannel fileChannel = fis.getChannel();

        ByteBuffer buffer1 = ByteBuffer.allocate(2);

        long readlength = fileChannel.read(buffer1,2);
        System.out.println(readlength);


        fileChannel.close();
        fis.close();
    }
}

9、设置位置与获取大小

position(long newposition)方法的作用是设置此通道的文件位置。将该位置设为大于文件当前大小的值是合法的,但这不会更新文件的大小,稍后试图在这样的位置读取字节将立即返回已到达文件末尾的指示,稍后试图在这个位置写入字节将导致文件扩大,以容纳新的字节,在以前的文件末尾和新写入字节之间的字节值是未规定的。

long size()方法的作用是返回此通道关联文件的当前大小。

public class Position {
    public static void main(String[] args) throws IOException {
        ByteBuffer buffer1 = ByteBuffer.wrap("odhvuwenive".getBytes());
        ByteBuffer buffer2 = ByteBuffer.wrap("ewhvownvebw".getBytes());
        FileOutputStream fos = new FileOutputStream(new File("F:\\Idea-Code\\NIO-Demo\\src\\com\\xiao\\nio\\channel\\aa.txt"));
        FileChannel channel = fos.getChannel();
        System.out.println("position = "+channel.position()+" , size = "+channel.size());
        channel.write(buffer1);
        System.out.println("position = "+channel.position()+" , size = "+channel.size());
        channel.position(2);
        System.out.println("position = "+channel.position()+" , size = "+channel.size());
        channel.write(buffer2);
        System.out.println("position = "+channel.position()+" , size = "+channel.size());
        channel.close();
        fos.flush();
        fos.close();

    }
}

10、截断缓冲区

truncate(long size)方法的作用是将此通道的文件截取为给定的大小。如果给定的大小小于该文件的大小,则截取该文件,丢弃文件新末尾后面的所有字节。如果给定大小大于或等于该文件的当前大小,则不修改文件。无论是哪种情况,如果此通道的文件位置大于给定大小,则将位置设置为该大小。下面我们看一段代码:

//如果给定大小大于或等于该文件的当前的大小,则不修改文件
public class Truncate {
    public static void main(String[] args) throws IOException {
        ByteBuffer buffer = ByteBuffer.wrap("xiaosd".getBytes());
        FileOutputStream fos = new FileOutputStream(new File("F:\\Idea-Code\\NIO-Demo\\src\\com\\xiao\\nio\\channel\\aa.txt"));
        FileChannel channel = fos.getChannel();
        channel.write(buffer);
        System.out.println("position = "+channel.position()+" , size = "+channel.size());
        channel.truncate(3000);
        System.out.println("position = "+channel.position()+" , size = "+channel.size());
        channel.close();
        fos.flush();
        fos.close();
    }
}

11、将数据传输到其他可写入字节通道

long transferTo(position,count,WritableByteChannel dest)方法的作用是将字节从此通道的文件传输到给定的可写入字节通道。transferTo()的功能相当于write()方法,只不过是将通道中的数据传输到另一个通道中,而不是缓冲区中。试图读取从此通道position开始的count个字节,并将其写入目标通道的当前位置。那么会发生以下几种情况

  1. 如果给定的位置大于该文件的当前大小,则不传输任何字节。
  2. 如果count的字节个数大于position到size之间的个数,则传输通道的size-position个字节数到目标通道。
  3. 如果count的字节数小于position到size之间的个数,则传输通道的count个字节数到目标通道。

下面来看一个例子

public class TransferTo {
    public static void main(String[] args) throws IOException {
        RandomAccessFile file1 = new RandomAccessFile("F:\\Idea-Code\\NIO-Demo\\src\\com\\xiao\\nio\\channel\\aa.txt","rw");
        RandomAccessFile file2 = new RandomAccessFile("F:\\Idea-Code\\NIO-Demo\\src\\com\\xiao\\nio\\channel\\bb.txt","rw");
        FileChannel fileChannel1 =  file1.getChannel();
        FileChannel fileChannel2 =  file2.getChannel();
        System.out.println("position = "+fileChannel2.position());
        fileChannel1.transferTo(1,5,fileChannel2);
        System.out.println("position = "+fileChannel2.position());
        fileChannel1.close();
        fileChannel2.close();

        file1.close();
        file2.close();
    }
}

12、将字节给定可读取字节通道传输到此通道的文件中

long transferFrom(ReadableByteChannel src , position ,count )方法的作用是将字节从给定的可读取的字节通道传输到此通道的文件中。transferFrom()方法的功能相当于read()方法,只不过是将通道中的数据传输到另一个通道,而不是缓存中。试着从源通道中最多读取count个字节,并将其写入到此通道中的文件中给定position处开始的位置。此方法的调用不一定传输所有请求的字节;是否传输取决于通道的性质和状态。如果源通道的剩余空间小于count个字节,或者如果源通道是非阻塞的,并且其输入缓冲区中可以直接可用的空间小于count个字节,则所传输的字节数要小于请求的字节数。

public class TransferToFrom {
    public static void main(String[] args) throws IOException {
        RandomAccessFile file1 = new RandomAccessFile("F:\\Idea-Code\\NIO-Demo\\src\\com\\xiao\\nio\\channel\\aa.txt","rw");
        RandomAccessFile file2 = new RandomAccessFile("F:\\Idea-Code\\NIO-Demo\\src\\com\\xiao\\nio\\channel\\bb.txt","rw");
        FileChannel fileChannel1 =  file1.getChannel();
        FileChannel fileChannel2 =  file2.getChannel();
        fileChannel2.position(4);
        System.out.println("position = "+fileChannel2.position());
        fileChannel1.transferFrom(fileChannel2,3,2);
        System.out.println("position = "+fileChannel2.position());
        fileChannel1.close();
        fileChannel2.close();

        file1.close();
        file2.close();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Java中的FileChannel和ByteBuffer来实现NIO操作,可以提高文件读取效率。以下是一个使用NIO读取文件的示例代码: ```java import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class NIOFileReader { public static void main(String[] args) throws IOException { // 打开文件通道 FileChannel fileChannel = new FileInputStream(new File("file.txt")).getChannel(); // 创建一个缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); // 从文件通道读取数据到缓冲区 while (fileChannel.read(buffer) != -1) { // 切换读写模式 buffer.flip(); // 读取缓冲区中的数据 while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); } // 清空缓冲区 buffer.clear(); } // 关闭文件通道 fileChannel.close(); } } ``` 在这个示例中,我们首先打开文件通道,然后创建一个ByteBuffer缓冲区来存储读取到的数据。在循环中,我们使用FileChannel的read()方法将数据读取到缓冲区中,然后使用ByteBuffer的flip()方法切换读写模式,从而可以读取缓冲区中的数据。最后使用ByteBuffer的clear()方法清空缓冲区。 需要注意的是,在使用FileChannel读取数据时,需要先将数据读取到缓冲区中,然后再从缓冲区中读取数据。同时,由于缓冲区的大小是有限的,因此需要在循环中反复读取数据,直到读取到文件的末尾为止。 除了读取数据外,FileChannel还可以用于写入数据、截取文件、移动文件指针等操作。使用Java中的NIO操作可以提高文件读取效率,适用于需要频繁读写文件的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值