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
Related
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.
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 am implementing a log management system and want the types of logs to be extendible. We get a base object parsed from JSON (from Filebeat) such as:
class LogLine {
String message
Date timestamp
String type
String source
}
Given this LogLine object, I want to be able to create different objects, which will also extend this LogLine.
class SomeLog extends LogLine {
int myfield
String username
}
class SomeOtherLog extends LogLine {
Date startTime
Date endTime
String username
String transactionID
}
So, in my current non-ideal implementation:
void parse(String s){
LogLine logLine = .....parseFromString(s)
if ( logline.type.equals('def') ){
SomeLog someLog = new SomeLog.Builder.build(logLine)
} else if ( logline.message.containts('abc') ){
SomeOtherLog someotherLog = new SomeOtherLog.Builder.build(logline)
}
}
However, as you can imagine the builders in subclasses copies the superclass LogLine object, is there anyway I can do that without copying the values as they are already subclasses? Is there a design pattern to achieve this? I would not like to rely on reflection like BeanUtils.copyProperperties
When you create a new object based on another it's a good idea to make a copy of all field. It's a best practice called defensive copying.
Since you parse a string, a defensive copy doesn't needed. Also I suppose you'll want to parse some specific fields from input string like myfield for SomeLog and startDate for SomeOtherLog. You could re-factor object creation like
LogLine result = null;
if (s.contains('type=def') {
result = SomeLog.parse(s);
} else if (trickyRegexp.mathces(s)) {
result = SomeOtherLog.parse(s);
} else {
result = LogLine.parse(s);
}
If you have many subclasses of LogLine then probably you'll want to move creation logic to a LogFactory which manages all the stuff regarding parsing string to specific object.
Introduce a factory interface for creating LogLine objects.
public interface LogLineFactory {
public LogLine createLog(LogLine logLine);
}
and use a Map for the lookup.
private Map<String, LogLineFactory > logLineFactories = new HashMap<>();
{
logLineFactories .put("def", new SomeLogFactory());
logLineFactories .put("abc", new SomeOtherLogFactory());
}
You can then ommit the if else branches using the map looup.
LogLine logLine = parseFromString(s);
LogFactory logFactory = logLineFactories.get(logLine.type);
if(logFactory != null) {
LogLine wrappedLogLine = logFactory.createLog(logLine);
}
Maybe you will need more information to create the LogLines and you have to change the interface.
public interface LogLineFactory {
public LogLine createLog(LogLine logLine, String s);
}
PS: with Java 8 you might want to use method references.
logLineFactories.put("def", SomeLog::new);
logLineFactories.put("abc", SomeOtherLog::new);
I don't really understand the logic behind this question and as a result, I can't really accurately search whether something like this already exists.
Let's say I create a Java class for an object. It only consists of instance variables, getters/setters, and a constructor to create an empty object. For example:
public class Bicycle {
private int speed;
private String model;
public Bicycle(int speed, String model) {
this.speed = speed;
this.model = model;
}
public int getSpeed() {
return speed;
}
public void setSpeed() {
this.speed = speed;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
And now let's say I had a text file of bicycles like:
Bike 1234, 60
Bike 333, 50
Bike 555, 20
How would I read the file and then assign each part of the text file to the variables in Bicycle.java?
I want to be able to use the getters and setters in other methods. For example:
public static String searchBikes(int speed, String model) {
if (Bicycle bike.getSpeed().equals(speed) && etc. etc.
I'm unsure as to how to assign each bicycle in the text file to the variables in Bicycle class. Do I need another class (like a database) of sorts? I'm just very lost on this.
Scanner fileScan = new Scanner(new File("yourfile.txt"));
List<Bicycle> bikes = new ArrayList<>();
while(fileScan.hasNextLine()){
String[] line = fileScan.nextLine().split(",");
Bicycle bike = new Bicycle(line[1],line[0]);
bikes.add(bike);
}
Bikes in your file are now in bikes list.
The best approach now a days is java serialization, you can define a human-readable data structure for the bikes and try to convert it to a POJO (plain old java objects), one of the options is using Json format, it es easy t read and there are many APIs for converting strings (son objects to POJO) Gson, Jackson etc etc
a json representation of your file can b:
{
"speed": 33,
"model": 50
}
and with this infomation you can get instances of a class Bicycles, where the attributes model and speed are available as in a "Normal" java object.
take a look at this and this tutorials...
You should read more on "classes and objects" to fully understand what is a CLASS and what is an instance of a class - object.
Oracle's web site can be a good start https://docs.oracle.com/javase/tutorial/java/concepts/
Concerning your concrete problem, well since we talk about object oriented programming, why not just copy the real world model ? Where are bicycles made - in a factory, using a specification of some sort, let's call it a blueprint.
You can create a class called BycicleFactory with a method called "createBycicles(File blueprints)" that returns an ArrayList for example. Here is some more info about the design pattern (best practice) called Factory Design Pattern http://www.tutorialspoint.com/design_pattern/factory_pattern.htm
Inside your factory, you will have to read the file (the blueprint) and create a new Bycicle object per line. I suggest you youse a Scanner, since it is the easiest. http://www.java2s.com/Code/JavaAPI/java.util/newScannerFileReaderfile.htm
I have some code for reading and writing objects in Java (using serialization) as well as some objects.
I would like to convert them to text, so someone else, from another platform can use these objects with my code (which is just provided as a package).
I could write a whole parser for text format for the objects. However, there is a deadline coming, and I was hoping it might be much easier to do it some other way, so that person can explore the objects and my Java code himself.
So I guess, my question is: what is the easiest way to migrate from Java serialization to writing objects in convenient ascii form? (though I suspect the answer is: write a parser yourself! :-))
I suggest you to use some standard format, such as YAML or aforementioned JSON or XML. If your objects form a hierarchical structure without circular dependencies, I'd choose JSON and use Jackson JSON processor - is actively developed and easy to use.
If your objects have circular dependencies, I'd choose YAML, because it can handle then using references, so it'll work even if you have complex object structures. SnakeYAML seems to be a good choice.
I was keen to test another library yamlbeans to see how it handles circular dependencies, so I made a small example for myself. Here it is:
// YamlEx.java
import java.io.*;
import java.util.*;
import com.esotericsoftware.yamlbeans.*;
public class YamlEx {
public static void main(String argv[])
throws Exception
{
Person p1 = new Person("John");
Person p2 = new Person("Paul");
Person p3 = new Person("Bob");
// create a circular dependencies, even to self (p1-p1)
p1.setFriends2(p2, p1, p3);
p2.setFriends2(p1);
p3.setFriends2(p1, p2);
// serialize
CharArrayWriter w = new CharArrayWriter();
YamlWriter writer = new YamlWriter(w);
writer.write(p1);
writer.close();
// print it to the console
System.out.println(w.toString());
// read it again
Reader r = new CharArrayReader(w.toCharArray());
YamlReader reader = new YamlReader(r);
p1 = reader.read(Person.class);
reader.close();
System.out.println(String.format("%s's friends: %s",
p1.name, p1.getFriends() ));
}
}
// Person.java
import java.util.*;
public class Person {
// A public field as a test.
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String toString() {
return name;
}
// A set/get pair as a test.
private List<Person> friends0;
public List<Person> getFriends() {
return friends0;
}
public void setFriends(List<Person> p) {
this.friends0 = p;
}
public void setFriends2(Person... p) {
setFriends(Arrays.asList(p));
}
}
Works as expected:
&1 !Person
name: John
friends: !java.util.Arrays$ArrayList
- &2 !Person
name: Paul
friends: !java.util.Arrays$ArrayList
- *1
- *1
- !Person
name: Bob
friends: !java.util.Arrays$ArrayList
- *1
- *2
John's friends: [Paul, John, Bob]
Using XML seems to be way to go here...
For a certain situation in our project we have used deserialization to XML via Xstream. You can also give it a shot... it's easy.
I'd suggest that your serialized object should be packed into Base64 string, then sent to another platform, decrypted from Base64 and casted to new object, just as described here: How to serialize an object into a string
I've seen many implementations of given problem and Base64 seems to be the easiest way.
I'd go for JSON, much easier and more human readable.
There are lot of JSON libraries for almost all languages. Especially for java i always prefer these two.
GSON
JACKSON
Where JACKSON has some performance advantages.
There is simple guide too.