1、java.io.NotSerializableException:
Student对象不支持序列化!!!!
2、参与序列化和反序列化的对象,必须实现Serializable接口。
3、注意:通过源代码发现,Serializable接口只是一个标志接口:
public interface Serializable { }
这个接口当中什么代码都没有。
那么它起到一个什么作用呢?
起到标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。
Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类自动生成
一个序列化版本号。
4、序列化版本号有什么用呢?
java.io.InvalidClassException: com.bjpowernode.java.bean.Student; local class incompatible: stream classdesc serialVersionUID = -684255398724514298(十年后), local class serialVersionUID = -3463447116624555755(十年前)
java语言中是采用什么机制来区分类的?
第一:首先通过类名进行比对,如果类名不一样,肯定不是同一个类。
第二:如果类名一样,再怎么进行类的区别?靠序列化版本号进行区分。
张三编写了一个类:com.bjpowernode.java.bean.Student implements Serializable
李四编写了一个类:com.bjpowernode.java.bean.Student implements Serializable
不同的人编写了同一个类,但“这两个类确实不是同一个类”。这个时候序列化版本就起上作用了。
对于java虚拟机来说,java虚拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口,
都有默认的序列化版本号,他们的序列化版本号不一样。所以区分开了。(这是自动生成序列化版本号的好处)
思考:
这种自动生成序列化版本号有什么缺陷?
这种自动生成的序列化版本号缺点是:一旦代码确定之后,不能进行后续的修改,因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类。(这样就不好了!)
最终结论:
凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。
这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类。
图解(引自b站老杜Java零基础);
示例代码01:
public class Student implements Serializable { // IDEA工具自动生成序列化版本号。 //private static final long serialVersionUID = -7998917368642754840L; // Java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号。 // 这里没有手动写出来,java虚拟机会默认提供这个序列化版本号。 // 建议将序列化版本号手动的写出来。不建议自动生成 private static final long serialVersionUID = 2280880218502988510L;// java虚拟机识别一个类的时候先通过类名,如果类名一致,再通过序列化版本号。 //private String name; // 过了很久,Student这个类源代码改动了。 // 源代码改动之后,需要重新编译,编译之后生成了全新的字节码文件。 // 并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会发生相应的改变。 int no; String name; private int age; private String email; private String address; public Student(){} public Student(int no,String name){ this.no = no; this.name = name; } public int getNo() { return no; } public void setNo(int no) { this.no = no; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student{" + "no=" + no + ", name='" + name + '\'' + ", age=" + age + ", email='" + email + '\'' + ", address='" + address + '\'' + '}'; } }
示例代码02:
public class ObjectOutputStreamTest01 { public static void main(String[] args) { //创建Student对象 Student s = new Student(1111,"zahngsan"); //序列化 ObjectOutputStream out = null; try { out = new ObjectOutputStream(new FileOutputStream("students")); //序列化对象 out.writeObject(s); //刷新 out.flush(); } catch (IOException e) { e.printStackTrace(); }finally { if(out != null){ try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
运行结果:
示例代码03:
public class ObjectInputStreamTest01 { public static void main(String[] args) { //反序列化 ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream("students")); //反序列化 Object o = ois.readObject(); // 反序列化回来是一个学生对象,所以会调用学生对象的toString方法。 System.out.println(o); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally{ if(ois != null){ try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
运行结果:
一次序列化多个对象
可以,可以将对象放到集合当中,序列化集合。
提示:
参与序列化的ArrayList集合以及集合中的元素User都需要实现 java.io.Serializable接口。
transient关键字表示游离的,不参与序列化。
示例代码04:
public class User implements Serializable { private static final long serialVersionUID = -203337022093748667L; private int no; // transient关键字表示游离的,不参与序列化。 private transient String name;// name不参与序列化操作! public User() { } public User(int no, String name) { this.no = no; this.name = name; } public int getNo() { return no; } public void setNo(int no) { this.no = no; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "no=" + no + ", name='" + name + '\'' + '}'; } }
示例代码05:
public class ObjectOutputStreamTest02 { public static void main(String[] args) { //序列化多个对象,可以把对象放在集合中操作 List<User> list = new ArrayList<>(); list.add(new User(1111,"zhangsan")); list.add(new User(2222,"lisi")); list.add(new User(3333,"wangwu")); list.add(new User(4444,"zhaoliu")); //序列化集合 ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream("user")); oos.writeObject(list); //流刷新 oos.flush(); } catch (IOException e) { e.printStackTrace(); }finally{ if(oos != null){ try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
运行结果:
示例代码06:
/* 反序列化集合 */ public class ObjectInputStreamTest02 { public static void main(String[] args) { //反序列化 ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream("user")); //反序列化 List<User> userList = (List<User>) ois.readObject(); //Object obj = ois.readObject(); //System.out.println(obj instanceof List); //System.out.println(userList); for(User users : userList){ System.out.println(users); } } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if(ois != null){ try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
运行结果: