1.什么是序列化
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。一般将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等。在网络传输过程中,可以是字节或是XML等格式。而字节的或XML编码格式可以还原完全相等的对象。这个相反的过程又称为反序列化。
2.如何使用
实现Serializable接口
写一个user实体类
public class User implements Serializable{
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
然后我们可以使用ObjectOutputStream 先把对象序列化到本地存储,然后使用ObjectInputStream读取出来,结果可以发现两者是一样的
public class Test {
public static void main(String[] args) {
User user = new User();
user.setUserName("yangzheng");
System.out.println("序列化前--->userName:"+user.getUserName());
//序列化
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("D:/user.txt"));
oos.writeObject(user);
oos.flush();
} catch (FileNotFoundException e) {
// TODO: handle exception
e.printStackTrace();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
} finally {
if(oos != null) {
try {
oos.close();
} catch (Exception e2) {
// TODO: handle exception
}
}
}
//反序列化
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/user.txt"));
user = (User) ois.readObject();
ois.close();
System.out.println("反序列化后获取出的数据--->userName:"+user.getUserName());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果
3.transient关键字
当用transient关键字修饰一个变量时,这个变量将不会参与序列化过程。也就是说它不会在网络操作时被传输,也不会再本地被存储下来,这对于保护一些敏感字段(如密码等...)非常有帮助。
我们给User实体类在加一个password属性,设为translent
public class User implements Serializable{
private String userName;
private transient String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
测试类里面设置属性值
public class Test {
public static void main(String[] args) {
User user = new User();
user.setUserName("yangzheng");
user.setPassword("1234565");
System.out.println("序列化前--->userName:"+user.getUserName()+",password:"+user.getPassword());
//序列化
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("D:/user.txt"));
oos.writeObject(user);
oos.flush();
} catch (FileNotFoundException e) {
// TODO: handle exception
e.printStackTrace();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
} finally {
if(oos != null) {
try {
oos.close();
} catch (Exception e2) {
// TODO: handle exception
}
}
}
//反序列化
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/user.txt"));
user = (User) ois.readObject();
ois.close();
System.out.println("反序列化后获取出的数据--->userName:"+user.getUserName()+",password:"+user.getPassword());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行后可以发现password 属性是null,
3.设置序列化版本
private static final long serialVersionUID = 856780694939330811L;
设置序列化版本可以让老版本对象的序列化的数据和反序列化到新版本的对象中,
如果不设置序列化版本,java会自动帮我们设置一个版本,如果我们修改了该实体类,java又会帮我们设置一个新的版本,以前老版本的序列化的数据就无法反序列化到新版本的对象中了.
我们先试试不设置版本的.
public class User implements Serializable{
private String userName;
private transient String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
public class Test {
public static void main(String[] args) {
User user = new User();
user.setUserName("yangzheng");
user.setPassword("1234565");
System.out.println("序列化前--->userName:"+user.getUserName()+",password:"+user.getPassword());
//序列化
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("D:/user_no_version.txt"));
oos.writeObject(user);
oos.flush();
} catch (FileNotFoundException e) {
// TODO: handle exception
e.printStackTrace();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
} finally {
if(oos != null) {
try {
oos.close();
} catch (Exception e2) {
// TODO: handle exception
}
}
}
//反序列化
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/user_no_version.txt"));
user = (User) ois.readObject();
ois.close();
System.out.println("反序列化后获取出的数据--->userName:"+user.getUserName()+",password:"+user.getPassword());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
同版本运行起来是正常的
然后我们再给User加一个email属性
public class User implements Serializable{
private String userName;
private transient String password;
private String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
再尝试把之前序列化的user_no_version.txt反序列化修改后的User对象
public class Test {
public static void main(String[] args) {
User user = new User();
user.setUserName("yangzheng");
user.setPassword("1234565");
System.out.println("序列化前--->userName:"+user.getUserName()+",password:"+user.getPassword());
//反序列化
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/user_no_version.txt"));
user = (User) ois.readObject();
ois.close();
System.out.println("反序列化后获取出的数据--->userName:"+user.getUserName()+",password:"+user.getPassword());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行起来发现报异常了,很明显不能在不同版本序列化反序列化
序列化前--->userName:yangzheng,password:1234565
java.io.InvalidClassException: com.yangzheng.bean.User; local class incompatible: stream classdesc serialVersionUID = -5247470809004322686, local class serialVersionUID = 1375299528455841668
at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:689)
at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1903)
at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1772)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2060)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1594)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:430)
at com.yangzheng.bean.Test.main(Test.java:44)
但是我们可以给User设置序列化版本
public class User implements Serializable{
private static final long serialVersionUID = 856780694939330811L;
private String userName;
private transient String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
public class Test {
public static void main(String[] args) {
User user = new User();
user.setUserName("yangzheng");
user.setPassword("1234565");
System.out.println("序列化前--->userName:"+user.getUserName()+",password:"+user.getPassword());
//序列化
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("D:/user_version.txt"));
oos.writeObject(user);
oos.flush();
} catch (FileNotFoundException e) {
// TODO: handle exception
e.printStackTrace();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
} finally {
if(oos != null) {
try {
oos.close();
} catch (Exception e2) {
// TODO: handle exception
}
}
}
//反序列化
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/user_version.txt"));
user = (User) ois.readObject();
ois.close();
System.out.println("反序列化后获取出的数据--->userName:"+user.getUserName()+",password:"+user.getPassword());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
同版本转换正常
再加上email属性
public class User implements Serializable{
private static final long serialVersionUID = 856780694939330811L;
private String userName;
private transient String password;
private String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
public class Test {
public static void main(String[] args) {
User user = new User();
user.setUserName("yangzheng");
user.setPassword("1234565");
System.out.println("序列化前--->userName:"+user.getUserName()+",password:"+user.getPassword());
//反序列化
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/user_version.txt"));
user = (User) ois.readObject();
ois.close();
System.out.println("反序列化后获取出的数据--->userName:"+user.getUserName()+",password:"+user.getPassword());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
修改后的也可以反序列化,因为我们已经给他设置了一个固定的版本.所有我们为了让我们的系统兼容性更好,方便后期维护,还是设置成固定的版本好一些.