More on this book
Community
Kindle Notes & Highlights
Item 7: Eliminate obsolete object references
“memory leak,” which can silently manifest itself as reduced performance due to increased garbage collector activity or increased memory footprint.
An obsolete reference is simply a reference that will never be dereferenced again.
Memory leaks in garbage-collected languages (more properly known as unintentional object retentions) are insidious.
null out references once they become obsolete.
It is always beneficial to detect programming errors as quickly as possible.
Nulling out object references should be the exception rather than the norm. The best way to eliminate an obsolete reference is to let the variable that contained the reference fall out of scope. This occurs naturally if you define each variable in the narrowest possible scope
whenever a class manages its own memory, the programmer should be alert for memory leaks.
Another common source of memory leaks is caches.
If you’re lucky enough to implement a cache for which an entry is relevant exactly so long as there are references to its key outside of the cache, represent the cache as a WeakHashMap; entries will be removed automatically after they become obsolete.
A third common source of memory leaks is listeners and other callbacks.
One way to ensure that callbacks are garbage collected promptly is to store only weak references to them, for instance, by storing them only as keys in a WeakHashMap.
debugging tool known as a heap profiler
Item 8: Avoid finalizers and cleaners
Finalizers are unpredictable, often dangerous, and generally unnecessary.
As of Java 9, finalizers have been deprecated, but they are still being used by the Java libraries. The Java 9 replacement for finalizers is cleaners. Cleaners are less dangerous than finalizers, but still unpredictable, slow, and generally unnecessary.
In Java, the garbage collector reclaims the storage associated with an object when it becomes unreachable, requiring no special effort on the part of the programmer.
One shortcoming of finalizers and cleaners is that there is no guarantee they’ll be executed promptly
never do anything time-critical in a finalizer or cleaner.
For example, it is a grave error to depend on a finalizer or cleaner to close files because open file descriptors are a limited resource.
never depend on a finalizer or cleaner to update persistent state.
Another problem with finalizers is that an uncaught exception thrown during finalization is ignored, and finalization of that object terminates
Normally, an uncaught exception will terminate the thread and print a stack trace, but not if it occurs in a finalizer—it won’t even print a warning.
There is a severe performance penalty for using finalizers and cleaners.
cleaners are much faster if you use them only as a safety net,
Finalizers have a serious security problem: they open your class up to finalizer attacks.
Throwing an exception from a constructor should be sufficient to prevent an object from coming into existence; in the presence of finalizers, it is not.
To protect nonfinal classes from finalizer attacks, write a final finalize method that does nothing.
Some Java library classes, such as FileInputStream, FileOutputStream, and ThreadPoolExecutor have finalizers that serve as safety nets.
A native peer is a native (non-Java) object to which a normal object delegates via native methods.
don’t use cleaners, or in releases prior to Java 9, finalizers, except as a safety net or to terminate noncritical native resources.
Item 9: Prefer try-with-resources to try-finally
try-finally - No longer the best way to close resources!
If you write a class that represents a resource that must be closed, your class should implement AutoCloseable too.
try-with-resources - the best way to close resources!
Item 10: Obey the general contract when overriding equals
So when is it appropriate to override equals? It is when a class has a notion of logical equality that differs from mere object identity and a superclass has not already overridden equals.
A value class is simply a class that represents a value, such as Integer or String.
Once you’ve violated the equals contract, you simply don’t know how other objects will behave when confronted with your object.
The Liskov substitution principle says that any important property of a type should also hold for all its subtypes so that any method written for the type should work equally well on its subtypes
override Object’s toString implementation in every instantiable class you write, unless a superclass has already done so. It makes classes much more pleasant to use and aids in debugging.
Java supports covariant return types. In other words, an overriding method’s return type can be a subclass of the overridden method’s return type. This eliminates the need for casting in the client.
Item 14: Consider implementing Comparable
Item 15: Minimize the accessibility of classes and members
The single most important factor that distinguishes a well-designed component from a poorly designed one is the degree to which the component hides its internal data and other implementation details from other components.
In Java 8, functional interfaces, lambdas, and method references were added to make it easier to create function objects. The streams API was added in tandem with these language changes to provide library support for processing sequences of data elements.
One line is ideal for a lambda, and three lines is a reasonable maximum. If you violate this rule, it can cause serious harm to the readability of your programs.
The primary advantage of lambdas over anonymous classes is that they are more succinct.
If you believe a condition is likely to allow for recovery, use a checked exception; if not, use a runtime exception. If it isn’t clear whether recovery is possible, you’re probably better off using an unchecked exception, for reasons discussed in Item 71.
To summarize, throw checked exceptions for recoverable conditions and unchecked exceptions for programming errors. When in doubt, throw unchecked exceptions. Don’t define any throwables that are neither checked exceptions nor runtime exceptions. Provide methods on your checked exceptions to aid in recovery.