(92)操作对象流ObjectStream、管道流PipedStream、任意流RandomStream
一、操作对象:ObjectInputStream与ObjectOutputStream
被操作的对象需要实现Serializable(标记接口)
①构造函数: ObjectOutputStream(OutputStream out) :创建写入指定OutputStream的ObjectOutputStream
②public final void writeObject(Object obj) throws IOException:写入对象
在被序列化的类要实现Serializable接口(否则会抛NotSerializableException 异常- 某个要序列化的对象不能实现 java.io.Serializable 接口。 )
但是Serializable中没有方法,简单理解就是要想序列化,必须实现Serializable,否则无法序列化。
public static void write()throws IOException {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("E:\\a.txt"));
oos.writeObject(new Person("zhangsan",10,"kr"));
oos.close();
}
public final Object readObject() throws IOException, ClassNotFoundException
public static void read()throws IOException{
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("e:\\a.txt"));
try {
Object obj=ois.readObject();
Person p=(Person)obj;
System.out.println(p.toString());
} catch (ClassNotFoundException e) {
throw new RuntimeException("序列类没有找到!");
}
ois.close();
}
注意①若Person中的int age;改为private int age; 对象文件将无法读取,可在类中自定义标记ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
②在Person中定义 static String country=”cn”;构造方法Person(String name,int age,String country)在创建对象时,改变country,序列化也不会输出该修改值,因为静态在方法区中,而对象在堆中。
③若是非静态,但是该字段也不想被序列化,可以transient int age;这样创建对象修改age,也不会改变序列化的值0。
二、PipedInputStream和PipedOutputStream
public class Read implements Runnable {
private PipedInputStream in;
Read( PipedInputStream in){
this.in=in;
}
public void run() {
try {
byte[] buf=new byte[1024];
int len=0;
while((len=in.read(buf))!=-1) {
System.out.print(new String(buf,0,len));
}
in.close();
}catch(IOException e) {
throw new RuntimeException("读取失败");
}
}
}
public class Write implements Runnable {
private PipedOutputStream out;
Write(PipedOutputStream out){
this.out=out;
}
public void run() {
try {
out.write("piped come hahah".getBytes());
out.close();
}catch(IOException e) {
throw new RuntimeException("读取失败");
}
}
}
public static void main(String[] args)throws IOException {
PipedInputStream in=new PipedInputStream();
PipedOutputStream out =new PipedOutputStream();
in.connect(out);
Read r=new Read(in);
Write w=new Write(out);
new Thread(r).start();
new Thread(w).start();
}
三、RandomAccessFile
该类不是IO体系中的子类。而是直接继承自Object。但是他是IO包中的成员,因为它本身具备读和写的功能。内部封装了一个数组,而且通过指针对数组的元素进行操作,可以通过getFilePointer获取指针位置,通过seek改变指针的位置。
完成读写的原理:内部封装了字节输入流、字节输出流 。
通过构造函数可以看出,该类只能操作文件 ,而且操作文件还得有个模式:只读r,读写rw等
* ①写文件*
write方法当数据大于8位时,只取最低8位写入文件,所以一般用writeInt
RandomAccessFile raf=new RandomAccessFile("E:\\ran.txt","rw");
raf.write("李四".getBytes());
raf.writeInt(8);//
raf.write("王五".getBytes());
raf.writeInt(99);
文件内容如↓所示:一个空格是一个字节,空了三个字节
②读文件
一个汉字是两个字节,一个int类型的数据是4个字节,因为内部封装了数组,所以可以通过指针来确定取哪个值
public void seek(long pos)throws IOException:确定数组指针
public int skipBytes(int n) throws IOException :跳过n个字节,它只能向后跳,不能随意跳
RandomAccessFile raf=new RandomAccessFile("E:\\ran.txt","r");
int count=0;//确定输出第几个人的信息,第一个人:count=0;第二个人:count=1....
raf.seek(8*count);//通过指针确定取哪个数组元素,调整对象中指针,但是要保证数据有规律
//raf.skipBytes(8);取第二个姓名和年龄
//取姓名:
byte[] buf=new byte[4];
raf.read(buf);
String name=new String(buf);
//取年龄
int age=raf.readInt();//数字是int类型输入的
System.out.println("name="+name);
System.out.println("age="+age);
③随意位置写,随意位置改
//随意位置写
RandomAccessFile raf=new RandomAccessFile("E:\\ran.txt","rw");
raf.seek(8*3);//在第三个位置开始写入数据(可以在随意位置写)
raf.write("周七".getBytes());
raf.writeInt(109);
raf.close();
//随意位置改
RandomAccessFile raf=new RandomAccessFile("E:\\ran.txt","rw");
raf.seek(8*0);//在第三个位置开始写入数据(可以在随意位置写)
raf.write("丽丽".getBytes());
raf.writeInt(200);
raf.close();
上面两个操作完成后,文件如↓↓
从③可以看出,若反对向的构造函数要操作的文件不存在,则会自动创建,如果存在,原文件的数据不会覆盖。
如果模式是只读r,不会创建文件,会去读取一个已经存在的文件,若文件不存在,则会报异常
如果模式rw,操作的文件不存在,会自动创建;如果存在,则不会覆盖
应用,比如在多线程中,每个线程写不同的段,即下载资源时多线程下载