Serializing a vector - java

I'm trying to implement loading and saving for a game I'm working on.
What I want to save is:
A char[][] (bidimensional array/matrix)
An ArrayList<Entity>
Entity is a super class for Dragon, Hero and Item. All three of these types can be contained at once in the ArrayList.
So far I have this:
package logic;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public final class LoadAndSave {
public static final transient boolean available = false;
public static final boolean serialize(Object obj) {
// Write to disk with FileOutputStream
FileOutputStream saveFile;
try {
saveFile = new FileOutputStream("game.sav");
} catch (FileNotFoundException e) {
return false;
}
// Write object with ObjectOutputStream
ObjectOutputStream objOut;
try {
objOut = new ObjectOutputStream(saveFile);
} catch (IOException e) {
//
return false;
}
// Write object out to disk
try {
objOut.writeObject(obj);
} catch (IOException e) {
return false;
}
return true;
}
public static final Object load() {
FileInputStream fileIn;
try {
fileIn = new FileInputStream("game.sav");
} catch (FileNotFoundException e1) {
return null;
}
// Read object using ObjectInputStream
ObjectInputStream objIn;
try {
objIn = new ObjectInputStream(fileIn);
} catch (IOException e) {
return null;
}
// Read an object
Object obj;
try {
obj = objIn.readObject();
} catch (IOException e) {
return null;
} catch (ClassNotFoundException e) {
return null;
}
return obj;
}
}
I think the code is pretty self-explanatory. Now for my questions:
Will this code suffice?
Do I need to implement specific serialization methods for Dragon, Item and Hero?
How will the serialization mechanism deal with the fact that I have an Entity vector full of types that are not Entity, but derived classes?
Thanks for your time!
OK, all seems to be well, except for one the ArrayList. It is either not getting saved or loaded (null pointer exception when calling size()).
What may this be due to?

