Make it immutable by keeping all state information in a form where it can not be changed after the object is created.
Java does not allow perfect immutability in some cases.
Serializable is something that you can do but it isn't perfect because there has to be a way to recreate an exact copy of an object when deserializing and it may not be sufficient to use the same constructors to deserialize and to create the object in the first place. That leaves a hole.
Some things to do:
- Nothing but private or final properties.
- Constructor sets any of those properties that are critical to operation.
Some other things to think about:
- static variables are probably a bad idea though a static final constant isn't a problem. There is no way to set these from the outside when the class is loaded but preclude setting them again later.
- if one of the properties passed to the constructor is an object, the caller can keep a reference to that object and, if it is not also immutable, change some internal state of that object. That effectively changes the internal state of your object that has stored a copy of that, now modified, object.
- someone could, theoretically, take the serialized form and alter it (or just build the serialized form from scratch) and then use that to deserialize, thus creating a modified version of the object. (I figure this is probably not worth worrying about in most cases.)
- you could write custom serialize/deserialize code that signs the serialized form (or encrypts it) such that modifications are detectable. Or you could use some form of transmission of the serialized form that guarantees it is not changed. (This assumes you have some control over the serialized form when not in transit.)
- There are byte code manipulators that can do anything they want to an object. For example, add a setter method to an otherwise immutable object.
The simple answer is that in most cases, just follow the two rules at the top of this answer and that will be good enough to handle your needs for immutability.