Creating and Destroying Objects

Consider static factory methods instead of constructors

  • flexible way of object instantiation

Pro

  • static factories have names, unlike constructors

  • not required to create a new object on each invocation

  • called instance controlled

  • enable singleton and non-instantiability guarantees

  • allows immutable value class to guarantee no two instances exist

  • forms the basis of Flyweight Pattern

  • Enum types provide this guarantee

  • can return an object of any subtype of their return type

  • lends itself to interface-based frameworks

  • allow the class of the returned object to vary from call to call as function of input params

  • do not require the class of returned object to exist when the class containing the method is written

    • form the basis of service provider frameworks ie jdbc

Cons

  • classes without public/protected constructors cannot be subclassed

  • encourages composition (which is good)

  • hard to find in documentation

  • they scale poorly with the increase of (optional) params

Naming

  • from()

  • of()

  • valueOf()

  • instance() or getInstance()

  • getType()

  • newType()

  • type()

Consider a builder when faced with many constructor params

  • Handle increase in params, esp optional ones

    • Solutions:

      • telescoping constructor - scales poorly

      • JavaBeans pattern - need setters

        • causes inconsistency

        • no immutablitiy

  • Builder solves these

Pro

  • static member class

  • client code is easy to write

    • fluent style

  • incorporate validity checks

    • can throw IllegalStateException

  • Can construct any subclass

    • generic builder with recursive type parameters

    • abstract self() simulates self-type which, combined with covariant return typing, obviates need for casting

  • flexible

cons

  • cost of creating a builder

  • verbose

Enforce the singleton property when a private constructor or enum type

how

  • with public final field

  • with static factory

  • with a single-element enum (preferred)

    • well-defended against reflection

    • prevents serialization issues

con

  • making a class a singleton can make it difficult to test

Enforce non-instantiability with a private constructor

  • class that is simply a grouping of static methods and static fields

  • uses

    • group related methods on primitive values or arrays

    • group static methods, including factories

      • default methods are also available (providing we own the interface)

    • group methods on a final class, since we can’t put them in a subclass

How

  • make such classes non-instantiable

    • provide an explanatory comment

    • throw an AssertionError from constructor instead of empty one, to guard against accidental constructions from within the class

    • as a side-effect, this class is now effectively final

      • the class cannot be subclassed as there are no available constructors

      • it is good to document this a make the class itself final

Prefer dependency injection to hard-wiring resources

  • do not use static utility methods or singletons to handle creations of class’ resources

  • favour dependency injection by supplying the required resource parametrically

    • testable

    • flexible (esp. with Factory Method pattern

      • Supplier is perfectly suited for representing factories

      • methods that take this interface should typically constrain the factory’s type parameter with a bounded wildcard type

        • the client should be able to pass in a factory that requires any subtype of a specified type

  • Can use frameworks to do this

Avoid creating unnecessary objects

  • avoided by using static factory methods

  • some object creations are much more expensive than others

    • it may be advisable to cache such objects

    • ie regex patterns, database connections

  • immutable objects can trivially be reused

  • Use Adapters aka views

    • adapter is an object delegating to a backing object, providing an alternative interface

    • has not state beyond the backing object thus provides a view of it

    • example: keySet() in Map interface

  • autoboxing can often subtly create unnecessary objects

  • almost never create own object pools

    • JVM gc will almost always outperform such pools

    • exception to this: very expensive objects

      • example: database connection objects

  • counterpoint to this is defencive copying

Eliminate obsolete object references

  • common sources of leaks

    • a resizing-array stack that doesn’t null out its references on pop()

    • classes that manage their own memory

    • caches

      • can be mitigated with WeakHashMap

      • sophisticated caches might need to use java.lang.ref directly

    • listeners and other callbacks

      • APIs that register callbacks but don’t deregister them explicitly

      • can be mitigated with WeakHashMap

  • anticipate such problems before they occur as they can be very costly to fix

Avoid finalizers and cleaners

  • finalizers and cleaners are used to reclaim non-memory resources

    • example: input/output streams, files, etc.

    • Different to deconstructors

  • finalizers are unpredictable, dangerous and generally unnecessary

  • cleaners are less dangerous than finalizers but still slow, unpredicatble and generally unnecessary

Cons

  • spec makes no guarantee when they’ll be executed

    • their execution is a function of GC algorithm, thus JVM implementation

    • never do anything time-critical in a finalizer or cleaner

    • providing a finalizer may arbitrarily delay reclamation of class’ instances

    • cleaners are a bit better but still run under the control of GC so still the same applies

    • any existing methods that claim to trigger finalization are decade-old traps

      • examples: System.runFinalizerOnExit or Runtim.runFinalizerOnExit

  • uncaught exception thrown during finalization is ignored until finalization of that object terminates

    • object is potentially left in corrupted state

    • normally, uncaught exception terminates the executing thread and dumps stacktrace but not if it occurs in finalizer

    • cleaners do not suffer from this problem

  • there is a severe performance GC penalty for using finalizers and cleaners

    • finalizers: ~50x slower reclamation

    • cleaners: ~5x slower reclamation, equal to finalizers if they’re used to clean all instance of the class

  • finalizers open up classes to finalizer attacks

    • if an exception is thrown during finalization, the finalizer leaves the class unreclaimed

    • attackers can exploit this and run code that shouldnt’ve existed

    • to protect non-final classes against it - write a final finalize() that does nothing

when

  • except as a safety net or to terminate non-critical native resources

Alternatives

  • instead of using finalizers or cleaners simply use AutoCloseable

    • require clients to invoke close() whenever instance is not required

Prefer try-with-resources to try-finally

  • Java libraries include many resources that must be closed manually by invoking close()

  • closing objects is often overlooked by clients

  • try-finally was the best way to guarantee a resource would be closed properly, even when facing exception or return

    • while it doesn’t look bad for a single resource, it doesn’t scale well with the increase of resources required to be closed

    • nested finally block complicate debugging

  • try-with-resources suffers none of this issues

    • shorter, more readable than try-finally

    • provides far better diagnostics

    • no exceptions are suppressed

Last updated