您的位置:首页 > 移动开发

RandomAccessFile和MappedByteBuffer

2017-04-26 00:00 525 查看
摘要: java nio学习的一些总结

Java NIO学习随笔

Java基本类型

类型字节范围包装类
byte1-128~127Byte
short2-32768~32767Short
int4-2147483648~217483647Integer
long8-9223372036854775808 ~ 9223372036854775807Long
float4-3.4E38~3.4E38Float
double8-1.7E308~1.7E308Double
char2从字符型对应的整型数来划分,其表示范围是0~65535Charater
boolean1true或falseBoolean
注意 注意各个基本类型的长度下面会用到

RandomAccessFile 查看完整api



类描述

此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置。
通常,如果此类中的所有读取例程在读取所需数量的字节之前已到达文件末尾,则抛出 EOFException(是一种 IOException)。如果由于某些原因无法读取任何字节,而不是在读取所需数量的字节之前已到达文件末尾,则抛出 IOException,而不是 EOFException。需要特别指出的是,如果流已被关闭,则可能抛出 IOException。


构造器

创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。将创建一个新的 FileDescriptor 对象来表示到文件的连接。
mode 参数指定用以打开文件的访问模式。允许的值及其含意如 RandomAccessFile(File,String) 构造方法所指定的那样。
如果存在安全管理器,则使用 name 作为其参数调用其 checkRead 方法,以查看是否允许对该文件进行读取访问。如果该模式允许写入,那么还使用 name 作为安全管理器的参数来调用其 checkWrite 方法,以查看是否允许对该文件进行写入访问。
参数:
name - 取决于系统的文件名
mode - 此访问 mode
抛出:
IllegalArgumentException - 如果此模式参数与 "r"、"rw"、"rws" 或 "rwd" 的其中一个不相等
FileNotFoundException - 如果该模式为 "r",但给定的字符串表示一个现有的常规文件,或者该模式以 "rw" 开头,但给定的字符串不表示一个现有的可写常规文件,而且无法创建具有该名称的新常规文件,或者在打开或创建该文件时发生一些其他错误
SecurityException - 如果存在安全管理器,并且其 checkRead 方法拒绝对该文件的读取访问,或者该模式为 "rw",并且该安全管理器的 checkWrite 方法拒绝对该文件的写入访问
另请参见:
SecurityException, SecurityManager.checkRead(java.lang.String), SecurityManager.checkWrite(java.lang.String)


使用

a. 向文件中写入double类型的数据

@Test
public void test() throws IOException {
/**
* RandomAccessFile
* 1. 是用来访问那些保存数据记录的文件的,你就可以用seek( )方法来访问记录,并进行读写了。这些记录的大小不必相同;但是其大小和位置必须是可知的。但是该类仅限于操作文件。
* 2. 构造函数需要一个表示以只读方式("r")或者以读写方式("rw")打开文件的参数 (和C的fopen( )一模一样)。
*    注意:它不支持只写文件。
* 3. 只有RandomAccessFile才有seek搜寻方法,而这个方法也只适用于文件
* 4.
*/
String filePath = "/tmp/buffer.txt";
RandomAccessFile file = new RandomAccessFile(filePath, "rw");
for (int i = 0; i < 10; i++) {
// 写入基本类型为double的数据 @{link http://blog.linuxcrypt.cn/2017/03/21/java%e5%9f%ba%e6%9c%ac%e6%95%b0%e6%8d%ae%e7%b1%bb%e5%9e%8b%e5%92%8c%e9%95%bf%e5%ba%a6/} file.writeDouble(i * 3.1415926);
//            file.writeBytes("ABC");
}
file.close();

file = new RandomAccessFile(filePath, "rw");
// 将指针移动地5个double后面, 每个double占8个字节
file.seek(5 * 8); // doubule 长度为8个字节, 和long也是8个字节
// 将第6个double复写
file.writeDouble(47.00001);
file.close();
file = new RandomAccessFile(filePath, "r");
for (int i = 0; i < 10; i++) {
System.out.println("Value" + i + " : " + file.readDouble());
}
file.close();
}


b. 基本类型的使用

