gpt4 book ai didi

javafx:如何为此对象编写 writeObject() 和 readObject()?

转载 作者:行者123 更新时间:2023-11-30 08:09:03 24 4
gpt4 key购买 nike

我有一个 Trade带有以下代码的对象,它实现了 Serializable接口(interface),但由于它包含 javafx 属性,我得到这个 java.io.NotSerializableException因此未能正确执行 writeObject()readObject() .我的最终目标是能够使用 ObjectOutputStream写入读取 这个对象。和 ObjectInputStream

我阅读了 3 个链接:

NotSerializableException on SimpleListProperty

Oracle doc

Java Custom Serialization

自从我的Trade类正在运行 ScheduledService要从 Google Finance 获取收盘价,我知道我需要调用 startService()readObject() 内确保当readObject()方法被调用并且对象被反序列化,线程将再次重新启动。

此外,我明白我需要在我的 Trade 中定义这两个私有(private)方法。类(class)。

private void writeObject(ObjectOutputStream out) throws IOException
{
out.defaultWriteObject();
// What else should I write in here?
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
// our "pseudo-constructor"
in.defaultReadObject();

// Not sure what else I need to write in here

// now we are a "live" object again, so let's run rebuild and start
startService();
}

问题:我已经阅读了上面的第 3 个链接,但我仍然对上面这两个私有(private)方法中还应该包含什么感到困惑?

因为我的贸易对象有相当多的属性,但都是它真正需要的只是buySell,transactionDate,symbol, double volume, double price构造一个对象。我应该将其余属性设置为 transient 吗?那么呢?

public class Trade implements Serializable{
// properties
private Long creationTime;
private int counter;
private ObjectProperty<LocalDate> transactionDate;
private StringProperty symbol;
private StringProperty details;
private StringProperty buySell;
private DoubleProperty volume;
private DoubleProperty price;
private ReadOnlyDoubleWrapper transactionFee;

private final ReadOnlyDoubleWrapper closingPrice;
private final PauseTransition delay;
private ReadOnlyBooleanWrapper caution;

private final ScheduledService<webScraping> stockService = new ScheduledService<webScraping>() {
// web scrape google finance data
...
}

// constructor
public Trade(BuySell buySell, LocalDate transactionDate, String symbol, double volume, double price){
...
startService();
creationTime = Calendar.getInstance().getTimeInMillis();
}

// getters and setters and instance methods that return the properties themselves
public Long getCreationTime(){
return this.creationTime;
}

private Object writeReplace() {
return new TradeProxy(this);
}

private void readObject(ObjectInputStream stream)
throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");

}

...

private static class TradeProxy implements Serializable{
private String buySell;
private LocalDate transactionDate;
private String stockTicker;
private double price;
private double volume;
private Long creationTime;

private TradeProxy(Trade trade){
this.buySell = trade.getBuySell();
this.transactionDate = trade.getTransactionDate();
this.stockTicker = trade.getStockTicker();
this.price = trade.getPrice();
this.volume = trade.getVolume();
this.creationTime = trade.getCreationTime();
}

private void writeObject(ObjectOutputStream s ) throws IOException{
s.defaultWriteObject();
}

private Object readResolve() throws ObjectStreamException{
return new Trade(this.buySell,this.transactionDate, this.symbol, this.volume, this.price);
}


}
}

更新:我更新了我的代码。但是因为 creationTime不是 Trade 的参数的构造函数,在我的例子中我不知道如何序列化/反序列化它。更准确地说,如果我创建一个 Trade对象在时间说 creationTime = 1443444301488 ,我希望这个对象被序列化,当我读入对象并反序列化它时,我希望 creationTime 与它原来的完全相同(即 1443444301488) 我不知道如何为了达成这个。这就是我现在面临的问题。

最佳答案

我会避免序列化 javafx 对象。而是创建一个包含应该被序列化的状态的 javabean 对象。然后您可以让您的 Trade 对象从序列化的代理 javabean 构建它自己。

class TradeSerialProxy {
private String simpleBeanFields;
private int moreSimpleStateFields;

//getters and setters
}

然后

public Trade (TradeSerialProxy proxy) {
//build the Trade object using the proxy.
}

您会在 Effective Java 一书中看到与此类似的内容。尽管在那本书中他出于安全目的使用代理。我遵循的规则是只序列化简单的 javabean 对象,仅此而已。避免序列化复杂的对象。

另外,如果您使用常规的 Java 序列化,那么只要您的类实现发生变化,您就可能会遇到版本问题。有很多方法可以解决这个问题,比如使用 JSON 和 GSON 进行序列化。因为我使用的是纯标准 Java,没有外部库/jar,所以我必须使用 HashMap 来完成此操作……我只序列化 HashMap 并使用传递给它的 HashMap 构建真实对象。我必须这样做以避免出现常量串行版本不匹配异常并坚持使用纯标准的 vanilla Java。

编辑:这是一个使用序列化代理模式的对象。此方法来自 Effective Java 第 2 版第 78 项。

