saving/loading array of objects using java serialization - java

I have the following class, which performs some calculations to fill its static arrays.
public class Amount implements Serializable{
private static final long serialVersionUID = 8141477444408242243L;
public static Amount values1[][] = new Amount[10][30];
public static Amount values2[][] = new Amount[10][30];
public static Amount values3[][] = new Amount[10][30];
double highestValue;
double highestAmount;
double lowestAmount;
double lowestValue;
...
}
As the calculations take 20 minutes or so, I am looking to store the arrays on file and load the values when the program starts. I am attempting to use the java serialization method and have the following functions
public static void loadFile(Amount[][] arr, String filename){
try {
FileInputStream fis = new FileInputStream(filename);
ObjectInputStream in = new ObjectInputStream(fis);
arr = (Amount[][])in.readObject();
in.close();
}
catch (Exception e) {
System.out.println(e);
}
}
public static void saveFile(Amount[][] arr, String filename){
try {
FileOutputStream fos = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(arr);
out.flush();
out.close();
}
catch (IOException e) {
System.out.println(e);
}
}
which I call like this saveFile(values1, "valueOneSaveFile"); and loadFile(values1, "valueOneSaveFile");
I have run the program once, saving all the arrays to various files. The files have been created and look to be around the correct size. When I change my program to call the loadFile functions, the arrays do not appear to initialize correctly. I am getting null pointer exceptions when trying to read a value from the array (which appears to be empty after the load)

The problem is in your LoadFile method.
Java passes parameters by value. In the case of objects a copy of the "pointer" is passed.
When you update the array:
arr = (Amount[][])in.readObject();
You are not updating Amount.values1 array, instead the local arr variable points to a new array.
You should change the method signature to:
public static Amount[][] loadFile(String filename)
And use it accordingly.

It might be an issue with the readObject and writeObject methods that you are implementing in your Amount class. A comprehensive example can be found here.
You might also consider using XStream to save/load your data. It is very easy to use, as shown here.

Related

Java record serialization and repeated calls to canonical constructor

In this post about serializable records it is stated that
Deserialization creates a new record object by invoking a record class’s canonical constructor, passing values deserialized from the stream as arguments to the canonical constructor. This is secure because it means the record class can validate the values before assigning them to fields, just like when an ordinary Java program creates a record object via new. “Impossible” objects are impossible.
This argues with a constructor that is used for validation only. However, when the constructor manipulates the arguments this results in rather strange behavior. Consider this very artificial simple example:
The following record manipulates a before saving it:
import java.io.Serializable;
public record TRecord (int a) implements Serializable {
public TRecord {
a = a-1;
}
}
And the following program just saves the serialized record the first time and loads it the subsequent times:
import java.io.*;
public class TestRecords {
public static void main(String args[]) {
TRecord a1 = null;
try {
FileInputStream fileIn = new FileInputStream("tmp");
ObjectInputStream in = new ObjectInputStream(fileIn);
a1 = (TRecord) in.readObject();
in.close();
fileIn.close();
} catch (IOException | ClassNotFoundException i) {
// ignore for now
}
if (a1 == null) {
try {
a1 = new TRecord(5);
FileOutputStream fileOut = new FileOutputStream("tmp");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(a1);
out.close();
fileOut.close();
System.out.printf("Serialized data is saved in /tmp/employee.ser");
} catch (IOException i) {
i.printStackTrace();
}
}
System.out.println(a1);
}
}
The output for the first run is TRecord[a=4], and TRecord[a=3] in subsequent runs, so the state that I get from deserialization differs from what I put in there. Using a comparable class like the following instead would have gotten me the same result TClass[a=4] every time.
import java.io.Serializable;
public class TClass implements Serializable {
private int a;
public TClass(final int a) {
this.a = a-1;
}
public int getA() {return a;}
public String toString() {
return "Class[" + a + "]";
}
}
So my question is: Is there any rule for records that forbids/discourages using the constructor for anything other than validations (I am thinking for example about hashing a password before storing the input)? Or is there another way to deserialize an object so that the initial state is restored?
If you look at the documentation for records it says the following:
For all record classes, the following invariant must hold: if a record
R's components are c1, c2, ... cn, then if a record instance is copied
as follows:
R copy = new R(r.c1(), r.c2(), ..., r.cn());
then it must be the case that r.equals(copy).
This is not the case for your record class however:
jshell> TRecord r1 = new TRecord(42);
r1 ==> TRecord[a=41]
jshell> TRecord copy = new TRecord(r1.a());
copy ==> TRecord[a=40]
jshell> r1.equals(copy)
$4 ==> false
In other words, your record type violates this invariant, and this is also the reason why you are seeing the inconsistent deserialization.

