缓存

缓存的意思是中间存储,相当于中转站,积累一定的货物,再往目的地运送。如果没有中转站,就会出现一件一件的运送,耗费大量的人力物力。

缓存的基类是:Buffer

缓存的基本子类有:Char/Byte/Short/Int/Long/Float/Double + Buffer

 

缓存一般用一个数组做存储,array()方法可以获取这个数组,但并不是所有Buffer都能够获取这个数组,使用hasArray()方法的返回值,查看Buffer是否能获取数组。有两种情况,Buffer是不能获取数组。

第一种情况,直接存储的Buffer:使用new创建一个数组,这个数组使用的存储空间是堆空间,如果使用JIN,然后直接从内存中开辟空间,这样就可以节省堆空间,解决一些堆空间不足的情况。因此,Buffer分堆存储,和直接存储。堆存储的Buffer使用allocate(int)静态方法创建,直接存储的Buffer使用allocateDirect(int)静态方法创建,wrap(...)静态方法是自己提供一个数组来创建Buffer,自己提供的数组肯定是属于堆空间上的。使用isDirect()方法,可以查看Buffer是否为直接储存,否则为堆存储。

第二种情况,只读模式的Buffer:缓存的基本操作就是读和写,使用get()方法进行读取操作,使用put()方法进行写入操作。Buffer创建的时候,既可以读,也可以写。有一种需求,Buffer需要提供给其它操作者,但只能读,不能写。使用asReadOnlyBuffer()方法就能创建出一个只读Buffer,这个只读Buffer与原Buffer是共享同一个数组,共享数组可以达到节省空间的目的。因此,为了不让操作者进行写操作,只读模式的Buffer就不允许操作者获得里面的数组。使用isReadOnly()方法,查看Buffer是否只读。

 

缓存在读取和写入数组单元时,其操作位与实际操作位会有偏差,使用arrayOffset()方法,可以获得这个偏差量。以下方法的参数和返回值,都是相对于偏差量的。

  • 当前操作位:position()/position(int)
  • 操作结束位:limit()/limit(int)
  • 操作结束位的最大值:capacity()

每次操作完后,当前操作位就会指向下一个数组单元。一直到操作结束位时,缓存就不允许再操作。通过limit()-position(),也可以使用remaining()方法,查看剩余操作量。使用hasRemaining()方法,查看是否还能操作。

当前操作位与操作结束位可以通过position(int)和limit(int)方法单独调整,也可以通过其它方法进行整体调整。

  • clear()方法,调整position为0,limit为capacity,表示清空数据,等待新数据写入。
  • flip()方法,调整limi为position,position为0,表示从写模式切换为读模式。
  • rewind()方法,调整position为0,表示重头操作(读/写)。
  • reset()方法,调整position为回档位,表示操作从回档位开始。回档位是需要事先使用mark()方法标记当前操作位为回档位,回档位不能大于当接操作位,否则在调整过程中,回档位被消除。
  • compact()方法,把未读数据复制至开始位置,调整position为剩余量,调整limit为capacity,表示清除已读数据,保留未读数据,并等待写入数据。

