Java基础(5)-----IO_2_NIO

思维导图

一.NIO的目的

    NIO存在的主要目的在于提高速度,并且,IO类也被NIO重新实现了,速度也有一定的提高。

二.NIO的原理

2.1运行模型

        在下图中可以看到,数据源相较于IO类没有多大的变化,通道类Channel则是通过IO类的FlieInputStream,FileOutputStream,RandomAccessFile类取得,而内存会利用一个ByteBuffer和通道类进行交互。打个比方来说,数据源相当于是一座矿山,Channel类相当于是通过上述三个IO类所打的隧道,ByteBuffer相当于车辆,往来于工地(内存)和隧道之间,进行货物(数据)的输送。

 在这幅图中, 数据源和Channel类由FlieInputStream,FileOutputStream,RandomAccessFile类的getChannel()方法取得。

货车ByteBuffer的核心是一个数组,用来存储数据,还有一些字段,用来对货车的数据进行一些操作。

ByteBuffer主要有以下几个字段:

  1. position:当前数组指针所在的地方
  2. capacity:容量,即数组的大小限制
  3. limit:限制,他是在容量这一限制下的又一个限制,即position最后可以达到的位置
  4. mark:一个标记,标记了当时position的值,在重置时有用。

那么货车一般会有什么操作呢:

  • put:放入数据

  • flip:当数据已经输入完成后,需要读取数据时,使用flip(),他会将limit设置在数据区最后的位置,标志数据已被读取完了。而position会被设置在数组的开头,表示开始读取数据。
  • get:从buffer里读一个字节,并把postion移动一位。上限是limit,即写入数据的最后位置

  • clear:将position置为0,并不清除buffer内容。 
  • mark: 将mark值赋为position的值。
  • reset:将此缓冲区的位置重置为以前标记的位置,调用此方法不更改也不丢弃标记的值。

2.2简单使用

