Okay, let's give an example of what happens when an object has soft, weak and phantom references to it:
The object is strongly reachable as long as there's a reference to it; either in a static field, an instance field of another strongly reachable object, or a local variable that is currently within scope.
When there are no more strong references to the object, it becomes softly reachable. Softly reachable objects stay softly reachable until the garbage collector determines that memory is running out, and they need to make room for other objects. The garbage collector then "clears" all the soft references (meaning, the soft reference will no longer refer to the object it used to refer to), and enqueues the references.
After that, the object becomes weakly reachable. The garbage collector will immediately clear all weak references to the object, and enqueue them. The object is then eligible for finalization.
Now, the garbage collection will run the finalize() method on the object, if it hasn't already done so. After the finalize() method is done, the object is finalized, and should** become phantom reachable. So an object that's phantom reachable only has phantom references to it. Phantom reachable objects are removed from memory; the space they take up is reclaimed by the garbage collector.
Phantom references are a special case. They *always* return null when asked about their referent. This is to prevent people from getting strong references to the object after it's been finalized. Secondly, phantom references aren't automatically cleared by the garbage collector before they are enqueued. You need to clear them manually after you've retrieved them from the reference queue, and performed cleanup operations.
Once all the phantom references to the object have been cleared or finalized, the object is no longer reachable. The object is unreachable.
I'll give some examples of use cases for these references. Soft references are only cleared when the JVM runs out of memory (this actually isn't a guarantee, the garbage collector may clear the references even if there's plenty of space). Therefore, they are great for building caches. They keep an object in memory as long as there's space, but they don't prevent the object from being collected when space runs out.
Weak references are great when you want to store extra information about an object, but that information is only useful for as long as the object exists. You can then have a weak reference to the object, which also strongly refers to the extra information. When the object has no strong references to it anymore, it becomes weakly reachable, is automatically cleared, and the weak reference is enqueued. The
thread that deals with the reference queue can then clean up the extra information when it retrieves the weak reference from the queue.
Finally, Phantom references are good for two things only. One is determining that the object is *really* dead and removed from memory. The second thing is performing cleanup. If you need to clean up a bunch of other objects when the first object is dead, you can do so, and then clear the phantom reference. This is exactly the same as just performing the finalize() method, except it's safer, because the finalize() method can perform cleanup before the object is physically removed from memory.
**An object that has been finalized *should* become phantom reachable. However, this is not always true. The finalize() method can resurrect a dead object by storing a reference to it in a variable, thereby making it strongly reachable once again. This is why cleanup in the finalize() method is dangerous. It's possible to clean up other objects, but then resurrecting the original object. When you perform operations on the object, the program may fail catastrophically.