transient 关键字

概述

transient 关键字用于标记不需要序列化的成员变量。当对象被序列化时,标记为transient的字段会被忽略,不会写入到序列化流中,反序列化时这些字段会被赋予默认值。

语法格式

private transient int field1;
private transient String field2;
private transient Object field3;

序列化基础

什么是序列化

序列化是将对象转换为字节流的过程,以便存储到文件、数据库或通过网络传输。反序列化是将字节流恢复为对象的过程。

Java序列化机制

Java通过Serializable接口实现序列化,transient关键字允许开发者控制哪些字段参与序列化。

基本用法

基础示例

import java.io.*;

public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    private String username;        // 会被序列化
    private String email;          // 会被序列化
    private transient String password;  // 不会被序列化(安全考虑)
    private transient int sessionId;   // 不会被序列化(临时数据)

    public User(String username, String email, String password, int sessionId) {
        this.username = username;
        this.email = email;
        this.password = password;
        this.sessionId = sessionId;
    }

    // Getter和Setter方法
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }

    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }

    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }

    public int getSessionId() { return sessionId; }
    public void setSessionId(int sessionId) { this.sessionId = sessionId; }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", email='" + email + '\'' +
                ", password='" + password + '\'' +
                ", sessionId=" + sessionId +
                '}';
    }
}

