Serializable: Giữ Object sống sót qua mọi thử thách!
Java – OOP

Serializable: Giữ Object sống sót qua mọi thử thách!

Author

Admin System

@root

Ngày xuất bản

22 Mar, 2026

Lượt xem

1 Lượt

Serializable interface

Serializable: Biến Object của bạn thành 'Ma Cà Rồng' bất tử!

Alo, alo, Gen Z code thủ đâu rồi! Hôm nay, anh Creyt sẽ kể cho mấy đứa nghe về một "siêu năng lực" có thể biến object của mấy đứa thành "ma cà rồng" bất tử, sống sót qua mọi thử thách: đó là Serializable interface trong Java. Nghe hấp dẫn không?

1. Serializable là gì và để làm gì?

Thử tưởng tượng thế này: Mấy đứa đang chơi game, tạo ra một đống nhân vật, item xịn sò. Xong, mấy đứa tắt máy đi ngủ. Sáng hôm sau mở game lên, ôi thôi, mọi thứ biến mất sạch sành sanh! Đau lòng không? Object của mấy đứa cũng vậy đó. Khi chương trình Java kết thúc, tất cả các object đang nằm trong bộ nhớ RAM cũng "bay màu" theo. Serializable chính là "phép thuật" để cứu vớt tình hình này.

Nói một cách hoa mỹ hơn, Serializable là một "thẻ VIP" mà một object cần có để được phép "đóng gói" thành một chuỗi byte (quá trình này gọi là Serialization). Sau đó, chuỗi byte này có thể được lưu vào file, gửi qua mạng, hay nhét vào database. Khi nào cần dùng lại, mấy đứa chỉ việc "mở gói" chuỗi byte đó ra, và tada, object sẽ sống lại y nguyên trạng thái ban đầu (quá trình Deserialization). Nó giống như mấy đứa chụp một tấm ảnh selfie của object rồi lưu lại, khi nào nhớ thì lấy ra ngắm vậy.

Điều đặc biệt là Serializable là một marker interface, tức là nó không có bất kỳ phương thức nào để mấy đứa phải implement. Chỉ cần thêm implements Serializable vào class là đủ, Java sẽ tự động lo phần còn lại. "Thẻ VIP" này đơn giản vậy đó!

2. Code Ví Dụ Minh Hoạ: "Phép Thuật" Biến Object thành Byte Stream và Ngược Lại

Giờ thì mình cùng xem "phép thuật" này hoạt động như thế nào qua một ví dụ cụ thể nhé. Anh Creyt sẽ tạo một class SinhVien và cho nó "bất tử".

import java.io.*;

// Bước 1: Class SinhVien cần "bất tử" thì phải implements Serializable
class SinhVien implements Serializable {
    // serialVersionUID là một ID duy nhất cho class này. Quan trọng lắm nha!
    private static final long serialVersionUID = 1L; 
    
    String maSV;
    String tenSV;
    int tuoi;
    // transient: Những trường này sẽ KHÔNG được serialize. Giống như đồ bạn không muốn mang đi xa.
    transient String matKhau;

    public SinhVien(String maSV, String tenSV, int tuoi, String matKhau) {
        this.maSV = maSV;
        this.tenSV = tenSV;
        this.tuoi = tuoi;
        this.matKhau = matKhau;
    }

    @Override
    public String toString() {
        return "SinhVien{" +
               "maSV='" + maSV + '\'' +
               ", tenSV='" + tenSV + '\'' +
               ", tuoi=" + tuoi +
               ", matKhau='" + matKhau + '\'' + // MatKhau sẽ là null sau khi deserialize nếu dùng transient
               '}';
    }
}

