So I have this class.
public class Player implements Serializable {
private static final long serialVersionUID = 1L;
public int health;
public String name;
}
I load the Player when they "connect", and I save the Player when they "Disconnect".
I save and load using the Object Input/Output stream.
Later on, I want to add a gold variable to the Player.
So the code will be:
public class Player implements Serializable {
private static final long serialVersionUID = 1L;
public int health;
public String name;
public int gold;
}
I still want the file to contain the health and name, but I want to update it with the gold variable; how would I go about doing that?
I know I can check if the gold is null, and if so append it to the next save. But is there a way where I can add as many variables as I will, and the code will automatically save the new variables in the class?
Is there a way for the code to automatically add new variables to the file?
Load Player:
try {
final FileInputStream fileInputStream = new FileInputStream(file);
final ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
try {
user = (User) objectInputStream.readObject();
} catch (ClassNotFoundException exception) {
exception.printStackTrace();
return false;
}
objectInputStream.close();
fileInputStream.close();
} catch (IOException exception) {
exception.printStackTrace();
return false;
}
Save Player:
try {
final FileOutputStream fileOutputStream = new FileOutputStream(file);
final ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(user);
objectOutputStream .close();
fileOutputStream.close();
} catch (IOException exception) {
exception.printStackTrace();
return false;
}
Java is strictly typed. Adding a field at runtime, though possible would take some of the dark arts (modify classes at runtime).
It seems to me you are better off basing your player on HashMap as internal data structure. It can be serialized and deserialized the way you need and allows you to add more values at runtime.
Java's built-in object serialization is not very flexible when you need to support different versions. You should change the serialVersionUID of your updated class. But then you won't be able to deserialize older versions. Some possible solutions are:
Define your own readObject and writeObject methods (see Javadoc on ObjectInputStream for details). Then use these to serialize/deserialize a Map of variable names to variable values, using this map as a transfer object to/from your actual class.
Use a different serialization mechanism, such as JSON. Libraries like Jackson are quite flexible in their ability to specify default values for missing fields.
If the contents of your Player class changes only occasionally, you could try the following:
Create a new class containing the new fields, calling it e.g. PlayerV2.
Create a separate program that goes through each of your saved player files. For each file you load the old player object; convert it to a new player object, then save the new player object.
Start using the new player class in your game.
If the player objects are expected to vary a lot over time, and maybe different players will have different contents in their objects, then go with a Map as suggested by the OP.
In this case, you should probably have a version entry in the map. This way you can detect when a player has been saved by an older version of your game, and provide some form of automatic upgrade of the contents of the map, if needed.
Alternatively, you should consider saving to a text-based format like JSON. This way, your savefiles are not as directly tied to a Java class as is the case when using Java serialization. Also, it will be easier to debug, and easier to make tools that handle the savefiles directly.
Related
In my java code I want to append massage objects to a file AT THE MOMENT THEY ARE BEING CREATED. When I try to append a new object it overwrites previously added data in the file. Can someone explain a method to append objects (not string type objects) one by one to a existing file without getting overwritten?
Constructor of the object:
public class savedEmail{
String email;
String subject;
String date;
public savedEmail(String email,String subject,String date){
this.email = email;
this.subject = subject;
this.date = date;
}
}
//The way which I tried to append the object to the file:
class sentEmail{
public static void save(saveEmail O) throws IOException{ObjectOutputStream op = new ObjectOutputStream(new
FileOutputStream("saved.bin",true));
op.write(0);
}
}
The way that I figured out to solve this was, putting all the objects into an ArrayList and writing that ArrayList into a file by object serialization. If you want to add a new object to it, take the saved ArrayList back by deseriazing it, then append the new object and put the ArrayList back to the file.
When you create a new ObjectOutputStream, an initial sequence of bytes, or header, is written. The Java Serialization Specification describes it:
stream:
magic version contents
So, creating a new ObjectOutputStream each time you want to append is not an option. You will need to open a single ObjectOuptutStream, and keep a reference to it.
By the way, op.write(0) (writing a zero) is not the same as op.write(O) (writing the object whose reference is in the variable whose name is the capital letter O). This is one reason O is a very poor choice for a variable name. Consider naming the method argument email instead.
I have an arrayList set up to hold player data. Each player contains several elements, namely jersey number, first name, last name, preferred position, goals, and assists. I want to be able to save this data so that when I restart the program the data is still in the exact same format. From some stuff that I have read it looks like serializing is the way to go? Is this correct or is there a better way? I am new to Java so any examples would be great. I have attached several applicable snippets of my code.
//creation of arrayList
public class LeagueDatabase extends javax.swing.JFrame {
ArrayList <blackTeam> blackTeam = new ArrayList <blackTeam>();
//how class is structured
class blackTeam {
int goals, assists;
String jerseyNum, firstName, lastName, prefPosition;
blackTeam (String _jerseyNum, String _firstName, String _lastName, String _prefPosition, int _goals, int _assists) {
jerseyNum = _jerseyNum;
firstName = _firstName;
lastName = _lastName;
prefPosition = _prefPosition;
goals = _goals;
assists = _assists;
}
}
//how data is added
black = new blackTeam(jerseyNum, firstName, lastName, prefPosition, goals, assists);
blackTeam.add(black);
//what I have so far for saving the data
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
try{
FileOutputStream fos= new FileOutputStream("blackTeam");
ObjectOutputStream oos= new ObjectOutputStream(fos);
oos.writeObject(blackTeam);
oos.close();
fos.close();
}catch(IOException ioe){
ioe.printStackTrace();
}
//close program
System.exit(0);
}
});
If someone could explain how to save the data to a file, and then re-load the data from that file when the program is started, that would be great.
To serialize list all objects that list holds have to be serializable.
So your class blackTeam has to implement Serializable interface.
class blackTeam implements Serializable { ... }
Yes it seems that serialization is the way to go...
For Objects with nested Objects it could be an idea to store the object-values in a java-Database (examples HSQLDB amd H2). These databases are included in the project as jar and as far as I remember store the values in files. The table model then could correspond directly to the Object hierarchy... would make it easier to store and retrieve objects, but may be an overhead for simple objects.
For bigger and nested objects i would suggest to evaluate this alternative as I believe to remember (don't hold me up on this) that serialization has it's limits.
Here two links on googling "java store object as file"
link1
link2
Serialization is the path you will need to go down to save your Objects to a file. This here is a good article on how to do so, along with it's examples.
Also, your naming conventions for Class names are incorrect. It should be UpperCamelCased instead of camelCased. However, your variables check out.
You have to decide which way you want to Serialize your data. You can use it with the Java built in Serialization, in which you have to implement the Serializable interface with your blackTeam class.
public class blackTeam implements Serializable {
//...
}
Now you already have a method to save the file, you just need a method to read the file into an object.
public List<blackTeam> loadTeam() throws IOException, ClassNotFoundException
{
FileInputStream fileIn = null;
ObjectInputStream in = null;
List<blackTeam> teams = null;
try
{
fileIn = new FileInputStream( "blackTeam" );
in = new ObjectInputStream( fileIn );
teams = (List<blackTeam>)in.readObject();
}
finally
{
if( fileIn != null )
fileIn.close();
if( in != null )
in.close();
}
return teams;
}
Note, there are other ways to serialize Objects. You could use the JSON notation and save your object in a JSON format to be more transferrable. There are external libraries you can use to do JSON formatting such as Jackson
I have a class which is used to get transfer data from the one application to another and then also to update if changes were made.
public class Data {
private String name;
private String number;
private String info;
... getters/setters...
}
Let's say name and number will be updated if you change them but e.g. info is not. What's the best way to tell programmers in the future that this is intended so they can recognize it immediately?
Update:
It's encoded as a JSON file and when I get it back I don't care about the info field anymore. It could be empty
You can create your custom annotation, specific to your application. If you are using any framework like Hibernate you can use #transient.
Probably not the correct way, but if you are just talking about "informing" other programmers, you could simply put the transient keyword on your info field.
But of course, that would be really "informal"; as it would probably not at all affect how your framework is dealing with your fields.
I would use serialisation combined with the transient keyword
What is object serialization?
import java.io.*;
import java.util.*;
// This class implements "Serializable" to let the system know
// it's ok to do it. You as programmer are aware of that.
public class SerializationSample implements Serializable {
// These attributes conform the "value" of the object.
// These two will be serialized;
private String aString = "The value of that string";
private int someInteger = 0;
// But this won't since it is marked as transient.
private transient List<File> unInterestingLongLongList;
There's no indication in your file that name or number are being persisted.
If you are going to put behavior into the file in some durable way, this isn't just a file, it's a representation of an object, where data and the related behavior live as one. Write a method clarifying the intent.
public boolean isStorable() {
boolean isOk = true;
isOk &= (name != null && name.length() > 0);
isOk &= (number > 0);
return isOk;
}
Makes it clear that not every one of these items contribute to being able to store the object, and that not every value within these items contribute to a valid storage state.
It also makes it clear that this object permits invalid states within its private data. That's a code smell that could indicate a design flaw. Perhaps you should look into whether that is a design flaw, and if it is, then fix it.
Start here https://docs.oracle.com/javaee/6/tutorial/doc/bnbpz.html
Almost any programmer seing a POJO like this will know that behaviour is what you explained....
#Table(name = "data")
public class Data {
#Id
#Column(name = "name")
private String name;
#Column(name = "number")
private String number;
private String info;
... getters/setters...
}
UPDATE: It's encoded as a JSON file and when I get it back I don't care about the info field anymore. It could be empty
Ok so I've been reading up on good practices for using constants and enums but I still am confused as to what should I do.
Basically I need to store strings with paths to assets like
"/assets/sprites/ball.png"
and then i need simple name strings like "ball" and I want them to be related to each other.
Those strings will be used very often and in different files/packages all across my game's code.
I have a TextureAtlas class that given the name ("ball") already provides me with the actual resource (graphics ready to draw etc). But when I need to load the resource i need to give actual path, not just name. I want to have 1 place where I will have all the constants declared and it would be nice if both the "name" constant and "path" constant were related somehow. So that I don't have to put actual path to the file whenever I want something to use that graphic, instead I want to use constant with the name String.
I hope you can understand what I mean.
Well, I think I could use a map, where key would be the name ("ball") and value would be path ("/assets/ball.png") but I still want to be able to just lets say, write something like this
Assets.GetSprite(Assets.BALL);
and get an actual resource, or even better
Assets.GetSprite(BALL);
So there is this problem of constants, that I need to define name and corresponding path for my graphics in 1 place and be able to use it from anywhere in the program if I need it.
It all needs to be in one place so that whenever I add new resource, like new sound or new sprite to the game I won't have to look all over my program to find the place where it should be manually typed, I want to have that 1 file that I will go over and just add new constant or something like that for that 1 file.
Cheers,
Pablo.
It might be a good idea to use properties file.
Java Properties
config.properties
ball = /assets/sprites/ball.png
user = /assets/sprites/user.png
To read these values, at the game startup, do following
FileHandle propertiesFileHandle = Gdx.files
.internal(PROPERTIES_FILE_PATH);
PROPERTIES = new Properties();
try {
PROPERTIES.load(new BufferedInputStream(propertiesFileHandle.read()));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Now you can share this PROPERTIES object wherever you need.
String ballPath = PROPERTIES.getProperty("ball");
Hope this helps.
What is the problem with the Map? Create a static HashMap and store you mapping there. You can directly get your values from there.
OR
if you simply need mappings where you key and values are constants then you can put them in an interface (Variables are by default public static and final).
I would go for something like this:
public enum Assets {
BALL, PLAYER // and so on
}
public class Configuration {
Map<Assets, String> values= new HashMap<Assets, String>();
public String getPathFor(Assets asset){
if(!values.containsKey(asset)) throw new IllegalArgumentException();
return values.get(asset);
}
/* Contains further logic to persist configuration */
}
So you have well defined Assets and a Configuration object, where you store further Information.
You can create more complicated enums with parameters. Take a look at the Planet enum in this example http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html or here is an example specific to your project. Remember that are required to create the constructor and the constructor must be private.
public class Asset {
public enum AssetType {
BALL("/assets/sprites/ball.png"),
USER("/assets/sprites/user.png");
private final String path;
AssetsType(String path) {
this.path = path;
}
public String getPath() {
return this.path;
}
}
private AssetType assetType;
public Asset(AssetType assetType) {
this.assetType = assetType;
}
// ...
}
I have an existing internal data model for a Picture, as follows:
package test.model;
public class Picture {
private int height, width;
private Format format;
public enum Format {
JPEG, BMP, GIF
}
// Constructor, getters and setters, hashCode, equals, toString etc.
}
I now want to serialize it using protocol buffers. I've written a Picture.proto file that mirrors the fields of the Picture class and compiled the code under the test.model.protobuf package with a classname of PictureProtoBuf:
package test.model.protobuf;
option java_package = "test.model.protobuf";
option java_outer_classname = "PictureProtoBuf";
message Picture {
enum Format {
JPEG = 1;
BMP = 2;
GIF = 3;
}
required uint32 width = 1;
required uint32 height = 2;
required Format format = 3;
}
Now I am now assuming that if I have a Picture that I want to serialize and send somewhere I have to create a PictureProtoBuf object and map all the fields across, like so:
Picture p = new Picture(100, 200, Picture.JPEG);
PictureProtoBuf.Picture.Builder output = PictureProtoBuf.Picture.newBuilder();
output.setHeight(p.getHeight());
output.setWidth(p.getWidth());
I'm coming unstuck when I have an enumeration in my data model. The ugly way that I'm using right now is:
output.setFormat(PictureProtoBuf.Picture.Format.valueOf(p.getFormat().name());
However, this is prone to breakage and relies on the enumeration name being consistent between my internal data model and the protocol buffer data model (which isn't a great assumption as enumeration names within .proto files need to be unique). I can see me having to hand-craft switch statements on enumerations if the .name() call from the internal model doesn't match the protobuf-generated enumeration name.
I guess my question is whether I'm going about this the right way? Am I supposed to scrap my internal data model (test.model.Picture) in favour of the protobuf-generated one (test.model.protobuf.PictureProtoBuf)? If so, how can I implement some of the niceties that I have done in my internal data model (e.g. hashCode(), equals(Object), toString(), etc.)?
Although the existing answers are good, I decided to go a bit further with Marc Gravell's suggestion to look into protostuff.
You can use the protostuff runtime module along with the dynamic ObjectSchema to create schemas at runtime for your internal data model
My code now reduces to:
// Do this once
private static Schema<Picture> schema = RuntimeSchema.getSchema(Picture.class);
private static final LinkedBuffer buffer = LinkedBuffer.allocate(DEFAULT_BUFFER_SIZE);
// For each Picture you want to serialize...
Picture p = new Picture(100, 200, Picture.JPEG);
byte[] result = ProtobufIOUtil.toByteArray(p, schema, buffer);
buffer.clear();
return result;
This is a great improvement over the Google protobuf library (see my question) when you have lots and lots of attributes in your internal data model. There is also no speed penalty that I can detect (with my use cases, anyway!)
If you have control over your internal data model, you could modify test.model.Picture so that the enum values know their corresponding protobuf equivalent, probably passing in the correspondence to your enum constructors.
For example, using Guava's BiMap (bidirectional map with unique values), we get something like
enum ProtoEnum { // we don't control this
ENUM1, ENUM2, ENUM3;
}
enum MyEnum {
ONE(ProtoEnum.ENUM1), TWO(ProtoEnum.ENUM2), THREE(ProtoEnum.ENUM3);
static final ImmutableBiMap<MyEnum, ProtoEnum> CORRESPONDENCE;
static {
ImmutableBiMap.Builder<ProtoEnum, MyEnum> builder = ImmutableBiMap.builder();
for (MyEnum x : MyEnum.values()) {
builder.put(x.corresponding, x);
}
CORRESPONDENCE = builder.build();
}
private final ProtoEnum corresponding;
private MyEnum(ProtoEnum corresponding) {
this.corresponding = corresponding;
}
}
and then if we want to look up the MyEnum corresponding to a ProtoEnum, we just do MyEnum.CORRESPONDENCE.get(protoEnum), and to go the other way, we just do MyEnum.CORRESPONDENCE.inverse().get(myEnum) or myEnum.getCorresponding().
One way is to only keep the generated enum:
package test.model;
public class Picture {
private int height, width;
private PictureProtoBuf.Picture.Format format;
// Constructor, getters and setters, hashCode, equals, toString etc.
}
I've used this a few times, it may or may not make sense in your case. Using the protobuf generated classes as you data model (or extending them to add functionality), is never recommended, though.