1.什么是Java序列化
Serialization(序列化)是一种将对象以一连串的字节描述的过程;反序列化deserialization是一种将这些字节重建成一个对象的过程。
它面向那些实现了Serializable 接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子。这一过程亦可通过网络进行。这意味着序列化机制能自动补偿操作系统间的差异。换句话说,可以先在 Windows 机器上创建一个对象,对其序列化,然后通过网络发给一台 Unix 机器,然后在那里准确无误地重新“装配”。不必关心数据在不同机器上如何表示,也不必关心字节的顺序或者其他任何细节。
2.为什么要进行序列化
- 当你想把的内存中的对象保存到一个文件中或者数据库中时候;
- 当你想用套接字在网络上传送对象的时候;
- 当你想通过RMI传输对象的时候;
3.如何实现序列化
1.最基本,实现Serializable接口即可
例子:
public class User implements Serializable {
private String username;
private String password;
public User(String username,String password) {
this.username = username;
this.password = password;
}
@Override
public String toString() {
return username+" "+password;
}
public static void main(String[] args) {
User user = new User("jason","123456");
System.out.println(user);
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.out"));
objectOutputStream.writeObject(user);
System.out.println("recover object");
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.out"));
User user1 = (User) objectInputStream.readObject();
System.out.println(user1);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出结果:
jason 123456
recover object
jason 123456
2.使用transient变量,保护一些变量不被序列化
password加入了transient变量,就不被序列化
private transient String password;
输出结果
jason 123456
recover object
jason null
password变量未被序列化
3.static变量不被序列化
public class User implements Serializable {
private String username;
private String password;
private static int a = 2;
public User(String username,String password) {
this.username = username;
this.password = password;
a++;
}
@Override
public String toString() {
return username+" "+password+" "+a;
}
public static void main(String[] args) {
User user = new User("jason","123456");
System.out.println(user);
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.out"));
objectOutputStream.writeObject(user);
System.out.println("recover object");
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.out"));
User user1 = (User) objectInputStream.readObject();
System.out.println(user1);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出结果:
jason 123456 3
recover object
jason 123456 3
不是说static变量不能被序列化么,怎么序列出来还是3
静态成员属于类级别的,所以不能序列化
static变量不能序列化指的是序列化信息中不包含这个静态成员域
上述代码测试成功是因为都在同一个机器(而且是同一个进程),因为当前jvm已经把count加载进来了,所以获取的是加载好的count,如果你是传到另一台机器或者你关掉程序重写写个程序读入user.out,或者将反序列化写在另外一个代码里,此时因为别的机器或新的进程是重新加载count的,所以count信息就是初始时的信息。
单独的测试代码
public class Client {
public static void main(String[] args) {
try {
System.out.println("recover object");
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.out"));
User user1 = (User) objectInputStream.readObject();
System.out.println(user1);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
测试结果:
recover object
jason 123456 2
还是2
4.如何序列化写入static变量
三种方法:
实现Externalilzable接口;
仍然实现Serializable接口,添加writeObject(), readObject()方法;
仍然实现Serializable,给ObjectOutputStream写入static变量,ObjectInputStream读入static变量
- 实现Externalilzable接口
public class User2 implements Externalizable {
private String username;
private String password;
private static int a = 2;
public User2() {
}
public User2(String username, String password) {
this.username = username;
this.password = password;
a++;
}
@Override
public String toString() {
return username+" "+password+" "+a;
}
public static void main(String[] args) {
User2 user = new User2("jason","123456");
System.out.println(user);
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.out"));
objectOutputStream.writeObject(user);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(username);
out.writeObject(password);
out.writeInt(a);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
username = (String) in.readObject();
password = (String) in.readObject();
a = in.readInt();
}
}
测试代码,另外一个文件
public class Client {
public static void main(String[] args) {
try {
System.out.println("recover object");
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.out"));
User2 user1 = (User2) objectInputStream.readObject();
System.out.println(user1);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出结果:
recover object
jason 123456 3
- 仍然实现Serializable接口,添加writeObject(), readObject()方法
注意writeObject()和readObject()两个方法的函数签名必须和下面一样
private void writeObject(ObjectOutputStream oos) throws IOException
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException
public class User3 implements Serializable {
private String username;
private String password;
private static int a = 2;
public User3(String username,String password) {
this.username = username;
this.password = password;
a = 3;
}
@Override
public String toString() {
return username+" "+password+" "+a;
}
public static void main(String[] args) {
User3 user = new User3("jason","123456");
System.out.println(user);
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.out"));
objectOutputStream.writeObject(user);
} catch (IOException e) {
e.printStackTrace();
}
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeInt(a);
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
a = ois.readInt();
}
}
输出结果:
recover object
jason 123456 3
- 仍然实现Serializable,给ObjectOutputStream写入static变量,ObjectInputStream读入static变量
public class User4 implements Serializable {
private String username;
private String password;
private static int a = 2;
public User4(String username, String password) {
this.username = username;
this.password = password;
a = 3;
}
@Override
public String toString() {
return username+" "+password+" "+a;
}
public static void main(String[] args) {
User4 user = new User4("jason","123456");
System.out.println(user);
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.out"));
User4.serializeStatic(objectOutputStream);
objectOutputStream.writeObject(user);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void serializeStatic(ObjectOutputStream oos) throws IOException {
oos.writeInt(a);
}
public static void deseralizeStatic(ObjectInputStream ois) throws IOException,ClassNotFoundException {
a = ois.readInt();
}
}
测试文件
public class Client {
public static void main(String[] args) {
try {
System.out.println("recover object");
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.out"));
User4.deseralizeStatic(objectInputStream);
User4 user4 = (User4) objectInputStream.readObject();
System.out.println(user4);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
测试结果
recover object
jason 123456 3
注意此时写入static变量在写入整体变量前面,否则报错
4.序列化总结
- transient变量不被序列化,这样就可包含一些变量,如密码之类的
- static变量默认不被序列化,为什么呢?
static是类共享的,当该类一个对象被序列化后,这个static变量可能会被另一个对象改变,所以这决定了静态变量是不能序列化的,但如果是final,就可以,因为这是一个常量 - 上述三种写入static的方法,其实也是序列化中提供给我们三种 自定义写序列化的方式,不仅仅是static变量,我们可以自己定义哪些变量需要写入,哪些变量不需要写入。
- 只要将所有东西都序列化到单独一个数据流里,就能恢复获得与以前写入时完全一样的对象网,不会不慎造成对象的重复。如果不在同一个序列化里面,我们就无法判断该对象是否和其他流里面的对象是否一样。
例子:
House.java
public class House implements Serializable {
}
Animal.java
public class Animal implements Serializable {
private String name;
private House preferredHouse;
public Animal(String name,House preferredHouse) {
this.name = name;
this.preferredHouse = preferredHouse;
}
@Override
public String toString() {
return name + "[" + super.toString() +
"], " + preferredHouse + "\n";
}
}
测试代码
public class Client {
public static void main(String[] args) {
House house = new House();
Vector<Animal> animals = new Vector<Animal>();
animals.addElement(new Animal("cat",house));
animals.addElement(new Animal("dog",house));
animals.addElement(new Animal("snake",house));
try {
ByteArrayOutputStream byteArrayOutputStream1 = new ByteArrayOutputStream();
ObjectOutputStream out1 = new ObjectOutputStream(byteArrayOutputStream1);
out1.writeObject(animals);
out1.writeObject(animals);
ObjectInputStream in1 = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream1.toByteArray()));
Vector<Animal> animals1 = (Vector<Animal>) in1.readObject();
Vector<Animal> animals2 = (Vector<Animal>) in1.readObject();
ByteArrayOutputStream byteArrayOutputStream2 = new ByteArrayOutputStream();
ObjectOutputStream out2 = new ObjectOutputStream(byteArrayOutputStream2);
out2.writeObject(animals);
ObjectInputStream in2 = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream2.toByteArray()));
Vector<Animal> animals3 = (Vector<Animal>) in2.readObject();
System.out.println(animals1);
System.out.println(animals2);
System.out.println(animals3);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出结果:
[cat[Serialization.Persistence.Animal@3764951d], Serialization.Persistence.House@4b1210ee
, dog[Serialization.Persistence.Animal@4d7e1886], Serialization.Persistence.House@4b1210ee
, snake[Serialization.Persistence.Animal@3cd1a2f1], Serialization.Persistence.House@4b1210ee
]
[cat[Serialization.Persistence.Animal@3764951d], Serialization.Persistence.House@4b1210ee
, dog[Serialization.Persistence.Animal@4d7e1886], Serialization.Persistence.House@4b1210ee
, snake[Serialization.Persistence.Animal@3cd1a2f1], Serialization.Persistence.House@4b1210ee
]
[cat[Serialization.Persistence.Animal@2f0e140b], Serialization.Persistence.House@7440e464
, dog[Serialization.Persistence.Animal@49476842], Serialization.Persistence.House@7440e464
, snake[Serialization.Persistence.Animal@78308db1], Serialization.Persistence.House@7440e464
]
我们发现,animal1,animal2是完全一样的,因为在一个流里面,不会造成对象重复,而恢复animal3没有办法知道另一个流内的对象是第一个流内对象的化身,所以会产生一个完全不同的对象