If you want the entire structure to be serializable, then you'll need to make the parts serialiable as well. What this means in practice is that you need to make your Dragon, Hero, and Item classes implement Serializable. It's just a marker interface, so you only need to add implements Serializable. Some ides (at least eclipse, anyway) may complain that you should add a private long SerialVersionUID which may or may not optimize the serialization a bit - in any case it's not strictly necessary.
This requirement is recursive - any of their subparts that are custom or non-serializable objects have to be taken care of as well, so for example if your Item class looks like this
public class Item implements Serializable {
private String s;
private MyClass c;
..
then you'll also need to make MyClass Serializable, any of it's instance variables, etc etc.

I don't see any reason for specific serialization methods for Dragon, Item and Hero unless you require some very special stuff that the default serialization mechanism cannot handle.
(Of course the classes need to be serializable as mentioned by Steve B)
You already have a lot of code so I suspect that you've already run some tests. Have you faced certain problems? Or how does it come that you are asking this?
Update:
I've noticed some aspects in your code which are not directly related to your question but may anyway be of interest for you:
Do you really mean to use Vector? Does your application make use of multithreading and serialization is required? If not you might prefer to use ArrayList or so instead.
The way you handle exceptions is quite strange. Perhaps you are only doing so as this is an example... You should not suppress exceptions by simply returning true or false. If you expect calling methods to be interested in exceptions you should simply add the exception to the methods signatur (throws...) and let the calling method care about it. If you feel this is not appropriate wrapping such exceptions in custom exceptions, which are more expressive in the context, might be an option. If you don't care about the exception at all wrapping it into a runtime exception is also possible. In case of success your method might simply return nothing...

Related

Invoke Java object constant using a variable

I'm very new to Java so it makes it hard for me to explain what I'm trying to do.
I have an abstract class that invokes several object constants like this:
public abstract class Enchantment implements Keyed {
/**
* Provides protection against environmental damage
*/
public static final Enchantment PROTECTION_ENVIRONMENTAL = new EnchantmentWrapper("protection");
In a different file I can access this perfectly fine with Enchantment value = Enchantment.PROTECTION_ENVIRONMENTAL;
However, I'm trying to use a string variable for this instead. Something like this:
String str = "PROTECTION_ENVIRONMENTAL";
Enchantment value = Enchantment.str;
Obviously that won't work. So I did a bunch of research and learned I need to use reflection for this. Using this source code's docs I figured I was looking for field data. So I tried both:
Field fld = Enchantment.class.getField("PROTECTION_ENVIRONMENTAL");
Field fld = Enchantment.class.getDeclaredField("PROTECTION_ENVIRONMENTAL");
But these returned me a NoSuchFieldException. As I was on it, I've tried both getMethod() and getDeclaredMethod() just as well equally with no luck.
I'm now at the point that these are probably "object constants"? I'm not sure how to call them. But I'm definitely at a loss on how to get this to work now and after everything I've tried myself, I figured it was time to ask for some help here.
That one comment is spot on: you absolutely do not use reflection here.
There are only two valid reasons to use reflection:
you are creating a framework that has to deal with classes it doesn't know about
you have for some other reason to deal with classes you don't know about at compile time
But your code perfectly knows about that Enchantment class, its capabilities, and so on. Therefore reflection is the wrong approach. You figured it yourself: it is damn hard to get right, and damn right to get it wrong in some subtle ways. And when you get it wrong, it always blows up at runtime. Reflection code compiling means nothing. It always waits for you to run it to throw up in your face.
So to answer your question by not answering it: use a Map. Like:
Map<String, Enchantment> enchantmentsByConstantName = new HashMap<>();
enchantmentsByConstantName.put("PROTECTION_ENVIRONMENTAL", PROTECTION_ENVIRONMENTAL);
Alternatively, these constants could go into an enum, as outlined in the other answer, but in a sightly different way:
enum EnchantmentHolder {
PROTECTION_ENVIRONMENTAL(new EnchantmentWrapper("protection")),
ANOTHER_ENCHANTMENT(...)
A_THIRD_ENCHANTMENT(...)
...;
private Enchantment enchantment;
private EnchantmentHolder(Enchantment enchantment) {
this.entchantment = entchantment;
}
public Enchantment getEntchantment() { return entchantment; }
You may want to look into enumerations if you know they're going to be constant values;
public enum Enchantment {
PROTECTION_ENVIRONMENTAL {
public void cast() {
// do enum-specific stuff here
}
},
ANOTHER_ENCHANTMENT {
public void cast() {
// do enum-specific stuff here
}
},
A_THIRD_ENCHANTMENT{
public void cast() {
// do enum-specific stuff here
}
};
public abstract void cast();
}
enums can be treated like classes and have methods and properties. You can also convert to and from strings Enchantment.valueOf("PROTECTION_ENVIRONMENTAL") but that's generally if you are reading from a configuration file - in code you'd reference the value directly.
Once you have the Field, you need to call Field.get(Object) with an instance (in this case the class). Something like,
Class<?> cls = Enchantment.class;
try {
Field f = cls.getField("PROTECTION_ENVIRONMENTAL");
System.out.println(f.get(cls));
} catch (Exception e) {
e.printStackTrace();
}
Since you want the Enchantment, you could then test that the instance you get is assignable to Enchantment. Something like,
Class<? extends Enchantment> cls = Enchantment.class;
try {
Field f = cls.getField("PROTECTION_ENVIRONMENTAL");
Object obj = f.get(cls);
if (cls.isAssignableFrom(obj.getClass())) {
Enchantment e = cls.cast(obj);
System.out.println(e);
}
} catch (Exception e) {
e.printStackTrace();
}
But the enum approach is better.

How can I prevent invoking a static method on an instance of my class? [duplicate]

This question already has answers here:
Can we call a static method with a null object in Java? If so, how?
(4 answers)
Closed 4 years ago.
I have the following class, which is used for controlling some debugging and beta testing options in various places in my Android app. It just contains some flags and some logic to (de)serialize it to/from JSON.
public class DevConfiguration {
public boolean dontSendSMS;
public static String toJsonString(DevConfiguration devConfiguration) {
JSONObject json = new JSONObject();
if( devConfiguration != null ) {
try {
json.put("dontSendSMS", devConfiguration.dontSendSMS);
} catch (JSONException e) {
e.printStackTrace();
}
}
return json.toString();
}
public static DevConfiguration fromJsonString(String jsonString) {
if( jsonString.isEmpty() )
return null;
DevConfiguration result = new DevConfiguration();
try {
JSONObject jsonObj = new JSONObject(jsonString);
result.dontSendSMS = jsonObj.optBoolean("dontSendSMS", false);
} catch (JSONException e) {
e.printStackTrace();
}
return result;
}
}
Now, in one of my services I receive a serialized DevConfiguration object in an Intent, and might later pass it on to another service:
serviceIntent.putExtra("dev_conf", DevConfiguration.toJsonString(mDevConfiguration));
I choose to make the toJsonString() method static, so that I don't risk invoking it on a null instance. However, it's still possible to make a mistake somewhere and invoking the static method on an instance - potentially a null instance!
mDevConfiguration.toJsonString(mDevConfiguration);
There is a Lint warning in Android Studio, but still it's a potential NullPointerException bug waiting to happen. I thought it might be possible to hide it by defining a similar private method but with a different signature
/** Hide instance implementation **/
private String toJsonString(Object o){ return ""; }
but of course calling it with a DevConfiguration parameter will invoke the static method anyway, and the IDE doesn't give any more warnings than before either.
Is there any way to "hide" the static method from instance variables?
EDIT
Comments make it clear that invoking a static method on a null instance is perfectly legal. However, the question is not "How do I prevent a NullPointerException when invoking a static method on a null instance?", but the more general "How can I prevent invoking a static method on an instance of my class?".
In other words - is there any way to prevent the compiler from compiling if one accidentally tries to invoke a static method on an instance?
Calling a static method on a variable with null value will not raise NullPointerException. Following code will print 42 even though variable i is null.
public class Test {
public static void main(String... args) {
Integer i = null;
System.out.println(i.parseInt("42"));
}
}
When calling static methods by variable, what really matters is the declared type of the variable and not the referenced type of its value. This is related to the fact that static methods in java are not polymorphic.
„How can I prevent invoking a static method on an instance of my class?"
Calling static methods by variable is just a regular language feature defined in the Java spec. I’d be surprised if there were any method to suppress it in general.
If I had to do it for a selected class, I would probably migrate static methods to a separate „companion” utility (as described in another answer).
But having such static (factory) methods in your class is a perfectly fine idiom (see for example: Joshua Bloch, „Effective Java”, Item 1: Consider static factory methods instead of constructors). I wouldn’t easily give up on it.
I see a few ways you could do this:
Use a Utils class:
public class Utils {
public static String toJsonString(DevConfiguration devConfiguration) {
JSONObject json = new JSONObject();
if( devConfiguration != null ) {
try {
json.put("dontSendSMS", devConfiguration.dontSendSMS);
} catch (JSONException e) {
e.printStackTrace();
}
}
return json.toString();
}
public static DevConfiguration fromJsonString(String jsonString) {
if( jsonString.isEmpty() )
return null;
DevConfiguration result = new DevConfiguration();
try {
JSONObject jsonObj = new JSONObject(jsonString);
result.dontSendSMS = jsonObj.optBoolean("dontSendSMS", false);
} catch (JSONException e) {
e.printStackTrace();
}
return result;
}
}
Now you can just makes calls to Utils.method() and avoid confusion.
Use Kotlin
Kotlin actually makes it really hard (if not impossible) to call a static method on a dynamic receiver. It won't show in the method suggestions, and will underline in red if you type it manually. It might not even compile, although I haven't gotten that far.
Kotlin also has built-in null protection: instance?.method(). The ? means method() just won't execute if instance is null.
Just don't call a static method on a dynamic receiver. If you do it by accident, go back and fix it. You shouldn't be relying on Java to work around your syntax errors for you.
Finally, why even do this? I highly doubt mDevConfiguration is ever null, unless you initialize it in a really weird spot. If it is, you may want to look at reorganizing your code. Because, again, you shouldn't be relying on Java to work around your syntax errors for you. Also, if it is null, it won't throw an NPE, at least in Java, since it doesn't need a dynamic receiver to run (this is probably different in Kotlin).
It's up to you to make code that works as it should, and implement the proper null checks, error handling, etc. If you miss something, it's no big deal; that's why you test your code and fix the crashes and bugs you catch before you release it. Anything you don't catch will be reported by the Google Play Console (if you publish there) or Firebase (if you implement that) or your users.
Sorry if the above sounds harsh, but I'm really having trouble seeing why you'd want to do this instead of just checking your code.
If you really want to keep this structure, at least make the constructor for DevConfiguration private:
public class DevConfiguration {
//...
private DevConfiguration() {}
//...
}
That way, only the static methods inside it can create an instance.

Writing Junit test to cover exception and catch block

I have written Junit test case for following function. When checked JACOCO test coverage. It is showing only try block is covered by test case. I am newbie to writing test cases. How the exceptions and catch block can be covered in test cases
Here is a method
public static List<Student> readCsvFile(String fileName)
{
BufferedReader fileReader = null;
//logic to read file
}
catch (Exception e)
{
System.out.println("Error in CsvFileReader !!!");
e.printStackTrace();
} finally
{
try
{
fileReader.close();
} catch (IOException e)
{
System.out.println("Error while closing fileReader !!!");
e.printStackTrace();
}
}
return students;
}
And TestMethod
#Test
public void ReadCsvFileTest()
{
String fileName = "test.csv";
List<Student> result = new ArrayList<Student>();
result = CsvFileReader.readCsvFile(fileName);
Student student1 = null;
Iterator<Student> it = result.iterator();
while (it.hasNext())
{
Student s = it.next();
if ("471908US".equals(s.getId()))
{
student1 = s;
break;
}
}
assertTrue(student1 != null);
}
In such situations you may often consider the introduction of additional dependencies to your class. Here is what I mean with a rough example. Create a factory for readers:
interface BufferedReaderFactory
{
public BufferedReader createBufferedReader(String fileName) throws IOException;
}
Then you will have a trivial implementation that hardly needs any testing, e.g. something similar:
class BufferedReaderFactoryImpl implements BufferedReaderFactory
{
#Override
public BufferedReader createBufferedReader(String fileName) throws IOException
{
return new BufferedReader(new FileReader(fileName));
}
}
Then you have to find a way to inject this dependency into your class. I usually use Guice in my daily work but you may try something as simple as using constructor injection and making your method non static. Here is an example:
class CsvFileReader
{
private final BufferedReaderFactory factory;
public CsvFileReader(BufferedReaderFactory factory)
{
this.factory = factory;
}
public List<Student> readCsvFile(String fileName)
{
BufferedReader fileReader = null;
try
{
fileReader = factory.createBufferedReader(fileName);
...
}
catch(IOException e)
{
...
}
finally
{
...
}
return new LinkedList<>();
}
}
With a mocking framework like Mockito the behavior of this class in case of IOException-s is easier to test now (note that you may also return mocks that throw exceptions from the factory). Here is a sample:
#RunWith(MockitoJUnitRunner.class)
public class MyTest
{
#Mock
private BufferedReaderFactory mockFactroy;
#Test
public void testIOException() throws IOException
{
String ivalidFileName = "invalid.txt";
//throw exception in case that invalid file name is passed to the factory
Mockito.when(mockFactroy.createBufferedReader(ivalidFileName)).thenThrow(new IOException("Hello!"));
CsvFileReader csvFileReader = new CsvFileReader(mockFactroy);
//invoke with a factory that throws exceptions
csvFileReader.readCsvFile(ivalidFileName);
//...
//and make a sensible test here, e.g. check that empty list is returned, or proper message is logged, etc.
}
}
You may do that without Mockito, of course - by implementing a test factory. But this is more cumbersome especially in more complicated use cases. Once the IOException is thrown you will get appropriate coverage report by JaCoCo.
Also mind a limitation of JaCoCo mentioned here, in section Source code lines with exceptions show no coverage. Why?
Given the current signature of your method under test, getting to full coverage isn't easy: your catch block is only executed when an exception is thrown within your try block.
One way to solve this: do not pass in the file name, but the reader object itself. Like:
public static List<Student> readCsvFile(String fileName) {
return readCsvFile(new BufferedReader(fileName));
}
static List<Student> readCsvFile(BufferedReader reader) {
try {
...
} catch( ...
Now you can write several specific unit tests for that second method. You keep your tests that simply do "correct" reading; but you add one where you pass in a mocked reader object ... that simply throws an Exception at some point. Please note that I made that new method just package protected - you probably don't want to use that "public"; and making it private would prevent it from being unit tested.
That should help you achieving full coverage. Of course you will also need at least one test to "cover" the string-taking method, too.
Some notes:
Be careful about re-inventing the wheel. There are many existing CSV parsers already. And be assured: writing a correct CSV parser that is able to deal with all "correct" input CSV is much harder than it sounds. If this is not for "learning purposes" I strongly advise to not write your own CSV parser.
Be careful about making such things static. As said, a real CSV parser is a complicated thing, and worth its complete own class. So no static helper methods - a normal class which you instantiate to then call methods on it (that would also for using dependency injection which would help with the problem you are asking about ... getting exceptions thrown within try blocks)
You are catching Exception in your code example. Don't do that - try to catch exactly those exceptions that your code can actually produce (probably IOException in your case).

Dynamically loading method from external class

I am trying to load methods Customer.cypher and Customer.cypherCBC method from my class Configuration. Customer class is rendering from different environments so few environmets are having cypherCBC() and cypher() method and few are having only cypher() method.
Now i want to check if cypherCBC if not there in Customer class then load cypher() method. My function is so far;
try {
Class<?> customerClass = Class.forName("com.myapp.impl.service.Customer");
Object obj = customerClass.newInstance();
//here getting "NoSuchMethodException" exception
Method methodCBC = customerClass.getDeclaredMethod("cypherCBC", String.class); //line - 7
if(methodCBC.getName().equals("cypherCBC")){
methodCBC.invoke(obj, new String(dbshPass));
System.out.println("CYPHER_CBC: "
+ methodCBC.invoke(obj, new String(dbshPass)));
}else{
Method method = customerClass.getDeclaredMethod("cypher", String.class);
method.invoke(obj, new String(dbshPass));
System.out.println("CYPHER: " + method.invoke(obj, new String(dbshPass)));
}
}catch (Exception e){
e.printStackTrace();
}
Getting an error at line 7.
NoSuchMethodException:
com.myapp.impl.service.Customer.cypherCBC(java.lang.String)
that means for particular environment class Customer doesn't having cypherCBC() method, but ideally it should come in else part and execute cypher() method.
Class<?> client = null;
Object obj = null;
try{
client = Class.forName("com.myapp.impl.service.Client");
obj = client.newInstance();
}catch (InstantiationException ex) {
System.err.println("Not able to create Instance of Class");
} catch (IllegalAccessException ex) {
System.err.println("Not able to access Class");
} catch (ClassNotFoundException ex) {
System.err.println("Not able to find Class");
}
try {
Method methodCBC = client.getDeclaredMethod("cypherCBC", String.class);
System.out.println("CYPHER_CBC: " + methodCBC.invoke(obj, new String(dbshPass)));
}catch (NoSuchMethodException ex) {
System.err.println("Not able to find Method on class");
ex.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
That is exactly what is to be expected: getDeclaredMethod() throws that exception when no method exists that meets your specification. And you are wondering that it throws an exception if the required method is missing? Hint: better read the javadoc next time. Don't assume that something does something, but verify your assumptions!
Besides: read your code again. What is it doing? You are asking "give me the method named 'foo'". And then, your next step is to ask that method "is your name 'foo'". So even without reading javadoc, it should become clear that your logic is flawed.
As solution, you can implement a non-throwing lookup yourself, like
private Method lookupCypher(Class<?> client, String methodName) {
for (Method declaredMethod : client.getDeclardMethods()) {
if (declaredMethod.getName().equals(methodName)) {
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
if (parameterTypes.length == 1 && parameterTypes[0].equals(String.class)) {
// so declaredMethod has the given name, and takes one string as argument!
return declaredMethod;
}
}
// our search didn't reveal any matching method!
return null;
}
Using that helper method, you can rewrite your code to:
Method toInvoke = lookupCypher(client, "cypherCBC");
if (toInvoke == null) {
toInvoke = lookupCypher(client, "cypher");
}
toInvoke(obj, new String ...
Or, with the idea from hunter in mind; a much more "OO like" version:
interface CustomerCypherWrapper {
void cypher(String phrase);
}
class NewCustomerWrapper() implements CustomerCypherWrapper {
#Override
void cypher(String phrase) {
new Customer.cypherCBC(phrase);
}
}
class oldCustomerWrapper() implements CustomerCypherWrapper {
#Override
void cypher(String phrase) {
new Customer.cypher(phrase);
}
}
And your client code boils down to:
CustomerCypherWrapper wrapper =
(lookupCypher(..., "cypherCBC") == null)
? new NewCustomerWrapper()
: new OldCustomerWrapper();
wrapper.cypher();
[ I hope you notice that my version A) is easier to read and B) doesn't contain any duplicated code any more. ]
And yes, an alternative implementation of the lookup method could just go like
private Method lookupCyper(Client<?>, String methodName) {
try {
return client.getDeclaredMethod(methodName, String.class);
} catch ....
and return null;
}
... return your public cypherCBC method
But that is an "uncommon practice" in Java. In Java, we ask for permission; instead of forgiveness. Unlike other languages
if you compile the application with a Customer class which has both method,you can use reflection once to check whether the cypherCBC method available or not at runtime, then you can keep that status, you can call the method without using reflection
if(newVersion)
{
customer.cypherCBC(arg);
}
else
{
customer.cypher(arg);
}
But to write a better application,you should use two version baselines.
even though this is a small code fragment you should setup a another module to hide this Customer class and its interactions,that module should have two versions. but your main module has only single version.Now when you you deliver the application , product should be packaged with right version baseline based on compatibility for the target environment.
Although reflection works (as explained in the other answers). if you have control over the Customer class, you can try a non-reflection approach.
interface CBCCypherable {
public String cypherCBC(String pass);
}
You can now have two versions of Customer class, one that implements CBCCypherable and one that doesn't. And when you call it, it looks like this:
Customer c = new Customer();
if (c instanceof CBCCypherable) {
((CBCCypherable)c).cypherCBC(pass);
} else {
c.cypher(pass);
}
What you get with this solution is much simpler code, and as a bonus the compiler will check that you use the correct method name and parameter types. Unlike with reflection, where that's all your job, and you have to run the code to find out if something's wrong.
P.s.: I don't know if this is just sample code or you are really encrypting/hashing passwords here, but it's generally considered a bad idea to roll your own security code.

Trouble accessing fields of a serialized object in Java

I have instantized a class that implements Serializable and I am trying to stream that object like this:
try{
Socket socket = new Socket("localhost", 8000);
ObjectOutputStream toServer = new ObjectOutputStream(socket.getOutputStream());
toServer.writeObject(myObject);
} catch (IOException ex) {
System.err.println(ex);
}
All good so far right? Then I am trying to read the fields of that object like this:
//This is an inner class
class HandleClient implements Runnable{
private ObjectInputStream fromClient;
private Socket socket; // This socket was established earlier
try {
fromClient = new ObjectInputStream(socket.getInputStream());
GetField inputObjectFields = fromClient.readFields();
double myFristVariable = inputObjectFields.get("myFirstVariable", 0);
int mySecondVariable = inputObjectFields.get("mySecondVariable", 0);
//do stuff
} catch (IOException ex) {
System.err.println(ex);
} catch (ClassNotFoundException ex) {
System.err.println(ex);
} finally {
try {
fromClient.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
But I always get the error:
java.io.NotActiveException: not in call to readObject
This is my first time streaming objects instead of primitive data types, what am I doing wrong?
BONUS
When I do get this working correctly, is the ENTIRE CLASS passed with the serialized object (i.e. will I have access to the methods of the object's class)? My reading suggests that the entire class is passed with the object, but I have been unable to use the objects methods thus far. How exactly do I call on the object's methods?
In addition to my code above I also experimented with the readObject method, but I was probably using it wrong too because I couldn't get it to work. Please enlighten me.
To answer your first question:
You need to use ObjectInputStream.readObject to deserialize. You cannot read individual fields from the stream*.
fromClient = new ObjectInputStream(socket.getInputStream());
Object myObject = fromClient.readObject();
Don't forget to flush the output stream when writing!
The second question is a little more complex. What the serialization mechanism does is write a class identifier to the stream followed by the serialized object data. When it deserializes it will read the class identifier and attempt to load that class (if it isn't already loaded). It will then instantiate the object using the no-arg constructor and call the private readObject(ObjectInputStream) method. Yes, that's right, it calls a private method from outside the class. Java serialization is special.
If the class cannot be found (i.e. if it's not on the classpath) then an exception will be thrown; otherwise you'll get a fully deserialized object of the correct type assuming no other errors are found.
For example, suppose you have the following classes:
class Server {
public static void main(String[] args) {
// Set up an OutputStream sink, e.g. writing to a socket (not shown)
...
ObjectOutputStream out = new ObjectOutputStream(sink);
out.writeObject(new Data("data goes here"));
out.flush();
out.close();
}
}
class Client {
public static void main(String[] args) {
// Set up an InputStream source (not shown)
...
ObjectInputStream in = new ObjectInputStream(source);
Data d = (Data)in.readObject();
System.out.println(d.getData());
}
}
class Data implements java.io.Serializable {
private String data;
public Data(String d) {
data = d;
}
public String getData() {
return data;
}
}
Now suppose you put those classes into three jars (one class per jar): server.jar, client.jar and data.jar. If you run the following commands then it should all work:
java -cp server.jar:data.jar Server
java -cp client.jar:data.jar Client
But if you do this:
java -cp server.jar:data.jar Server
java -cp client.jar Client
then you'll get a ClassNotFoundException because the client doesn't know how to find the Data class.
Long story short: the class itself is not written to the stream. If deserialization succeeds then you will have access to the object as though it had been created locally, but you will have to downcast the result of readObject to the expected type.
There is some complexity around versioning that I've ignored for now. Take a look at serialVersionUID and how to deal with changes to serializable classes if versioning is likely to be an issue.
*Not strictly true. You can call readFields inside the serializable object's readObject method (or readResolve), but you cannot call it from outside the deserialization mechanism. Does that make sense? It's a little hard to explain.
Looking at the code for ObjectInputStream.readFields(), that exception is called because the curContext field is null. You should call fromClient.readObject() before calling readFields(), as it will set the curContext. Note that readObject() will return the instance that is being serialized, which may be of more use to you.

Categories