You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX中SimpleStringProperty序列化报错求可行解决方案

JavaFX序列化报错:java.io.NotSerializableException: javafx.beans.property.SimpleStringProperty

我是JavaFX新手,正在开发患者/咨询师/就诊记录系统,要求将所有数据序列化到文件中,后续还要从文件读取。目前碰到报错:java.io.NotSerializableException: javafx.beans.property.SimpleStringProperty。

已经给所有StringProperty类型变量加了transient修饰,还尝试把writeFile()的参数类型改为Serializable,但问题依旧。不想采用9年前那种手动编写writeObject()的复杂方案,求简单可行的解决思路。


相关代码

1. SerialStorage类

package model;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class SerialStorage {
    private static final String fileName = "serialPatientsData.ser";

    
    public static boolean writeFile(Object o) {
        boolean result = true;
        try {
            FileOutputStream file = new FileOutputStream(fileName);
            ObjectOutputStream out = new ObjectOutputStream(file);
            out.writeObject(o);
            out.close();
            file.close();
            System.out.println("Object has been serialized");
        } catch (IOException ex) {
            System.out.println("line 24 " + ex);
            result = false;
        }
        return result;
    }
    
    
    public static Object readFile() {
        Object outObject = null;
        try {
            FileInputStream file = new FileInputStream(fileName);
            ObjectInputStream in = new ObjectInputStream(file);
            
            outObject = in.readObject();
            in.close();
            file.close();
            System.out.println("Object has been deserialized");
            
        } catch (IOException ex) {
            System.out.println("IOException is caught 42 " + ex);
        } catch (ClassNotFoundException ex) {
            System.out.println("ClassNotFoundException is caught 44 " + ex);
        }
        return outObject;
    }
}

2. Name类(包含SimpleStringProperty)

package model;

import java.io.Serializable;
import java.util.Objects;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Name implements Serializable{
    private transient StringProperty firstName;
    private transient StringProperty lastName;

    
    public Name(String firstName, String lastName) {
        super();
        this.firstName = new SimpleStringProperty(firstName);
        this.lastName = new SimpleStringProperty(lastName);
    }
    
    public StringProperty getFirstNameProperty() {
        return firstName;
    }
    
    public StringProperty getLastNameProperty() {
        return lastName;
    }
    
    public String getFirstName() {
        return firstName.get();
    }
    
    public String getLastName() {
        return lastName.get();
    }
}

3. AddPatientDialog中的绑定代码

public class AddPatientDialog extends Dialog<Patient> {
    
    private Patient patient;
    private Consultant consultant;
    private AlertBox alertBox = new AlertBox();

    private TextField firstNameInput, lastNameInput, phoneInput;
    private ComboBox<String> comboBox;
    private int selectedIndex = -1;
    private transient Property<Number> indexInput;

    public AddPatientDialog() {
        super();
        this.setTitle("Add Patient");
//      this.patient = p;
        this.patient = new Patient(new Name("", ""), "");
        buildUI();
        setPropertyBinding();
        setResultConverter();
    }

[more code] 

private void setPropertyBinding() {
        firstNameInput.textProperty().bindBidirectional(patient.getName().getFirstNameProperty());
        lastNameInput.textProperty().bindBidirectional(patient.getName().getLastNameProperty());
        phoneInput.textProperty().bindBidirectional(patient.getPhoneProperty());
    }

[more code]

}

解决思路

1. 补全模型类的序列化字段

你的Name类只定义了transient的StringProperty,没有保存原始字符串的普通字段——序列化时transient属性会被忽略,反序列化后没有原始数据可以重构Property,这是核心问题之一。

修改模型类,同时保留普通字符串字段和transient的Property,并通过监听器保持两者同步:

public class Name implements Serializable{
    // 用于序列化的普通字符串字段
    private String firstName;
    private String lastName;
    // 用于UI绑定的transient属性
    private transient StringProperty firstNameProperty;
    private transient StringProperty lastNameProperty;

    public Name(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
        initProperties();
    }

    // 初始化Property并添加同步监听器
    private void initProperties() {
        firstNameProperty = new SimpleStringProperty(firstName);
        lastNameProperty = new SimpleStringProperty(lastName);
        
        firstNameProperty.addListener((obs, oldVal, newVal) -> this.firstName = newVal);
        lastNameProperty.addListener((obs, oldVal, newVal) -> this.lastName = newVal);
    }

    public StringProperty getFirstNameProperty() {
        // 反序列化后Property为null,懒加载初始化
        if (firstNameProperty == null) {
            initProperties();
        }
        return firstNameProperty;
    }

    public StringProperty getLastNameProperty() {
        if (lastNameProperty == null) {
            initProperties();
        }
        return lastNameProperty;
    }

    // 直接操作普通字段的getter/setter
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
        if (firstNameProperty != null) {
            firstNameProperty.set(firstName);
        }
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
        if (lastNameProperty != null) {
            lastNameProperty.set(lastName);
        }
    }
}

2. 检查全对象依赖链

序列化会递归检查对象的所有引用,确保Patient、Consultant等所有被序列化的类:

  • 都实现了Serializable接口
  • 所有非Serializable的属性(比如其他JavaFX Property)都加了transient修饰
  • 嵌套的子对象也满足上述条件

3. 反序列化时重构Property

如果不想修改模型类的字段结构,也可以在含transient Property的类中实现readObject()方法,手动初始化Property:

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    // 先读取默认序列化的内容
    in.defaultReadObject();
    // 用已保存的字符串值重构Property
    firstName = new SimpleStringProperty(getFirstName());
    lastName = new SimpleStringProperty(getLastName());
}

这种方式需要确保类中有可读取的原始字符串数据(比如你的Name类需要先保存普通字符串字段)。


内容的提问来源于stack exchange,提问作者gabsdevtobe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 20:44:57