Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。

将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。

整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。

类 ObjectInputStream (readObject())和 ObjectOutputStream (writeObject(Object x))是高层次的数据流,它们包含反序列化和序列化对象的方法。

关于序列化,常又称为持久化,将其写入磁盘中。

进而对于编码规则来说:

任一一个实体类必须要去实现 Serializable 接口,方便以后将该类持久化,或者将其用于转为字节数组,用于网络传输。

对于一个实体类,不想将所有的属性都进行序列化,有专门的关键字 transient:

private transient String name;

当对该类序列化时,会自动忽略被 transient 修饰的属性。

public transient int SSN;

当对象被序列化时,假设属性 SSN 的值为 111222333,但是因为该属性是短暂的,该值没有被发送到输出流。所以反序列化后 Employee 对象的 SSN 属性为 0。

序列化

import java.io.*;
 
public class SerializeDemo
{
   public static void main(String [] args)
   {
      Employee e = new Employee();
      e.name = "Reyan Ali";
      e.address = "Phokka Kuan, Ambehta Peer";
      e.SSN = 11122333;
      e.number = 101;
      try
      {
         FileOutputStream fileOut =
         new FileOutputStream("/tmp/employee.ser");
         ObjectOutputStream out = new ObjectOutputStream(fileOut);
         out.writeObject(e);
         out.close();
         fileOut.close();
         System.out.printf("Serialized data is saved in /tmp/employee.ser");
      }catch(IOException i)
      {
          i.printStackTrace();
      }
   }
}

反序列化

import java.io.*;
 
public class DeserializeDemo
{
   public static void main(String [] args)
   {
      Employee e = null;
      try
      {
         FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
         ObjectInputStream in = new ObjectInputStream(fileIn);
         e = (Employee) in.readObject();
         in.close();
         fileIn.close();
      }catch(IOException i)
      {
         i.printStackTrace();
         return;
      }catch(ClassNotFoundException c)
      {
         System.out.println("Employee class not found");
         c.printStackTrace();
         return;
      }
      System.out.println("Deserialized Employee...");
      System.out.println("Name: " + e.name);
      System.out.println("Address: " + e.address);
      System.out.println("SSN: " + e.SSN);
      System.out.println("Number: " + e.number);
    }
}

序列化流与反序列化流

ObjectOutputStream(序列化流)

ObjectOutputStream是序列化流,可以将Java程序中的对象写到文件中。

ObjectOutputStream 构造方法:

ObjectOutputStream(OutputStream out):参数要传递字节输出流。

ObjectOutputStream写对象的方法(特有方法):

void writeObject(Object obj): 向文件中写对象。

ObjectOutputStream 的使用步骤:

  •  创建序列化流,用来写。
  •  调用 writeObject 方法,写对象。
  •  释放资源。

tips: 要使用序列化流向文件中写的对象,必须实现 Serializable 接口。

public class Demo01ObjectOutputStream {
    public static void main(String[] args) throws IOException {
        //1. 创建序列化流,用来写
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day12\\file03-obj.txt"));
        //2. 调用writeObject方法,写对象
        Person p = new Person("张三丰", 100);
        oos.writeObject(p);
        //3. 释放资源。
        oos.close();
    }
}

ObjectInputStream(反序列化流)

ObjectInputStream 是反序列化流, 可以将文件中的对象读取到 Java 程序中。

ObjectInputStream 的构造方法:

ObjectInputStream(InputStream in):参数要传递字节输入流。

ObjectInputStream 读取对象的方法(特有的方法):

Object readObject(): 从文件中读取对象,并将该对象返回。

反序列化流的使用步骤:

  •  创建 ObjectInputStream 反序列化流。
  •  调用 readObject 方法,读取对象。
  •  释放资源。

tips:调用 readObject 方法读取对象时,对象所对应的类不存在,那么会报错(ClassNotFoundException)

特殊情况:

被 static 修饰的成员变量无法序列化,无法写到文件。

如果不希望某个成员变量写到文件,同时又不希望使用 static 关键字, 那么可以使用 transient。transient 关键字表示瞬态,被 transient 修饰的成员变量无法被序列化。

//向文件中写Person对象
    public static void writePerson() throws IOException {
        //创建序列化流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day12\\file04-obj.txt"));
        //向文件中写Person对象
        oos.writeObject(new Person("张三丰", 100));
        //关流
        oos.close();
    }

 

public class Demo03StaticAndTransient {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        writePerson();
        readPerson();
    }

    //从文件中读取Person对象
    public static void readPerson() throws IOException, ClassNotFoundException {
        //创建反序列化流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day12\\file04-obj.txt"));
        //从文件中读取对象
        Object obj = ois.readObject();
        System.out.println(obj);
        //释放资源
        ois.close();
    }

    //向文件中写Person对象
    public static void writePerson() throws IOException {
        //创建序列化流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day12\\file04-obj.txt"));
        //向文件中写Person对象
        oos.writeObject(new Person("张三丰", 100));
        //关流
        oos.close();
    }
}

例子:


练习: 1. 将存有多个Student对象的集合序列化操作,保存到list.txt 文件中。 2. 反序列化list.txt ,并遍历集合,打印对象信息。 步骤: 1. 创建集合,用来保存Student 2. 向集合中添加Student对象。 3. 创建ObjectOutputStream序列化流,用来写。 4. 调用writeObject方法,向文件中写集合对象。 5. 释放资源。 6. 创建ObjectInputStream反序列化流对象,用来读取 7. 调用readObject方法,从文件中读取对象。 8. 将读取到的集合进行遍历,并输出结果。 注意:如果想要将多个对象保存在文件中,最好的一个方式可以将多个对象放入到一个集合中,然后直接将集合写到文件中。


public class Demo05Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1. 创建集合,用来保存Student
        List<Student> list = new ArrayList<>();
        //2. 向集合中添加Student对象。
        list.add(new Student("李云龙", 20));
        list.add(new Student("二营长", 22));
        list.add(new Student("秀琴", 25));
        //3. 创建ObjectOutputStream序列化流,用来写。
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day12\\list.txt"));
        //4. 调用writeObject方法,向文件中写集合对象。
        oos.writeObject(list);
        //5. 释放资源。
        oos.close();
        //6. 创建ObjectInputStream反序列化流对象,用来读取
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day12\\list.txt"));
        //7. 调用readObject方法,从文件中读取对象。
        List<Student> list2 = (List<Student>) ois.readObject();
        //8. 将读取到的集合进行遍历,并输出结果。
        for (Student stu : list2) {
            System.out.println(stu);
        }
    }
}