//定义输入的数据源
        File file = new File("e:/in.txt");
        try {
            //通过输入流获得隧道
            FileChannel fc = new FileInputStream(file).getChannel();
            //定义一个ByteBuffer货车出来,必须给货车分配容量,才能更快。
            ByteBuffer bb = ByteBuffer.allocate(1024);
            //隧道指定货车读取数据
            fc.read(bb);
            //准备读取货车中的数据
            bb.flip();
            //输出到控制台
            while(bb.hasRemaining()){
                System.out.println((char)bb.get());
            }

            //写入到另一份文件中
            File outFile = new File("e:/out.txt");
            FileChannel fc2 = new FileOutputStream(outFile).getChannel();
            //由于刚才读取过货车中的信息,所以现在positon位于limit上,即数据已经读取完了
            // 所以重置以下,方便读取
            bb.flip();
            fc2.write(bb);
            //也可以使用其他的方法写入数据wrap方法会指定新的ByteBuffer货车,进行运输
            //方法中的Byte数组会直接打包写入
            fc2.write(ByteBuffer.wrap("new way".getBytes()));

            fc.close();
            fc2.close();


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch(IOException e){
            e.printStackTrace();
        }

 

 三.常用功能

3.1字符输出

在上面的程序中,如果输入数据源中有中文,那么输出到控制台就会出现乱码,所以,我们可以在输入数据时进行编码,或者在输出数据时解码。

//定义输入的数据源
        File file = new File("e:/in.txt");
        try {
            //通过输入流获得隧道
            FileChannel fc = new FileInputStream(file).getChannel();
            //定义一个ByteBuffer货车出来,必须给货车分配容量,才能更快。
            ByteBuffer bb = ByteBuffer.allocate(1024);
            //隧道指定货车读取数据
            fc.read(bb);
            bb.flip();
            //准备读取货车中的数据
            Charset charset = Charset.forName("GBK");
            //进行解码
            System.out.println(charset.decode(bb));

            //写入到另一份文件中
            File outFile = new File("e:/out.txt");
            FileChannel fc2 = new FileOutputStream(outFile).getChannel();
            //由于刚才读取过货车中的信息,所以现在positon位于limit上,即数据已经读取完了所以重置以下,方便读取
            bb.flip();
            //输入时不编码也可以将中文输入,当然输入编码,输出就可以不解码了
            fc2.write(bb);
            //输入字符串则可以采用这种编码方式
            fc2.write(ByteBuffer.wrap("小狗".getBytes("GBK")));

            fc.close();
            fc2.close();


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch(IOException e){
            e.printStackTrace();
        }

3.2格式化:视图

在NIO包中,除了ByteBuffer外,还有IntBuffer,FloatBuffer等一些列的基本类型的Buffer,利用他们是ByteBuffer的视图,依旧是说,实际上的处理还是ByteBuffer,我们可以利用这些Buffer对基本类型的数据进行处理。

//定义输入的数据源
        File inFile = new File("e:/in.txt");
        File outFile = new File("e:/out.txt");
        try {
            //获得隧道
            FileChannel inFc = new FileInputStream(inFile).getChannel();
            FileChannel outFc = new FileOutputStream(outFile).getChannel();

            //定义一个ByteBuffer货车出来,必须给货车分配容量,才能更快。
            ByteBuffer bb = ByteBuffer.allocate(1024*20);
            bb.asIntBuffer().put(new int[]{1,2,3,4});
            //此处写出后,在文本中看见的是乱码,因为输出的是int(4字节)的而二进制文件,文本不
            //能显示二进制文件,所以乱码
            outFc.write(bb);

            //读取数据,可以一个一个或者成批次的读取
            bb.flip();
            int i = 0;
            IntBuffer ib = bb.asIntBuffer();
            while((i = ib.get()) != 0){
                System.out.println(i);
            }
            //成批次获取
            ib.flip();
            //数组的大小必须符合ByteBuffer中数据的数量,不然会报错
            int[] temp = new int[ib.limit()];
            ib.get(temp);
            for(int j : temp){
                System.out.print(j);
            }

            inFc.close();
            outFc.close();


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch(IOException e){
            e.printStackTrace();
        }

 

3.3内存映射文件

    由于虚拟机内存太小,或者某些数据太大,导致不能将某些数据放入内存,所以需要内存映射文件,他能直接在内存中映射大文件,可以把它当作非常大的数组访问。

    在写入时,可以使用FileOutputStream,但映射文件的输出则必须使用RandomAccessFile。所以说,最好使用RandomAccessFile

try {
            //先获取隧道
            FileChannel fc = new RandomAccessFile("e:/out.txt","rw").getChannel();
            //通过隧道获取MappedByteBuffer,此类继承自ByteBuffer,是一个抽象类,表示内存映射文件
            //三个参数分别为:使用权限,起始映射位置,映射大小
            MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE,0,1024);
            IntBuffer ib = mbb.asIntBuffer();
            ib.put(1);
            fc.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }

3.4文件加锁

    NIO中由文件加锁机制,可以保证文件的同步或者异步访问。锁能直接映射到系统的文件锁,所以无论是不是Java线程,都不能访问加锁后的文件。

try {
            //先获取隧道
            FileChannel fc = new RandomAccessFile("e:/out.txt","rw").getChannel();
            //tryLock是非阻塞式的,当不可获得时会返回。

            //position - 锁定区域开始的位置;必须为非负数
            //size - 锁定区域的大小;必须为非负数,并且 position + size 的和必须为非负数
            //shared - 要请求共享锁定,则为 true,在这种情况下此通道必须允许进行读取(可能是写入)操作;
            // 要请求独占锁定,则为 false,在这种情况下此通道必须允许进行写入(可能是读取)操作
            
            FileLock fl1 = fc.tryLock(0,512,true);
            //lock是阻塞式的,他将阻塞进程,直到可以获得锁,或者调用lock的通道被关闭
            FileLock fl2 = fc.lock();
            //释放锁
            fl1.release();
            fl2.release();

            fc.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章