对象流
package demo10;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ObjectStream {
public static void main(String[] args) {
//序列化:将内存中的java对象保存到磁盘中或者通过网络传输出去
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
oos.writeObject(new String("I love java"));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
这个文件不是用来双击打开看的,就是把序列化的对象持久的保存在磁盘上。
package demo10;
import java.io.*;
public class ObjectStream {
public static void main(String[] args) {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("object.txt"));
Object o = ois.readObject();
String str = (String) o;
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
这个操作就反序列化了,将磁盘文件中的对象还原为内存中的java对象。
自定义类实现序列化和反序列化
标识接口 Serializable
package demo10;
import java.io.Serializable;
public class Person implements Serializable {
public static final long serialVersionUID = 475463534532L;
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package demo10;
import java.io.*;
public class ObjectStream {
public static void main(String[] args) {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
oos.writeObject(new String("I love java"));
oos.flush();
oos.writeObject(new Person("Hawkeye",20));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("object.txt"));
Object o = ois.readObject();
String str = (String) o;
Person p = (Person) ois.readObject();
System.out.println(str);
System.out.println(p);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
serialVersionUID的理解
如果我们对一个带UID的Person类序列化,那么进行一些操作比如添加一个属性id,重写toString,反序列化读回来的仍然是那个Person而且id为默认值0,如果我们对一个不带UID的Person类序列化,再执行上述操作,会报异常。
简而言之:一个对象,作为序列化的二进制流,如果没有UID,它的类改变了,就还原不回来了。
其他要求
所有属性都需要可序列化。
transient关键字
我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化 。然而在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。
总之,java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
static修饰的属性不能被序列化
随机存取文件流
package demo10;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomTest {
public static void main(String[] args) {
//
RandomAccessFile raf = null;
RandomAccessFile raf2 = null;
try {
raf = new RandomAccessFile(new File("1.png"), "r");
raf2 = new RandomAccessFile(new File("11.png"), "rw");
byte[] buffer = new byte[1024];
int len;
while ((len = raf.read(buffer)) != -1) {
raf2.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf != null && raf2 != null) {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
raf2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
发现可以完成文件的复制。
下面我们新建一个1.txt
package demo10;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomTest {
public static void main(String[] args) {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(new File("1.txt"), "rw");
raf.write("xyz".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
结果如果本文件存在,则不是对文件直接覆盖,而是从源文件开头一个个覆盖,这里表现为xyz覆盖abc,按内容长度覆盖。
RandomAccessFile实现对数据插入
seek方法
package demo10;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomTest {
public static void main(String[] args) {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(new File("1.txt"), "rw");
//将指针调到角标为3的位置
raf.seek(3);
raf.write("xyz".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
从3开始往后覆盖。
下面实现插入
更改1.txt如下
package demo10;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomTest {
public static void main(String[] args) {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(new File("1.txt"), "rw");
//将指针调到角标为3的位置
raf.seek(3);
//减少stringbuilder底层char数组扩容
StringBuilder sb = new StringBuilder((int) new File("1.txt").length());
//保存指针3后面的所有数据到sb中
byte[] buffer = new byte[20];
int len;
while ((len = raf.read(buffer))!=-1){
sb.append(new String(buffer,0,len));
}
//此时指针在最后,需要调回来
raf.seek(3);
raf.write("xyz".getBytes());
//写出去之后指针自动在z后面,再写sb中内容
raf.write(sb.toString().getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
实现了从第三个插入xyz