接下里我们将学习,IO包中,用于操作对象的类——ObjectInputStream 和 ObjectOutputStream。
ObjectOutputStream类,可以将Java对象的基本数据类型和图形写入 OutputStream ,在OutputStream中关联文件后,这些对象将会实现,持久化的存储。
ObjectInputStream类,可以读取 本地文件中,存储的对象。
但是,ObjectOutputStream只支持,实现了java.io.Serializable 接口的对象写入到流中。
因此,对于将要被持久化存储的对象,必须实现上述接口。
对象中的默认序列化机制写入内容包括:对象的类,类签名(修饰符),以及非瞬态和 非静态字段的值。所谓的瞬态,就是被关键字——transient 修饰的字段,因为序列化机制是为了达到持久化的目的,但是有些东西不想持久化,就可以将它标记为 transient,这样就可以 确保不会持久化。
下面详细说明一下,Serializable接口。
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。可序列化类可以通过声明名为 "serialVersionUID" 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的serialVersionUID:
private static final long serialVersionUID = ****L;
上述内容可以这么理解:
假如对于已经持久化到硬盘上的对象,他们在被持久化时,用的是Person.class,这些类的序列号为 A;但是,当想要对这些持久化的对象,反序列化是,用的Person.class 被修改了,这个时候,序列号就会发生改变,加入是B。但是,我们根本看不出来,继续进行操作的话,就会因为:序列号不一样而发生 异常—— 即 InvalidClassException 。
具体示例:
public class Person implements Serializable{
private static final long serialVersionUID = 123456L;
private static String name;
private transient int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import cn.itast.bean.Person;
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// writeObj();
readObj();
}
//反序列化。
public static void readObj() throws IOException, IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("tempfile\\obj.txt"));
Person p = (Person)ois.readObject();
System.out.println(p.getName()+":"+p.getAge());
ois.close();
}
//使用ObjectOutputStream写一个对象到文件中。 序列化。如果目的是硬盘等设备,也就对象的持久化。
public static void writeObj() throws IOException, IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempfile\\obj.txt"));
oos.writeObject(new Person("xiaoming",20));
oos.close();
}
}
其他对象:
RandomAccessFile
1. 不是字节流或者字符流体系中的成员。
2. 该类是用于操作File的类。
3. 该对象既可以读取也可以写入。
4. 该对象中封装了一个byte类型的数组
5. 其实它的内部就是封装了字节输入流和字节输出流。
6. 通过seek方法设置数组的指针就可以实现对文件数据的随机读写。
7. 它可以通过seek指定任意位置,来修改文件内容。
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileDemo {
public static void main(String[] args) throws IOException {
writeFile();
// readFile();
}
//通过RandomAccessFile读取姓名和年龄信息。
public static void readFile() throws IOException{
RandomAccessFile raf = new RandomAccessFile("tempfile\\random.txt","r");
raf.seek(8);
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
System.out.println(raf.getFilePointer());
int age = raf.readInt();
System.out.println(name+":"+age);
System.out.println(raf.getFilePointer());
raf.close();
}
//通过RandomAccessFile类写入一些姓名年龄信息到文件中。
public static void writeFile() throws IOException{
/*
* 文件不存在会创建,文件存在,不创建。
*/
RandomAccessFile raf = new RandomAccessFile("tempfile\\random.txt","rw");
//指定一个指针的位置。
raf.seek(8*3);
//写数据。
raf.write("旺财".getBytes());
raf.writeInt(104);
// raf.write("李四".getBytes());
// raf.writeInt(99);
raf.close();
}
}
具体应用:
多线程断点下载。
管道流:
PipedInputStreamPipedOutputStream
在使用管道流时,必须得使用多线程。
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class PipedStreamDemo {
public static void main(String[] args) throws IOException {
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream();
//想管道连接。
pis.connect(pos);
new Thread(new Input(pis)).start();
new Thread(new Output(pos)).start();
}
}
//定义不同的线程的任务,一个负责输入,一个负责输出。;
class Input implements Runnable{
private PipedInputStream in;
public Input(PipedInputStream in) {
super();
this.in = in;
}
@Override
public void run() {
byte[] buf = new byte[1024];
try {
int len = in.read(buf);
String str = new String(buf, 0, len);
System.out.println("read:" + str);
in.close();
} catch (Exception e) {
}
}
}
class Output implements Runnable{
private PipedOutputStream out;
public Output(PipedOutputStream out) {
super();
this.out = out;
}
@Override
public void run() {
try {
out.write("hello piped!!".getBytes());
out.close();
} catch (Exception e) {
}
}
}
操作基本数据类型的对象:
DataInputStream DataOutputStream
操作字节数组:
ByteArrayInputStreamByteArrayOutputStream
1. 用于操作字节数组的对象,其实他们就是对应设备为内存的流对象。
2. 关闭ByteArrayOutputStream 无效,因为没有调用过系统资源。
3. 按照流的读写思想操作数组中元素。
在使用这些对象的时候,我们只要把握住一下几点就可以了
1. 查阅API,看他们的构造方法,那个是我们想要的;
至此,Java 的 I/O 体系总结完毕!