storing the value of static variables in java

I have used a static variable in a Class and I want that the value of this variable is kept unchanged after restart of the jvm.
The idea is to store this value.
Can someone help me ?
If you want a variable whose value can change during the execution of the code, to be restored to whatever it was when you stopped the JVM, the next time you start the JVM, then you need to persist the value to an external medium. This is typically done by storing the value in a file or database, and retrieving it the next time the program starts. A key question when solving this problem is how to represent the value outside the executing program. For simple types like numbers and strings, this is not much of an issue. For values that are objects of non-trivial classes, the problem becomes more interesting. This is known as object serialization.
With the disclaimer that there are many different ways to persist data, here is a minimal example using Java Serialization to make the point more concrete.
class MyClassData implements Serializable {
private String myString = "A string";
private int myNumber = 5;
// To make the point that the state of the object stored in the
// variable can change at run-time.
public void changeThings( String myString, int myNumber) {
this.myString = myString;
this.myNumber = myNumber;
}
}
public class MyClass {
private static MyClassData data = restore();
// Call before exiting the program
private static void store() {
try( ObjectOutputStream out =
new ObjectOutputStream(new FileOutputStream("data.dat"))) {
out.writeObject(data);
}
catch( IOException e ) {
// Deal with this
}
}
private static MyClassData restore() {
try( ObjectInputStream in =
new ObjectInputStream(new FileInputStream("data.dat"))) {
return (MyClassData) in.readObject();
}
catch( IOException | ClassNotFoundException e ) {
return new MyClassData();
}
}
}
You restart the jvm every thing will be clear.So you can't get the values from static variables. If you use database then only you get the values without failing.

Casting double[] to byte[] using stream in Java

Hey I'm working on an app that uses Paho mqtt
Now I'm trying to cast the contents of a couple of objects to byte arrays so I can send them to the broker. There are a couple of different objects that all adhere to a abstract class, but the one I started with contains a double[]
Here's the function I'm trying to implement:
#Override
public byte[] getBytes() {
return Arrays.stream(driveVector).map(d -> Double.valueOf(d).byteValue()).toArray();
}
I thought this would work, but I get an error that the return value is a double[]
I think I either don't understand the map method or I'm goin about this all wrong in general (I looked at the ByteBuffer class, but it seems like a pain to implement this with it)
Thanks in advance
You can't cast a double[] to a byte[] for the fundamental reason that they are unrelated types, and you can only cast between related types.
Casts in Java, unlike, say, C++, don't actually create a new object: they are merely a way to the compiler "I know more about the type of this object than you; trust me." For example, you might know that a variable of type Object actually holds a reference to a String, something which the compiler cannot know; in that case, you can cast the reference.
You can, however, construct a new array:
byte[] output = new byte[input.length];
for (int j = 0; j < input.length; j++) {
output[j] = (byte) input[j];
}
There is no way to do this with streams. Or rather, there is, in that you could crowbar this code into a stream operation on a Stream<double[]>, say; but involving streams like that clearly adds no benefit.
You can use ByteBuffer for it:
double[] doubles = new double[] {1,2,3,4,5};
ByteBuffer buffer = ByteBuffer.allocate(doubles.length * Double.BYTES);
Arrays.stream(doubles).forEach(buffer::putDouble);
buffer.array();
Java Streams is not the right tool here, especially not since there is no ByteStream in Java.
Your method can be implemented as a simple for loop.
#Override
public byte[] getBytes() {
byte[] arr = new byte[driveVector.length];
for (int i = 0; i < arr.length; i++)
arr[i] = (byte) driveVector[i];
return arr;
}
In my MQTT application I read a single double value and post that to the broker. However, there is no real difference between a single and an array of doubles. The client needs to know the array length, while with a single value it always knows there is one.
I'm confident that you can adapt my code to writing multiple values, adapt the toMessage to write multiple double values.
public abstract class SensorMonitor {
protected final MqttAsyncClient client;
protected final String topic;
protected final Logger logger = Logger.getLogger(getClass().getName());
private final ByteArrayOutputStream byteOut = new ByteArrayOutputStream(8);
private final DataOutputStream dataOut = new DataOutputStream(byteOut);
public SensorMonitor(MqttAsyncClient mqttClient, String topic) {
this.client = mqttClient;
this.topic = topic;
}
public void start(ScheduledExecutorService service) {
service.scheduleWithFixedDelay(this::publish, 0, 30, TimeUnit.SECONDS);
}
protected void publish() {
try {
MqttMessage message = toMessage(readNewValue());
client.publish(topic, message);
} catch (MqttException | IOException e) {
logger.log(Level.SEVERE, "Could not publish message", e);
}
}
private MqttMessage toMessage(double value) throws IOException {
byteOut.reset();
dataOut.writeDouble(value);
return new MqttMessage(byteOut.toByteArray());
}
protected abstract double readNewValue();
}
The DataOutputStream.writeDouble uses Double.doubleToLongBits to create a IEEE 754 floating-point "double format" bit layout.
In my case I could pre-alloc and reuse the byteOut output stream as I knew upfront the needed size of the byte[].