public class SerializationDemo {
    public static void main(String[] args) {
        // Tạo một object SinhVien
        SinhVien sv1 = new SinhVien("SV001", "Nguyen Van A", 20, "password123");
        System.out.println("Original Object: " + sv1);

        // --- Bước 2: Serialization (Biến object thành byte stream và lưu vào file) ---
        try {
            // Tạo luồng ghi dữ liệu vào file (output stream)
            FileOutputStream fileOut = new FileOutputStream("sinhvien.ser");
            // Tạo ObjectOutputStream để ghi object
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            
            // Ghi object sv1 vào file
            out.writeObject(sv1);
            
            out.close();
            fileOut.close();
            System.out.println("\nObject đã được serialize và lưu vào file sinhvien.ser");
            System.out.println("Kiểm tra file 'sinhvien.ser' trong thư mục dự án của bạn.");
            
        } catch (IOException i) {
            i.printStackTrace();
        }

        sv1 = null; // Đặt object về null để chứng minh nó đã bị "xóa" khỏi bộ nhớ
        System.out.println("\nObject gốc đã bị xóa khỏi bộ nhớ (sv1 = null).");

        // --- Bước 3: Deserialization (Đọc byte stream từ file và biến lại thành object) ---
        SinhVien sv2 = null;
        try {
            // Tạo luồng đọc dữ liệu từ file (input stream)
            FileInputStream fileIn = new FileInputStream("sinhvien.ser");
            // Tạo ObjectInputStream để đọc object
            ObjectInputStream in = new ObjectInputStream(fileIn);
            
            // Đọc object từ file và cast về kiểu SinhVien
            sv2 = (SinhVien) in.readObject();
            
            in.close();
            fileIn.close();
            System.out.println("Object đã được deserialize từ file.");
            System.out.println("Deserialized Object: " + sv2);
            
        } catch (IOException i) {
            i.printStackTrace();
            return;
        } catch (ClassNotFoundException c) {
            System.out.println("Không tìm thấy class SinhVien");
            c.printStackTrace();
            return;
        }

        // Kiểm tra xem object đã được phục hồi thành công chưa
        System.out.println("\nKiểm tra:");
        System.out.println("Mã SV: " + sv2.maSV);
        System.out.println("Tên SV: " + sv2.tenSV);
        System.out.println("Tuổi: " + sv2.tuoi);
        // Lưu ý: matKhau sẽ là null vì nó được đánh dấu là transient
        System.out.println("Mật khẩu (transient): " + sv2.matKhau);
    }
}

Khi chạy code này, mấy đứa sẽ thấy một file sinhvien.ser được tạo ra. Đó chính là "linh hồn" của object sv1 được đóng gói thành byte. Sau đó, chúng ta đọc file đó lên, và phù phép cho sv2 sống lại với đầy đủ thông tin (trừ mật khẩu vì nó là transient).

Illustration

Gợi Ý Đọc Tiếp
Implements trong Java: Hợp đồng code cho GenZ

4 Lượt xem

3. Mẹo (Best Practices) để ghi nhớ và dùng thực tế

  • serialVersionUID - Người giữ cửa phiên bản: Luôn khai báo private static final long serialVersionUID = 1L; trong class Serializable của mấy đứa. Nếu không, Java sẽ tự động tạo một cái dựa trên cấu trúc class. Khi mấy đứa thay đổi cấu trúc class (thêm/bớt trường), cái ID tự động này sẽ thay đổi, và khi deserialize một object cũ, Java sẽ "nhận nhầm" class, quăng ra InvalidClassException. serialVersionUID giống như số căn cước công dân của class vậy, giúp Java nhận diện đúng class dù cấu trúc có thay đổi chút đỉnh (miễn là mấy đứa đừng thay đổi nó).
  • transient - Kẻ giấu mặt: Dùng từ khóa transient cho những trường mà mấy đứa không muốn hoặc không thể serialize. Ví dụ: mật khẩu (không nên lưu trực tiếp), các đối tượng liên quan đến hệ thống như Socket, Thread, InputStream, OutputStream (vì chúng gắn liền với phiên làm việc hiện tại và không có ý nghĩa khi deserialize). Giống như khi mấy đứa đi du lịch, có những thứ riêng tư hoặc cồng kềnh quá không thể mang theo vali được vậy.
  • Cẩn thận với hiệu năng: Serialization có thể chậm, đặc biệt với các object lớn hoặc khi làm việc với số lượng object khổng lồ. Hãy cân nhắc khi sử dụng trong các hệ thống đòi hỏi hiệu năng cao.
  • Bảo mật là số 1: Không bao giờ serialize trực tiếp các thông tin nhạy cảm như mật khẩu, token mà không mã hóa. Byte stream có thể dễ dàng bị đọc nếu không được bảo vệ. Hãy nghĩ đến việc mã hóa hoặc sử dụng các phương pháp bảo mật khác.
  • Kế thừa và Serializable: Nếu một class cha implements Serializable, thì tất cả các class con của nó cũng mặc định là Serializable. Ngược lại, nếu class cha không Serializable, thì các trường của class cha sẽ không được serialize khi deserialize class con (trừ khi class con tự xử lý). Nhớ kỹ điểm này nha!

4. Ví dụ thực tế các ứng dụng/website đã ứng dụng

