java static variable serialization - java

How are the values of static variables persisted during serialization(If at all persisted). I have read similar questions on stack where it says that static variables are inherently transient i.e their state or current values is not serialized.
I was just doing a very simple example where i serialized a class and saved it to a file and then again reconstructed the class from the file.Surprisingly I find that the value of the static variable at and when the serialization happened is saved.
How does this happen. Is this because the class template along with it's instance information is saved during serialization. Here is the code snippet -
public class ChildClass implements Serializable, Cloneable{
/**
*
*/
private static final long serialVersionUID = 5041762167843978459L;
private static int staticState;
int state = 0;
public ChildClass(int state){
this.state = state;
staticState = 10001;
}
public String toString() {
return "state" + state + " " + "static state " + staticState;
}
public static void setStaticState(int state) {
staticState = state;
}
and here is my main class
public class TestRunner {
/**
* #param args
*/
public static void main(String[] args) {
new TestRunner().run();
}
public TestRunner() {
}
public void run() {
ChildClass c = new ChildClass(101);
ChildClass.setStaticState(999999);
FileOutputStream fo = null;
ObjectOutputStream os = null;
File file = new File("F:\\test");
try {
fo = new FileOutputStream(file);
os = new ObjectOutputStream(fo);
os.writeObject(c);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if(null != os)os.close();
if(null != fo) fo.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
FileInputStream fi = null;
ObjectInputStream ois = null;
ChildClass streamed;
try {
fi = new FileInputStream(file);
ois = new ObjectInputStream(fi);
Object o = ois.readObject();
if(o instanceof ChildClass){
streamed = (ChildClass)o;
//ChildClass cloned = streamed.clone();
System.out.println(streamed.toString());
}
} catch (IOException | ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if(null != ois)ois.close();
if(null != fi) fi.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Note : There is nothing wrong with the code. I just am wondering how the value of the static variable 'staticState' in the class 'ChildClass' gets saved. Will the state be saved if i transmit this serialized data over a network then

The static field value was not serialized. The output is printing the new value of the static field simply because you modified it to 999999 but you never reset its value to the old one before de-serizalizing. Since the field is static, the new value is reflected in any instance of ChildClass.
To properly assert that the field is not serialized, reset the value to 10001 before de-serializing the object, and you will notice that its value is not 999999.
...
ChildClass.setStaticState(10001);
FileInputStream fi = null;
ObjectInputStream ois = null;
ChildClass streamed;
...
// when de-serializing, the below will print "state101 static state 10001"
System.out.println(streamed.toString());

Related

Serialization of String[]: how to convert String value to String[] one?

here is code for main
public static void main(String[] args) {
Container container= new Container();
Serializator serializator = new Serializator();
container.setvalue("1st val");
serializator.serialization(container);
}
here is code for container
public class Container implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Holds the elements of a container.
*/
private String[] values;
public String[] getvalue() {
return values;
}
public void setvalue(String[] values) {
this.values=values;
}
}
here is code for a serializator
public class Serializator {
public boolean serialization(Container container) {
boolean flag=false;
File file= new File("C:/conatiner.data");
ObjectOutputStream oos = null;
try {
FileOutputStream fos= new FileOutputStream(file);
if(fos != null) {
oos= new ObjectOutputStream(fos);
oos.writeObject(container);
flag=true;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(oos != null) {
try {
oos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return flag;
}
}
Well, the program should be working in a following way: you create a container that has an array of strings,(you can set the values in it) and then the program must serialize it. but the problem is that the tutorial worked with the String value, but not the String[] one. how can i make it understand the String[] value and insert it?
The crashlog is the following
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The method setvalue(String[]) in the type Container is not applicable for the arguments (String)
at ua.khpi.oop.taradai06.program6.main(program6.java:7)
"container.setvalue("1st val");"
First, let's focus on the bug:
In the container, you promised you will send an array of string in the function setValue, but you are sending a single string.
There are two things you could do;
keep the container code as it is and send array of strings from main
Change the container code and let setValue get a single string value and add it to the values array
And you main question related with serialisation please check that post
In order to accept both String and String[] as an input parameter to Container::setvalue method, the varargs should be used, then a single String is accepted as an array consisting of one element.
Also, Java naming conventions for getters/setters of Java Beans specify to capitalize the property names after get/set verb: Getter and setter method names are composed of the word get or set, respectively, plus the property name with the first character of each word capitalized, so the methods should be names as getValues/setValues:
// Container class
public void setValues(String... values) {
this.values = values;
}
Then this method can be invoked as follows without additional overloading:
container.setValues(); // empty array new String[0]
container.setValues("a string"); // new String[1]{"a string"}
container.setValues("a", "b"); // new String[2]{"a", "b"}
container.setValues(new String[]{"1", "2", "3"}); // common array
If you promised you'll use the String[] parameter, then you should note that.
The following version of using setvalue does this work perfectly
container.setvalue(new String[] {"1st val","2nd val","3rd val"});
OK I hope I helped you look in the class "Container" in "setvalue" function, you gived it the parameter "1st value"(Its one string) but it needs a String array. So you can replace your code with this:
main class:
public class main {
public static void main(String[] args) {
Container container= new Container();
Serializator serializator = new Serializator();
container.setvalue("1st value", 0);
serializator.serialization(container);
}
}
Container class:
import java.io.Serializable;
public class Container implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Holds the elements of a container.
*/
int AnyNumberYouWant = 100;
private String[] values = new String[AnyNumberYouWant];
public String[] getvalue() {
return values;
}
public void setvalue(String value, int index) {
this.values[index]=value;
}
}
Serializator class
import java.io.*;
public class Serializator {
public boolean serialization(Container container) {
boolean flag=false;
File file= new File("C:/Container/container.data");
ObjectOutputStream oos = null;
try {
FileOutputStream fos= new FileOutputStream(file);
if(fos != null) {
oos= new ObjectOutputStream(fos);
oos.writeObject(container);
flag=true;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(oos != null) {
try {
oos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return flag;
}
}
And I solved another errors that was there in the code. So I tried it and worked correctly. I hope that I helped you.

Why is this Class' object not serialized properly in different processes?

Context
I made a Java application, and need to run two instances of that application, synchronizing some of their attributes via socket each time there's some change. To communicate those changes, Serializable objects are sent through a socket using ObjectStreams (input and output) using read/writeUTF() for an identifier, and read/writeObject() and flush(). The app is the exact same .jar, run twice with some changes like having different ports and ip (if necessary).
Problem
I noticed that objects of some of my classes (e.g. Notification) were sent and received without any troubles, but objects from another class (RegisteredUsers) weren't sent (or received) properly. So I ran some tests to send objects between the two apps and found that the object is being sent and isn't null, it's attribute (a HashMap<String,User>) is also being sent and isn't null, but is always empty.
So I decided to scale it down to what the problem was exactly: I'm trying to write an object through a Stream, and read it in a different process of the same .jar, and with most classes it seems to work, but it doesn't with one.
There seems to be something I'm missing or don't understand about this serialization process, if the object is written and read during the execution of the same process it works, but not if this object is read on another instance of the same app. I even added a HashMap to Notification with the same creation process, but it still works, I really don't get it, what am I missing?
Code
I have taken some code from the bigger app and trimmed it down to the basic problem if anyone wants to test it. To reproduce the errors, run Main1, which will create the two files with an object persisted in each one (one with a Notification object and the other with a RegisteredUsers object) and shows their information, then Main2, which reads them from the files and shows their information, and the problem should be printed. That being that reg3's HashMap is empty and thus neither of the Users are registered.
Main1
public class Main1 {
public static void main(String[] args) {
String regFile = "registry.txt";
String notificationFile = "notification.txt";
Persistence pers = new Persistence();
RegisteredUsers reg1 = new RegisteredUsers();
RegisteredUsers reg2 = new RegisteredUsers();
reg1.register("Name1", "127.0.0.1");
reg1.register("Name2", "127.0.0.1");
try {
pers.writeReg(reg1, regFile);
} catch (IOException e) {
System.out.println("Error writing registry.");
}
try {
reg2 = pers.readReg(regFile);
} catch (IOException e) {
System.out.println("Error reading registry.");
}
System.out.println("Original registry: ");
System.out.println(reg1.isRegistered("Name1") + " " + reg1.isRegistered("Name2"));
System.out.println("Registry read from file: ");
System.out.println(reg2.isRegistered("Name1") + " " + reg2.isRegistered("Name2"));
Notification noti1 = new Notification("Name", "127.0.0.1");
Notification noti2 = new Notification(); //not necesary but it's the way it's done in the bigger app.
try {
pers.writeNotif(noti1, notificationFile);
} catch (IOException e) {
System.out.println("Error writing notification.");
}
try {
noti2 = pers.readNotif(notificationFile);
} catch (IOException e) {
System.out.println("Error reading notification.");
}
System.out.println("Original notification: ");
System.out.println(noti1.getAttributes().get(0) + " " + noti1.getAttributes().get(1));
System.out.println(noti1.getMap());
System.out.println("Notification read from file: ");
System.out.println(noti2.getAttributes().get(0) + " " + noti2.getAttributes().get(1));
System.out.println(noti2.getMap());
}
}
Main2
public class Main2 {
public static void main(String[] args) {
String regFile = "registry.txt";
String notificationFile = "notification.txt";
Persistence pers = new Persistence();
RegisteredUsers reg3 = new RegisteredUsers();
try {
reg3 = pers.readReg(regFile);
} catch (IOException e) {
System.out.println("Error reading registry.");
}
if (reg3 == null) {
System.out.println("reg3 is null");
}
if (reg3.getMap() == null)
System.out.println("reg3 has a null map");
if (reg3.getMap().isEmpty())
System.out.println("reg3 has an empty map");
System.out.println("Registry read from file on another process: ");
System.out.println(reg3.isRegistered("Name1") + " " + reg3.isRegistered("Name2"));
Notification noti3 = new Notification(); //not necesary but it's the way it's done in the bigger app.
try {
noti3 = pers.readNotif(notificationFile);
} catch (IOException e) {
System.out.println("Error reading notification.");
}
System.out.println("Notification read from file on another process: ");
System.out.println(noti3.getAttributes().get(0) + " " + noti3.getAttributes().get(1));
System.out.println(noti3.getMap());
}
}
A Class to persist the objects in the files:
public class Persistence {
public void writeReg(RegisteredUsers regus, String file) throws IOException {
try(FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);) {
oos.writeObject(regus);
oos.flush();
}
}
public RegisteredUsers readReg(String file) throws IOException {
RegisteredUsers regus = null;
try(FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);) {
regus = (RegisteredUsers) ois.readObject();
} catch (ClassNotFoundException e) {
System.out.println("Wrong class.");
}
return regus;
}
public void writeNotif(Notification regus, String file) throws IOException {
try(FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);) {
oos.writeObject(regus);
oos.flush();
}
}
public Notification readNotif(String file) throws IOException {
Notification notif = null;
try(FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);) {
notif = (Notification) ois.readObject();
} catch (ClassNotFoundException e) {
System.out.println("Wrong class.");
}
return notif;
}
}
RegisteredUsers
public class RegisteredUsers implements Serializable {
private static HashMap<String, User> users;
public RegisteredUsers() {
users = new HashMap<String, User>();
}
public HashMap<String, User> getMap() {
return users;
}
public boolean isRegistered(String name) {
User us = users.get(name);
return us != null;
}
public void register(String name, String ip) {
users.put(name, new User(name, ip, false));
}
}
Notification
public class Notification implements Serializable {
private ArrayList<String> attributes;
private HashMap<String, User> map = new HashMap<>();
public Notification() {
}
public Notification(String name, String ip) {
attributes = new ArrayList<String>();
attributes.add(0, name);
attributes.add(1, ip);
map.put(ip, new User(name, ip, false));
}
public ArrayList<String> getAttributes() {
return attributes;
}
public HashMap<String, User> getMap() {
return map;
}
}
User
public class User implements Serializable {
private String name;
private String ip;
private boolean connection_state;
public User(String name, String ip, boolean connection_state) {
this.name = name;
this.ip = ip;
this.connection_state = connection_state;
}
}
In java static fields are implicitly transient, and transient fields are not serialized.
If you modify the RegisterdUsers to
public class RegisteredUsers implements Serializable {
private HashMap<String, User> users; // static modifier is removed
...
}
The serialization will work.

How to bind messages in javafx without using eclipse.osgi NLS?

I have a messages.properties file that contains all string messages used in my application.
I would like to bind these messages to a java class fields and use directly in other classes.
Can this be achieved without using NLS? By some approach in javafx? Because I do not want to add eclipse dependency in UI classes.
Java provides property file reading capability right from the box. You can do adjustment to suit your actual use-case.
For example:
public final class Messages {
private Messages() {
loadFile();
}
private static final class ThreadSafeSingleton {
private static final Messages INSTANCE = new Messages();
}
public static Messages getInstance() {
return ThreadSafeSingleton.INSTANCE;
}
private final Properties props = new Properties();
private void loadFile() {
InputStream is = null;
try {
is = new FileInputStream("messages.properties");
props.load(is);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public String getMessage(String key) {
if (key == null && key.isEmpty()) return "";
return props.getProperty(key);
}
}
Edit
In order to use these values as if it is a constant, you need to pretty much make everything static:
public final class Messages {
private Messages() {} // Not instantiable
private static final Properties props = loadFile(); // Make sure this static field is at the top
public static final String FOO = getMessage("foo");
public static final String BAR = getMessage("bar");
private static Properties loadFile() {
final Properties p = new Properties();
InputStream is = null;
try {
is = new FileInputStream("messages.properties");
p.load(is);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return p;
}
public static String getMessage(String key) {
if (key == null && key.isEmpty()) return "";
return props.getProperty(key);
}
}
Be warned again, the Properties field must always be the top-most field declared in the class, because the class loader will load the fields top-down for all static fields whose value is computed at runtime (i.e. set by a static method).
Another point, this example does not handles what happens if the file is not file - it simply returns a Properties that has no value.

Using same Map in several classes after Serialization?

My app is saving a hashmap before it stops and when it starts again loads the same hashmap so changes could be made to it. I am using Serialization.
Storage class:
public class Storage {
private Map<String, String> storage;
private String projectStorageFilePath;
public Storage() {
this.storage = new ConcurrentHashMap<String, String>();
makeDir();
}
/**
* If the file in which the map objects will be saved doesn't exist in the
* user home directory it creates it.
*/
private void makeDir() {
File projectHomeDir = new File(System.getProperty("user.home"), ".TestMap");
String projectHomeDirPath = projectHomeDir.getAbsolutePath();
File projectStorageFile = new File(projectHomeDirPath, "storage.save");
projectStorageFilePath = projectStorageFile.getAbsolutePath();
if (!projectHomeDir.exists()) {
projectHomeDir.mkdir();
try {
projectStorageFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#SuppressWarnings("unchecked")
public boolean load() {
boolean isLoaded = false;
ObjectInputStream ois = null;
try {
File file = new File(projectStorageFilePath);
if (file.length() != 0) {
//loading the map
ois = new ObjectInputStream(new FileInputStream(file));
storage = (ConcurrentHashMap<String, String>) ois.readObject();
isLoaded = true;
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (null != ois) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return isLoaded;
}
public boolean save() {
boolean isSaved = false;
ObjectOutputStream oos = null;
try {
//saving
oos = new ObjectOutputStream(new FileOutputStream(projectStorageFilePath));
oos.writeObject(storage);
isSaved = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != oos) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return isSaved;
}
public Map<String, String> getStorage() {
return this.storage;
}
}
The class in which I am trying to do something with that hashmap:
public class DoSomethingWithMap {
private Map<String, String> storage;
public DoSomethingWithMap(Map<String, String> storage) {
this.storage = storage;
}
public void addToMap(String key, String value) {
this.storage.put(key, value);
}
public void printMap() {
System.out.println(this.storage);
}
}
When I run it the first time it works fine:
public class Main {
public static void main(String[] args) {
Storage s = new Storage();
DoSomethingWithMap something = new DoSomethingWithMap(s.getStorage());
if (s.load()) {
System.out.println(s.getStorage());
}
something.addToMap("2", "test2");
something.addToMap("4", "test4");
something.addToMap("5", "test5");
if (s.save()) {
System.out.println(s.getStorage());
}
}
}
Output:
{} //empty map which is ok because it has never been saved before
{3=test3, 4=test4, 5=test5} //changes during runtime are saved
The problem is when I start Main again and try to make changes to the saved map:
public static void main(String[] args) {
Storage s = new Storage();
DoSomethingWithMap something = new DoSomethingWithMap(s.getStorage());
if (s.load()) {
System.out.println(s.getStorage());
}
something.printMap();
something.addToMap("6", "newTest");
something.addToMap("7", "newTest");
something.addToMap("8", "newTest");
something.printMap();
if (s.save()) {
System.out.println(s.getStorage());
}
}
Output:
{3=test3, 4=test4, 5=test5} //loading the map works fine
{} //here it should be same as previous line but is not
{6=newTest, 7=newTest, 8=newTest} //DoSomethingWithMap.printMap is printing only the changes during runtime
{3=test3, 4=test4, 5=test5} // changes during runtime are not saved
It is obvious DoSomethingWithMap class is not using the map which was given to it. Why? Which map is using? How I can fix that?
Thank you.
You are creating a new instance of the Map in your load method:
storage = (ConcurrentHashMap<String, String>) ois.readObject();
To fix you can clear the current map and then add all the values from the loaded one:
//loading the map
ois = new ObjectInputStream(new FileInputStream(file));
storage.clear();
storage.putAll((ConcurrentHashMap<String, String>) ois.readObject());
To prevent such error in the future, you could make those fields final and thus you will get error reports.

why static transient field is serialized?

I´ve been reading that the static fields are not serialized but, after testing it, I saw that´s not true.
The static modifier even overrides the transient modifier and makes the field serializable.
I write one example from a book that shows that a static transient field is serialized.
import java.io.*;
class USPresident implements Serializable {
private static final long serialVersionUID = 1L;
#Override
public String toString() {
return "US President [name=" + name
+ ", period=" + period + ", term=" + term + "]";
}
public USPresident(String name, String period, String term) {
this.name = name;
this.period = period;
this.term = term;
}
private String name;
private String period;
private static transient String term;
}
class TransientSerialization {
public static void main(String[] args) {
USPresident usPresident = new USPresident("Barack Obama", "2009 to --", "56th term");
System.out.println(usPresident);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("USPresident.data"))) {
oos.writeObject(usPresident);
} catch (IOException ioe) {
// ignore
}
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("USPresident.data"))) {
Object obj = ois.readObject();
if (obj != null && obj instanceof USPresident) {
USPresident presidentOfUS = (USPresident) obj;
System.out.println(presidentOfUS);
}
} catch (IOException ioe) {
// ignore
} catch (ClassNotFoundException e) {
// ignore
}
}
}
Is wrong the general concept that static fields are not serialized? Is it just a recommendation?
Why the transient modifier doen't take effect with static ?
note: I understand that initialize a static field in a constructor is an odd code, but the compiler let me do it and it's just in order to understand static fields serialization.
This has nothing to do with serialization but due to the fact that you are setting the static field when you create your usPresident variable. This sets the field for the class of that JVM. Try reading in the serialized president in a different program and see that the transient field is not serialized.
As an aside: consider not ignoring your exceptions.
For example, refactored, your code could look like this:
class USPresident implements Serializable {
private static final long serialVersionUID = 1L;
#Override
public String toString() {
return "US President [name=" + name + ", period=" + period + ", term="
+ term + "]";
}
public USPresident(String name, String period, String term) {
this.name = name;
this.period = period;
this.term = term;
}
private String name;
private String period;
private static transient String term;
}
class TransientSerialization {
public static void main(String[] args) {
serializePresident();
deserializePresident();
}
private static void deserializePresident() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream(
"USPresident.data"));
Object obj = ois.readObject();
if (obj != null && obj instanceof USPresident) {
USPresident presidentOfUS = (USPresident) obj;
System.out.println(presidentOfUS);
}
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void serializePresident() {
USPresident usPresident = new USPresident("Barack Obama", "2009 to --",
"56th term");
System.out.println(usPresident);
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("USPresident.data"));
oos.writeObject(usPresident);
oos.close();
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
The second time your run it, change the main method to:
public static void main(String[] args) {
// serializePresident();
deserializePresident();
}
And see what comes up.
For me, the first run returns:
US President [name=Barack Obama, period=2009 to --, term=56th term]
US President [name=Barack Obama, period=2009 to --, term=56th term]
and the second run returns:
US President [name=Barack Obama, period=2009 to --, term=null]

Categories