用非常硬核的JAVA序列化手段實現物件流的持久化儲存

智慧zhuhuix發表於2020-05-28

背景

在OOP(物件導向程式設計)中處處是物件,我們當然希望可以有一種資料格式來儲存這種物件的集合,以實現持久化。比如部門類所形成的部門物件集合,員工類所形成的員工物件集合,甚至是這樣一個類所形成的物件:公司中有多個部門,每個部門有多個員工,我們希望將這樣一個物件以檔案的方式實現持久化儲存。

物件流的概念

為實現物件的持久化儲存,我們需要引入Java語言的物件序列化(object serialization)機制,這種機制可以將任何物件輸出到流中:比如

/**
*流物件
*/
Object object = new Object();
//建立物件流並輸出到檔案object.dat
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("c:\\object.dat"));
//將object物件寫到檔案中
output.writeObject(object);

ObjectInputStream input = new ObjectInputStream(new FileInputStream("c:\\object.dat"));
object = input.readObject();

物件流例項

引入一張組織結構圖

在這裡插入圖片描述

定義組織架構圖的類
  • 公司:代表了組織架構的外在存在;公司是由部門和職員組成的一個完整實體。
  • 部門:代表了組織架構中的運作單位;部門按型別不同可以分為不同的業務部門。
  • 職員:代表了組織架構中的最小單位;職員按職位不同存在於不同的業務部門。