// 测试序列化和反序列化
public class TransientExample {
    public static void main(String[] args) {
        User originalUser = new User("alice", "alice@example.com", "secret123", 12345);
        System.out.println("原始对象: " + originalUser);

        // 序列化
        try {
            FileOutputStream fos = new FileOutputStream("user.ser");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(originalUser);
            oos.close();
            fos.close();
            System.out.println("序列化完成");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 反序列化
        try {
            FileInputStream fis = new FileInputStream("user.ser");
            ObjectInputStream ois = new ObjectInputStream(fis);
            User deserializedUser = (User) ois.readObject();
            ois.close();
            fis.close();

            System.out.println("反序列化后: " + deserializedUser);
            System.out.println("密码是否丢失: " + (deserializedUser.getPassword() == null));
            System.out.println("会话ID值: " + deserializedUser.getSessionId()); // 应该是0(int默认值)
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

安全敏感信息

import java.io.*;

public class BankAccount implements Serializable {
    private static final long serialVersionUID = 1L;

    private String accountNumber;
    private String accountHolderName;
    private double balance;

    // 敏感信息不序列化
    private transient String pin;           // PIN码
    private transient String socialSecurityNumber; // 社会保险号
    private transient String secretKey;     // 加密密钥

    // 临时计算字段不序列化
    private transient double interestEarned;    // 利息收入(可重新计算)
    private transient long lastAccessTime;     // 最后访问时间(运行时确定)

    public BankAccount(String accountNumber, String accountHolderName, 
                      double balance, String pin, String ssn) {
        this.accountNumber = accountNumber;
        this.accountHolderName = accountHolderName;
        this.balance = balance;
        this.pin = pin;
        this.socialSecurityNumber = ssn;
        this.secretKey = generateSecretKey();
        this.lastAccessTime = System.currentTimeMillis();
        this.interestEarned = calculateInterest();
    }

    private String generateSecretKey() {
        return "SECRET_" + System.currentTimeMillis();
    }

    private double calculateInterest() {
        return balance * 0.02; // 2%利率
    }

    // 反序列化后的初始化方法
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject(); // 先执行默认反序列化

        // 重新初始化transient字段
        this.secretKey = generateSecretKey();
        this.lastAccessTime = System.currentTimeMillis();
        this.interestEarned = calculateInterest();

        System.out.println("反序列化后重新初始化敏感和临时字段");
    }

    public void displayInfo() {
        System.out.println("账户信息:");
        System.out.println("  账号: " + accountNumber);
        System.out.println("  持有人: " + accountHolderName);
        System.out.println("  余额: $" + balance);
        System.out.println("  PIN: " + (pin != null ? "已设置" : "未设置"));
        System.out.println("  SSN: " + (socialSecurityNumber != null ? "已设置" : "未设置"));
        System.out.println("  密钥: " + (secretKey != null ? secretKey : "未设置"));
        System.out.println("  利息收入: $" + interestEarned);
        System.out.println("  最后访问: " + new java.util.Date(lastAccessTime));
    }

    // Getter和Setter方法
    public String getAccountNumber() { return accountNumber; }
    public String getAccountHolderName() { return accountHolderName; }
    public double getBalance() { return balance; }
    public void setBalance(double balance) { 
        this.balance = balance;
        this.interestEarned = calculateInterest(); // 重新计算利息
    }

    public void setPin(String pin) { this.pin = pin; }
    public boolean verifyPin(String inputPin) {
        return pin != null && pin.equals(inputPin);
    }
}

// 测试银行账户序列化
public class BankAccountTest {
    public static void main(String[] args) {
        BankAccount account = new BankAccount(
            "123456789", 
            "John Doe", 
            10000.00, 
            "1234", 
            "555-12-3456"
        );

        System.out.println("=== 序列化前 ===");
        account.displayInfo();

        // 序列化
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(account);
            byte[] serializedData = baos.toByteArray();
            oos.close();

            System.out.println("\n=== 序列化成功,数据大小: " + serializedData.length + " bytes ===");

            // 反序列化
            ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
            ObjectInputStream ois = new ObjectInputStream(bais);
            BankAccount deserializedAccount = (BankAccount) ois.readObject();
            ois.close();

            System.out.println("\n=== 反序列化后 ===");
            deserializedAccount.displayInfo();

            // 验证PIN(应该失败,因为PIN是transient的)
            System.out.println("\n=== PIN验证测试 ===");
            System.out.println("验证PIN '1234': " + deserializedAccount.verifyPin("1234"));

        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

缓存和计算字段

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class ShoppingCart implements Serializable {
    private static final long serialVersionUID = 1L;

    private List<Item> items;
    private String customerId;

    // 计算字段 - 不需要序列化,可以重新计算
    private transient double totalPrice;
    private transient int totalItems;
    private transient double averageItemPrice;

    // 缓存字段 - 不需要序列化
    private transient String formattedTotal;
    private transient boolean isCalculated;

    public ShoppingCart(String customerId) {
        this.customerId = customerId;
        this.items = new ArrayList<>();
        this.isCalculated = false;
    }

    public void addItem(Item item) {
        items.add(item);
        invalidateCache(); // 使缓存失效
    }

    public void removeItem(Item item) {
        items.remove(item);
        invalidateCache();
    }

    private void invalidateCache() {
        isCalculated = false;
        formattedTotal = null;
    }

    private void calculateTotals() {
        if (isCalculated) return;

        totalPrice = 0;
        totalItems = items.size();

        for (Item item : items) {
            totalPrice += item.getPrice() * item.getQuantity();
        }

        averageItemPrice = totalItems > 0 ? totalPrice / totalItems : 0;
        formattedTotal = String.format("$%.2f", totalPrice);
        isCalculated = true;

        System.out.println("重新计算购物车统计信息");
    }

    // 反序列化后重新计算
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        // 重新初始化transient字段
        invalidateCache();
        calculateTotals();
        System.out.println("购物车反序列化完成,重新计算统计信息");
    }

    public double getTotalPrice() {
        calculateTotals();
        return totalPrice;
    }

    public int getTotalItems() {
        calculateTotals();
        return totalItems;
    }

    public double getAverageItemPrice() {
        calculateTotals();
        return averageItemPrice;
    }

    public String getFormattedTotal() {
        calculateTotals();
        return formattedTotal;
    }

    public void displayCart() {
        System.out.println("购物车 (客户ID: " + customerId + "):");
        for (Item item : items) {
            System.out.println("  " + item);
        }
        System.out.println("总价: " + getFormattedTotal());
        System.out.println("商品数量: " + getTotalItems());
        System.out.println("平均价格: $" + String.format("%.2f", getAverageItemPrice()));
    }

    // 静态内部类
    public static class Item implements Serializable {
        private static final long serialVersionUID = 1L;

        private String name;
        private double price;
        private int quantity;

        public Item(String name, double price, int quantity) {
            this.name = name;
            this.price = price;
            this.quantity = quantity;
        }

        public String getName() { return name; }
        public double getPrice() { return price; }
        public int getQuantity() { return quantity; }

        @Override
        public String toString() {
            return name + " (价格: $" + price + ", 数量: " + quantity + ")";
        }
    }
}

// 测试购物车序列化
public class ShoppingCartTest {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart("USER123");

        // 添加商品
        cart.addItem(new ShoppingCart.Item("笔记本电脑", 999.99, 1));
        cart.addItem(new ShoppingCart.Item("鼠标", 29.99, 2));
        cart.addItem(new ShoppingCart.Item("键盘", 79.99, 1));

        System.out.println("=== 序列化前 ===");
        cart.displayCart();

        // 序列化
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(cart);
            byte[] serializedData = baos.toByteArray();
            oos.close();

            System.out.println("\n=== 序列化成功 ===");

            // 反序列化
            ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
            ObjectInputStream ois = new ObjectInputStream(bais);
            ShoppingCart deserializedCart = (ShoppingCart) ois.readObject();
            ois.close();

            System.out.println("\n=== 反序列化后 ===");
            deserializedCart.displayCart();

        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

线程相关字段

import java.io.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;

public class TaskProcessor implements Serializable {
    private static final long serialVersionUID = 1L;

    private String processorName;
    private int maxConcurrentTasks;

    // 线程相关字段不应该被序列化
    private transient Thread workerThread;
    private transient ReentrantLock lock;
    private transient AtomicLong processedCount;
    private transient volatile boolean isRunning;

    // 状态信息(会被序列化)
    private long totalProcessedTasks;
    private long creationTime;

    public TaskProcessor(String processorName, int maxConcurrentTasks) {
        this.processorName = processorName;
        this.maxConcurrentTasks = maxConcurrentTasks;
        this.totalProcessedTasks = 0;
        this.creationTime = System.currentTimeMillis();

        initializeTransientFields();
    }

    private void initializeTransientFields() {
        this.lock = new ReentrantLock();
        this.processedCount = new AtomicLong(totalProcessedTasks);
        this.isRunning = false;

        System.out.println("初始化线程相关字段");
    }

    // 反序列化后重新初始化transient字段
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        initializeTransientFields();
        System.out.println("TaskProcessor反序列化完成,重新初始化线程相关字段");
    }

    public void startProcessing() {
        lock.lock();
        try {
            if (isRunning) {
                System.out.println("处理器已在运行中");
                return;
            }

            isRunning = true;
            workerThread = new Thread(() -> {
                System.out.println(processorName + " 开始处理任务");

                while (isRunning) {
                    try {
                        // 模拟任务处理
                        Thread.sleep(1000);
                        long count = processedCount.incrementAndGet();
                        totalProcessedTasks = count;
                        System.out.println(processorName + " 处理了任务 #" + count);

                        if (count >= 5) { // 处理5个任务后停止
                            isRunning = false;
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }

                System.out.println(processorName + " 停止处理任务");
            }, processorName + "-Worker");

            workerThread.start();
        } finally {
            lock.unlock();
        }
    }

    public void stopProcessing() {
        lock.lock();
        try {
            isRunning = false;
            if (workerThread != null) {
                workerThread.interrupt();
            }
        } finally {
            lock.unlock();
        }
    }

    public void waitForCompletion() {
        if (workerThread != null) {
            try {
                workerThread.join();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public void displayStatus() {
        System.out.println("处理器状态:");
        System.out.println("  名称: " + processorName);
        System.out.println("  最大并发任务: " + maxConcurrentTasks);
        System.out.println("  总处理任务数: " + totalProcessedTasks);
        System.out.println("  创建时间: " + new java.util.Date(creationTime));
        System.out.println("  当前运行状态: " + (isRunning ? "运行中" : "已停止"));
        System.out.println("  工作线程: " + (workerThread != null ? workerThread.getName() : "未创建"));
    }

    public String getProcessorName() { return processorName; }
    public long getTotalProcessedTasks() { return totalProcessedTasks; }
    public boolean isRunning() { return isRunning; }
}

// 测试任务处理器序列化
public class TaskProcessorTest {
    public static void main(String[] args) {
        TaskProcessor processor = new TaskProcessor("MainProcessor", 3);

        System.out.println("=== 启动处理器 ===");
        processor.displayStatus();
        processor.startProcessing();
        processor.waitForCompletion();

        System.out.println("\n=== 处理完成后 ===");
        processor.displayStatus();

        // 序列化
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(processor);
            byte[] serializedData = baos.toByteArray();
            oos.close();

            System.out.println("\n=== 序列化成功 ===");

            // 反序列化
            ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
            ObjectInputStream ois = new ObjectInputStream(bais);
            TaskProcessor deserializedProcessor = (TaskProcessor) ois.readObject();
            ois.close();

            System.out.println("\n=== 反序列化后 ===");
            deserializedProcessor.displayStatus();

            // 测试反序列化后的功能
            System.out.println("\n=== 测试反序列化后的功能 ===");
            deserializedProcessor.startProcessing();
            deserializedProcessor.waitForCompletion();
            deserializedProcessor.displayStatus();

        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

使用场景

1. 敏感信息保护

public class LoginSession implements Serializable {
    private String userId;
    private long loginTime;
    private transient String password;     // 密码不序列化
    private transient String sessionToken; // 会话令牌不序列化
}

2. 临时计算字段

public class Rectangle implements Serializable {
    private double width;
    private double height;
    private transient double area;    // 面积可以重新计算
    private transient double perimeter; // 周长可以重新计算
}

3. 系统资源

public class DatabaseConnection implements Serializable {
    private String connectionString;
    private transient Connection connection; // 数据库连接不能序列化
    private transient PreparedStatement statement;
}

4. 缓存数据

public class UserProfile implements Serializable {
    private String username;
    private String email;
    private transient String cachedDisplayName; // 缓存的显示名
    private transient long cacheTimestamp;      // 缓存时间戳
}

最佳实践

  1. 安全考虑

    • 将密码、密钥等敏感信息标记为transient
    • 考虑使用自定义序列化方法进一步控制
  2. 性能优化

    • 大型对象或集合如果可以重新获取,可以标记为transient
    • 临时计算结果不需要序列化
  3. 资源管理

    • 文件句柄、网络连接、线程等系统资源必须是transient
    • 在反序列化后重新初始化这些资源
  4. 与serialVersionUID配合

    public class MyClass implements Serializable {
        private static final long serialVersionUID = 1L;
        private String data;
        private transient String cache;
    }
    

注意事项

  1. 默认值恢复

    • transient字段在反序列化后会被设置为默认值
    • 对象引用为null,数值类型为0,布尔类型为false
  2. 继承影响

    • 子类中的transient字段不会影响父类的序列化
    • 父类的transient字段在子类序列化时仍然被忽略
  3. static字段

    • static字段本身就不参与序列化
    • 不需要同时使用static和transient
  4. 自定义序列化

    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();
        // 自定义序列化逻辑
    }
    
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        // 重新初始化transient字段
    }
    

transient关键字是Java序列化机制中的重要组成部分,正确使用它能够提高安全性、性能和代码的健壮性。

powered by Gitbook© 2025 编外计划 | 最后修改: 2025-07-28 16:25:54

results matching ""

    No results matching ""