(注:图源于http://www.cnblogs.com/hvicen/p/6138690.html)

该图表示出Buffer在写与读的时候,limit所处的位置,左边是写模式,右边是读模式。使用flip()方法,使写模式转变为读模式。使用clear()和compact()方法,使读模式转变为写模式,clear()是清空所有数据,compact()只清除已读数据,保留未读数据。

 

使用duplicate()方法,拷贝出一个属性和状态一致的Buffer,两个Buffer是共享数组。

使用slice()方法,构建出一个Buffer,这个Buffer以当前操作位的实际操作位为偏差量,以剩余操作量为操作结束位的最大值。换句话说,slice的意思是切片,切出一个从当前操作位到操作结束位的Buffer,这个Buffer的读与写只能在这一段上面进行,同样切出来的Buffer与原Buffer是共享数组。

一个数据单元可以切分成多个字节,例如一个整型数据可以切分成四个字节,存储在数组时,可以从高位到低位存储,也可以从低位到高位存储。使用order()/order(ByteOrder)方法,可以查看和设置一个数据单元的排列方式,低位到高位排列为ByteOrder.BIG_ENDIAN,高位到低位排列为ByteOrder.LITTLE_ENDIAN。使用ByteOrder.nativeOrder()方法,查看本地的数据排列方式。

 

关于CharBuffer与ByteBuffer之间的编码与解码的转换,如图所示:

示例代码:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;

public class Test {
    public static void main(String[] args) throws IOException, InterruptedException {
        String fileName = "f:/test.txt";
        writeFile(fileName);
        readFile(fileName);
    }
    
    public static void writeFile(String fileName) throws IOException{
        CharBuffer cb = CharBuffer.allocate(100);
        cb.put("中文abcdefg");
        cb.flip();
        
        // FileOutputStream file = new FileOutputStream(fileName);
        RandomAccessFile file = new RandomAccessFile(fileName, "rw");
        int length = 0;
        FileChannel channel = file.getChannel();

        // 创建字节缓存,为测试效果,设置空间大小为5
        ByteBuffer bb = ByteBuffer.allocate(5);
        // 创建编码器
        CharsetEncoder encoder = Charset.defaultCharset().newEncoder();
        // 设置字符编码不完整时的处理方式
        // encoder.onMalformedInput(CodingErrorAction.REPLACE);
        // 设置字符编码不能映射时的处理方式
        // encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);

        CoderResult result = null;
        boolean end = false;
        while(true){
            if(!end) {
                // 编码,第3个参数表示告诉编码器,输入空间的数据是否已经是最后一批数据
                result = encoder.encode(cb, bb, true);
            } else {
                // 添加尾部:有些字符集需要添加尾部
                result = encoder.flush(bb);
            }
            // 下溢,表示输入数据不足,即数据转换完毕;
            if(result.isUnderflow()){
                if(!end)
                    end = true;
                else {
                    // 切换读模式
                    bb.flip();
                    // 写入文件
                    length += channel.write(bb);
                    // 测试是否还有数据未写入文件
                    if(bb.hasRemaining()){
                        throw new RuntimeException("还有剩余数据未写入");
                    }
                    break;
                }
            }
            // 上溢,表示输出空间不足;
            else if(!result.isOverflow())
                result.throwException();
            // 切换读模式
            bb.flip();
            // 写入文件
            length += channel.write(bb);
            // 切换写模式
            bb.compact();
        }
        
        // 设置文件大小
        file.setLength(length);
        // file.flush();
        // 关闭通道
        channel.close();
        // 关闭文件
        file.close();
    }
    
    public static void readFile(String fileName) throws IOException{
        CharBuffer cb = CharBuffer.allocate(100);
        
        FileInputStream file = new FileInputStream(fileName);
        // RandomAccessFile file = new RandomAccessFile(fileName, "rw");
        FileChannel channel = file.getChannel();
        
        // 创建字节缓存,为测试效果,设置空间大小为5
        ByteBuffer bb = ByteBuffer.allocate(5);
        bb.flip();
        
        // 创建解码器
        CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
        // 设置字符编码不完整时的处理方式
        // decoder.onMalformedInput(CodingErrorAction.REPLACE);
        // 设置字符编码不能映射时的处理方式
        // decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
        
        CoderResult result = null;
        boolean eof = false;
        do {
            // 切换写模式
            bb.compact();
            // 读取文件
            eof = (channel.read(bb) == -1);
            // 切换读模式
            bb.flip();
            // 解码,第3个参数表示告诉解码器,输入空间的数据是否已经是最后一批数据
            result = decoder.decode(bb, cb, eof);
            // 下溢,表示输入数据不足;上溢,表示输出空间不足;
            if(!result.isUnderflow())
                result.throwException();
        } while(!eof);
        // 添加尾部:有些字符集需要添加尾部
        result = decoder.flush(cb);
        // 下溢,表示输入数据不足;上溢,表示输出空间不足;
        if(!result.isUnderflow())
            result.throwException();
        
        // 关闭通道
        channel.close();
        // 关闭文件
        file.close();

        // 切换读模式
        cb.flip();
        // 打印读取的文件内容
        System.out.println(cb.toString());
    }
}

 (参考文献:《Java字符编码解码的实现详解》

 

 

本文原创,待续更新!

转载于:https://www.cnblogs.com/hvicen/p/6146154.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值