java IO篇-NIO

旧的IO系统一次只能处理一个字节,使得效率并不高。
输入,输出流都是阻塞式的输入,输出

新的IO采用内存映射文件的方式处理输入输出,将文件的一端区域映射到内存中。模拟了虚拟内存的概念,系统是一次处理一块数据,所以提高了效率。

核心对象:ChannelBuffer

Channel(通道)

Channel(通道)与传统的inputstream,outputstream的最大区别是提高了map()方法。将一个数据映射到内存中。(块处理)

  • 将文件部分或全部映射到Buffer
  • 不可直接操作Channel数据,需要跟Buffer进行交互。

最常用的方法:

  • map();
  • read();
  • write();

Buffer(容器)

Buffer容器,本质为一个数组。发送到channel中的所有对象必须先放到buffer中,相当于前面的竹筒,一次次向channel取出。3个重要的概念

  • 容量(capacity):最大的数据容量
  • 界限(limit):limit后的数据既不可读也不可以写。
  • 位置(position):指明下一个可以被读取或写入的缓存区位置索引。

其他方法:

  • mark(标记前一个数据的区域间):可通过reset()position直接定位到mark的位置上。
  • flip():将ilmit设置为position,并将position设为0。为输出数据做准备
  • clear()positino0,再讲limitcapacity.为装数据做好准备。
  • put()和get()来存数据和读取数据

byteBuffer
byteBuffer提供allocatDirect()直接创建Buffer,比allocate()效率高,但成本也高,适用于长期存在的Buffer的情况下,否则用allocate()

使用步骤

读取:

  1. 通过对应流的getChannel方法得到对应的Channel实例

    1
    FileChannel inChannel=new FileInputStream(f).getChannel();
  2. 使用channelmap方法映射到对应的MappedByteBuffer

    1
    MappedByteBuffer buffer=inChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length());

注意:需要对map方法设置对应的模式READ_ONLY

  • 此处的f.length()为获取文件的大小,而fFile
  • 如果此处为:FileInputStream,那么需要通过FileInputStream.getChannel获取FileChannel然后通过FileChannel的size方法去获取
    1. 创建解码器:
      1
      2
      3
      4
      //使用gbk编码创建解码器
      Charset charset=Charset.forName("GBK");
      //创建解码器
      CharsetDecoder decoder=charset.newDecoder();
  1. 进行解码,将byte转成char,通过这个方法才可以对应的输出工程

    1
    2
    3
    //使用解码器将byteBuffer转成charBuffer
    CharBuffer charBuffer=decoder.decode(byteBuffer);
    System.out.println(charBuffer);
  2. 使用outChannel进行输出到文件

    1
    outChannel.write(byteBuffer);

上面的步骤使用两个FileChannel,一个进行读取,一个负责写入。但使用RandomAccessFile即可使用一个FileChannel解决问题。如下代码(进行追加文字)

1
2
3
4
5
6
7
8
9
10
11
try(
RandomAccessFile raf=new RandomAccessFile(f, "rw");//这里设置rw,可读可写
FileChannel randomChannel=raf.getChannel();
){
ByteBuffer byteBuffer=randomChannel.map(MapMode.READ_ONLY, 0, f.length());
//订到最后并追加的末尾
randomChannel.position(f.length());
randomChannel.write(byteBuffer);
}catch (Exception e) {
e.printStackTrace();
}

进行重复读取(文件太大,直接进行映射将影响性能,需要进行竹筒重复取水)
关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//设置竹筒的长度
ByteBuffer byteBuffer=ByteBuffer.allocate(64);
while(fcin.read(byteBuffer)!=-1){
//锁定byteBuffer的空白区
byteBuffer.flip();
//创建charset对象
Charset charset=Charset.forName("GBK");
//常见解码器
CharsetDecoder charsetDecoder=charset.newDecoder();
//将内容转码
CharBuffer cBuffer=charsetDecoder.decode(byteBuffer);
System.out.println(cBuffer);
//重置byteBuffer操作,为下一次数据做准备
byteBuffer.clear();
}

ByteBuffer相互转换

  • String转换成ByteBuffer
    1
    2
    3
    public static ByteBuffer getByteBuffer(String str){
    return ByteBuffer.wrap(str.getBytes());
    }

当中使用str.getBytes()需要指定编码,否则将会产生乱码的问题。

1
2
str.getBytes("gb2312") ;
str.getBytes("utf-8") ;

指定编码转换成字符串:

1
2
3
String str = "张三" ;
byte[] jiema= str.getBytes("gb2312") ; //解码
String bianma = new String(jiema,"UTF-8");//编码 如果上面的解码不对 可能出现问题

  • ByteBuffer转成InputStream
    1
    2
    ByteBuffer fileBytes = (ByteBuffer) context.get("excel_file");
    InputStream inputStream = new ByteArrayInputStream(fileBytes.array());

参考:
String ByteBuffer转换
java对字符的编码处理

错误

  • open failed: EROFS (Read-only file system)AndroidManifest.xml没有生命读权限