Serializable không phải là "phép thuật" mới toanh đâu, nó đã được dùng ở nhiều nơi rồi:

  • Java RMI (Remote Method Invocation): Khi mấy đứa gọi một phương thức trên một object nằm ở một máy tính khác, các tham số và giá trị trả về (nếu là object) thường phải Serializable để có thể "bay" qua mạng.
  • Hibernate/JPA: Trong một số trường hợp, các ORM framework như Hibernate có thể serialize các entity để lưu vào cache hoặc truyền giữa các lớp của ứng dụng.
  • Android Development (Legacy): Hồi xưa, để truyền một object phức tạp giữa các Activity hay Service, người ta hay dùng Serializable. Tuy nhiên, bây giờ Parcelable được ưa chuộng hơn vì hiệu năng tốt hơn nhiều cho Android.
  • Distributed Systems / Messaging Queues: Các hệ thống phân tán cần truyền dữ liệu giữa các node, và Serializable là một cách để đóng gói các thông điệp.
  • Web Servers (Session Management): Một số web server có thể serialize các đối tượng session của người dùng để lưu trữ trên đĩa hoặc chia sẻ giữa các server trong một cluster, giúp duy trì trạng thái đăng nhập của người dùng.
  • Gaming: Lưu trạng thái game (save game) để người chơi có thể tiếp tục từ lần chơi trước.

5. Thử nghiệm đã từng và hướng dẫn nên dùng cho case nào

Hồi xưa anh Creyt mới vào nghề, cứ tưởng Serializable là "thần dược" lưu trữ, quăng gì vào cũng được. Ai dè, có những đứa "khó tính" (như Socket hay Thread objects) không cho serialize đâu nha! Cứ cố gắng là ăn NotSerializableException ngay lập tức. Bài học rút ra là không phải object nào cũng "đóng gói" được.

Khi nào nên dùng Serializable?

  • Lưu trữ object tạm thời trong file: Khi mấy đứa cần lưu trữ một vài object đơn giản vào file để dùng lại trong cùng một ứng dụng Java, hoặc giữa các ứng dụng Java với nhau.
  • Truyền object giữa các ứng dụng Java bằng RMI: Đây là trường hợp kinh điển mà Serializable tỏa sáng.
  • Caching object trong bộ nhớ: Một số hệ thống cache có thể dùng Serializable để lưu trữ object.
  • Khi tốc độ không phải là ưu tiên hàng đầu và chỉ làm việc trong môi trường Java: Vì Serializable là đặc trưng của Java, nó không tương thích tốt với các ngôn ngữ khác.

Khi nào nên cân nhắc các giải pháp khác?

  • Trao đổi dữ liệu giữa các hệ thống/ngôn ngữ khác nhau: JSON, XML, Protocol Buffers, Avro... là những lựa chọn tốt hơn nhiều vì chúng độc lập với ngôn ngữ. Giống như mấy đứa muốn giao tiếp với bạn bè quốc tế thì phải dùng tiếng Anh chứ không phải tiếng Việt vậy.
  • Hiệu năng là cực kỳ quan trọng: Nếu object lớn, số lượng nhiều, hoặc cần tốc độ cao, hãy tìm đến các thư viện serialization chuyên dụng hiệu quả hơn hoặc giải pháp như Externalizable (cho phép mấy đứa tự điều khiển quá trình serialize/deserialize để tối ưu).
  • Dữ liệu nhạy cảm: Không nên dùng Serializable một mình. Cần thêm lớp mã hóa hoặc các cơ chế bảo mật khác.
  • Object chứa tài nguyên hệ thống: Như đã nói ở trên, các object như Socket, Thread, Connection không nên serialize. Chúng gắn liền với một phiên làm việc cụ thể và không thể tái tạo lại một cách đơn giản từ byte stream.

Vậy đó, Serializable là một công cụ mạnh mẽ nhưng cũng cần được sử dụng đúng cách. Nắm vững nó, mấy đứa sẽ có thêm một "siêu năng lực" để "bất tử hóa" dữ liệu của mình!

Thuộc Series: Java – OOP

Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

#tech #cyberpunk #laravel
Chỉnh sửa bài viết

Bình luận (0)

Vui lòng Đăng Nhập để Bình luận

Hỗ trợ Markdown cơ bản
Nguyễn Văn A
1 ngày trước

Tính năng này đỉnh quá ad ơi, chờ mãi mới thấy một blog Tiếng Việt có UI/UX xịn như vầy!