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
相关产品推荐
相关产品推荐

