Kryo does serialization very similar to what you would write by hand. Eg, take this class:
public class Moo {
public int damage;
public String name;
}
Kryo will write an int ID that represents the class using variable length encoding, so it will be 1 byte. Then it writes an int for “damage” using variable length encoding, so numbers closer to zero will be 1 byte, 2 bytes further away, etc. If you know the damage is always positive, you can tell Kryo and it will write the int more efficiently for positive numbers. Next it writes the name string. If the string is short and it detects ASCII, it will use a 7 bit encoding with the 8th bit to denote if there is another character. Otherwise it will write the number of characters, then the UTF8 characters. The code to do all this would be:
kryo.writeObject(output, moo);
You would be hard pressed to represent the information more efficiently by hand and certainly not with less code. Kryo uses code generation to access fields if they are public, protected, or default access, so doing that is recommended. Serialization speed is not going to be your bottleneck, but doing it by hand is faster. Kryo can still help when doing it by hand. There are two ways to do that, like this…
public class Moo implements KryoSerializable {
public int damage;
public String name;
public void write (Kryo kryo, Output output) {
output.writeInt(damage, true);
output.writeString(name);
}
public void read (Kryo kryo, Input input) {
damage = input.readInt(true);
name = input.readString();
}
}
Or like this:
kryo.register(Moo.class, new Serializer<Moo>() {
public void write (Kryo kryo, Output output, Moo moo) {
output.writeInt(moo.damage, true);
output.writeString(moo.name);
}
public Moo read (Kryo kryo, Input input, Class<Moo> type) {
Moo moo = new Moo();
moo.damage = input.readInt(true);
moo.name = input.readString();
return moo;
}
});
The parameter name for the true passed to write/readInt is named “optimizePositive”, which gets you the variable length encoding. Pass nothing for a 4 byte int. If you know the string is ASCII you can call writeAscii, otherwise writeString does the magic described above to detect ASCII or write UTF8. Either way, you always use readString.
If you have ArrayLists, Maps, or other objects, Kryo can do most or all of the work for you, without a speed penalty. I can post an example if you want.
It sounds like you are only passing objects over the network, but if you are putting data into a database, you might think about how your data will evolve. If you add a field, all the data in your database won’t deserialize. See that link for more. If hand rolling serialization, you can do this…
public class Moo implements KryoSerializable {
public int damage;
public String name;
public void write (Kryo kryo, Output output) {
output.writeInt(damage, true);
output.writeString(name);
output.writeBoolean(false);
}
public void read (Kryo kryo, Input input) {
damage = input.readInt(true);
name = input.readString();
input.readBoolean();
}
}
Now when you add a field later:
public class Moo implements KryoSerializable {
public int damage;
public String name;
public float newField;
public void write (Kryo kryo, Output output) {
output.writeInt(damage, true);
output.writeString(name);
output.writeBoolean(true);
output.writeFloat(newField);
output.writeBoolean(false);
}
public void read (Kryo kryo, Input input) {
damage = input.readInt(true);
name = input.readString();
if (input.readBoolean()) {
newField = input.readFloat();
input.readBoolean();
}
}
}
This costs 1 extra byte, but means you can additively evolve your classes without invalidating previously serialized bytes. Note you don’t need this for objects that are only going over the network, only for objects going to longer term storage (disk/prefs/db/etc).