I have to save and load a chess game. In Chess I have:
public class Chess
{
private Piece[][] pieceArray;
private Board board;
private int moves;
private boolean turn;
...
Set's and get's
}
I would have to load the turn, moves and the matrix. For now Im only saving and loading the matrix (Pieces[][])
Now I have these methods for saving and loading the game in another class
In this class I have a FTPClient connected to a server.
Saving the game:
public boolean saveGame(Chess chess) {
boolean error = false;
try {
File file = new File("game.save");
FileOutputStream fis = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fis);
oos.writeObject(chess.getArray());
oos.close();
// Save that file in the server
FileInputStream fis = new FileInputStream(new File("game.save"));
client.storeFile("game.save", fis);
fis.close();
file.delete();
} catch (IOException e) {
e.printStackTrace();
}
return error;
Saving the game gives me no problem and goes smoothly.
And now this is the method I use to load the game, which is the one throwing the invalidClassException.
try {
FileInputStream fis = new FileInputStream(new File("game.save"));
ObjectInputStream ois = new ObjectInputStream(fis);
chess.setArray((Piece[][]) ois.readObject());
chess.paintInBoard();
ois.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
Whenever I try to read the matriz I get "java.io.InvalidClassException: [LPiece;; invalid descriptor for field "
I have implemented the Serializable interface in Piece and Chess.
I have tried saving the whole Chess class but doing that I would have to implement the Serializable interface in other 8 classes as well and I'm trying to avoid that.
Do I have to read each Piece individually?
Thank you a lot.
It is hard to determine what the problem might be because the Piece interface nor its implementing classes are not provided, but here are my thoughts on this problem:
I personally would avoid saving an array or a matrix. I would instead save the pieces in a container class, for example: PieceCollection.
I can't see any specific problem with your provided code (unless chess.getArray() returns something else than pieceArray).
I believe that the main problem here is that the ObjectInputStream can't distinguish the various implementations of Piece. I would suggest that you try to add serialVersionUID to the implementing Piece classes. For more information, see the following link: https://docs.oracle.com/javase/7/docs/platform/serialization/spec/class.html
The Piece classes are missing a no-arg constructor. See the following link for more information: https://docs.oracle.com/javase/8/docs/api/index.html?java/io/InvalidClassException.html
Good luck! I hope that this answer will help you.
I tried saving the saves locally and it worked. The problem was that the server I was using corrupted the file every time I uploaded it, giving me that exception. Changing the server did the job.
Related
I want to load and render multiple objects to a gameworld using data from a class with a save/load functionality using (de)serialization, but i cant figure out how to properly reintroduce them to the program since they contain transient objects that are nessesary to render/display them.
The first time i run my program, it tries to load data from a txt file called 'Data.txt' that doesnt exist. The program creates a new Data.txt file, adds a Campfire object called 'campfire'to it and saves it. As long as the program runs it will be rendered to the screen (this part works fine). After closing the program and restarting it its supposed to check for the file, find it and load the ArrayList 'data' with the previously saved/serialized campfire object from it. Then it adds another Object of the same type to it and renders both of them. This part throws a NullPointerException in the render method of my Campfire class.
This is the Main class:
public class MainClass extends ApplicationAdapter{
public static SpriteBatch batch;
private Data data;
private Campfire campfire;
#Override
public void create () {
batch = new SpriteBatch();
data = new Data();
data.load();
campfire = new Campfire();
data.addCampfire(campfire);
System.out.println(data.getSize());
data.save();
}
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
data.render(batch);
batch.end();
}
The class that loads all the files:
public class Data {
private String filename;
private ArrayList<Campfire> data;
public Data() {
data = new ArrayList<Campfire>();
filename = "Data.txt";
}
public void save(){
try {
FileOutputStream file = new FileOutputStream (filename);
ObjectOutputStream out = new ObjectOutputStream (file);
out.writeObject(data);
out.close();
file.close();
}catch (IOException e) {e.printStackTrace();}
}
public void load() {
try {
FileInputStream file = new FileInputStream (filename);
ObjectInputStream in = new ObjectInputStream(file);
data = (ArrayList<Campfire>)in.readObject();
in.close();
file.close();
}
catch (IOException io) {System.out.println("File not found.");}
catch (ClassNotFoundException ex) {System.out.println("Cant load from file.");}
}
public void addCampfire(Campfire object) {
data.add(object);
}
public void render(SpriteBatch batch) {
for(int i = 0;i < data.size(); i++) {
Campfire camp = data.get(i);
camp.render(batch);
//data.get(i).render(batch);
}
}
public Campfire get(int index) {
return data.get(index);
}
public void set(int index, Campfire object) {
data.set(index, object);
}
public int getSize() {
return data.size();
}
And this is the class that throws the NullPointerException in render():
public class Campfire implements Serializable{
private transient Texture img;
private int id;
public Campfire() {
img = new Texture("campfire.png");
}
public void render(SpriteBatch batch) {
batch.draw(img, 200,200, 2000,2000);
}
The reason for this is that the Texture 'img' is nonexistent since i need to use the transient keyword with it and therefore it will not be saved/loaded to/from the data file.
i need a different way to create objects based on the array list i am loading from. I cant work with the objects in the arrayList because they cant be rendered (the transient keyword has to be assigned to the Texture() object - its not serializable). I have watched plenty of tutorials and read a good amount on articles about this topic, but i cant find one that mentions how i can reintroduce the object so i can make use of its functions that rely on not (de)serializable parts of it.
So heres my question:
Is there a better way than my attempt (creating the object again and then assigning the deserialized values to it? I have no clue how to properly load and work with the 'data' ArrayList after the deserialization process and i am thankful for every advice on how to reintroduce them to the program.
Thanks in advance,
M
I would recommend not using java serialization for this. It can have advantages in terms of the size of its serialized format, but in my experience it is a little bit of a pain to work with. You have encountered this pain, because you cannot serialize that Texture object because it is not Serializable. You avoided the issue by marking it as transient, which is a mistake. There are probably workarounds for working with objects that you do not own - registering some handler, but tbh I do not know the details because I avoid it.
Instead, go for a nice friendly format like json. This will mean that your serialized data file will be a bit bigger, but, you can open it up and read it and see what you have saved.
And... libgdx comes with some tools for it. See their wiki page on usage here
There are alternatives - like googles gson library as well, but why not just stick with libgdx's tools - I have found them to be pretty solid.
Just an extra warning - if you are using some sort of code obfuscation/minimization tool in Android, you will need to configure that to not rename any object that you serialize, because it does rely on the package and object names for deserialization. It is also something to be aware of if you save a game state - then change the names/packages of your objects and then try to load that game state.
I have an issue with java.io.NotSerializableException at java.io.ObjectOutputStream.writeObject0(Unknown Source). Here is the important part of the code.
public class PlayerConfigAccess implements Serializable{
private static final long serialVersionUID = 1L;
//some load and create methods
public static void saveFile (File file, Player player)
{
Object object = (Object) PlayerConfigContent.getContent(player);
if(!existFile(file))
{
createFile(file);
}
try{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(object);
oos.flush();
oos.close();
PlayerConfigContent.remove(player);
}catch(Exception e){
e.printStackTrace();
}
}
The object I am about to save is a PlayerConfig custom thing, that I convert back to Object.
The method gets the file that should be saved when its called, I made sure it exists, so there cant be the problem.
Does someone know who I can fix this issue. Thanks :)
The object that you're actually writing needs to be serializable, not the class that is actually holds the writing code. What object type does PlayerConfigContent.getContent return?
In the book "Sams Teach Yourself Java", the author often writes a bunch of code in a constructor and puts the main block outside of it and then calls the constructor inside the main block. Is there any advantage to doing this instead of writing the code inside the main block as some do?
Example:
import java.io.*;
import java.util.*;
class Configurator {
Configurator() {
try {
// load the properties file
File configFile = new File("program.properties");
FileInputStream inStream = new
FileInputStream(configFile);
Properties config = new Properties();
config.load(inStream);
// create a new property
Date current = new Date();
config.setProperty("runtime", current.toString());
// save the properties file
FileOutputStream outStream = new
FileOutputStream(configFile);
config.store(outStream, "Properties settings");
inStream.close();
config.list(System.out);
} catch (IOException ioe) {
System.out.println("IO error " + ioe.getMessage());
}
}
public static void main(String[] arguments) {
Configurator con = new Configurator();
}
}
As far as the quality of the constructor goes, there are other answers/comments which have explained why this is a bad idea. Regarding specifically placing all of your code into a constructor rather than directly in main, it has benefit when it comes to automated testing and portability.
Moving all code into a constructor allows that code to be tested by automated testing frameworks much more easily than testing main. In addition by placing your entire app inside a class without a main you allow it to be plucked and used in other applications.
That book is quite notorious for being quite bad. But there is a heck of a lot fo code in that constructor, constructors are inteded to 'construct' an object, such as setting values of the object etc. Not perform so many tasks, create methods in the class that the constructor can call on instead.
For example, the main is a static method and it wants only static variables, so it's not good write a great amount of code in.
just started using the Serializable-thingy, I want to save a couple of lists and some other objects, but I can't seem to get some things right.
I have some JLists with their own "DefaultListModel" that I use to sort things in the list, I call the class SortModel, when I run the program I do this:
SortModel sortModel = new SortModel();
JList jList = new JList(sortModel);
Then later when the program runs, objects are added and sorted according to the specified needs, now, when I save the lists and load them again, they're empty.
I also save a object of a class that holds the background for the program (user chooses one themself), after saving it I need to add it again to the program (the object, background is stored in it), I need to add it to the program again not only load it "as it where", plus I have some objects that I added on that object with their own listeners. After I somehow succeeded in loading it, the objects are there but I can't use them, so I figure listeners don't get saved?
* explaining edit
The class that is the program extends JFrame, nothing funny about that I think.
The "background obect" (call it map) extends JComponent, I add this to (let's call it program for now...) the program and it pops up with the image which it holds. Onto this map I then add objects that also extends JComponent (call them dots), the dots are assigned their own listeners before they're added, the listeners might not be "real" listeners, but they act the same way, they're "MouseAdapter" does that makes any difference?.
/explaining edit *
* code edit *
code for saving:
FileOutputStream fOut = new FileOutputStream("testFile.mpd");
ObjectOutputStream outSt = new ObjectOutputStream(fOut);
outSt.writeObject(savedMap);
"testFile.mpd" is what it sounds like, I'm quite sure the .mpd shouldn't matter, you can make up your own formats, right? :) (main-class is called Mappedoodle, .mpd sounds reasonable, no?)
"savedMap" is an object of said Mappedoodle and holds all lists and other information needed to be saved.
code for loading:
FileInputStream fIn = new FileInputStream("testFile.mpd");
ObjectInputStream inSt = new ObjectInputStream(fIn);
Mappedoodle openedMap = (Mappedoodle)inSt.readObject();
The information in openedMap is used (well... it should be...) to overwrite certain things in the program.
* /code edit *
Adding everything back onto this object, even adding everything back into the lists wouldn't be so hard since that's just some more lists and a few loops, but I feel like I just don't really get Serializable ^^ so, someone care to try to explain why not everything gets saved? And if it is, why I can't access it? But if I can, how? :)
I don't know what more code should be relevant, please tell me what more information you would need to help me solve this, pasting the whole program would be really messy since it's 11 classes and quite a few lines.
Thanks <3
The code you must show us must be sufficient to demonstrate your error, and I unfortunately must state that yours doesn't. For instance if I use your code in a very simple example (something I recommend that you do), you'll see that it works. My test code:
Mappedoodle.java
import java.io.Serializable;
public class Mappedoodle implements Serializable {
private static final long serialVersionUID = -1760231235147491826L;
private String text;
private int value;
public Mappedoodle(String text, int value) {
this.text = text;
this.value = value;
}
public String getText() {
return text;
}
public int getValue() {
return value;
}
#Override
public String toString() {
return text + ", " + value;
}
}
MappedoodleSerialize.java
import java.io.*;
public class MappedoodleSerialize {
public static void main(String[] args) {
Mappedoodle savedMap = new Mappedoodle("Fubar", 200);
FileOutputStream fOut;
try {
// your code below
fOut = new FileOutputStream("testFile.mpd");
ObjectOutputStream outSt = new ObjectOutputStream(fOut);
outSt.writeObject(savedMap);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
MappedoodleDeSerialize.java
import java.io.*;
public class MappedoodleDeSerialize {
public static void main(String[] args) {
try {
// your code below
FileInputStream fIn = new FileInputStream("testFile.mpd");
ObjectInputStream inSt = new ObjectInputStream(fIn);
Mappedoodle openedMap = (Mappedoodle) inSt.readObject();
System.out.println(openedMap);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
}
}
}
This bit of code compiles, runs and outputs as expected. Your error must lie in code that you've not shown us.
This problem was solved 5 years ago, the solution is lost due to bad memory though.
I did some reading on the API for File IO and read the following blog post: http://techknock.blogspot.com/2008/05/file-hadling-in-android.html. His code works fine when everything is in the same activity. But what I am trying to do is create an IOInterface class that I can use to open multiple databases to populate multiple lists.
ListA.java
public class ListA
{
public List<ClassA> list;
private final String DBA = "dbA";
private IOInterface database;
public List()
{
list = new ArrayList<ClassA>();
database = new IOInterface();
}
...
public void initListA() throws IOException
{
database.openForWriting(DBA);
String myStr = new String("content");
database.dos.writeBytes(myStr);
database.dos.flush();
database.dos.close();
}
}
IOInterface.java
public class IOInterface
{
public DataOutputStream dos;
private FileOutputStream fos;
public void openForWriting(String database)
{
try {
fos = openFileOutput(database, Content.MODE_PRIVATE);
dos = new DataOutputStream(fos);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
Eclipse underlines fos = openFileOutput(database, Content.MODE_PRIVATE);. With the comment that openFileOutput() does not exist. The resolution to this is to extend the IOInteface class as an Activity. I suppose then openFileOutput() is a method of an activity class.
So my question is how do accomplish what I am trying to do? Standard Java file io such as:
File fp = new File("database");
FileOutputStream fos = new FileOutputStream(fp);
does not work. It catches a FileNotFoundException. This has to be doable though. Any ideas?
Thanks,
Method openFileOutput() is contained in the Context class, so you can pass an instance of this class to your method that opens files. And you should always use methods from Context when you want to work with files.
You can read about using the internal and external storages in the development guide.