Accessing private transient object fields from any method in class must be controlled with some code. What is the best practice?
private transient MyClass object = null;
internal get method:
private MyClass getObject() {
if (object == null)
object = new MyClass();
return object;
}
// use...
getObject().someWhat();
or "make sure" method:
private void checkObject() {
if (object == null)
object = new MyClass();
}
// use...
checkObject();
object.someWhat();
or something clever, more safe or more powerful?
Transient fields are lost at serialization but you need them only after deserialization, so you have to restore them to what you need in the readObject method...
Have to post a new answer about transient because it's too long for a comment. Following code prints
Before: HELLO FOO BAR
After: HELLO null null
public class Test {
public static void main(String[] args) throws Exception {
final Foo foo1 = new Foo();
System.out.println("Before:\t" + foo1.getValue1() + "\t" + foo1.getValue2() + "\t" + foo1.getValue3());
final File tempFile = File.createTempFile("test", null);
// to arrange for a file created by this method to be deleted automatically
tempFile.deleteOnExit();
final FileOutputStream fos = new FileOutputStream(tempFile);
final ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(foo1);
oos.close();
final FileInputStream fis = new FileInputStream(tempFile);
final ObjectInputStream ois = new ObjectInputStream(fis);
final Foo foo2 = (Foo) ois.readObject();
ois.close();
System.out.println("After:\t" + foo2.getValue1() + "\t" + foo2.getValue2() + "\t" + foo2.getValue3());
}
static class Foo implements Serializable {
private static final long serialVersionUID = 1L;
private String value1 = "HELLO";
private transient String value2 = "FOO";
private transient String value3;
public Foo() {
super();
this.value3 = "BAR";
}
public String getValue1() {
return this.value1;
}
public String getValue2() {
return this.value2;
}
public String getValue3() {
return this.value3;
}
}
}
Most safe (and normal) way would be either directly initializing it:
private transient MyClass object = new MyClass();
or using the constructor
public ParentClass() {
this.object = new MyClass();
}
Lazy loading in getters (as you did in your example) is only useful if the constructor and/or initialization blocks of MyClass is doing fairly expensive stuff, but it is not threadsafe.
The transient modifier doesn't make any difference. It only skips the field whenever the object is about to be serialized.
Edit: not relevant anymore. As proven by someone else, they indeed don't get reinitialized on deserialization (interesting thought though, it will actually only happen if they are declared static). I'd go ahead with the lazy loading approach or by resetting them through their setters directly after deserialization.
Related
In Java, given
Class c = ...
We can make an object of this class by first obtaining a constructor. For example, if we want to use the default (no parameters) constructor,
c.getConstructor().newInstance()
This seems straightforward, and seems to match how things are done in Java source code.
But, curiously, it is not how things are done in JVM byte code. There, creating an object is done in two steps: new to actually create the object, then invokespecial to call an appropriate constructor.
Is there a way to bypass the constructor when what you have is a Class (with the actual class to be determined at runtime)? If not, was the rationale for the difference between how this works, and how the byte code works, ever documented?
You wanna allocate an uninitialized object.
You can try the library named Objenesis.
Otherwise, you can create an object by serialization. This is a widely used method to create a uninitialized object.
public class Serialization {
static class TestSerialization implements Serializable {
int val = 0;
public TestSerialization() {
System.out.println("constructor");
val = 1;
}
#Override
public String toString() {
return "val is " + val;
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
TestSerialization testSerialization = new TestSerialization();
// constructor
// val is 1
System.out.println(testSerialization);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(testSerialization);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
Object obj = ois.readObject();
// val is 1
System.out.println(obj);
}
}
One step closer, you can use ReflectionFactory to create an empty uninitialized object.
public class Main {
static class TestClass {
public int val = 0;
public TestClass() {
val = 1;
}
#Override
public String toString() {
return "value is " + val;
}
}
public static void main(String[] args) throws Exception {
// by constructor
TestClass obj = new TestClass();
// value is 1
System.out.println(obj);
// by reflect
Constructor<TestClass> constructor = TestClass.class.getConstructor();
obj = constructor.newInstance();
// value is 1
System.out.println(obj);
// by ReflectionFactory
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
Constructor<Object> objectConstructor = Object.class.getDeclaredConstructor();
Constructor<?> targetConstructor = reflectionFactory.newConstructorForSerialization(TestClass.class, objectConstructor);
obj = (TestClass) targetConstructor.newInstance();
// value is 0
System.out.println(obj);
}
}
I have a fairly basic Java class with some class variables. I have overwridden toString() to provide me with a string output (which will eventually be output to a text file).
I am trying to elegantly create a way for me to use this string output to recreate the object with all of the variables set as before. The class looks something like this:
public class Report {
private String itemA;
private String itemB;
private String itemC;
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Items are::");
sb.append("\nItem A is: ").append(itemA);
sb.append("\nItem B is: ").append(itemB);
sb.append("\nItem C is: ").append(itemC);
return sb.toString();
}
}
this is how I can potentially tackle it using reflection:
public class Report {
private String itemA;
private String itemB;
private String itemC;
private final Map<String, String> MAPPING = new HashMap<>();
public Report(String itemA, String itemB, String itemC) {
this.itemA = itemA;
this.itemB = itemB;
this.itemC = itemC;
MAPPING.put("Item A is: ", "itemA");
MAPPING.put("Item B is: ", "itemB");
MAPPING.put("Item C is: ", "itemC");
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Items are::");
MAPPING.entrySet().forEach(entry -> {
sb.append("\n").append(entry.getKey()).append(BeanUtils.getProperty(this, entry.getValue()));
});
return sb.toString();
}
public Report createReportFromString(String reportString) {
List<String> reportLines = Arrays.asList(reportString.split("\n"));
HashMap<String, String> stringObjectRelationship = new HashMap<>();
reportLines.forEach(reportLine -> {
Optional<String> matchingKey = MAPPING.keySet().stream().filter(reportLine::contains).findFirst();
matchingKey.ifPresent(key -> {stringObjectRelationship.put(MAPPING.get(key), reportLine.split(key)[1]);});
});
stringObjectRelationship.forEach((variableName, variableValue) -> BeanUtils.setProperty(this, variableName, variableValue));
return this;
}
}
I basically want to relate the key in the report ("Item A is: ") to the name of the corresponding variable ("itemA") and use this relationship in both the toString() method and the createReportFromString(String string) method. Now when doing this there are a lot of possible exceptions that can be thrown and need to either be handled or thrown - and it then looks a lot less elegant than I would like.
I don't know if this is possible to do without reflection - or perhaps I could rearrange this class to make this possible?
What I can`t change is the structure of the string output in the toString().
Reflection bears multiple features:
Automatic discovery of features of a program at runtime
Support for dealing with features unknown at compile-time
Provide an abstraction of program features (e.g. methods or fields)
Your approach suggests that you don’t want an automatic discovery, as you are specifying the three elements explicitly. This is a good thing, as it makes your program more robust regarding future changes, as dealing with automatically discovered, potentially unknown program elements will destroy any help from the compiler, as it can’t tell you when there are mismatches.
You only want the third point, an abstraction over the elements of your report. You can create such an abstraction yourself, tailored to your use case, without Reflection, which will be more robust and even more efficient:
public class Report {
static final class Element {
final String header;
final Function<Report,String> getter;
final BiConsumer<Report,String> setter;
final Pattern pattern;
Element(String header,
Function<Report, String> getter, BiConsumer<Report, String> setter) {
this.header = header;
this.getter = getter;
this.setter = setter;
pattern = Pattern.compile("^\\Q"+header+"\\E(.*?)$", Pattern.MULTILINE);
}
}
static final List<Element> ELEMENTS = List.of(
new Element("Item A is: ", Report::getItemA, Report::setItemA),
new Element("Item B is: ", Report::getItemB, Report::setItemB),
new Element("Item C is: ", Report::getItemC, Report::setItemC));
private String itemA, itemB, itemC;
public Report(String itemA, String itemB, String itemC) {
this.itemA = itemA;
this.itemB = itemB;
this.itemC = itemC;
}
#Override public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Items are:");
ELEMENTS.forEach(e ->
sb.append('\n').append(e.header).append(e.getter.apply(this)));
return sb.toString();
}
public static Report createReportFromString(String reportString) {
return new Report("", "", "").setValuesFromString(reportString);
}
public Report setValuesFromString(String reportString) {
Matcher m = null;
for(Element e: ELEMENTS) {
if(m == null) m = e.pattern.matcher(reportString);
else m.usePattern(e.pattern).reset();
if(!m.find())
throw new IllegalArgumentException("missing \""+e.header+'"');
e.setter.accept(this, m.group(1));
}
return this;
}
public String getItemA() {
return itemA;
}
public void setItemA(String itemA) {
this.itemA = itemA;
}
public String getItemB() {
return itemB;
}
public void setItemB(String itemB) {
this.itemB = itemB;
}
public String getItemC() {
return itemC;
}
public void setItemC(String itemC) {
this.itemC = itemC;
}
}
This works with Java’s out-of-the-box features, not requiring another library to simplify the operation.
Note that I changed the code pattern, as createReportFromString is a misleading name for a method modifying an already existing object. I used the name for a factory method truly creating a new object and added a another method for setting the values of the object (as a direct counter-part to toString).
If you are still using Java 8, you can replace List.of(…) with Arrays.asList(…) or better Collections.unmodifiableList(Arrays.asList(…)).
You can also remove the .reset() call in the setValuesFromString method. When you remove it, the elements in the input string are required to be in the same order as the toString() method produces. This makes it a bit less flexible, but also more efficient if you expand the code to have a lot more elements.
#JimboMcHiggins assuming I can change the toString output how exactly would you tie together serialization and deserialization with some common mapping?
I would leave the toString unchanged and move the responsibility of serialization to java.io.Serializable. Correct me if this is not an acceptable approach. The mapping would be defined by the class fields of your Report pojo. This would also allow you to change your toString without breaking deserialization of existing objects.
import java.io.Serializable;
public class Report implements Serializable {
private static final long serialVersionUID = 1L;
private String itemA;
private String itemB;
private String itemC;
public Report(String itemA, String itemB, String itemC) {
this.itemA = itemA;
this.itemB = itemB;
this.itemC = itemC;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Items are::");
sb.append("\nItem A is: ").append(itemA);
sb.append("\nItem B is: ").append(itemB);
sb.append("\nItem C is: ").append(itemC);
return sb.toString();
}
}
Example Usage
public class Test1 {
public static void main(String[] args) {
Report report = new Report("W", "O", "W");
System.out.println(report);
String filename = "file.ser";
// Serialization
try
{
//Saving of report in a file
FileOutputStream file = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(file);
// Method for serialization of report
out.writeObject(report);
out.close();
file.close();
System.out.println("Report has been serialized");
}
catch(IOException ex)
{
System.out.println("IOException is caught");
}
Report report1 = null;
// Deserialization
try
{
// Reading the report from a file
FileInputStream file = new FileInputStream(filename);
ObjectInputStream in = new ObjectInputStream(file);
// Method for deserialization of report
report1 = (Report)in.readObject();
in.close();
file.close();
System.out.println("Report has been deserialized ");
System.out.println(report1);
}
catch(IOException ex)
{
System.out.println("IOException is caught");
}
catch(ClassNotFoundException ex)
{
System.out.println("ClassNotFoundException is caught");
}
}
}
Output
Items are::
Item A is: W
Item B is: O
Item C is: W
Report has been serialized
Report has been deserialized
Items are::
Item A is: W
Item B is: O
Item C is: W
Getting same Hashcode every time before serialization and after deserialization of object without using readResolve() method in Java why ?
Here is my class
public class SerializedSingletonClass implements Serializable{
private static final long serialVersionUID = 18989987986l;
private SerializedSingletonClass(){};
private static class InstanceHelper {
private static SerializedSingletonClass obj = new SerializedSingletonClass();
}
public static SerializedSingletonClass getInstance(){
return InstanceHelper.obj;
}
}
Test Class --
public class TestSingleton {
public static void main(String[] args) throws FileNotFoundException,
IOException, ClassNotFoundException {
// Test Serialization for singleton pattern
SerializedSingletonClass instanse1 = SerializedSingletonClass
.getInstance();
ObjectOutputStream obs = new ObjectOutputStream(new FileOutputStream(
"filename1.ser"));
obs.writeObject(instanse1);
obs.close();
ObjectInputStream objInputStream = new ObjectInputStream(
new FileInputStream("filename1.ser"));
SerializedSingletonClass instance2 = (SerializedSingletonClass) objInputStream
.readObject();
objInputStream.close();
System.out.println("instance1==" + instanse1.getClass().hashCode());
System.out.println("instance2==" + instance2.getClass().hashCode());
}
}
Output ::
instance1==1175576547
instance2==1175576547
Your objects are instances of the same class, SerializedSingletonClass. You're getting the hashCode from the class, not from the instance. instanse1.getClass() evaluates to the same thing as instance2.getClass(), so of course they produce the same hashCode.
To find the hashCode of the objects, use instanse1.hashCode() and instance2.hashCode().
I'm trying to load an object in from a file. I first create the file by saving an object to it. If I save only one object to a file I can get it loaded in by casting the object into a variable stead of an arraylist. But if I try to cast mulitple objects into an arraylist I keep getting errors. I sometimes will get this:
animalkingdom.AnimalBuild; local class incompatible: stream classdesc
serialVersionUID = 8814442576780984798, local class serialVersionUID =
-7073710162342893881
or this
Exception in thread "main" java.lang.ClassCastException:
animalkingdom.AnimalBuild cannot be cast to java.util.ArrayList at
animalkingdom.AnimalKingdom.readFile(AnimalKingdom.java:146) at
animalkingdom.AnimalKingdom.main(AnimalKingdom.java:123) Java Result:
1
write function
// function to write object to file
public static void writeToFile(ArrayList<AnimalBuild> a) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream("animal2.txt"));
for (AnimalBuild s : a) { // loop through and write objects to file.
oos.writeObject(s);
}
}
read function
// function to read from file
public static void readFile() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("animal2.txt"));
#SuppressWarnings("unchecked")
ArrayList<AnimalBuild> animalList = (ArrayList<AnimalBuild>)ois.readObject(); // casting object
Iterator it = animalList.iterator();
while(it.hasNext()) {
String obj = (String)it.next();
System.out.println(obj);
}
}
Animal Build
class AnimalBuild implements Serializable {
private static final long serialVersionUID = 8814442576780984798L;
//private static final long serialVersionUID = -12049485535619732L;
public String Animaltype, Species, Color;
public AnimalBuild (String animaltype , String species, String color )
{
this.Animaltype = animaltype;
this.Species = species;
this.Color = color;
}
public String getType() {
return this.Animaltype;
}
public String getSpecies() {
return this.Species;
}
public String getColor() {
return this.Color;
}
public String setType(String newType) {
return (this.Animaltype=newType);
}
public String setSpecies(String newSpecies) {
return (this.Species=newSpecies);
}
public String setColor(String newColor) {
return (this.Color=newColor);
}
public String toString ()
{
return "\n\n Animal Type: " + this.Animaltype + "\n Species: " + this.Species + "\n Color: " + this.Color + "\n";
}
}
When you serialize data you need to read it in a compatible manner to how it was written. You are writing each element individually so to read this you would need to read them individually.
However, writing a list is simpler.
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("animal2.txt"))) {
oos.writeObject(a);
}
To read the list
List<AnimalBuild> animalList;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("animal2.txt"))) {
animalList = (List<AnimalBuild>) ois.readObject(); // casting object
}
Read the object one by one then add to list. Just reverse the way you write the object. Check sample code below:
List<AnimalBuild> animalList = new ArrayList<AnimalBuild>();
Object obj = null;
while ((obj = ois.readObject()) != null) {
if (obj instanceof AnimalBuild) {
AnimalBuild ab = (AnimalBuild) obj;
animalList .add(ab);
}
}
You could write the entire list as one object to the file and then read it back as same object rather than writing one by one.
I'm trying to implement an equivalent to String.intern(), but for other objets.
My goal is the following:
I've an object A which I will serialize and then deserialize.
If there is another reference to A somewhere, I want the result of the deserialization to be the same reference.
Here is one example of what I would expect.
MyObject A = new MyObject();
A.data1 = 1;
A.data2 = 2;
byte[] serialized = serialize(A);
A.data1 = 3;
MyObject B = deserialize(serialized); // B!=A and B.data1=1, B.data2=2
MyObject C = B.intern(); // Here we should have C == A. Consequently C.data1=3 AND C.data2=2
Here is my implementation atm. (the MyObject class extends InternableObject)
public abstract class InternableObject {
private static final AtomicLong maxObjectId = new AtomicLong();
private static final Map<Long, InternableObject> dataMap = new ConcurrentHashMap<>();
private final long objectId;
public InternableObject() {
this.objectId = maxObjectId.incrementAndGet();
dataMap.put(this.objectId, this);
}
#Override
protected void finalize() throws Throwable {
super.finalize();
dataMap.remove(this.objectId);
}
public final InternableObject intern() {
return intern(this);
}
public static InternableObject intern(InternableObject o) {
InternableObject r = dataMap.get(o.objectId);
if (r == null) {
throw new IllegalStateException();
} else {
return r;
}
}
}
My unit test (which fails):
private static class MyData extends InternableObject implements Serializable {
public int data;
public MyData(int data) {
this.data = data;
}
}
#Test
public void testIntern() throws Exception {
MyData data1 = new MyData(7);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(data1);
oos.flush();
baos.flush();
oos.close();
baos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
MyData data2 = (MyData) ois.readObject();
Assert.assertTrue(data1 == data2.intern()); // Fails here
}
The failure is due to the fact that, when deserializing, the constructor of InternableObject is called, and thus objectId will be 2 (even if the serialized data contains "1")
Any idea about how to solve this particular problem or, another approach to handle the high level problem ?
Thanks guys
Do not use the constructor to create instances. Use a factory method that checks if an instance already exists first, only create an instance if there isn't already a matching one.
To get serialization to cooperate, your class will need to make use of readResolve() / writeReplace(). http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serial-arch.html#4539
The way you implemented your constructor, you're leaking a reference during construction, which can lead to very hard to nail down problems. Also, your instance map isn't protected by any locks, so its not thread save.
Typically intern() forms an aspect, and maybe should not be realized as a base class, maybe too restricting its usage in a more complex constellation.
There are two aspects:
1. Sharing the "same" object.
Internalizing an object only gives a profit, when several objects can be "internalized" to the same object. So I think, that InternalableObjecte. with a new sequential number is not really adequate. More important is that the class defines a fitting equals and hashCode.
Then you can do an identity Map<Object, Object>:
public class InternMap {
private final Map<Object, Object> identityMap = new HashMap<>();
public static <I extends Internalizable<?>> Object intern(I x) {
Object first = identityMap.get(x);
if (first == null) {
first = x;
identityMap.put(x, x);
}
return first;
}
}
InternMap could be used for any class, but above we restrict it to Internalizable things.
2. Replacing a dynamically created non-shared object with it's .intern().
Which in Java 8 could be realised with a defualt method in an interface:
interface Internalizable<T> {
public static final InternMap interns = new InternMap();
public default T intern(Class<T> klazz) {
return klazz.cast(internMap.intern(this));
}
class C implements Internalizable<C> { ... }
C x = new C();
x = x.intern(C.class);
The Class<T> parameter needed because of type erasure. Concurrency disregarded here.
Prior to Java 8, just use an empty interface Internalizable as _marker: interface, and use a static InternMap.