zoukankan      html  css  js  c++  java
  • java8--NIO(java疯狂讲义3复习笔记)

    NIO采用内存映射文件的方式处理输入输出,NIO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了(这种方式模拟了操作系统上的虚拟内存的概念),通过这种方式来进行输入输出比传统的输入输出要快得多.

    Java中与NIO相关的包如下:

    java.nio包:主要包含各种与Buffer相关的类.

    java.nio.channels包:与Channle和Selector相关的类.

    java.nio.charset包:主要包含与字符集相关的类

    java.nio.channels.spi包:与Channel相关的服务提供编程接口

    java.nio.charset.spi包:包含与字符集想的服务提供者编程接口

    NIO中的新特点:Channel(通道)和Buffer(缓冲),Channle与传统的InputStream,OutputStream最大的区别在于它提供了一个map()方法,通过该方法可以海子街将"一块数据"映射到内存中.如果说传统的输入/输出系统是面向流的处理,则NIO则是面向块的处理.

    Buffer可以被理解成一个容器,它的本质是一个数组,发送到Channel中的所有对象都必须首先放到Buffer中,而从Channel中读取的数据也必须先放到Buffer中.

    除了ByteBuffer之外,它们都采用相同或相似的方法来管理数据,只是各自管理的数据类型不同而已.

    NIO还提供了加工Unicode字符串映射成字节序列以及逆映射操作的Charset类, 也提供了用于支持费阻塞式输入输出的Selector类.

    15.9.2使用Buffer

    Buffer就像是一个数组,可以保存多个类型相同的数据.Buffer是一个抽象类,最常用的子类是ByteBuffer,它可以在底层字节数组上进行get/set操作. 也有CharBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer, DoubleBuffer.

    这些实现类都没提供构造器,使用如下方式是构造:

    ByteBuffer byteBuffer = ByteBuffer.allocate(160);

    或者 

    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(160);//ByteBuffer 独有的创建方法,称为直接BUffer

     

    直接Buffer的创建成本比普通的Buffer创建成本高,但效率也高,所以只适用于长生存期的Buffer,不适用于短生存期,一次用完就丢弃的Buffer.


    但实际使用较多的是ByteBuffer和CharBuffer,其他Buffer子类则较少用到.其中ByteBuffer还有一个子类: MappedByteBuffer,它用于表示Channel将磁盘文件的部分或全部内容映射到内存中后得到的结果,通常MappedByteBuffer对象由Channel的map()方法返回.

    Buffer中三个重要的概念: 容量(capacity),界限(limit),位置(position)

    Buffer的主要作用就是装入数据,然后输出数据. 开始时,Buffer的Position为0,limit为capacity, 程序可通过put()方法向Buffer中放入一些数据(或者从Channel中获取一些数据),每放入一些数据,Buffer的position相应地向后移动一些位置.

    当Buffer装入数据结束后,调用Buffer的flip()方法,该方法将limit设置为position所在位置,并将position设为0,这就使得Buffer的读写指针又移动到了开始的位置.也就是说,Buffer调用filp()方法之后,Buffer为输出数据做好准备;当Buffer输出数据结束后,Buffer调用clear()方法,clear方法不是清空Buffer的数据,它仅仅将position指为0,将limit置为capacity,这样为再次向Buffe热衷装入数据做好准备.

    Buffer包含的一些常用方法:

    这些方法大概可以分类

    1.标记功能: mark()和reset()

    2.position操作: position() position(int newPs) rewind() reset()

    3.limit操作: limit() limit(int newLt)

    4.剩余元素操作: hasRemaining() remaining()

    5.容器大小: capacity()

    重点:

    put()方法:用于向Buffer中放入数据

    get()方法:用于从Buffer中取出数据

    这两个方法,既支持对单个数据的访问,也支持对批量数据的访问.

    而且,分为相对和绝对两种.

    相对(relative):从Buffer的当前position处开始读取或写入数据, 然后将位置(position)的值按处理的元素的个数增加.

    绝对(Absolute):直接根据索引向Buffer中读取或写入数据,使用绝对方式访问Buffer里的数据时,并不会影响位置(position)的值.

        public static void bufferTest(){
            CharBuffer buff = CharBuffer.allocate(8);    //
            System.out.println("capacity: "    + buff.capacity());
            System.out.println("limit: " + buff.limit());
            System.out.println("position: " + buff.position());
            // 放入元素
            buff.put('a');
            buff.put('b');
            buff.put('c');      //
            System.out.println("加入三个元素后,position = "
                + buff.position());
            // 调用flip()方法
            buff.flip();      //
            System.out.println("执行flip()后,limit = " + buff.limit());
            System.out.println("position = " + buff.position());
            // 取出第一个元素
            System.out.println("第一个元素(position=0):" + buff.get());  //
            System.out.println("取出一个元素后,position = "
                + buff.position());
            // 调用clear方法
            buff.clear();     //
            System.out.println("执行clear()后,limit = " + buff.limit());
            System.out.println("执行clear()后,position = "
                + buff.position());
            System.out.println("执行clear()后,缓冲区内容并没有被清除:"
                + "第三个元素为:" +  buff.get(2));    //
            System.out.println("执行绝对读取后,position = "
                + buff.position());
        }

    15.9.3 使用Channel

    Channel类似于传统的流对象,但与传统的流对象有两个主要区别

    1. Channel可以直接将指定文件的部分或全部直接映射成Buffer.

    2. 程序不能直接访问Channel中的数据,包括读取, 写入,都不行,Channel中取出一些数据,然后让程序从Buffer中取出这些数据:如果要将程序中的数据写入Channel, 一样先让程序将数据放入Buffer中,程序再将Buffer里的数据写入Channel中.

    Channle接口的实现类很多,这里只介绍FileChannel的用法.

    所有的Channle都不应该通过构造器来直接创建,而是通过传统的节点InputStream,OutputStream的getChannle()方法来返回对应的Channle.

    Channle中最常用的三类方法是map(),read(),write(),其中map()方法用于将Channle对应的部分或全部数据映射成ByteBuffer,而read()和write()方法都有一系列重载形式,这些方法用于从Buffer中读取数据或者向Buffer中写入数据.

    public static void fileChannelTest(){
            File f = new File(".project");
            try(
                // 创建FileInputStream,以该文件输入流创建FileChannel
                FileChannel inChannel = new FileInputStream(f).getChannel();
                // 以文件输出流创建FileBuffer,用以控制输出
                FileChannel outChannel = new FileOutputStream("aw.txt")
                    .getChannel())
            {
                // 将FileChannel里的全部数据映射成ByteBuffer
                MappedByteBuffer buffer = inChannel.map(FileChannel
                    .MapMode.READ_ONLY , 0 , f.length());   //// 直接将buffer里的数据全部输出
                outChannel.write(buffer);     //// 再次调用buffer的clear()方法,复原limit、position的位置
                buffer.clear();
                
                // 使用GBK的字符集来创建解码器
                Charset charset = Charset.forName("UTF-8");
                // 创建解码器(CharsetDecoder)对象
                CharsetDecoder decoder = charset.newDecoder();
                // 使用解码器将ByteBuffer转换成CharBuffer
                CharBuffer charBuffer =  decoder.decode(buffer);
                // CharBuffer的toString方法可以获取对应的字符串
                System.out.println(charBuffer);
            }
            catch (IOException ex)
            {
                ex.printStackTrace();
            }
        }

     下面给一个出问题的例子,这个是

    RandomAccessFile生成的FileChannle,可是与疯狂讲义里说的有偏差,主要问题是当插入的数据超出FileChannle的范围时,FileChannle不会自动扩大,反而变成等待状态,
    所访问的文件直接挂掉,一点那个文件,eclipse也挂掉,最后Mac都无法关机,只能强制关机。也不知道哪里没用对。
    public static void randomFileChannelTest() throws FileNotFoundException, IOException{
            File f = new File("write.txt");
            try(
                // 创建一个RandomAccessFile对象
                
                FileChannel randomChannel = new RandomAccessFile(f, "rw").getChannel();
                )
            {
                // 将Channel中所有数据映射成ByteBuffer
                randomChannel.position(1000);
                System.out.println(f.length());
                System.out.println(randomChannel.size());
                ByteBuffer buffer = randomChannel.map(FileChannel.MapMode.READ_ONLY, 0 ,1);
                System.out.println(buffer.limit());
                System.out.println(buffer.capacity());
                // 使用GBK的字符集来创建解码器
                Charset charset = Charset.forName("UTF-8");
                // 创建解码器(CharsetDecoder)对象
                CharsetDecoder decoder = charset.newDecoder();
                // 使用解码器将ByteBuffer转换成CharBuffer
                CharBuffer charBuffer = decoder.decode(buffer);
                // CharBuffer的toString方法可以获取对应的字符串
                System.out.println(charBuffer);
                // 把Channel的记录指针移动到最后
                randomChannel.position(10);
                System.out.println("randomChannel.position():"+randomChannel.position());
                // 将buffer中所有数据输出
                buffer.flip();
                randomChannel.write(buffer);
                buffer.clear();
                randomChannel.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }

    编码解码(字符集和Charset),(在String类里也提供了一个getBytes(String charset)方法,该方法返回byte[],也是使用指定的字符集将字符串转换成字节序列,跟这个Charset的功能类似).

    public class CharsetTransform
    {
        public static void main(String[] args)
            throws Exception
        {
            // 创建简体中文对应的Charset
            Charset cn = Charset.forName("GBK");
            // 获取cn对象对应的编码器和解码器
            CharsetEncoder cnEncoder = cn.newEncoder();
            CharsetDecoder cnDecoder = cn.newDecoder();
            // 创建一个CharBuffer对象
            CharBuffer cbuff = CharBuffer.allocate(8);
            cbuff.put('');
            cbuff.put('');
            cbuff.put('');
            cbuff.flip();
            // 将CharBuffer中的字符序列转换成字节序列
            ByteBuffer bbuff = cnEncoder.encode(cbuff);
            // 循环访问ByteBuffer中的每个字节
            for (int i = 0; i < bbuff.capacity() ; i++)
            {
                System.out.print(bbuff.get(i) + " ");
            }
            // 将ByteBuffer的数据解码成字符序列
            System.out.println("
    " + cnDecoder.decode(bbuff));
        }
    }

    15.9.5 文件锁

  • 相关阅读:
    anaconda在公司内网如何避免安装过程中HTTP0的错误?(windows)
    tensorflow视频学习笔记
    RNN,LSTM,SRNN,Long Short-Term Memory as a Dynamically Computed Element-wise Weighted Sum
    全国大学生数学建模竞赛广东省分赛 A题 CT系统参数标定及成像 方法总结
    elasticsearch
    crawler
    【英语学习】 第39天翻译练习之办公室环境
    【英语学习】第三周翻译练习之出国留学
    【英语学习】第二周翻译练习之网上购物
    【英语学习】第一周翻译练习之博物馆
  • 原文地址:https://www.cnblogs.com/lakeslove/p/6278722.html
Copyright © 2011-2022 走看看