Effective Java summary By Doo and Cheolho

Effective Java 3/E summary item 86 to 90 (Longer version)

Item 86: Implement Serializable with great caution

  • it decreases the flexibility to change a class’s implementation once it has been released.
  • it increases the likelihood of bugs and security holes.
  • it increases the testing burden associated with releasing a new version of a class.

Item 87: Consider using a custom serialized form

  • Use the default serialized form only if it is a reasonable description of the logical state of the object; otherwise design a custom serialized form that aptly describes the object.
  • Choosing the wrong serialized form can have a permanent, negative impact on the complexity and performance of a class.

Item 88: Write readObject methods defensively

  • For classes with object reference fields that must remain private, defensively copy each object in such a field. Mutable components of immutable classes fall into this category.
  • Check any invariants and throw an InvalidObjectException if a check fails. The checks should follow any defensive copying.
  • If an entire object graph must be validated after it is deserialized, use the ObjectInputValidation interface (not discussed in this book).
  • Do not invoke any overridable methods in the class, directly or indirectly.

Item 89: For instance control, prefer enum types to readResolve

  • Use enum types to enforce instance control invariants wherever possible.
  • If this is not possible and you need a class to be both serializable and instance-controlled, provide a readResolve method and ensure that all of the class’s instance fields are either primitive or transient.

Item 90: Consider serialization proxies instead of serialized instances

  • consider the serialization proxy pattern whenever you find yourself having to write a readObject or writeObject method on a class that is not extendable by its clients.

Effective Java 3/E summary chanpter 10 (Shorter version)

Chanpter 11 : Serialization

You can find the longer version of this summary here.

Serialization

  • Use JSON, protocol buffer instead of Java serialization.
    • JSON is human-readable.
    • Because the protocol buffer is a binary representation, it is highly efficient.

Prohibit use of Serializable

  • Serailzable is vulnerable to security
  • Prohibit Serailzable in classes designed for inheritance and interfaces.
  • Inner classes should not implement serialization.
  • If not for inheritance, use serialization proxy pattern
    • Create nested class
    • Implement Outer class writeReplace() + readObject()
    • Implement Nested class readResolve()

If you’re still going to use Seralizable,

  • Judgment of logical and physical differences.
  • Logic == physics
    • Just implement Serailzable
  • Logic != Physics
    • Custom serialization
    • transient + writeObject() + readObject()
    • UID explicitly granted (compatibility)
    • When writing readObejct() (like public constructor)
      • Defensive copy
      • Prohibit calling overridable methods
  • Instance control class
    • Enum type for singleton class
    • If the enum type is not available, readResolve() + transient

Effective Java 3/E summary item 76 to 85 (Longer version)

Item 76: Strive for failure atomicity

  • Generally speaking, a failed method invocation should leave the object in the state that it was in prior to the invocation.
  • Even where failure atomicity is possible, it is not always desirable. For some operations, it would significantly increase the cost or complexity. That said, it is often both free and easy to achieve failure atomicity once you’re aware of the issue.

Item 77: Don’t ignore exceptions

  • If you choose to ignore an exception, the catch block should contain a comment explaining why it is appropriate to do so, and the variable should be named ignored.

Item 78: Synchronize access to shared mutable data

  • Not only does synchronization prevent threads from observing an object in an inconsistent state, but it ensures that each thread entering a synchronized method or block sees the effects of all previous modifications that were guarded by the same lock.
  • Synchronization is required for reliable communication between threads as well as for mutual exclusion.
  • It is not sufficient to synchronize only the write method! Synchronization is not guaranteed to work unless both read and write operations are synchronized.

Item 79: Avoid excessive synchronization

  • To avoid liveness and safety failures, never cede control to the client within a synchronized method or block.
  • As a rule, you should do as little work as possible inside synchronized regions.

Item 80: Prefer executors, tasks, and streams to threads

  • Not only should you refrain from writing your own work queues, but you should generally refrain from working directly with threads. When you work directly with threads, a Thread serves as both a unit of work and the mechanism for executing it. In the executor framework, the unit of work and the execution mechanism are separate.