@Test
public void randomAccessFileLean() throws IOException {
RandomAccessFile file = new RandomAccessFile("/tmp/file", "rw");
file.write((byte) 'L');// 写入一个byte,占1个字节
file.writeInt(20);// 写入一个int, 占4个字节
file.writeUTF("This is utf");// 这个长度写在当前文件指针的前两个字节处, 可用readShort() 读取
file.writeLong(450L); // 写入一个long, 占8个字节
file.writeBoolean(true); // 写入一个boolean,占1个字节
file.writeShort(35); // 写入一个short,占2个字节
file.writeDouble(3.145926); // 写入一个double,占8个字节
file.writeFloat(3.14f);// 写入一个float, 占4个字节
file.writeChar('a'); // 写入一个chat, 占2字节
file.writeUTF("end write");

file.seek(0); // 把文件指针未知设置到文件起始处

System.out.println("--------------read data from file------------");
System.out.println((char)file.readByte());// 应该是L
System.out.println(file.readInt()); // 应该是20
System.out.println(file.readUTF());
file.skipBytes(9); // 跳过 long boolean
System.out.println(file.readShort()); // 35
file.skipBytes(8);
System.out.println(file.readFloat());
System.out.println(file.readChar());
// 跳过utf
file.skipBytes(file.readShort());

System.out.println("----------------文件复制(从file到fileCopy)---------------");

file.seek(0);
RandomAccessFile fileCopy = new RandomAccessFile("/tmp/file_copy", "rw");
int len = (int) file.length();// 获取file文件的长度(字节总数)
byte[] buf = new byte[len];
file.readFully(buf);
fileCopy.write(buf);
System.out.println("复制完成");
}


c. 多线程的使用

@Test
public void fileSplit() throws IOException, InterruptedException {
RandomAccessFile raf =  new RandomAccessFile(multipThreadWriteFileName, "rw");
raf.setLength(1024*1024);
raf.close();

// 设置需要写入的内容
String s1 = "我是一名程序猿";
String s2 = "正在研究Java NIO";
String s3 = "演示RandomAccessFile的使用";
String s4 = "如果是大文件可以使用MappedByteBuffer";
String s5 = "注意RandomAccessFile的构造参数,确定位置和读写权限";
CountDownLatch latch = new CountDownLatch(5);
new FileWriteThread(1024*1, s1.getBytes(), latch).start();
new FileWriteThread(1024*2, s2.getBytes(), latch).start();
new FileWriteThread(1024*3, s3.getBytes(), latch).start();
new FileWriteThread(1024*4, s4.getBytes(), latch).start();
new FileWriteThread(1024*5, s5.getBytes(), latch).start();
// 等待所有线程全部执行完进行文件获取
latch.await();
raf = new RandomAccessFile(multipThreadWriteFileName, "r");
long length = raf.length();
byte[] buf = new byte[(int) length];
for(int i=0; i<raf.length(); i++){
raf.seek(i);
buf[i] = raf.readByte();
}

System.out.println(new String(buf, "utf-8"));
raf.close();
}
static final String multipThreadWriteFileName = "/tmp/multip_file";
static class FileWriteThread extends Thread{
private int skip;
private byte[] content;
private CountDownLatch latch;

public FileWriteThread(int skip, byte[] content, CountDownLatch latch){
this.skip = skip;
this.content = content;
this.latch = latch;
}

@Override
public void run() {
RandomAccessFile raf  = null;
try {
raf = new RandomAccessFile(multipThreadWriteFileName, "rw");
raf.seek(skip);
raf.write(content);
}catch (Exception e){

}finally {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
latch.countDown();
}
}
}

当读取大文件RandomAccessFile不太合适使用,应该使用内存映射文件

/**
* 内存映射文件
* 内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件。
* 有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问。
* 这种解决办法能大大简化修改文件的代码。
* fileChannel.map(FileChannel.MapMode mode, long position, long size)将此通道的文件区域直接映射到内存中。
* 注意,你必须指明,它是从文件的哪个位置开始映射的,映射的范围又有多大;也就是说,它还可以映射一个大文件的某个小片断。
* MappedByteBuffer是ByteBuffer的子类,因此它具备了ByteBuffer的所有方法,但新添了force()将缓冲区的内容强制刷新到存储设备中去、load()将存储设备中的数据加载到内存中、isLoaded()位置内存中的数据是否与存储设置上同步。
* 这里只简单地演示了一下put()和get()方法,除此之外,你还可以使用asCharBuffer( )之类的方法得到相应基本类型数据的缓冲视图后,可以方便的读写基本类型数据。
* @throws IOException
*/
@Test
public void bigFile() throws IOException {
// 为了可读可写方法打开文件,当文件不存在时进行创建,需要w权限,如果存在则使用存在的文件,使用RandomAccessFile创建文件
FileChannel fc = new RandomAccessFile("/tmp/test_big.dat", "rw").getChannel();

// <b>注意:</b> 文件通道的可读可写要建立在文件流本身可读写的基础之上
MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, 0, length);

for (int i = 0; i < length; i++) {
out.put((byte) 'm');
}
System.out.println(Integer.MAX_VALUE);
System.out.println("Finished writing, length: " + length);

// 读取文件中间6个字符

for (int i = length / 2; i < length / 2 + 6; i++) {
System.out.println((char) out.get(i));
}
fc.close();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java io nio