【Java学习笔记】27:文件的随机访问&对象流
文件的随机访问
文件的随机访问用到一个类RandomAccessFile类,它是一个直接继承Object类的独立的类,通过指针的转移来随机访问某一块的内容(像按下标访问数组那样)。
随机访问的意义:如果你需要在一个较大的文件尾部(或者中间某处)插入一些信息,如果把文件里所有的信息读到内存再在内存中操作,再放回文件,这样内存占用是很大的,但文件的随机访问可以通过移动指针来直接对文件进行操作,在这种情况下优势很大。
package day27;
import java.io.IOException;
import java.io.RandomAccessFile;
public class Test {
public static void main(String[] args) {
write();
read();
}
private static void read() {
RandomAccessFile raf=null;
try {
raf=new RandomAccessFile("demo.dat","rw");//读写模式
System.out.println(raf.readInt());
System.out.println(raf.readBoolean());
raf.skipBytes(2);
System.out.println(raf.readUTF());
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void write() {
RandomAccessFile raf=null;
try {
raf=new RandomAccessFile("demo.dat","rw");//读写模式
raf.writeInt(100);
raf.writeBoolean(true);
raf.writeChar('A');
raf.writeUTF("Hello");
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果:
100
true
Hello
除了可以用该类对象的skipBytes()方法跳过指定的字节数以外,还可以使用seek()方法直接移动文件指针到第几个字节后的位置,但seek方法内存开销比较大,尽量不用。另外,getFilePointer()方法可以返回文件记录指针的当前位置(我觉得在调试时会很好用)。
用它还可以实现文件的多线程下载,即从文件的多个位置(文件指针)处开始run()复制文件,在其它文章中有看到过(但似乎效果不太理想?)。
对象流
即是指对对象做I/O,要对对象的序列化再I/O,这里涉及到两个类ObjectOutputStream和ObjectInputStream,分别对对象做输出和读入。
Java的serialization(序列化)提供了一种持久化对象实例的机制。
要使对象能够序列化,必须使生成该对象的类实现一个Serializable(可序列化)接口,这个接口中没有任何方法,仅仅标识这个类生成的对象可以序列化(可以选中->右键->打开声明以查看)。
package day27;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Test {
public static void main(String[] args) {
write();
read();
}
//写
private static void read() {
ObjectInputStream ois=null;
try {
ois=new ObjectInputStream(
new FileInputStream("demo.dat"));
Customer c=(Customer)ois.readObject();//读出一个对象并强制转换成格式类型
System.out.println(c);//Customer类覆写了toString()方法可直接输出
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(ois!=null)
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//读
private static void write() {
ObjectOutputStream oos=null;
try {
oos=new ObjectOutputStream(
new FileOutputStream("demo.dat"));
oos.writeObject(new Customer(7,"刘知昊",20,300.7f));
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(oos!=null)
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class Customer implements Serializable{
private int id;
private String name;
private transient int age;//用transient关键字标记的成员变量不参与序列化过程
private double salary;
//覆写Object类的toString()用于输出
@Override
public String toString() {
return id+","+name+","+age+","+salary;
}
//生成的构造方法
public Customer(int id, String name, int age, double salary) {
super();
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
//生成的get和set方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
运行结果:
7,刘知昊,0,300.70001220703125
注意用transient关键字标记的成员变量不参与序列化过程,所以写进去的age字段实际上是0。利用这个方法可以方便地选择哪些成员是我们不想让它参与持久化的。
上一篇: 获取properties文件中的值