/**
 * 用物件流儲存資訊--公司類
 *
 * @author  zhuhuix
 * @date 2020-05-27
 */
 class Company implements Serializable {

    //公司id
    private int id;
    //公司名稱
    private String name;
    //公司部門列表
    private List<Department> departments;

    //預設建構函式
    Company() {
    }

    //初始化建構函式
    Company(int id, String name) {
        this.id = id;
        this.name = name;
        this.departments = new ArrayList<>();
    }

    //增加部門
    public void addDepartment(Department department) {
        this.departments.add(department);
    }

    //裁撤部門
    public void deleteDepartment(Department department) {
        this.departments.remove(department);
    }

    //定位部門
    Department findDepartmentByName(String departmentName) {
        Optional<Department> optional = departments.stream().filter(department ->
                department.getName().equals(departmentName)).findFirst();
        return optional.get();
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Department> getDepartments() {
        return departments;
    }

    public void setDepartments(List<Department> departments) {
        this.departments = departments;
    }
}

/**
 * 用物件流儲存資訊--部門類
 *
 * @author  zhuhuix
 * @date 2020-05-27
 */
 class Department  implements Serializable {

    //部門id
    private int id;
    //部門名稱
    private String name;
    //上級部門
    private Integer parentId;
    //部門職員列表
    private List<Employee> employees;
    //預設建構函式
    Department(){}
    //初始化建構函式
    Department(int id,String name,Integer parentId){
        this.id=id;
        this.name=name;
        this.parentId=parentId;
        this.employees = new ArrayList<>();
    }
    //增加職員
    public void addEmployee(Employee employee){
        this.employees.add(employee);
    }
    //裁撤職員
    public void deleteEmployee(Employee employee){
        this.employees.remove(employee);
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getParentId() {
        return parentId;
    }

    public void setParentId(Integer parentId) {
        this.parentId = parentId;
    }

    public List<Employee> getEmployees() {
        return employees;
    }

    public void setEmployees(List<Employee> employees) {
        this.employees = employees;
    }
}

/**
 * 用物件流儲存資訊--職員類
 *
 * @author  zhuhuix
 * @date 2020-05-27
 */
 class Employee implements Serializable {

    //職員id
    private int id;
    //職員姓名
    private String name;
    //職員性別
    private String sex;
    //職員年齡
    private int age;
    //職員職位
    private String position;
    //入職日期
    private Date hireDate;
    //當前薪水
    private BigDecimal salary;
    //預設建構函式
    Employee(){}
    //初始化建構函式
    public Employee(int id, String name, String sex, int age, String position, Date hireDate, BigDecimal salary) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.position = position;
        this.hireDate = hireDate;
        this.salary = salary;
    }

    //升職、調崗、調動
    public void setPosition(String position){
        this.position = position;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getPosition() {
        return position;
    }

    public Date getHireDate() {
        return hireDate;
    }

    public void setHireDate(Date hireDate) {
        this.hireDate = hireDate;
    }

    public BigDecimal getSalary() {
        return salary;
    }

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }
}

類的完整結構

在這裡插入圖片描述

用物件流儲存組織架構的物件資訊

有了類及建構函式完成物件的初始化過程,我們就具備了建立整個組織架構的能力,接下來我們完整地建立一個公司的組織架構:

/**
 1. 用物件流儲存組織架構資訊
 2.  * @author  zhuhuix
 3. @date 2020-05-27
 */
public class ObjectStreamSave {
    //定義一個全域性靜態變數,作為控制組織架構的id
    public static int id = 0;

    public static void main(String[] args) throws IOException {


        //設立公司
        Company company = new Company(id++, "網際網路股份有限公司");

        //公司設立總經理室
        Department manageDept = new Department(id++, "總經理室", null);
        company.addDepartment(manageDept);

        //在總經理室下設立產品部
        Department productDept = new Department(id++, "產品部", manageDept.getId());
        company.addDepartment(productDept);
        //在產品部下設立產品A、B組
        company.addDepartment(new Department(id++, "產品A組", productDept.getId()));
        company.addDepartment(new Department(id++, "產品B組", productDept.getId()));

        //在總經理室下設立研發部
        Department developmentDept = new Department(id++, "研發部", manageDept.getId());
        company.addDepartment(developmentDept);
        //在研發部下設立軟體組與硬體組
        company.addDepartment(new Department(id++, "軟體組", developmentDept.getId()));
        company.addDepartment(new Department(id++, "硬體組", developmentDept.getId()));

        //在總經理室下設立市場部
        Department marketDept = new Department(id++, "市場部", manageDept.getId());
        company.addDepartment(marketDept);
        //在市場部下設立創意組與渠道組
        company.addDepartment(new Department(id++, "創意組", marketDept.getId()));
        company.addDepartment(new Department(id++, "渠道組", marketDept.getId()));

        //總經理室人事任命
        manageDept.addEmployee(new Employee(id++, "Mike", "男", 35, "總經理",
                new Date(), BigDecimal.valueOf(100000)));
        manageDept.addEmployee(new Employee(id++, "Tom", "男", 34, "副總經理",
                new Date(), BigDecimal.valueOf(60000)));

        //研發部人事任命
        developmentDept.addEmployee(new Employee(id++, "Jack", "男", 30, "研發部主管",
                new Date(), BigDecimal.valueOf(40000)));
        company.findDepartmentByName("軟體組")
                .addEmployee(new Employee(id++, "Kate", "女", 26, "組員",
                        new Date(), BigDecimal.valueOf(20000)));
        company.findDepartmentByName("硬體組")
                .addEmployee(new Employee(id++, "Will", "男", 24, "組員",
                        new Date(), BigDecimal.valueOf(20000)));

        //產品部人事任命
        productDept.addEmployee(new Employee(id++, "Jerry", "男", 28, "產品部主管",
                new Date(), BigDecimal.valueOf(40000)));
        company.findDepartmentByName("產品A組")
                .addEmployee(new Employee(id++, "Merry", "女", 28, "組員",
                        new Date(), BigDecimal.valueOf(20000)));
        company.findDepartmentByName("產品B組")
                .addEmployee(new Employee(id++, "Leo", "男", 27, "組員",
                        new Date(), BigDecimal.valueOf(20000)));

        //市場部人事任命
        marketDept.addEmployee(new Employee(id++, "Rose", "女", 29, "市場部主管",
                new Date(), BigDecimal.valueOf(40000)));
        company.findDepartmentByName("創意組")
                .addEmployee(new Employee(id++, "Amy", "", 25, "組員",
                        new Date(), BigDecimal.valueOf(20000)));
        company.findDepartmentByName("渠道組")
                .addEmployee(new Employee(id++, "Tony", "男", 23, "組員",
                        new Date(), BigDecimal.valueOf(20000)));


        //遍歷公司組織結構
        int deptCount = 0;
        int empCount = 0;
        Iterator<Department> deptIterator = company.getDepartments().iterator();
        while (deptIterator.hasNext()) {
            deptCount++;
            Department department = deptIterator.next();
            System.out.println("部門:" + department.getName());
            if (department.getEmployees() != null) {
                Iterator<Employee> empIterator = department.getEmployees().iterator();
                while (empIterator.hasNext()) {
                    empCount++;
                    Employee employee = empIterator.next();
                    System.out.print(" 人員:" + employee.getName() + " 職位:" + employee.getPosition() + ",");
                }
                System.out.println();
            }
        }
        System.out.println("總共部門數:" + deptCount);
        System.out.println("總共職員數:" + empCount);

        //通過物件流將公司組織架構儲存到檔案中
        ObjectOutputStream companyStream = new ObjectOutputStream(new FileOutputStream("c:\\company.dat"));
        companyStream.writeObject(company);
        companyStream.writeObject(company.getDepartments());
        for (int i = 0; i < company.getDepartments().size(); i++) {
            List<Employee> employees = company.getDepartments().get(i).getEmployees();
            companyStream.writeObject(employees);
        }


    }
}
核心程式碼
  1. 通過物件流的方式建立一個company.dat檔案
  2. 將公司物件寫入檔案
  3. 將公司物件中的部門列表集合寫入檔案
  4. 遍歷部門列表,將每個部門下的職員列表集合寫入檔案

在這裡插入圖片描述
生成的檔案如下:
在這裡插入圖片描述
二進位制資訊:
在這裡插入圖片描述

用物件流讀取檔案並輸出
/**
 * 用物件流讀取資訊
 *
 * @author  zhuhuix
 * @date 2020-05-27
 */
public class ObjectStreamRead {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream companyStream = new ObjectInputStream(new FileInputStream("c:\\company.dat"));
        if (companyStream!=null){
           Company company=(Company) companyStream.readObject();

            //遍歷公司組織結構
            int deptCount = 0;
            int empCount = 0;
            Iterator<Department> deptIterator = company.getDepartments().iterator();
            while (deptIterator.hasNext()) {
                deptCount++;
                Department department = deptIterator.next();
                System.out.println("部門:" + department.getName());
                if (department.getEmployees() != null) {
                    Iterator<Employee> empIterator = department.getEmployees().iterator();
                    while (empIterator.hasNext()) {
                        empCount++;
                        Employee employee = empIterator.next();
                        System.out.print(" 人員:" + employee.getName() + " 職位:" + employee.getPosition() + ",");
                    }
                    System.out.println();
                }
            }
            System.out.println("總共部門數:" + deptCount);
            System.out.println("總共職員數:" + empCount);

        }

    }
}

核心程式碼
  1. 通過物件流的方式獲取company.dat檔案
  2. 讀取物件資訊

在這裡插入圖片描述
輸出如下:

在這裡插入圖片描述

總結

在本文中,我們使用序列化將物件集合儲存到磁碟檔案中,並按照它們被儲存的樣子獲取它們,我們學習到了如下資訊:

  • ObjectOutputStream(OutputStream out) 建立一個ObjectOutputStream使得你可以將物件寫出到指定的OutputStream。
  • void writeObject(Object obj) 寫出指定的物件到ObjectOutputStream,這個方法將儲存指定物件的類、類的簽名以及這個類及其超類中所有非靜態和非瞬時的域的值。
  • ObjectInputStream(InputStream in) 建立一個ObjectInputStream用於從指定的InputStream中讀回物件資訊。
  • Object readObject()從ObjectInputStream中讀入一個物件。特別是,這個方法會讀回物件的類、類的簽名以及這個類及其超類中所有非靜態和非瞬時的域的值。它執行的反序列化允許恢復多個物件引用。

相關文章