In our term project, we are responsible for making the desktop version of a board game. My role is to implement save and load methods. For this reason, I am working on serialization. I am trying to serialize objects that contain other objects. To visualize, I prepared some example classes.
import java.io.Serializable;
public class Player implements Serializable
{
static int id;
static String name;
public Player(String name)
{
this.name = name;
}
}
import java.io.Serializable;
public class Team implements Serializable
{
String name;
transient Player c;
int[] numbers;
public Team(String name, String capt){
this.name = name;
this.c = new Player(capt);
}
public Player getC()
{
return c;
}
public String getName()
{
return name;
}
}
public class test implements Serializable
{
public static void main(String args[])throws IOException
{
Team t1 = new Team("Juventus", "Ronaldo");
Team t2 = new Team("Barcelona", "Messi");
File file = createSave("1");
save(file,t1,t2);
Team[] teams = load(file);
System.out.println("Team 1 is: " + teams[0].getName());
System.out.println("Team 2 is: " + teams[1].getName());
System.out.println("Captain of team 1 is: " + teams[0].getC().name);
System.out.println("Captain of team 2 is: " + teams[1].getC().name);
}
private static File createSave(String gameId) throws IOException
{
File file = new File(gameId + ".txt");
if (file.createNewFile())
{
System.out.println("File is created!");
}
else
{
System.out.println("File already exists.");
}
return file;
}
private static void save(File file, Team t1, Team t2)throws IOException
{
FileOutputStream f = new FileOutputStream(file);
ObjectOutputStream o = new ObjectOutputStream(f);
o.writeObject(t1);
o.writeObject(t2);
o.close();
f.close();
}
private static Team[] load(File file)throws IOException
{
try {
Team[] teams = new Team[2];
FileInputStream fi = new FileInputStream(file);
ObjectInputStream oi = new ObjectInputStream(fi);
Team team1 = (Team) oi.readObject();
Team team2 = (Team) oi.readObject();
teams[0] = team1;
teams[1] = team2;
oi.close();
fi.close();
return teams;
} catch (FileNotFoundException e) {
System.out.println("File not found");
} catch (IOException e) {
System.out.println("Error initializing stream");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
To summarize the code part: I create a player class and a team class that contains player objects. In team class, there is a method that returns the captain of the team. I create two team instance, first one is Juventus and the captain is Ronaldo. The second one is Barcelona and the captain is Messi. Then I stored these two team objects to a txt file by using ObjectOutputStream. And in the load method, I loaded the two teams by using ObjectInputStream and return them as an array.
When I tried to reach the name variables of two team object I could. However, I could not reach the captains of the teams. Only last created player object is available. Let me share my output:
File already exists.
Team 1 is: Juventus
Team 2 is: Barcelona
Captain of team 1 is: Messi
Captain of team 2 is: Messi
As seen in the output I can only reach lastly created inner object. The Player Ronaldo is lost now.
I would be very happy if you can help me.
Well first of all you need practice and learn more about foundamentals of java.
The mistake what you did is in the class Player. You set name as a static field. All of you Players can have only 1 name.
Try replace you class like this:
import java.io.Serializable;
public class Player implements Serializable {
private int id;
private String name;
public Player(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Next in Team class just remove transient
Player c;
And in the main class use
System.out.println("Captain of team 1 is: " + teams[0].getC().getName());
System.out.println("Captain of team 2 is: " + teams[1].getC().getName());
Now your code should work :) It's not perfect but this should resolve your problem
I would always recommend GSON for serializing and deserializing objects. Take look at it :)
Related
I want to remove the duplicates of userNames in the ArrayList. I've tried to convert the ArrayList to an HashSet, but for some reason it doesn't work. The reason why I chose to convert the ArrayList into a HashSet is because it does not allow duplicated values, however, when I use it on my code, it only changes the order in the list:
My code output:
Choreography - Imran Sullivan
Goodfella - Khalil Person
DarknianIdeal - Sophia Jeffery
DarknianIdeal - Clyde Calderon
Frolicwas - Taylor Vargas
Reliable - Aryan Hess
DarknianIdeal - Liyah Navarro
Deservedness - Eadie Jefferson
Reliable - Angel Whitehouse
Choreography - Priya Oliver
How the output should be:
Choreography - Imran Sullivan
Goodfella - Khalil Person
DarknianIdeal - Sophia Jeffery
Frolicwas - Taylor Vargas
Reliable - Aryan Hess
Deservedness - Eadie Jefferson
This is the code. I've splitted the data into an Array so I can print out the data individually.
import java.util.*;
import java.io.*;
public class Task1 {
public static void main(String[] args) {
List<Person> personFile = new ArrayList<>();
Set<Person> splitUserNameList = new HashSet<>(personFile);
try {
BufferedReader br = new BufferedReader(new FileReader("person-data.txt"));
String fileRead = br.readLine();
while (fileRead != null) {
String[] personData = fileRead.split(":");
String personName = personData[0];
String userNameGenerator = personData[1];
String[] userNameSplit = userNameGenerator.split(",");
String newUserNameSplit = userNameSplit[0];
Person personObj = new Person(personName, newUserNameSplit);
splitUserNameList.add(personObj);
fileRead = br.readLine();
}
br.close();
}
catch (FileNotFoundException ex) {
System.out.println("File not found!");
}
catch (IOException ex) {
System.out.println("An error has occured: " + ex.getMessage());
}
for (Person userNames: splitUserNameList) {
System.out.println(userNames);
}
}
}
/* Person Class */
public class Person {
private String personName;
private String userNameGenerator;
public Person(String personName, String userNameGenerator) {
this.personName = personName;
this.userNameGenerator = userNameGenerator;
}
public String getPersonName() {
return personName;
}
public String getUserNameGenerator() {
return userNameGenerator;
}
#Override
public String toString() {
return userNameGenerator + " - " + personName;
}
}
You need to override the equals and hashCode methods on your Person object in order for Set to know which objects are considered the same.
It seems you want any two people with the same userNameGenerator field to be considered equal. In that case, the following will suit your needs:
public class Person {
private String commonName;
private String userNameGenerator;
...
#Override
public int hashCode()
{
return userNameGenerator.hashCode();
}
#Override
public boolean equals(Object o)
{
if (this == o) return true;
if (this.getClass() != o.getClass()) return false;
final Person that = (Person) o;
return this.userNameGenerator.equals(that.userNameGenerator);
}
}
Some important things to note:
In the case of duplicates, the Set will only allow in the first one
so insertion order becomes important.
Sets should not contain mutable elements because mutability destroys
their internal consistency. Your Person class is immutable (has no
setters) which is good but you may want to enforce this immutability even
further by declaring all of it's fields final.
Following on from this question;
Please give a short example of how one would de-serialize an object with two references.
I've included the below code so all answers refer to the same object names.
public class Person implements Serializable{
private String name;
private int age;
private boolean single;
private int numKids;
private final static long serialVersionUID = 1L;
public Person(String name, int age, boolean single, int numKids) {
this.name = name;
this.age = age;
this.single = single;
this.numKids = numKids;
this.surname = surname;
}
}
Assuming the following:
A text file has already been created
We created a Person object jim
There is another Person bob that references the jimobject
The question already referenced describes how only jim is written to the file. The bob reference is not. How then do we read the values to jim and bob if the file might contain another few objects of the Person class? How can we ensure that bob has the correct value?
You seem to be confused a bit. Your Person class does not allow for referencing another Person object. You would need a Person field within the Person class to get there! From that perspective, your question doesn't make sense: jim and bob would be two completely independent objects, and serializing one of them will not at all serialize the other one!
But beyond that: typically, when you think in plurals of things (like multiple persons) then you would have an enclosing "container" object (like a List<Person>) that you think about. You don't deal with single Person objects then - but with such containers when thinking about serialization!
You are trying to build an artificial example that simply doesn't work out. Rather think of a situation where the Person class has maybe a field Person spouse. Then alice could be referenced by bob. And when you now have those Person objects, and some more in a list, and you serialize that list - then the system will make sure that alice and bob are serialized just once.
I am little confused your question - maybe because I do not see any issue with serialization and deserialization of the same object and/or multiple objects with or without references to any objects.
The point is that serialization is like creating copy of object (in file system or somewhere). This copy can be recreated in memory (deserialization).
You may create object in memory (deserialization) once or more times.
It something like:
object A --serialization--> file A
file A --deserialization--> object A'
file A --deserialization--> object A"
object A, A' and A" are different objects - but all fields will be have the same values.
If object A contains an sophisticated structure (which can be serialized/deserialized) it can be also another objects then the same mechanism are working also for these objects.
All fields will have the same values but object will be different.
see sample code:
package test;
import java.io.Serializable;
public class Person implements Serializable {
int id;
String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
}
and kind of test
package test;
import java.io.*;
public class Main {
public static void main(String... args) {
Person p1 = new Person(1, "aaa");
Person p1a = null;
Person p1b = null;
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.data"))) {
oos.writeObject(p1);
} catch (IOException e) {
e.printStackTrace();
}
try (ObjectInputStream oos = new ObjectInputStream(new FileInputStream("test.data"))) {
p1a = (Person) oos.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (ObjectInputStream oos = new ObjectInputStream(new FileInputStream("test.data"))) {
p1b = (Person) oos.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
assert p1a != null && p1b != null;
assert p1a.id == p1b.id;
assert p1.id == p1b.id;
assert p1.name.equals(p1a.name);
assert p1a.name.equals(p1b.name);
System.out.println(String.format("Three different object: %s, %s, %s", p1, p1a, p1b));
}
}
PS. How java.lang.String objects are copied/stored/managed in memory it is different story.
I am programming an TBAP (Texted base adventure program) just because. I just started it, and I am already having issues with it. What I want to do is have a main class that introduces the program, in output text. At the end of the class it asks "Where would you like to go on your adventures?" It has five options 3 of them are separate adventures of two of them are inventory classes. Right now I am stuck on the my first adventure class. I have an int variable called path. If path == 1, you go to fantasy island class go on your adventure. Is there any to call that adventure with an if statement? I made a constructor and getters and setters with my variables name and path.
Summerproject class:
package summerproject;
import java.util.Scanner;
import static summerproject.Fanastyisland.name;
import static summerproject.Fanastyisland.path;
public class Summerproject {
private static int path;
private static String name;
public Summerproject (int path, String name)
{
this.path = path;
this.name = name;
}
public String getname() {
return name;
}
public void setname(String name) {
this.name = name;
}
public int getPath() {
return path;
}
public void setPath(int path) {
this.path = path;
}
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.println("Welcome to the adventure text program! You are the choosen one to save the universe");
System.out.println("Press any key to continue...");
try
{
System.in.read();
}
catch(Exception e)
{}
System.out.println("Welcome. You are the choose one, a legend,a becon of hope to save the universe from the forces of evil.");
System.out.println("Only with you skills and your great power can you destroy the evil doing world.");
System.out.println("Please enter heros name");
name = in.next();
System.out.println("Okay " + name + ", lets begin our adventure!!");
System.out.println("The world can be saved, there is hope. But in order to save the world, \n "
+ "+ you must complete 9 tasks in three diffrent places in three diffrent periods of time. The past, the present and the future.");
System.out.println("Press any key to continue...");
try
{
System.in.read();
}
catch(Exception e)
{}
System.out.println("The three places are the past in the year 1322 in Fantasy island");
System.out.println("The present is the evil little town of Keene N.H.");
System.out.println("And the future to the year 2567 in Space!");
System.out.println("Where would you like to go on your adventures?");
System.out.println(" 1). Fantasy Island");
System.out.println(" 2). Keene");
System.out.println(" 3). Outer space");
System.out.println(" 4). Buy wepons or potions!");
System.out.println(" 5). Sell wepons!");
path = in.nextInt();
if (path == 1)
{
}
}
}
here is my fantasy island class:
package summerproject;
import java.util.Scanner;
import static summerproject.Fanastyisland.name;
import static summerproject.Fanastyisland.path;
public class Fanastyisland extends Summerproject {
public static String name;
public static int path;
public Fanastyisland (String name, int path)
{
super(path,name);
name = name;
path = path;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPath() {
return path;
}
public void setPath(int Path) {
this.path = path;
}
public static void main(String[] args)
//this is where the fantasy island adventure begins.
{
System.out.println("Welcome to fantasy island!!")
}
}
Like I said, I want to call the sub classes with an if statement and I don't know how to do that. If I type in one 1, I want to go to the fantasy island class. I haven't programmed the adventure yet, I will get to it once it is fixed, I just want the output for now to be "Welcome to fantasy island!" when I type 1. Any help would be great! Thank you!
Something like this:
Summerproject adventure = null;
switch (path) {
case 1:
adventure = new FantasyIsland (...);
break;
case 2:
adventure = new Keene (...);
break;
...
default:
System.out.println ("Illegal choice(" & path & "): try again");
}
if (adventure != null) {
adventure.play ();
...
You could just create a common interface
public interface Adventures{
public void start();
}
Every adventure could implement this interface and override the start method
public class AdventureA implements Adventures {
#Override
public void start() {
// Do whatever you want
}
}
You summerproject could simply have a class variable with the type of the interface.
public class Summerproject {
private static int path;
private static String name;
private Adventure adventure;
...
}
Afterwards in the if statements you could just assign this adventure and call the start method.
if (path == 1)
{
adventure = new AdventureA();
adventure.start();
}
Currently I am teaching myself Java but I came across a simple problem but have no one to ask from. From one of the exercises, I wrote a class and write a driver class that instantiates and updates several objects. I am confused by "instantiates and updates several objects." Here is what I mean: So here is my class:
public class PP43Car {
private String make = "";
private String model = "";
private int year;
public PP43Car(String ma, String m, int y)
{
make = ma;
model = m;
year = y;
}
public void setMake(String ma)
{
make = ma;
}
public String getMake()
{
return make;
}
public void setModel(String m)
{
model = m;
}
public String getModel()
{
return model;
}
public void setYear(int y)
{
year = y;
}
public int getYear()
{
return year;
}
public String toString()
{
String result = "Make of the vehicle: " + make +
" Model of the vehicle " + model +
" Year of the vehicle: " + year;
return result;
}
}
Which instantiates make, model and year. Then once I was writing the driver class, the way I began was:
import java.util.Scanner;
public class PP43CarTest {
public static void main(String[] args) {
PP43Car car1;
Scanner scan = new Scanner(System.in);
System.out.println("Enter the model of the vehicle:");
car1.getModel();
}
}
But this class produces error and here is where I am stuck. Do I keep on going with this or is this what is meant by "instantiating and updating several objects?"
import java.util.Scanner;
public class PP43CarTest {
static PP43Car car1;
public static void main(String[] args) {
//Scanner scan = new Scanner(System.in);
car1 = new PP43Car("Millenia", "Mazda", 2011);
}
}
If the above code is correct, then can anyone show me how I can use the Scanner class to actually get the user input and update it that way because I would like to learn that as well?
Well, in your last fragment of code you are indeed instantiating an object, since you are doing:
car1 = new PP43Car("Millenia", "Mazda", 2011);
When you create a new object, you are creating a new instance of the class, so yes, you are instantiaing an object.
But you aren't updating it anywhere, because I think here updating means modifying the object, but you only create the object, not modify it...
Something like this would be an update:
car1.setYear(2013);
Since you are setting a different value for an attribute of the object, you are updating it...
EDIT: Try this code, it can't throw any exception, it's Java basics! I hope it clarifies your doubts...
public class PP43CarTest {
public static void main(String[] args) {
//Declaring objects
PP43Car car1;
PP43Car car2;
PP43Car car3;
//Instantiating objects
car1 = new PP43Car("Millenia", "Mazda", 2011);
car2 = new PP43Car("Aaaa", "Bbb", 2012);
car3 = new PP43Car("Ccc", "Ddd", 2012);
//Updating objects
car1.setMake("Xxx");
car1.setMake("Yyy");
car1.setYear(2013);
//Printing objects
System.out.println("CAR 1: " + car1.toString());
System.out.println("CAR 2: " + car2.toString());
System.out.println("CAR 3: " + car3.toString());
}
}
I am trying to do some funky stuff which i have never done before.
So what i am trying to do is:
I create an object by doing the following
Player playerVar = new Player(1234);
Players constructor will then look for a player called 1234, if it exists, it will then deserialize and store the loaded object under 'playerVar', if not it'll just follow through and give a'blank'` player object.
I am not sure if its even possible to make the current object an other instance of the same object, so i am posting here.
This is essentially what i am trying to do.
this = deserielizedObject
I know this can all be done by loading the object, then setting all the necessary variables manually, but that is hardly ideal. How can i 'replace' an object with another instance of itself, from within itself
This is the code i currently have
player.java
public class Player implements java.io.Serializable
{
Player(String GUID) // when loading a new player
{
Player player = loadPlayer(GUID);
//i want to set this to player
// something like this = player If you know what i mean....
}
Player()//when creating a new player
{
}
private Player loadPlayer(String GUID)
{
File f = new File("data/players/"+GUID+".ser");
if(!f.exists())
{
Player player = new Player();
return player;
}
Player player = null;
try
{
FileInputStream fileIn = new FileInputStream("data/players/"+GUID+".ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
player = (Player) in.readObject();
in.close();
fileIn.close();
}
catch(IOException i)
{
i.printStackTrace();
return null;
}
catch(ClassNotFoundException c)
{
System.out.println("Cant find Player Class!");
c.printStackTrace();
return null;
}
return player;
}
private int guid;
private String name;
private int bankMoney;
.......
...
.
}
You could use a factory class/method. The simpliest way in your case would probably be to just have loadPlayer as a public static method.
public static Player loadPlayer(String GUID)
{
...
return player;
}
then:
Player playerVar = Player.loadPlayer(1234);
What you describe is not possible. But you could create a (static) factory method instead of the constructor, which either deserializes or creates a new instance.
You cannot assign a value to this, not possible. Why not directly call the loadPlayer method:
Player player = Player.loadPlayer(GUID); //having loadPlayer as public static