Item 81: Prefer concurrency utilities to wait and notify

  • There is seldom, if ever, a reason to use wait and notify in new code.

Item 82: Document thread safety

  • Common cases: immutable, unconditionally thread-safe, conditionally thread-safe, not thread-safe, thread-hostile.

Item 83: Use lazy initialization judiciously

  • Under most circumstances, normal initialization is preferable to lazy initialization.

Item 84: Don’t depend on the thread scheduler

  • Any program that relies on the thread scheduler for correctness or performance is likely to be nonportable.
  • Thread priorities are among the least portable features of Java.

Item 85: Prefer alternatives to Java serialization

  • There is no reason to use Java serialization in any new system you write.

Effective Java 3/E summary chanpter 10 (Shorter version)

Chanpter 10 : Concurrency

You can find the longer version of this summary here.

Concurrency satisfaction

  • Exclusive execution: synchronized keyword
  • Communication between threads: volatile field, both read and write sychronization

Synchronization failure

  • Unresponsive, safety failure
    • Avoid calling alien methods
    • Minimize the synchronization area

Executor rather than thread, use task, use concurrency utility

  • Normally use newCachedThreadPool
  • Use newFixedThreadPool on heavy servers
  • If you want full control, use ThreadPoolExecutor class directly
  • ConcurrentHashMap, BlockingQueue, CountDownLatch, Semaphore, Phaser

Documentation

  • Immutable: No need to sync (@Immutable)
  • Unconditional thread safety: No need to sync (@ThreadSafe)
  • Conditional thread safety: synchronization for some methods (@ThreadSafe)
  • Not thread safe: should be wrapped with an external synchronization mechanism (@NotThreadSafe)

Caution

  • Avoid using lazy initialization (synchroized, volatile)
  • Avoid priority for Thread (using Thread.yield)

Effective Java 3/E summary item 56 to 65 (Longer version)

Item 66: Use native methods judiciously

  • It is rarely advisable to use native methods for improved performance. In early releases (prior to Java 3), it was often necessary, but JVMs have gotten much faster since then.
  • Disadvantages: native methods are not safe, less portable, harder to debug, may leak memory.

Item 67: Optimize judiciously

Premature optimization is the root of all evil.
We follow two rules in the matter of optimization:
Rule 1. Don’t do it.
Rule 2 (for experts only). Don’t do it yet—that is, not until you have a perfectly clear and unoptimized solution.

  • Strive to write good programs rather than fast ones.
  • Strive to avoid design decisions that limit performance.
  • It is a very bad idea to warp an API to achieve good performance.

Item 68: Adhere to generally accepted naming conventions

  • Use common sense.

Item 69: Use exceptions only for exceptional conditions

  • Don’t use it for control flows.
  • A well-designed API must not force its clients to use exceptions for ordinary control flow.

Item 70: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors

  • Throw checked exceptions for recoverable conditions and unchecked exceptions for programming errors.

Item 71: Avoid unnecessary use of checked exceptions

  • If callers won’t be able to recover from failures, throw unchecked exceptions. If recovery may be possible and you want to force callers to handle exceptional conditions, first consider returning an optional. Only if this would provide insufficient information in the case of failure should you throw a checked exception.

Item 72: Favor the use of standard exceptions

  • such as IllegalArgumentException, IllegalStateException, NullPointerException.

Item 73: Throw exceptions appropriate to the abstraction

  • If it isn’t feasible to prevent or to handle exceptions from lower layers, use exception translation, unless the lower-level method happens to guarantee that all of its exceptions are appropriate to the higher level. Chaining provides the best of both worlds: it allows you to throw an appropriate higher-level exception, while capturing the underlying cause for failure analysis.

Item 74: Document all exceptions thrown by each method

  • document every exception that can be thrown by each method that you write. Use throws clause and @throws tag.