One of the most striking features of the Java(tm) Technology(tm) Programming Language(tm) is that it is not possible for a local variable or member field to literally be an object - only to be a reference to an object.
Advantages
The exact type of an object need not be known at compile-time, allowing for greater flexibility. The enormous effect of this simple fact cannot be overstated. It also greatly cuts down on errors, confusion and general chaos because there are only one type of object related variables - references - rather than two (Objects and Pointers to Objects, as in C++).
Disadvantages
Objects which comprise the "deep-structure" of some object must be defensively copied at times. But when? And which ones? And what if you forget? If an object were to directly contain another object, and "=" were always a copying operator, all these problems would solve themselves. (It works somewhat like this in C++.)
Not only that, but if you're really paranoid that everything might be actively trying to undermine the integrity of a java object, then for objects that will be serialized you have to implement Externalizable and implement readExternal() to read a copy of your deep structure from the stream - the default serialization behaviour would allow multiple objects to gain references to a deep-structure object if the stream has been tampered with since being serialized. And if you're really paranoid, one would also need to disallow reading private fields by reflection - which you might do anyway, but you might consider allowing [it] if only you could be sure that the caller would get a copy, not the object itself.
Resolution Is there a way to have my cake and eat it too? I doubt it. It has been suggested to me that if certain members were labelled "internal" and the "=" operator automatically made copies of internal members, we might have only the advantages of EverythingIsaReference, but I have my doubts.
Effectively labelling something as 'internal' is really treating the object as if it were a value rather than a reference, the difference between int and int& in C++.
Another possible resolution is to design all objects to be immutable (or at least to appear immutable). If you want to change an object, you have to call a function which returns a new, changed object while leaving the original alone. This is more efficient than it sounds - an immutable object can be shared without limitation. Inserting into an immutable binary tree, for example, only creates lg(n) new nodes, where n is the number of items in the tree. The new nodes are all along the path to the root. Most of the nodes are shared between the old tree and the new tree.
Indeed. This is the default approach taken by FunctionalProgramming, since in general their objects do not mutate. The issue, I think, is the fundamental difference between C++ mutables and Java mutables. In C++, mutable objects are a really fine idea, since objects are variables, and most people expect variables to be able to change. C++ makes it easy to tell who you're effecting when you change something. If it's a variable, you're only affecting yourself; an lvalue refernce, your caller; an rvalue reference (since C++11), a temporary which was going to die soon anyway; a pointer, whatever it's pointing too. Plus, variables being objects means you can actually define your own behaviors for varibles themselves. In Java, though, EverythingIsaReference; objects' identities are values, not variables. That's why value objects should be immutable--except that Java doesn't really have any good way to write classes for value objects. The sad thing is, they could have easily done things the C++ way, but they didn't. (Why does a language with checked exceptions not have a way to annotate that some functions have side effects?!)