Deep copying an array of objects

I'm still pretty new to Java and right now I'm trying to make a copy of Menu. I think I've done a little bit of it where I created a new Menu object with new MenuItems in it. MenuItems is another class with two string variables and a double variable, the itemName and itemDescription and the itemPrice. So I'm trying to copy the contents, the three variables of the original MenuItems into the MenuItems copy, but I don't know how. I got stuck on trying to set the clone copy's name to the original's name.
public class Menu
{
Menu()
{
}
final int maxItems = 50;
MenuItem[] food = new MenuItem[maxItems + 1];
public Object clone()
{
Menu menuClone = new Menu();
MenuItem[] foodClone = new MenuItem[maxItems + 1];
for(int i = 1; i <= maxItems + 1; i++)
{
foodClone[i] = new MenuItem();
foodClone[i] = food[i].setItemName();
}
}
This is the MenuItem class:
public class MenuItem
{
private String name;
private String descrip;
private double price;
MenuItem()
{
}
public String getItemName()
{
return name;
}
public String getItemDescrip()
{
return descrip;
}
public double getPrice()
{
return price;
}
public void setItemName(String itemName)
{
name = itemName;
}
public void setItemDescrip(String itemDescrip)
{
descrip = itemDescrip;
}
public void setPrice(double itemPrice) throws IllegalArgumentException
{
if(itemPrice >= 0.0)
price = itemPrice;
else
throw new IllegalArgumentException("Enter only positive values");
}
public String toString(){
return "Name: " + name + ", Desc: " + descrip;
}
}
You are almost there, where you have:
foodClone[i] = food[i].setItemName();
You probably want (in addition to the other variables of MenuItem)
foodClone[i].setItemName(food[i].getItemName())`
However, it's best to use the clone method or a copy constructor (well, copy constructor arguably might be best).
I do prefer using a copy constructor, such an example would be:
MenuItem(MenuItem menuItemToClone)
{
this.name = menuItemToClone.name;
this.descrip = menuItemToClone.descrip;
this.price = menuItemToClone.price;
}
Then you would just do:
foodClone[i] = new MenuItem(food[i]);
Cloning only provides a shallow copy, despite some of the previous recommendations.
A common solution to the deep copy problem is to use Java Object Serialization (JOS). The idea is simple: Write the object to an array using JOS’s ObjectOutputStream and then use ObjectInputStream to reconstitute a copy of the object. The result will be a completely distinct object, with completely distinct referenced objects. JOS takes care of all of the details: superclass fields, following object graphs, and handling repeated references to the same object within the graph. Figure 3 shows a first draft of a utility class that uses JOS for making deep copies.
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
/**
* Utility for making deep copies (vs. clone()'s shallow copies) of
* objects. Objects are first serialized and then deserialized. Error
* checking is fairly minimal in this implementation. If an object is
* encountered that cannot be serialized (or that references an object
* that cannot be serialized) an error is printed to System.err and
* null is returned. Depending on your specific application, it might
* make more sense to have copy(...) re-throw the exception.
*
* A later version of this class includes some minor optimizations.
*/
public class UnoptimizedDeepCopy {
/**
* Returns a copy of the object, or null if the object cannot
* be serialized.
*/
public static Object copy(Object orig) {
Object obj = null;
try {
// Write the object out to a byte array
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(orig);
out.flush();
out.close();
// Make an input stream from the byte array and read
// a copy of the object back in.
ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(bos.toByteArray()));
obj = in.readObject();
}
catch(IOException e) {
e.printStackTrace();
}
catch(ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
return obj;
}
}
Unfortunately, this approach has some problems:
It will only work when the object being copied, as well as all of the other objects references directly or indirectly by the object, are serializable. (In other words, they must implement java.io.Serializable.) Fortunately it is often sufficient to simply declare that a given class implements java.io.Serializable and let Java’s default serialization mechanisms do their thing.
Java Object Serialization is slow, and using it to make a deep copy requires both serializing and deserializing. There are ways to speed it up (e.g., by pre-computing serial version ids and defining custom readObject() and writeObject() methods), but this will usually be the primary bottleneck.
The byte array stream implementations included in the java.io package are designed to be general enough to perform reasonable well for data of different sizes and to be safe to use in a multi-threaded environment. These characteristics, however, slow down ByteArrayOutputStream and (to a lesser extent) ByteArrayInputStream.
Source: http://javatechniques.com/blog/faster-deep-copies-of-java-objects/

2 dimensional array changing during serialisation

I'm serialising and deserialising a large two dimensional array of objects. Each object contains instructions to creating a BufferedImage - this is done to get around BufferedImage not being directly serializable itself.
The class being serialised is:
public final class MapTile extends TransientImage
{
private static final long serialVersionUID = 0;
private transient BufferedImage f;
transient BufferedImage b;
int along;
int down;
boolean flip = false;
int rot = 0;
public MapTile(World w, int a, int d)
{
// f = w.getMapTiles();
along = a;
down = d;
assignImage();
}
public MapTile(World w, int a, int d, int r, boolean fl)
{
// f = w.getMapTiles();
along = a;
down = d;
rot = r;
flip = fl;
assignImage();
}
public int getA()
{
return along;
}
public int getD()
{
return down;
}
#Override
public void assignImage()
{
if (f == null)
{
f = World.mapTiles;
}
b = f.getSubimage(along, down, World.squareSize, World.squareSize);
if (rot != 0)
{
b = SmallMap.rotateImage(b, rot);
}
if (flip)
{
b = SmallMap.flipImage(b);
}
super.setImage(b);
f.flush();
b.flush();
f = null;
b = null;
}
}
which extends:
public abstract class TransientImage implements Serializable
{
private transient BufferedImage image;
public BufferedImage getImage()
{
return image;
}
public void setImage(BufferedImage i)
{
image = i;
}
public abstract void assignImage();
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
in.defaultReadObject();
assignImage();
}
}
This will ultimately be part of a map - usually it is created randomly but certain areas must be the same each time, hence serialising them and reading the array back in. As I will never need to save the image during normal usage I am putting in the write code:
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("verticalroad.necro")))
{
//out.writeObject(mapArray);
//}
//catch (IOException e) {
//}
in the class that creates the map, the read code:
try{
FileInputStream door = new FileInputStream(new File(f.getPath()+ "//verticalroad.necro"));
ObjectInputStream reader = new ObjectInputStream(door);
homeTiles = (MapTile[][]) reader.readObject();
}
catch (IOException | ClassNotFoundException e)
{
System.out.println("Thrown an error" + e.getMessage());
}
in the initialising class and commenting in and out as needed.
However. Each time I run the program the contents of the two dimensional array (mapArray in write, homeTiles in read) is different. Not only different from the one I (thought) I wrote, but also different each time the program is opened.
As can be seen, I'm printing out the toString to System.out which reveals further oddities. As its just a standard array, the toString isn't 100% helpful but it seems to cycle between several distinct values. However, even when the toStringg gives the same value, the contents of the array as displayed are not the same.
An example of a toString is hometiles:[[Lriseofthenecromancer.MapTile;#7681720a Looking at the documentation for Array.toString (here) it seems to be badly formed, lacking a trailing ]. I'm not sure if this is a clue to the issue or if its simply that the array is very large (several thousand objects) and its an issue of display space (I'm using NetBeans).
Any insight as to why this is changing would be appreciated. My working assumption is that its serializing the array but not the contents. But I have no idea a) if that's the case and b)if it is, what to do about it.
EDIT: Looking into this a bit further, it seems that instance variables aren't being set immediately. Printing them out directly after the call to setImage() has them all at zero, printing them from the calling class has them where they should be.
The underlying problem was that I'm an idiot. The specific expression of this in this particular case was that I forgot that subclasses couldn't inherit private methods. As such, the assignImage call wasn't being made and the image wasn't being set up.
Sorry for wasting the time of anyone who looked at this. I feel quite embarrassed.

Categories