- Introduction
- most of the rules derive from a few fundamental principles
- clarity and simplicity are of paramount importance
- user should not be surprised by component behavior
- code should be reused rather than copied
- dependencies between components should be kept to a minimum
- errors should be detected as soon as possible
- for the most part, this book is not about performance
- Creating and Destroying Objects
- Item 1: Consider static factory methods instead of constructors
- pros
- they have names
- not required to create a new object each time they’re invoked (e.g. for immutable classes, singletons)
- they can return an object of any subtype
- clients can focus on API of interface
- class of returned object can vary from call to call based on input parameters
- class of returned object need not exist when the class containing the method is written
- cons
- classes without public or protected constructors cannot be subclassed
- good since it encourages composition
- required for immutable classes
- static factory methods may be hard to find
- draw attention to in documentation and adhere to common naming conventions
- from, of, valueOf, instance, getInstance, create, newInstance, getType, newType, type
- often preferable to public constructors
- Item 2: Consider a builder when faced with many constructor parameters
- steps
- client calls a constructor or static factory with required parameters and gets a builder object
- client calls setter-like methods for optional parameters
- finally, use a parameterless build method to generate an object (typically immutable)
- pros
- simulates named optional parameters
- check invariants
- well suited for class hierarchies
- abstract builders for abstract classes and concrete builders for concrete classes
- cons
- this may be an issue in performance-critical situations
- more verbose and should be used when enough parameters (4+), but often add more so often better to start with a builder
- The builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters
- Item 3: Enforce the singleton property with a private constructor or an enum type
- 1) field method: e.g. INSTANCE
- 2) static factory method: e.g. getInstance
- advantages
- can change your mind about being a singleton and keep API
- can write a generic singleton factory
- a method reference can be used as a supplier e.g. Elvis::instance
- unless one of these advantages is relevant, use field
- a privileged client could call private constructor reflectively if you need to protect, modify the constructor to throw an exception if it’s asked to create a second instance
- to make serializable must declare all instance fields transient and provide a readResolve method otherwise new instance created when deserialized
- 3) declare a single-element enum
- provides serialization machinery for free
- ironclad guarantee against multiple instantiations in the face of sophisticated serialization or reflection
- a single-element enum type is often the best way to implement a singleton
- can’t use this approach if your singleton must extend a superclass other than Enum
- enums can implement an interface however
- Item 4: Enforce noninstantiability with a private constructor
- e.g. may want a grouping of static methods and static fields
- include a private constructor
- default is only generated if no constructor
- can throw an AssertionError to prevent it from being called within the class
- also prevents subclassing
- Item 5: Prefer dependency injection in hardwiring resources
- static utility classes and singletons are inappropriate for classes whose behavior is parameterized by an underlying resource
- pass the resource into the constructor when creating a new instance
- a useful variant is to pass a resource factory into the constructor
- Supplier<T> interface (added in Java 8) is perfect for representing factories
- constrain type parameter with bounded wildcard type
- dependency injection adds clutter for large projects but can be addressed by using a dependency injection framework
- Item 6: Avoid creating unnecessary objects
- an object can always be reused if it is immutable or mutable (but won’t be modified)
- can often avoid by using a static factory method
- if you use a constructor a new object will be created
- if you need an “expensive object” may be advisable to cache e.g. regex pattern
- adapters are immutable so only need to create one to a given object
- autoboxing may result in unnecessary object creation e.g. long and Long
- prefer primitives to boxed primitives and watch out for unintentional autoboxing
- you should generally avoid maintaining your own object pool unless objects are extremely heavyweight e.g. database connections
- Item 7: Eliminate obsolete object references
- whenever a class manages its own memory, the programmer should be alert for memory leaks e.g. stack
- object references should be nulled out
- caches
- use WeakHashMap if entry is relevant exactly as long as there are references to its key outside of the cache
- if the lifetime of entry is not well-defined can clean with a background thread or remove the oldest entry
- listeners and other callbacks
- registered callbacks can accumulate
- can store as only keys in a WeakHashMap
- often only discovered with careful code inspection or heap profiler so desireable to prevent them from happening
- Item 8: Avoid finalizers and cleaners
- no guarantees on when or if they will run
- never do anything time-critical in a finalizer or cleaner
- sever performance penalty
- opens class up to attacks
- protect by writing a final finalize method that does nothing
- better to have your class implement AutoCloseable
- legitimate uses
- as a safety net
- native peers (non-Java) objects the GC doesn’t know about assuming performance is acceptable
- Item 9: Prefer try-with-resources to try-finally
- using try-finally no longer the best way
- nesting gets hairy and easy to make mistakes
- exceptions in close may cover the initial exception and complicate debugging
- always favor try-with-resources
- the resources must implement AutoCloseable
- shorter and more readable
- exceptions thrown in closed are suppressed improving diagnostics
- Methods Common to All Objects
- Item 10: Obey the general contract when overriding equals
- the easiest way to avoid the problem is not to override
- if each instance is inherently unique e.g. threads
- there is no need to provide “logical equality” e.g. regex pattern
- a superclass has already overridden equals and its appropriate for this class
- class is private and you are certain it will never be invoked
- can override and throw assertion to be extra safe
- override when
- logical equality differs from object equality and superclass has not already overridden e.g. value classes
- enables instances to serve as map keys or set elements
- equivalence relation contract for Object
- reflexive
- symmetric
- x.equals(y) and y.equals(x)
- transitive
- x.equals(y)
- y.equals((z)
- x.equals(z)
- consistent
- non-nullity
- if not null, x.equals(null) always returns false
- many classes depend on equals e.g. collection classes
- there is no way to extend an instantiable class and add a value component while preserving the equals contract (you can for abstract classes)
- use composition, create a class that has the class as a private member
- do not write equals that depend on unreliable resources
- recipe for high equality equals
- use the == operator to check if the argument is a reference to this object
- use the instanceof operator to check if correct type
- cast the argument to the correct type
- check each significant field
- == for primitive types (except float and double)
- equals for mobjects
- Float.compare, Double.compare for float and doubles
- Array.equals
- ask yourself if it is symmetric, transitive, consistent? and test
- always override hashCode
- don’t be too clever
- don’t substitute another type or it won’t override
- Item 11: Always override hashCode when you override equals
- otherwise won’t function properly for HashMap or HashSet
- must consistently return the same value if the object is not modified
- two equal objects must return the same hash code
- good hash function produces unequal hash codes for unequal instances
- simple recipe
- initialize int result with hash code of first significant field
- combine hash codes using: result = 31 * result + c
- write unit tests to verify
- can use Objects.hash, but may not be most performant
- can make it lazy if don’t expect to use a lot
- do not exclude significant fields
- do not provide any specifications for what value is returned so it can be changed
- Item 12: Always override toString
- concise but informative
- makes it pleasant to use and easier for system debugging
- if you fail to provide logs or test message is useless
- should return all interesting information or a summary if too big
- if you specify a format, clearly document and understand other people will depend on it
- otherwise, clearly state it’s subject to change
- provide accessors for relevant fields or else the string format becomes the API
- override in every instantiable class you write, unless a superclass has already one so
- Item 13: Override clone judiciously
- a class implementing Cloneable needs to provide a properly functioning clone method
- immutable classes should never provide a clone method
- when designing a class for inheritance, the class should not implement Cloneable / let subclass decide if it wants to implement
- clone method must be properly synchronized
- if you clone you must:
- override clone with a public method whose return type is the class itself
- the method should first call super.clone
- fix any fields that need fixing e.g. copying any mutable objects that comprise the internal “deep structure”
- internal copies can usually be cloned recursively, but not always best (don’t need to if immutable or enums)
- a better approach to object copying is to provide a copy constructor or copy factory
- Item 14: Consider implementing Comparable
- by implementing Comparable, allows your class to interoperate with many generic algorithms
- contract: returns negative, zero, positive if object is less than, equal, r greater
- provisions to compareTo
- if you reverse the direction of comparison, expected thing happens
- if one is greater than two and two is greater than three, one is greater than three
- all objects that compare as equal must yield the same result when compared to any object object
- must obey same restrictions imposed by equals: reflexivity, symmetry, and transitivity
- can’t extend an instantiable class with a new value component while preserving compareTo contract so use composition
- should generally return the same result as equals
- recommended to NOT use relational operators like > or <
- normal pattern to only allow comparison between the same types
- Java 8 provides construction methods that can simplify
- Classes and Interfaces
- Item 15: Minimize the accessibility of classes and members
- the single most important factor that distinguishes well-designed components is the degree to which the component hides its internal data and other implementation details
- this decouples the components of a system and increases software reuse
- make each class or member as inaccessible as possible
- anything you export you’re required to support to maintain compatibility
- instance fields should rarely be public
- ensure objects referenced by public static fields are immutable
- Item 16: In public classes, use accessor methods, not public fields
- breaks encapsulation
- can’t change the representation without changing the API
- if a class is package-private or a private nested class, there is nothing inherently wrong with exposing its data fields
- less harmful if fields are immutable
- Item 17: Minimize mutability
- rules to ensure
- don’t provide methods to modify object’s state
- ensure that the class can’t be extended
- make all fields final
- make all fields private
- if any fields refer to mutable objects, ensure clients cannot obtain a reference
- they are simpler, thead-safe, and can be shared freely
- classes should be immutable unless there is a good reason not to be
- if class implements Serializable and contains one or more fields that refer to mutable objects, must provide an explicit readObject or readResolve mtehod or ObjectOutputStream.writeUnshared and ObjectInputStream.readUnshared
- Item 18: Favor composition over inheritance
- safe to use inheritance when extending classes specifically designed and documented for extension
- inheritance violates encapsulation
- composition with wrapper class or decorator pattern
- Item 19: Design and document for inheritance or else prohibit it
- the class must document its self-use of overridable methods
- the class may have to provide hooks into its internal workings in the form of judiciously chosen protected methods
- the only way to test a class designed for intheritance is to write subclasses
- constructors must not invoke overridable methods because of fields may not be initialized
- it is safe to invoke private methods, final methods and static methods, none of which are overridable from a constructor
- designing a class for inheritance requires great effort and place substantial limitations on the class
- the best solution is to prohibit subclassing in classes not designed and documented to be safely subcalssed
- Item 20: Prefer interfaces to abstract classes
- existing classes can easily be retrofitted to implement a new interface
- can only extend one abstract calss
- can combine advantages of interfaces and abstract classes by providing an abstract skeletal implementation class or AbstractInterfaces
- create an abstract class that implements the interface
- create a class that implements the interface and has an inner class that subclasses the abstract class
- good documentation is absolutely essential in skeletal implementation
- Item 21: Design interfaces for posterity
- can add default methods in interfaces starting in Java 8
- can’t always write default methods that maintain all invariants of every implementation
- default methods may compile but can cause runtime errors
- design interfaces with great care
- test out multiple client programs
- while may be possible to correct some interface flaws after an interface is released, you can’t count on it
- Item 22: Use interfaces only to define types
- do not use for constants
- include constants in class if tightly associated
- can include as enum or make a utility class
- Item 23: Prefer class hierachies to tagged classes
- the type is essentially defined in a member
- multiple constructors for each “type”
- tagged classes are verbose, error-prone and inefficent
- use a class hierarchy
- Item 24: Favor static member classes over nonstatic
- 4 types: static member classes, nonstatic member classes, anonymous classes, local classes
- non static member classes are implicitly associated with an enclosing instance which can harm GC and cause memory leak
- on common use of nonstatic class is adapter
- lambda preferred over anonymous classes now
- Item 25: Limit source files to a single to-level class
- never put multiple top-level classes or interfaces in a single source file
- no benefits only risks
- can override class definitions depending on how it was compiled
- alternative is defining multiple static member classes instead
- Generics
- Item 26: Don’t use raw types
- you lose all the safety and expressiveness benefits of generics
- e.g. List vs List<Object>
- Item 27: Eliminate unchecked warnings
- they are areas where you can get a ClassCastException at runtime
- if you can’t eliminate it, but prove the code is correct supress it
- suppress smallest scope possible and include rationale for why
- Item 28: Prefer lists to arrays
- arrays are reified (don’t lose type info at runtime) and covariant can result in type errors at runtime
- generics types only at compile time at runtime type parameters are Objects
- if you’re using generics, favor lists
- Item 29: Favor generic types
- if dealing with arrays, can use Object[] and cast to generic array type and suppress warning
- e.g. elements = (E[]) new Object[LENGTH]
- other approach is change the element type
- e.g. E result = (E) elements[–size];
- first is preferable
- can also use bounded types
- e.g. class DelayQueue<E extends Delayed> where E must be a subtype of Delayed
- Item 30: Favor generic methods
- if you just use <E> must all match exactly that type
- use bounded wildcard to be more flexible
- Item 31: Use bounded wildcards to increase AP flexibility
- parameterized types are invariant e.g. List<Integer> is not a subtype of List<Number>
- e.g. pushAll method of stack can use “Iterable<? extends E> src” (Iterable of some subtype of E)
- the parameter src is a producer of E
- if we have a popAll method that adds as popped elements to another collection want super type
- public void popAll(Collection<? super E> dst)
- dst must be some collection of super types of E
- PECS: producers extend, consumer-super
- do not use bounded wildcards as return types
- if users has to think about wildcard types there is probably something wrong with its API
- if type parameter appears only once ina method replace it with a wildcard
- if you need to put something into a e.g. List<?>, create a private method that uses unbounded type parameter
- Item 32: Combine generics and varargs judiciously
- varargs build atop arrays and arrays have different type rules from generics
- is allowed but take precautions
- annotation with “safevarargs” annotation
- safe if the method doesn’t store anything into the array and doesn’t allow a reference to the array to escape
- alternatively use a List as a parameter
- Item 33: Consider typesafe heterogeneous containers
- can have collections hold multiple types by parameterizing the key
- “Class” objects can be used as keys (type token)
- or use custom keys e.g. DatabaseRow type can have a generic type Column<T> as it’s key
- Enums and Annotations
- Item 34: Use enums instead of int constants
- to associate data with enum constants declare field and pass into constructor
- use when you need a set of constants whose members are known at compile time
- Item 35: Use instance fields instead of ordinals
- never derive a value associated with an enum from its ordinal
- hard to update, brittle, may not match up with what you’re trying to represent
- Item 36: Use EnumSet instead of bit fields
- bit set harder to interpret
- enumset combines conciseness with performance
- Item 37: Use EnumMap instead of ordinal indexing
- rarely appropriate to use ordinals to index into arrays
- Item 38: Emulate extensible enums with interfaces
- enums can implement arbitrary interfaces
- while you cannot write an extensible enum type, you can emulate it by writing an interface to accomponay a basic enum type that implements the interface
- Item 39: Prefer annotations to naming patterns
- no reason to use naming patterns when you can use annotations instead
- e.g. requiring “Test” in method name
- Item 40: Consistently use the Override annotation
- use Override annotation on every moethod that you believe to override a superclass declaration
- easy to make mistakes
- Item 41: Use marker interfaces to define types
- marker interfaces define a type that is implemented by instances
- marker interfaces can be targeted more precisely
- chief advantage of marker annotations is that they are part of the larger annotation facility allowing for more consistency
- must use annotation if applies to anything other than class or interface
- otherwise, use interface since yo ucan use it as a type benefiting from compile time safety
- marker interface if no methods
- Lambdas and Streams
- Item 42:Prefer lambda to anonymous classes
- omit type of lambda parameters unless makes clearer or required
- if computation isn’t self-explanatory don’t use
- should rarely, if ever, serialize a lambda
- if you need to use an instance of a private static nested class
- don’t use anonymous classes for function objects unless you have to create instances of types that aren’t functional interfaces
- Item 43: Prefer method references to lambdas
- method references usually shorter, clearer, and easier to document
- sometimes lambda easier e.g. x -> x
- use the one that is shorter and clearer
- Item 44: Favor the use of standard functional interfaces
- many existing ones provided
- don’t be tempted to use with boxed primitives can be very bad for performance
- can write your own under some circumstances
- will be commonly used and could benefit from a descriptive name
- has a strong contract associated with it
- could benefit from custom default methods
- always annotate your functional interfaces with @FunctionalInterface
- Item 45: Use stream judiciously
- overuse can make programs hard to read and maintain
- without types, careful naming of lambda parameters is essential for readability
- use helper methods
- where you may want to use iterative
- read or modify local variables
- return, break, continue, or throw unchecked exception
- where you may want to use stream
- uniformly transform
- filter
- combine
- accumulate
- search
- often down to personal and team preference
- Item 46: Prefer side-effect-free functions in streams
- forEach should only report the result
- side-effects in streams is code smell
- customary and wise to import all members of Collectors because it makes stream pipelines more readable
- collectors provided for creating collections and resolving conflicts e.g. on Maps
- Item 47: Prefer Collection to Stream as a return type
- stream doesn’t extend iterable
- provide interface users can stream or iterate on
- Use Collection or an appropriate subtype
- Item 48: Use caution when making streams parallel
- unlikely to increase performance if source is from Stream.iterate or limit is used
- Do not parallelize indiscriminately
- performance gains from parallelism are best on streams over ArrayList, HashMap, HashSet, ConcurrentHashMap, arrays, int and long rages
- can be accurately and cheaply split into subranges
- good-to-excellent locality of reference
- sequential element references are stored together in memory
- pipeline operation also affects effectiveness
- reductions are good e.g. min, max, count, sum
- short-circuiting e.g. anyMatch, allMatch
- collect method: not good, because overhead of combining collections
- can also lead to incorrect results and incorrect behavior
- accumulator and combiner must be associative, non-interfering, and stateless
- must also be doing enough work to offset costs of paralleism
- must test the performance before and after with realistic load
- Methods
- Item 49: Check parameters for validity
- most methods have restrictions
- enforce at beginning
- detect errors asap
- use requireNonNull method to avoid manual null checks
- use exception translation to throw correctly documented exception
- document restrictions
- Item 50: Make defensive copies when needed
- careful around mutable objects
- make copy before checks
- return defensive copies for accessors
- do not use clone of a parameter whose type is subclassable by untrusted parties
- wherever possible use immutable objects
- Item 51: Design method signatures correctly
- chose names carefully
- don’t go overboard in providing convenience methods
- each method should pull its own weight
- if in doubt, leave it out
- avoid long parameter lists
- long sequences of identical typed parameters are especially harmful
- can break method up into multiple
- create a helper class
- use builder pattern
- for parameter types, favor instances over classes
- prefer two-element enums to booleans unless boolean meaning is clear
- Item 52: Use overloading judiciously
- choice of which overloading to invoke is made at compile time (vs runtime for overriding)
- avoid confusing use of overloading
- can always give methods different names
- writeBoolean, writeInt, writeLong
- when exporting overloadings with same number of parameters unlikely to confuse if types are “radically different”
- do not overload methods to take different functional interfaces in the same argument position
- Item 53: Use varargs judiciously
- constructs and array and passes in at runtime
- if you require at least one, use multiple parameters
- consider array allocation and initialization for performance critical situations
- Item 54: Return empty collections or arrays, not nulls
- error-prone to return nulls
- only optimize after measuring impace
- Item 55: Return optionals judiciously
- in spirit of checked exeption, client must know how to handle
- if expensive to get default value, use supplier and orElseGet
- returning optional that contains boxed primitive type is prohibitively expensive
- never return an optional of a boxed primitive i.e. use OptionalInt, OptionalLong, etc
- almost never appropriate to use as key, value or element in collection
- Item 56: Write doc comments for all exposed API elements
- document API properly, precede every exported class, interface, constructor, method, and field delcaration with doc comment
- should be readable in source and generate documentation, but favor readability in generated documentation
- no two members or constructors should have the same summary description
- document type parameters for generics
- document constants in enums
- document members of annotation type
- document thread-safety
- document serialized form if serializable
- General Programming
- Item 57: Minimize the scope of local variables
- most powerful technique for minimizing scope of a local variable is to declare it where it is first used
- prefer for loops to while loops
- keep methods small and focused
- Item 58: Prefer for-each loops to traditional for loops
- e.g. (Element e: elements)
- can’t use for loop if destructive filtering, transforming, parallel iteration
- Item 59: Know and use the libraries
- take advantage of the knowledge of experts who wrote it and the experience of people who’ve used it
- e.g. use ThreadLocalRandom
- numerous features are added so pay attention to these additions
- every programmer should be famililar with the basics of java.lang, java.util and java.io
- Item 60: A void float and double if exact answers are required
- float and double are not exact do not use for monetary calculations
- use BigDecimal, int, or long instead
- Item 61: Prefer primitive types to boxed primitives
- Applying the == operator to boxed primitives is almost always wrong
- boxing/unboxing in a loop can have severe performance degradation
- use boxed primitives for collections and type parameters
- Item 62: Avoid strings where other types are more appropriate
- strings a poor substitute for other values
- commonly misued as primitives, enums, or aggregate types
- Item 63: Beware the performance of string concatenation
- repeatedly concatenating n strings requires time quadradic in n
- don’t use with more than a few strings
- use StringBuilder
- Item 64: Refer to objects by their interfaces
- program will be more flexible
- use class if no appropriate interface exists
- sometimes appropriate to use class if using a class-based framework
- use concrete class if depending on some characteristic of that class e.g. ordering of LinkedHashSet
- if there is no appropriate interface, use th least specific clas that provides the required functionality
- Item 65: Prefer interfaces to reflection
- reflection can be flexible but at a cost:
- lose compile-time checking
- clumsy and verbose code
- performance loss
- can benefit by using in a very limited form
- create instances reflectively and accesss them normally via interfaces or superclasses
- Item 66: Use native methods judiciously
- may need for accessing platform specific facilities
- access to libraries that don’t exist in java
- rarely advisable to use native methods for improved performance
- disadvantages
- no longer immune to memory corruption errors
- glue code that is difficult to read and tedious to write
- Item 67: Optimize judiciously
- often produce software that is neither fast nore correct
- strive to write good programs rather than fast ones
- good programs embody principle of information hiding and localize design decisions
- architectural flaws that limit performance can be impossible to fix
- strive to avoid design decisions that limit performance
- interactions between components and the outside world
- consider the performance consequences of your API design decisions, wire-level protocols, and persistent data formats
- measure performance before and after each attempted optimization
- Java has a weaker performance model and bigger abstraction gap
- need to measure optimization on different implementations and hardware platforms
- the complexity of processors, VMs, libraries have grown over time
- Item 68: Adhere to generally accepted naming conventions
- refer to the Java Language Specification
- don’t need to follow slavishly if conventions hold otherwise use common sense
- Exceptions
- Item 69: Use exceptions only for exceptional conditions
- because for exceptional cases, little incentive for jvm implementors to optimize
- try/catch block inhibits certain optimizations
- can obfuscate code and reduce performance
- never use for ordinary control flow
- well-designed API does not force clients to use exceptions for ordinary control flow
- can provide a state-testing method (e.g. next and hasNext) or optional
- mildy prefer state-testing, unless synchronization is an issue
- Item 70: Use checked exceptions for recoverable condition and runtime exceptions for programming errors
- only for cases where caller can reasonably recover from
- runtime exceptions indicate programming error
- all unchecked throwables should subclass RuntimeException
- exceptions are just objects, include methods that furnish information
- Item 71: Avoid unnecessary use of checked exceptions
- can force programmers to deal with problems and enhance readability
- overuse can make them less pleasant to use
- can’t be used in streams
- use sparingly
- prefer optional unless you need to return information
- Item 72: Favor the use of standard exceptions
- strive for high degree of code reuse
- most common: IllegalArgumentException, IllegalStateException, NullPointerException, IndexOutOfBoundsException, ConcurrentModificationException, UnsupportedOperationException
- use only if conditions under which you throw are consistent with exception’s documentation
- Item 73: Throw exceptions appropriate to the abstractionÂ
- high layers should catch lower-level exceptions and throw exceptions that can be explained in terms of the higher level abstraction
- use exception chaining to pass lower level exception, but don’t overuse
- best way to deal with low level exceptions is to avoid them by ensuring lower-level methods succeed
- If it is impossible to prevent them, have higher level silently work around, but log exceptions so programmers can investigate
- Item 74: Document all exceptions thrown by each method
- always declare checked exceptions individually, no supertype
- wise to document unchecked and checked exceptions
- every public method documentation should describe preconditions
- Use @throws to document each exception, but not for unchecked exceptions
- if an exception is thrown by many methods for the same reason, document at class level
- Item 75: Include failure-capture information in detail messages
- detail message should contain the values of all parameters and fields that contribute to the exception
- do not include passwords or other sensitive information
- stack trace intended to be analyzed along with documentation and source code so don’t need to be too verbose
- can ensure exception captures correct information by including in constructor
- not a highly used idiom, but highly recommended
- allows programmatic access to information around failure cases
- Item 76: Strive for failure atomicity
- should leave object in state that it was in prior to invocation
- 1) check parameters for validity prior to performing operations
- 2) make a temporary copy first
- 3) write recovery code
- not always achievable or desirable
- when rule is violated, API should clearly state what state the object will be left in
- Item 77: Don’t ignore exceptions
- empty catch defates the purpose of exceptions
- if you ignore, add a comment explaining rationale in catch block and name the exception ignored
- will result in a program that continues to fail silently
- letting exception propagate can at lease cause program to fail swiftly and perserve information to aid in debugging
- Concurrency
- Item 78: Synchronize access to shared mutable data
- synchronized ensures only a single thread can execute a method or block at one time
- guarantees no method will ever observe the object in an inconsistent state
- without synchronization, changes may not be visisble to other threads
- ensures threads see effects of all previous modifications
- synchronization required for reliable communication between threads as well as for mutual exclusion
- synchronization not guaranteed to work unless both read and write operations are synchronized
- volatile keyword can be used to ensure communication effect
- use synchronized to ensure multiple invocataions won’t be interleaved
- don’t share mutable data if you can
- confine mutable data to a single thread
- Item 79: Avoid excessive synchronization
- never cede control to the client within a synchronized method or block e.g an alien method
- do as little work as possible inside synchronized regions
- for mutable classes, either allow client to synchronize externally or make class threadsafe by synchronizing internally
- only do internal, if you can achieve higher concurrency than you can with using client
- Item 80: Prefer executors, tasks, and streams to threads
- use executor framework
- runnable or callable task
- fork-join to steal work and increase utilization
- Item 81: Prefer concurrency utilities to wait and notify
- given difficulty of using wait and notify use higher-level concurrency utilities
- e.g. Executor framework, concurrent collections, synchronizers
- use concurrent implementation of data structures e.g. ConcurrentHashMap
- Item 82: Document thread safety
- synchronized is an implementation detail
- immutable
- unconditionally thread safe (don’t need synchronization) e.g. ConcurrentHashMap
- conditionally thread-safe (some methods require external synchronization)
- not thread-safe: clients must srround each method invocation with synchronization
- thread-hostile: unsafe even with synchronization
- lock fields should always be declared final
- Item 83: Use lazy initialization judiciously
- under most circumstances, normal initialziation is preferrable
- if you use lazy initialization to break an initialization circularity, use synchronized accessor
- if you need for performance on a static field, use the lazy initialization holder class idiom
- if you need lazy init for performance on a instance field, use the double check idiom
- Item 84: Don’t depend on the thread scheduler
- program will not be portable
- threads should not be run if they aren’t doing useful work
- resist temptation to “fix” program by putting in calls to Thread.yield
- Thread priorities are among the least portable features of Java
- Serialization
- Item 85: Prefer alternatives to Java Serialization
- allows attacker to do dangerous things
- e.g. execute arbitrary native code on underlying hardware
- e.g. denial-of-service attacks with deserialization bombs
- best way is to never deserialize anything
- use cross-platform structured data representations e.g. JSON or protobufs
- don’t deserialize untrusted datat
- if you do, use class whitelists
- Item 86: Implement Serializabl with great caution
- decreases flexibility to change a class’s implementation
- byte-stream encoding becomes part of its exported API
- increases liklihood of bugs and security holes
- increases testing burden
- classes designed for inheritance should rearely implement Serializable
- Item 87: Consider using a custom serialized form
- do not the default serialized form without first considering whether it is appropriate
- even if default form is appropriate, must provide a readObject method to ensure invariants
- before deciding to make a field nontransient, convince yourself that its value is part of the logical state of the object
- declare an explicit serial version UID and do not change if you want to maintain compatibility
- Item 88: Write readObject methods defensively
- readObject is effectively another public constructor
- when an object is deserialized it is critical to defensively copy any field containing an object reference that a client must not possess
- when writing a public constructor do not assume that the byte stream reprsents an actual serialized instance
- Item 89: For instance control prefer enum types to readResolve
- Java guarantees there can be no instance besides the declared constants for enums
- if you can’t use enums and class needs to be serializable and instance controlled, you must 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
- design a private static nested class that concisely represents the logical state of an instance of the enclosing class
- consider this pattern whenever you find yourself having to write a readObject or writeObject method on a class that is not extendable by its clients
Like this:
Like Loading...