I am trying to create a form where the user saves three pieces of information (id, name and surname). The following is the code of the person:
public class Person implements Serializable {
private String Personfirstname;
private String Personlastname;
private String PersonID;
/**
* #return the Personfirstname
*/
public String getPersonfirstname() {
return Personfirstname;
}
/**
* #param Personfirstname the Personfirstname to set
*/
public void setPersonfirstname(String Personfirstname) {
this.Personfirstname = Personfirstname;
}
/**
* #return the Personlastname
*/
public String getPersonlastname() {
return Personlastname;
}
/**
* #param Personlastname the Personlastname to set
*/
public void setPersonlastname(String Personlastname) {
this.Personlastname = Personlastname;
}
/**
* #return the PersonID
*/
public String getPersonID() {
return PersonID;
}
/**
* #param PersonID the PersonID to set
*/
public void setPersonID(String PersonID) {
this.PersonID = PersonID;
}
public void savecons()
{
try {
File selectedFile = new File("Consultant - " + PersonID + ".txt");
FileOutputStream fileStream = new FileOutputStream(selectedFile);
ObjectOutputStream oos = new ObjectOutputStream(fileStream);
oos.writeObject(this);
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
private String toString(int ConsultantID) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
public static Person loadcons() throws Exception
{
Person loadcons = null;
JFileChooser chooser = new JFileChooser();
int chooserOption = chooser.showSaveDialog(null);
chooserOption = JFileChooser.APPROVE_OPTION;
try {
File file = new File (chooser.getSelectedFile().getAbsolutePath());
ObjectInputStream input = new ObjectInputStream(new FileInputStream(file));
loadcons = (Person) input.readObject();
input.close();
return loadcons;
} catch (IOException ex) {
System.out.println(ex.getMessage());
} catch (ClassNotFoundException ex) {
System.out.println(ex.getMessage());
}
throw new Exception("No files were selected");
}
private String toString(String PersonID) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
The code have the 3 variables and 2 methods. One of the methods saves the variable information into a text files (the text files is being outputted but I am not sure if the information is getting into it as its all symbols. The other method is a load button that will import the data back in the fields.
Then I created a form with the following code. There the saving is:
Person cons_save = new Person();
cons_save.setPersonfirstname(this.jTextField1.getText());
cons_save.setPersonlastname(this.jTextField2.getText());
cons_save.setPersonID(this.jTextField3.getText());
this.jTextField1.setText("");
this.jTextField2.setText("");
this.jTextField3.setText("");
cons_save.savecons();
and the loading is the below:
Person cons_load = Person.loadcons();
this.jTextField1.setText(cons_load.getPersonfirstname());
this.jTextField2.setText(cons_load.getPersonlastname());
this.jTextField3.setText(cons_load.getPersonID());
When I press the loading button it doesn't work as it needs an exception but when I create the exception the button works but when I chose the file, the information is not going to the fields.
Person cons_load;
try {
cons_load = Person.loadcons();
this.jTextField1.setText(cons_load.getPersonfirstname());
this.jTextField2.setText(cons_load.getPersonlastname());
this.jTextField3.setText(cons_load.getPersonID());
} catch (Exception ex) {
Logger.getLogger(CreateConsultant.class.getName()).log(Level.SEVERE, null, ex);
}
I appreciate every help I can get as this is the first time I am trying to program in java oop.
You will need to mark the classes you want to save to file with the Serializable interface. This should allow the serialization of the objects you are after.
As per the JavaDoc (I highlighted some text in bold):
Serializability of a class is enabled by the class implementing the
java.io.Serializable interface. Classes that do not implement this
interface will not have any of their state serialized or deserialized.
All subtypes of a serializable class are themselves serializable. The
serialization interface has no methods or fields and serves only to
identify the semantics of being serializable.
Essentially, this: public class Person needs to become this: public class Person implements Serializable { static final long serialVersionUID = ....
It is important that serialVersionUID is unique for each class since it is used for serialization and deserialization purposes.
EDIT: As per the comments below, I copied your code and ran it. I managed to save and read it back without issues. The code ran as is in your question, seeing that you have added the marker interface (it is good practice to also include your serialVersionUID field).
I then removed the implements Serializable section of your code, and I got this error: writing aborted; java.io.NotSerializableException: so.Person. This essentially shows that you are trying to store a non serializable item.
Below is what the content of the file looks like when the exception is thrown:
Related
I had a class Inventory that contains a list of Treasure class. I designed the Reader to read the Inventory as a list of Treasure, but when I want to call the reader from my main function, it says that Inventory is not the same as a list of Treasure. And now I don't know what should I do, should I change the read method to only read Inventory, but then again Inventory contains all the list of Treasure that I need. I am very lost as how to do it.
public class Inventory implements SavedGames {
private ArrayList<Treasure> inventory;
private static Inventory instance;
// EFFECTS: constructs an empty player inventory
private Inventory() {
this.inventory = new ArrayList<Treasure>();
inventory.add(new Treasure("Potion", 4));
}
private void loadGames() {
try {
MainCharacter character = Reader.readCharacter(new File(CHARACTER_FILE));
Monster monster = Reader.readMonster(new File(MONSTER_FILE));
List<Treasure> inventory = Reader.readInventory(new File(INVENTORY_FILE));
this.character = character;
this.monster = monster;
this.inventory = inventory;
} catch (IOException e) {
System.out.println("Unable to read files, resetting...");
startApp();
}
}
private static List<Treasure> parseInventory(List<String> fileContent) {
List<Treasure> inventory = new ArrayList<>();
for (String line : fileContent) {
ArrayList<String> lineComponents = splitString(line);
inventory.add(parseTreasure(lineComponents));
}
return inventory;
}
private static Treasure parseTreasure(List<String> components) {
String description = components.get(0);
int amount = Integer.parseInt(components.get(1));
return new Treasure(description, amount);
}
}
Serialization is a mechanism of converting the state of an object into a byte stream. Deserialization is the reverse process where the byte stream is used to recreate the actual Java object in memory. This mechanism is used to persist the object.
The byte stream created is platform independent. So, the object serialized on one platform can be deserialized on a different platform.
To make a Java object serializable we implement the java.io.Serializable interface.
The ObjectOutputStream class contains writeObject() method for serializing an Object.
public final void writeObject(Object obj)
throws IOException
The ObjectInputStream class contains readObject() method for deserializing an object.
public final Object readObject()
throws IOException,
ClassNotFoundException
Presently, I am writing a Java application that uses the Singleton pattern and serialisation. I have a dedicated serialiser class that serialises and deserialises an object to and from a given file path. One of my objects is serialised and deserialised without issue: I make some changes to my application's state while it's open, then close the application, and those changes are still there when I reopen it. However, this does not work with another one of my objects, even though as far as I can tell there is no major difference between them that should cause this to be the case.
Here is the code for my Serialiser class:
public static Boolean serialise(Object target, String filePath){
try (FileOutputStream fileOut = new FileOutputStream(filePath);
ObjectOutputStream objectOut = new ObjectOutputStream(fileOut);){
objectOut.writeObject(target);
return true;
} catch (FileNotFoundException ex) {
Logger.getLogger(Serialiser.class.getName()).log(Level.SEVERE, null, ex);
return false;
} catch (IOException ex) {
Logger.getLogger(Serialiser.class.getName()).log(Level.SEVERE, null, ex);
return false;
}
}
public static Object deserialise(String filePath){
try (FileInputStream fileIn = new FileInputStream(filePath);
ObjectInputStream objectIn = new ObjectInputStream(fileIn);){
Object readObject = objectIn.readObject();
return readObject;
} catch (IOException | ClassNotFoundException ex) {
Logger.getLogger(Serialiser.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
}
And here is the relevant code for my objects. First, the one that works:
public class AccountManager implements Serializable {
private static final long serialVersionUID = 1L;
private static final String FILE_PATH = "data//account_manager.ser";
private static AccountManager instance;
private ArrayList<Account> accounts = null;
private AccountManager(){
accounts = new ArrayList<>();
}
public static AccountManager getInstance(){
if (instance == null){
instance = (AccountManager)Serialiser.deserialise(FILE_PATH);
if (instance == null){
instance = new AccountManager();
Serialiser.serialise(instance, FILE_PATH);
}
}
return instance;
}
And now the one that does not work:
public class MessageManager implements Serializable {
private static final long serialVersionUID = 1L;
private static final String FILE_PATH = "data//message_manager.ser";
private static MessageManager instance = null;
private ArrayList<Message> messages = null;
private MessageManager(){
messages = new ArrayList<>();
}
public static MessageManager getInstance(){
if (instance == null){
instance = (MessageManager)Serialiser.deserialise(FILE_PATH);
if (instance == null){
instance = new MessageManager();
Serialiser.serialise(instance, FILE_PATH);
}
}
return instance;
}
Essentially, both classes work the same way: they store an list of objects of a certain type and provide access to and perform operations on the contents of their respective lists. When the Singleton instance of the classes are accessed, it checks if it could deserialise an instance from a file. If it can't it instantiates a new one and serialises it. Again, this works for one, AccountManager, but not for the other, MessageManager. That is, if create new Account object and store it using the AccountManager while the application is running, it will still be there if I restart the application. The same is not true for MessageManager and Message objects.
When a new Account is created, the instance, and presumably its associated fields, i.e. the accounts list are serialised.
public Account createAccount(String password, String givenName, String surname, String address, Gender gender, LocalDate dateOfBirth){
String id = IDGenerator.getInstance().generateID(AccountType.PATIENT, accounts);
Account createdAccount = null;
if (id != null){
createdAccount = new PatientAccount(id, password, givenName, surname, address, gender, dateOfBirth);
if (createdAccount != null){
accounts.add(createdAccount);
Serialiser.serialise(instance, FILE_PATH);
}
return createdAccount;
}
return null;
}
In MessageManager, when a new Message instance is added its list, and the instance, and again, presumably its list, are serialised:
public Boolean sendMessage(Message message){
if (messages.add(message)){
Serialiser.serialise(instance, FILE_PATH);
return true;
}
return false;
}
Both Account and Message implement Serializable, and both have serialVersionUIDs. I do not get any NotSerializable exceptions.
Any help would in fixing this problem would be greatly appreciated. If necessary, I can provide more of my application's code.
I managed to solve my problem. My issue was in a different part of my application. Essentially, I had believed that MessageManager was not be serialised because I couldn't get the data I was expecting back from it. It turns out the issue was in the method that returned the data: I was using an object reference to identify what data to return. Of course, deserialisation creates new instances, so, after recreating my objects, I could not longer access data identified by an old instance.
To fix my issue, I used a different means of identifying data: a string property whose value would be the same across instances, before and after deserialisation.
Thank you to everyone who looked at my question and/or attempted to answer it.
I checked that the interface of
Serializable
Has no function definition, yet when I define
private void readObject(ObjectOutputStream oos){
System.out.println("readObject!!");
}
private void writeObject(ObjectOutputStream oos){
System.out.println("writeObject!!");
}
function in a class, they're called while object is being serialized.
This is odd to me, if the interface defines these 2 functions, then I should override them to make sure they're called.
But in Serializable, how does compiler generate code that if I define my own "writeObject"/"readObject", they're called while serialization?
I tried to append
#Override
annotation on top of both functions, compiler reports error.
So how it works at all, would you help to give some explanations?
Thanks a lot!
java.io.Serializable is a functional interface, so that means it doesn't define any methods in it. #Override annotation is put if only you really wanna make sure noone will try to modify your overridden method. The reason you got a compiler error on #Override is that there is no such method in Serializable, but instead you can find them in ObjectInputStream and ObjectOutputStream (which use as a low-level classes FileInputStream and FileOutputStream respectively).
If you really wanna do Serialization on let's say, a list, you can do it like this:
package Chaper8.IO;
import java.io.*;
import java.util.*;
public class Serialization_Deserialization {
public static void main(String [] args){
/*
* try-catch with resources, JVM makes sure to close the resources after you've finished using it
* much easier than using finally and getting an exception for each resource closed
*
*/
try(FileOutputStream out = new FileOutputStream("C:\\Users\\Andrei\\Desktop\\Exemple\\worker.txt");
ObjectOutputStream oos = new ObjectOutputStream(out);
FileInputStream in = new FileInputStream("C:\\Users\\Andrei\\Desktop\\Exemple\\worker.txt");
ObjectInputStream ois = new ObjectInputStream(in);){
//instances of the Worker class
Worker w1 = new Worker("Worker1", 123456 , 2000.5);
Worker w2 = new Worker("Worker2", 765436, 1500.15);
Worker w3 = new Worker("Worker3", 364582, 1700.45);
Worker w4 = new Worker("Worker4", 878234, 2100.34);
ArrayList<Worker> list = new ArrayList<>();
//just adding the persons in the list
list.add(w1);
list.add(w2);
list.add(w3);
list.add(w4);
System.out.println("Doing serialization");
oos.writeObject(list);
System.out.println("Doing deserialization");
ois.readObject();
}catch(IOException | ClassNotFoundException e){
e.printStackTrace();
}
}
}
Worker.java
/*
* Worker class, basic type with variables, constructor, and toString() overridden
* Here I have implemented Serializable for the reason that I need to make sure that
* I will serialize the object within the class
*
* Note that I used transient for id. transient is a special keyword which makes sure
* that id will not be serialized, used for security reasons.
*
* serialVersionUID is another variable which is used during deserialization
* to verify that the sender and receiver of a serialized object have loaded
* classes for that object that are compatible with respect to serialization.
* Throws InvalidClassException if the object has a different serialVersionUID
* than that of the corresponding sender's class.
*
*/
import java.io.*;
class Worker implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private transient int id;
private double wage;
public Worker(String name, int id, double wage){
this.name = name;
this.id = id;
this.wage = wage;
}
public String toString(){
return "Person with name " +
name + " and with id " +
id + " has a salary of " + wage + "$";
}
}
I'm kind of stuck with a problem. I do understand the concept of serialization. Nevertheless I'm getting errors when I try to serialize/deserialize (deepCopy) an object:
I have a basic domain objects that hold information (two files):
public class DomainObject implements java.io.Serializable {
private String defaultDescription = "";
private List<Translation> translations;
public DomainObject() {
;
}
public void setTranslations(final List<Translation> newTranslations) {
this.translations = newTranslations;
}
public List<Translation> getTranslations() {
return this.translations;
}
public void setDefaultDescription(final String newDescription) {
this.defaultDescription = newDescription;
}
public String getDefaultDescription() {
return this.defaultDescription;
}
}
public class Translations implements java.io.Serializable {
private String description = "";
public Translation() {
;
}
public void setDescription(final String newDescription) {
this.description = newDescription;
}
public String getDescription() {
return this.description;
}
}
I also have a frame so the user can fill in all the necessary information for this domain object. Since I have multiple domain objects (this example only shows one) with different fields I have different frames for each domain object. Each of these frames includes a "MultiLanguageFrame" which gives the user the ability to add optional translations for this domain object's description.
public class MultiLanguageFrame extends org.eclipse.swt.widgets.Composite {
private List<Translation> translations = new ArrayList<Translation>();
public MultiLanguageFrame(final Composite parent, final int style) {
super(parent, style);
...
}
public List<Translation> getTranslations() {
return translations;
}
}
I deepCopy objects via this method:
...
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
oos.flush();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception t) {
logger.error(deepCopy() error: " + t.getMessage()); //$NON-NLS-1$
throw new RuntimeException("deepCopy() error", t); //$NON-NLS-1$
}
So now to the error:
When i try to do something like this:
MultiLanguageFrame frame = new MultiLanguageFrame(parent, SWT.NONE);
DomainObject dom = new DomainObject();
dom.setDefaultDescription("Testobject");
dom.setTranslations(frame.getTranslations())
deepCopy(dom);
I receive an error telling me that MultiLanguageFrame is not Serializable. Why would Java try to serialize the frame when I only want that DomainObject?
I thought maybe it is because of the reference in frame. So when I add the Serializable-Interface to MultiLanguageFrame and markt the SWT-Components as transient it tells me that no valid constructor was found. I can't add a parameterless constructor because it would logically make no sense and also SWT-Components need a parent to exist.
I'm really stuck with this problem because I do not know how to work around this. Thanks for answers in advance!
I found the solution myself. I'll just post this so others can see it, it might help.
Thanks to #greg-449 who lead the way. I do have an inner class TranslationHelper which extends Translation in MultiLanguageFrame. The purpose of this is so I can save some flags (deleted, changed, new) for Translations without changing Translation itself. When I call frame.getTranslations() I cast the elements from TranslationsHelper to Translation. The instance of the object remains a TranslationHelper though.
Now it all makes sense that MultiLanguageFrame was involved in all of this.
I'm implementing a method that does something like:
...
try {
myPojo.setProperty("foo");
myService.execute(myPojo);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
...
If some exception is thrown by my service from this try block on pojo property will have the new value. Is there some way to start a kind of transaction for pojo changes and roll it back if something goes wrong?
Something like:
PojoTransaction pt = startPojoTransaction();
transactionedPojo = pt.handleByTransaction(myPojo);
try {
transactionedPojo.setProperty("foo");
myService.execute(transactionedPojo);
pt.commit;
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
Or something similar...
Take a look at the Memento Pattern, it includes a Java example.
http://en.wikipedia.org/wiki/Memento_pattern
I toyed around with the idea, this is far from perfect, just a simple proof of concept. There are pitfalls in this implementation:
It only tries to call a parameterless constructor of the given source
object to create the target-copy, would need some logic to select a correct constructor (or only support Cloneables?)
Only copies fields declared in the class, not from superclasses (this problem can be solved walking through the inheritance tree and copying any superclass fields)
If the fields are complex types, only the references are copied to target-object, so any changes to them will not be transactional, as both the source and target share the same instance (solvable by recursively creating copies of nested objects and copying their values, requires walking through the entire object-graph, starting from source, and then doing it vice-versa on commit-time)
But, improving from here, I believe it could become very usable. Here's the POC:
import java.lang.reflect.Field;
import org.junit.Assert;
import org.junit.Test;
public class PojoTransactionTest
{
public static class PojoTransaction<T>
{
/**
* This is the original (unmodified) object
*/
private T source;
/**
* This is the object modified by within the transaction
*/
private T target;
/**
* Creates a new transaction for the given source object
* #param source Source object to modify transactionally
*/
public PojoTransaction(T source)
{
try
{
this.source = source;
this.target = (T)source.getClass().newInstance(); //Note: this only supports parameterless constructors
copyState(source, target);
}
catch(Exception e)
{
throw new RuntimeException("Failed to create PojoTransaction", e);
}
}
/**
* Copies state (member fields) from object to another
* #param from Object to copy from
* #param to Object to copy to
* #throws IllegalAccessException
*/
private void copyState(T from, T to) throws IllegalAccessException
{
//Copy internal state to target, note that this will NOT copy fields from superclasses
for(Field f : from.getClass().getDeclaredFields())
{
f.setAccessible(true);
f.set(to, f.get(from));
}
}
/**
* Returns the transaction target object, this is the one you should modify during transaction
* #return Target object
*/
public T getTransactionTarget()
{
return target;
}
/**
* Copies the changes from target object back to original object
*/
public void commit()
{
try
{
copyState(target, source);
}
catch(Exception e)
{
throw new RuntimeException("Failed to change state of original object", e);
}
}
}
public static class TestData
{
private String strValue = "TEST";
private int intValue = 1;
private float floatValue = 3.1415f;
public String getStrValue()
{
return strValue;
}
public void setStrValue(String strValue)
{
this.strValue = strValue;
}
public int getIntValue()
{
return intValue;
}
public void setIntValue(int intValue)
{
this.intValue = intValue;
}
public float getFloatValue()
{
return floatValue;
}
public void setFloatValue(float floatValue)
{
this.floatValue = floatValue;
}
}
#Test
public void testTransaction()
{
//Create some test data
TestData orig = new TestData();
//Create transaction for the test data, get the "transaction target"-object from transaction
PojoTransaction<TestData> tx = new PojoTransaction<TestData>(orig);
TestData target = tx.getTransactionTarget();
target.setFloatValue(1.0f);
target.setIntValue(5);
target.setStrValue("Another string");
//Original object is still at the original values
Assert.assertEquals(1, orig.getIntValue());
Assert.assertEquals(3.1415f, orig.getFloatValue(), 0.001f);
Assert.assertEquals("TEST", orig.getStrValue());
//Commit transaction
tx.commit();
//The "orig"-object should now have the changes made to "transaction target"-object
Assert.assertEquals(5, orig.getIntValue());
Assert.assertEquals(1.0f, orig.getFloatValue(), 0.001f);
Assert.assertEquals("Another string", orig.getStrValue());
}
}
The question is a bit vague, but it sounds like you are wrestling with the basic design pattern for transaction management. You would benefit greatly from the experience that has gone into the production of the pattern used here:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html
Perhaps Spring Transaction management would suit you well for your project anyway.