public class UserProfile implements Serializable {

///////////////////////////////////////////////////////////////////////////
//private variables
private String profileName = null;
private int version = 0;
private LeaderboardPermissions leaderboardState = LeaderboardPermissions.ASK;
private boolean upgradeWalkThrough = true;
private final Map<GameType, GameTypeStats> gameTypeStats;
private final String id;
private boolean offNet = true;

///////////////////////////////////////////////////////////////////////////
//serialization stuff
private static final long serialVersionUID = 7625672295622776890L;

private UserProfile(UserProfileProxy t) {
this.profileName = t.profileName;
this.version = t.version;
this.leaderboardState = t.leaderboardState;
this.upgradeWalkThrough = t.upgradeWalkThrough;
this.gameTypeStats = t.gameTypeStats;
this.id = t.id;
this.offNet = t.offNet;
}

private Object writeReplace() {
return new UserProfileProxy(this);
}

private void readObject(ObjectInputStream stream)
throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");

}

///////////////////////////////
//serialization proxy
private static class UserProfileProxy implements Serializable {

private String profileName = null;
private int version = 0;
private final LeaderboardPermissions leaderboardState;
private boolean upgradeWalkThrough = true;
private final Map<GameType, GameTypeStats> gameTypeStats;
private String id;
private static final long serialVersionUID = 6985672045622776890L;
private boolean offNet;

private UserProfileProxy(UserProfile t) {
this.profileName = t.profileName;
this.version = t.version;
this.leaderboardState = t.leaderboardState;
this.upgradeWalkThrough = t.upgradeWalkThrough;
this.gameTypeStats = t.gameTypeStats;
this.id = t.id;
this.offNet = t.offNet;
}

private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
}

private Object readResolve() throws ObjectStreamException {
return new UserProfile(this);
}

}

这种方法被嵌入到 Java 对象序列化协议(protocol)中。我现在使用的另一种方法是使用 HashMap<String, Object> 作为代理。

这是界面。我必须让这个接口(interface)中的方法返回它们的散列,因为我广泛使用加密序列化对象的散列来防止篡改保存的文件。我不一定要推荐这个,而是展示序列化代理的可能性。

public interface MapSerializable {

public static String CLASS_KEY = "MapSerializable.CLASS_KEY";

/**
* Object will populate a HashMap of objects that it can use at some later
* point to reinitialize itself. Return the hash of the objects used to
* build itself.
*
* @param serial
* @return
* @throws IOException
*/
public int populateSerialMap(HashMap<String, Object> serial) throws IOException;

/**
* Object will initialize itself using the input HashMap. Returns the hash
* of the objects that were used to initialize itself from the Map.
*
* @param serial
* @return hash of the objects that were used to load yourself.
* @throws IOException
*/
public int initializeFromMap(HashMap<String, Object> serial) throws IOException;

}

这是一个使用它的对象的例子。

public class GameType implements MapSerializable {

////////////////////////////////////////////////////////////////////////////
//private variables
private String displayName = null;

////////////////////////////////////////////////////////////////////////////
//constrcutor
public GameType(String name) {
this.displayName = name;
}

GameType() {
}

////////////////////////////////////////////////////////////////////////////
//public methods
@Override
public int populateSerialMap(HashMap<String, Object> serial) throws IOException {
serial.put("displayName", displayName);
return 17 * Objects.hashCode(displayName);
}

@Override
public final int initializeFromMap(HashMap<String, Object> serial) throws IOException {
int hash = 0;
ObjectHashPair<String> ohp = model.utils.SerialUtils.getObjectFromMap(serial, "displayName", "");
displayName = ohp.obj;
hash += 17 * ohp.hash;
return hash;
}

}

EDIT2:对第一种方法的更深入的解释。

您需要先了解 Java 序列化的一些基础知识。 Java 为您完成了大部分繁重的工作,它实际上有一个 writeObject 和 readObject 在大多数情况下都可以正常工作。这对你来说是个好消息,因为你需要做的就是决定哪些字段需要进入代理只是你想要序列化的东西(状态)而不必担心实际进行序列化(添加/删除对象到溪流)。接下来,您需要能够使用代理初始化您的主类,反之亦然。因此,在您的主类中创建一个构造函数,该构造函数将代理对象作为输入,在该构造函数中初始化您的主类。对代理对象执行相同的操作。最后,Java 甚至让您能够使用代理通过 writeReplace 和 readResource 方法进行写入和读取。主类的 writeReplace 将返回一个代理实例,实质上告诉 Java 改为序列化该对象。另一方面,在代理中,您需要一个 readResolve 来在反序列化期间返回主对象的实例。

所以让我们通过项目符号列表中的步骤:

1) 决定哪些字段需要保存并创建你的代理类(我使用了一个内部嵌套类)来拥有这些字段。
2)在主类和代理类中创建构造函数。 Main(Proxy obj)Proxy(Main obj) .
3)分别在主类和代理类上实现writeReplace和readResolve。

希望对您有所帮助。

关于javafx:如何为此对象编写 writeObject() 和 readObject()?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32770574/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com