String
is "immutable" because its API (the public methods it defines) provides no way for a caller to change its state. Other classes, like StringBuilder
, are mutable because they do provide "mutators", or methods that change the state of the class.
Under the covers there's nothing different between mutable or immutable objects. An object is just the primitive and reference values that make up its fields, and all fields are conceptually mutable (ignoring the final
keyword for the moment). But by restricting the visibility of these fields (e.g. making them private
) and defining methods that limit what a caller can do (like defining a getter but not a setter) you can provide a guarantee that instances of the class will be immutable, which is to say you promise none of its fields (or the objects they reference) will change over its lifetime.
By using the final
keyword you can be even more explicit that a class is immutable. If you simply don't provide mutator methods, it's still possible for a class to be mutable if some private method mutates the class. If all the fields in a class are marked final
, that's not (normally) possible. You can be confident any class with only final
primitive (or immutable) fields is immutable*. That isn't the only way to guarantee immutability, but it is the clearest - any attempt to mutate the class would be a compiler error.
* The class itself also has to be declared final
, otherwise someone could potentially create a mutable subclass. But again, there are other ways to ensure immutability. Using final
everywhere is just the easiest to see.