NIO核心对象

NIO vs BIO

NIO区别于传统IO的主要特征在于:

NIO采用内存映射文件的机制。这种机制是将文件的某一字节块部分映射到内存中,从而加快程序执行效率。因为传统的IO是通过指针在文件上的移动按字节处理的,它把文件当成存在硬盘里的一长串字节序列,然后通过操作系统提供的封装访问。

一个框架必然要让功能变得简而有效,NIO就是提供一个更加优雅的接口。我不认为初学者应该了解整个NIO体系的很多类,通常,只需要一个简单的使用和练习掌握使用NIO的模式就已经足够了。其他功能可以只在必要时进行

小例子

buffer和channel类,这两个类相互依存,buffer提供了channel类的缓冲区,而channel类负责信息之间的传输。

其实,NIO的风格和IO也有点相似:

InputStream的构造器没有暴露出来,而是需要子类构造器返回子类引用,然后利用多态特性将子类引用赋值父类引用

   InputStream input=new FileInputStream(url);

Channel也是一样,但存在对传统IO的依赖,这一点我认为不太好,这样增加代码耦合度
一个可行的解决方案是:将部分IO类迁移到NIO项目下的一个IOdependcy包中,这样一个组件就是一个独立的组件。

你需要通过传统的IO对象获得channel对象:

  Channel input=new FileInputStream(new File(url)).getChannel();

Channel对象给我最好的印象就是将InputStream和OutputStream集合到了一块,因为时间一长,我发现
InputStream和OupputStream通常成对出现用于端到端的数据传递,除了InputStreamObject.read(byte[] buffer)将数据读入内存字节数组和OutputStreamObject.write(byte[] buffer)将缓冲区中的字节写入OutputStream持有目标端,好像也没什么区别。

而且最让我诟病的是,还要像这样:

while((len=input.read(buffer)!=-1)){
}
// 或者
while((input.readLine())!=null){
}

所以Channel是提供了read和write,输入端和输出端Channel对象在命名上区分明显有就行了。

  FileChannel in=new FileInputStream(new File(sourceurl)).getChannel();
  FileChannel out=new FileOutputStream(new File(targeturl)).getChannel();

你不需要去创建一个新的字节数组去当作Channel类的缓冲区,而是利用封装好的方法:

  Buffer buffer=in.map(FileChannel.MapMode.($你的模式放在这里),0,len);

map返回的MappedByteBuffer是Buffer类的子类,你完全可以使代码的简洁性更高。

多注释代码

下面这个小练习将a.txt内容写入到b.txt

public class NIOCopyFile {

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		File source=new File("C:\\Users\\onesl\\Desktop\\a.txt");
        File target=new File("C:\\Users\\onesl\\Desktop\\b.txt");
        
        try {
			FileChannel in=new FileInputStream(source).getChannel();   //输入通道
			FileChannel out=new FileOutputStream(target).getChannel(); //输出通道
			//映射文件字节序列到内存
			Buffer buffer=in.map(FileChannel.MapMode.READ_ONLY, 0, source.length());
			out.write((ByteBuffer) buffer);      //文件写入b.txt
		} catch (Exception e) {
		}
	}

}

Buffer

Buffer在NIO中就是采矿车,一个萝卜一个坑吗!其有非常重要的几个特征。

  • capacity: Buffer存储的数组长度(不可变)
  • limit: 第一个不可读或不可写的字节
  • position: 下一个可读或可写的位置
  • mark: 就是在一个字节序列里面标记一个标记点(是一个int值,默认-1),你读到的序列的后面想回来重读,可以从标记的地方开始

flip

下面转载自谷歌图片
在这里插入图片描述
下面从概念上解析一下典型场景:

读文件的时候要先向Buffer当中写入数据,此时可用写空间和Buffer的容量相同(limit=capacity),然后Channel就往Buffer里填萝卜,填一个萝卜,position就往前走一个+1。

在这里插入图片描述
坑填完了,position位置代表最后写入的字节位置。这个时候也该读了。因为position是读写针,所以应该设置position=0,limit标志可读或可写极限,应该设置limit等于刚才写到的位置,即limit=position。这个操作就是flip().写模式到读模式的切换。

compact

比如写操作把字节数组写满了,后买一次读操作没有读完,只读到一半。而这个时候又想往字节数组里写数据。这个时候只能把读过的字节所占用的空间腾出来,将没有读过的字节左移。这就是compact操作。

ByteBuffer

字节缓冲区分成堆外和堆内:

在这里插入图片描述
如何获取到一个ByteBuffer呢?

通过allocate(int capacity)直接申请或者使用现成的数组wrap(byte[] array,int offset, int length)包装。这样创建的ByteBuffer拥有整个字节数组的使用权

Appendix A:

NIO性能测试

文件较小的情况下,普通BIO(带缓冲区)优于NIO直接缓冲。在文件大概100M左右的情况下,两者逐渐相等。当文件较大情况下,NIO逐渐占据优势。

    public static void main(String[] args) throws IOException {

        FileChannel source=new FileInputStream("large.txt").getChannel();
        FileChannel target=new FileOutputStream("a.txt").getChannel();

        long now=System.nanoTime();
        // direct 313046, not direct 345952
        ByteBuffer buffer=ByteBuffer.allocate(1024); //change to ByteBuffer.allocate(1024)
        int n; // bytes read from source
        while ((n=source.read(buffer))!=-1){
            buffer.flip();
            target.write(buffer);
            buffer.compact();
        }
        long end=System.nanoTime();
        System.out.println((end-now)/1_000);
/*
        long now_bio=System.nanoTime();
        //326547
        InputStream source1=new FileInputStream("large.txt");
        OutputStream target1=new FileOutputStream("a.txt");
        byte[] bioBuffer=new byte[1024];
        int bioCount;
        while ((bioCount=source1.read(bioBuffer))!=-1){
            target1.write(bioBuffer,0,bioCount);
        }
        long end_bio=System.nanoTime();
        System.out.println((end_bio-now_bio)/1_000);*/

     /*  OutputStream stream=new FileOutputStream("D:\\personal\\workbunch\\sonaranalysis\\large.txt");

        for (int i = 0; i < Math.pow(2,29); i++) {
            stream.write(i);
        }*/
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值