Repository files navigation
Part 1: Creating and Destroying Objects
Consider static factory methods instead of constructors : Use static methods for better flexibility and
readability.
Consider a builder when faced with many constructor parameters : Simplify complex object creation using the
builder pattern.
Enforce the singleton property with a private constructor or an enum : Use singletons for classes requiring a
single instance.
Enforce noninstantiability with a private constructor : Prevent instantiation of utility classes.
Prefer dependency injection to hardwiring resources : Decouple classes from dependencies using injection.
Avoid creating unnecessary objects : Reuse objects to reduce overhead.
Eliminate obsolete object references : Avoid memory leaks by cleaning up unused references.
Avoid finalizers and cleaners : Use try-with-resources or close methods for resource management.
Prefer try-with-resources to try-finally : Automatically manage resources using try-with-resources.
Part 2: Methods Common to All Objects
Override equals judiciously : Ensure equals adheres to reflexivity, symmetry, transitivity, consistency, and
null checks.
Always override hashCode when you override equals : Ensure equal objects have consistent hash codes.
Always override toString : Provide a clear and informative string representation of objects.
Override clone judiciously : Prefer copy constructors or static factory methods over clone.
Consider implementing Comparable : Define a natural order for your objects.
Part 3: Classes and Interfaces
Minimize the accessibility of classes and members : Use encapsulation to reduce exposure.
In public classes, use accessor methods, not public fields : Preserve flexibility by using getter and setter
methods.
Minimize mutability : Favor immutable objects for simplicity and thread safety.
Favor composition over inheritance : Reduce fragility by composing objects instead of inheriting.
Design and document for inheritance or else prohibit it : Clearly define how inheritance should be used or
prevent it.
Prefer interfaces to abstract classes : Interfaces provide greater flexibility for multiple implementations.
Design interfaces for posterity : Ensure interfaces are forward-compatible and extensible.
Use interfaces only to define types : Avoid constant interfaces.
Prefer class hierarchies to tagged classes : Use polymorphism instead of type codes.
Favor static member classes over nonstatic : Reduce coupling with static nested classes.
Limit source files to a single top-level class : Simplify file organization and reduce conflicts.
Don’t use raw types : Always specify type parameters for type safety.
Eliminate unchecked warnings : Resolve or suppress unchecked warnings for cleaner code.
Prefer lists to arrays : Lists are type-safe, whereas arrays are not.
Favor generic types : Use generics to create reusable and type-safe classes.
Favor generic methods : Use generics to create flexible and type-safe methods.
Use bounded wildcards to increase API flexibility : Support more use cases with ? extends and ? super.
Combine generics and varargs judiciously : Avoid heap pollution when combining generics with varargs.
Consider typesafe heterogeneous containers : Use generics and Class objects to create flexible containers.
Part 5: Enums and Annotations
Use enums instead of int constants : Enums are safer, more expressive, and more powerful.
Use instance fields instead of ordinals : Avoid using ordinal to store data; use fields instead.
Use EnumSet instead of bit fields : Simplify set operations with EnumSet.
Use EnumMap instead of ordinal indexing : Use EnumMap for mapping enums to values.
Emulate extensible enums with interfaces : Combine enums with interfaces for extensibility.
Prefer annotations to naming patterns : Use annotations for metadata and behavior tagging.
Consistently use the @Override annotation : Prevent errors by ensuring methods override superclass methods.
Use marker interfaces to define types : Marker interfaces provide compile-time type information.
Part 6: Lambdas and Streams
Prefer lambdas to anonymous classes : Lambdas are more concise and readable.
Prefer method references to lambdas : Use method references for clarity and simplicity.
Favor the use of standard functional interfaces : Use Function, Supplier, Consumer, etc., for common tasks.
Use streams judiciously : Streams are powerful but can reduce clarity for simple cases.
Prefer side-effect-free functions in streams : Avoid modifying external state in stream operations.
Prefer Collection to Stream as a return type : Collections offer more flexibility than streams.
Use caution when making streams parallel : Ensure thread safety and avoid performance pitfalls in parallel
streams.
Check parameters for validity : Validate inputs to avoid runtime errors.
Make defensive copies when needed : Protect internal state by copying mutable inputs and outputs.
Design method signatures carefully : Choose method names and parameter types thoughtfully.
Use overloading judiciously : Avoid ambiguous overloaded methods.
Use varargs judiciously : Validate input and avoid excessive use of varargs.
Return empty collections or arrays, not nulls : Simplify client code by avoiding null returns.
Return Optionals judiciously : Use Optional for potentially absent values, but not excessively.
Part 8: General Programming
Adhere to generally accepted naming conventions : Follow consistent naming standards for clarity.
Avoid unnecessary use of checked exceptions : Use unchecked exceptions for programming errors.
Favor the use of standard exceptions : Use exceptions like IllegalArgumentException for common scenarios.
Throw exceptions appropriate to the abstraction : Avoid exposing implementation details in exceptions.
Document all exceptions thrown by each method : Help users handle exceptions properly.
Include failure-capture information in exceptions : Provide detailed information in exception messages.
Strive for failure atomicity : Ensure operations leave objects in a consistent state on failure.
Don’t ignore exceptions : Always handle exceptions appropriately.
Consider using APIs over handwritten code : Leverage standard libraries for efficiency and maintainability.
Refer to objects by their interfaces : Use interfaces for flexibility and better design.
Prefer interfaces to reflection : Avoid reflection unless necessary for dynamic behavior.
Use native methods judiciously : Prefer Java code over native methods for portability and maintainability.
Optimize judiciously : Optimize only when performance is a verified issue.
Adhere to the general contract when overriding equals : Ensure consistent behavior in equality checks.
Always override hashCode when you override equals : Ensure consistent behavior in hash-based collections.
Always override toString : Provide meaningful string representations for debugging and logging.
Override clone judiciously : Prefer alternatives to clone for copying objects.
Implement Serializable judiciously : Serialization introduces risks; consider alternatives.
Prefer atomic variables to synchronized variables : Use atomic variables for better performance and clarity.
Avoid excessive synchronization : Minimize synchronized blocks to reduce contention.
Prefer executors and tasks to threads : Use the Executor framework for scalable concurrency.
Use concurrent collections judiciously : Prefer ConcurrentHashMap and other utilities over manual
synchronization.
Document thread safety : Clearly indicate if a class is thread-safe, not thread-safe, or conditionally
thread-safe.
Avoid thread-local variables when possible : Use sparingly to avoid memory leaks and complexity.
Part 12: General Good Practices
Avoid using Java serialization : Prefer modern serialization frameworks like JSON or Protocol Buffers.
Use dependency injection judiciously : Inject dependencies for better decoupling.
Avoid reflection for performance and security : Use reflection sparingly to avoid performance and maintainability
issues.
Follow Java conventions for serialization : Follow established practices when implementing Serializable.
Prefer enums to singletons : Enums are the best way to implement singletons.
Part 13: Additional Best Practices
Use lazy initialization judiciously : Avoid premature or excessive use of lazy initialization.
Minimize scope of local variables : Declare variables in the narrowest possible scope.
Prefer immutable objects : Reduce complexity with immutable classes.
Avoid instance pooling : Favor object creation for simplicity and performance.
Document thread safety assumptions : Ensure proper usage by explicitly documenting thread safety.
Part 14: Concurrency and Performance
Use dependency injection to improve flexibility : Decouple dependencies for easier testing and maintenance.
Avoid excessive synchronization : Overuse of synchronized blocks can degrade performance and cause deadlocks.
Prefer concurrency utilities to low-level synchronization : Use frameworks like java.util.concurrent.
Be cautious with parallel streams : Ensure proper thread safety when using parallel streams.
Part 15: General Practices
Use enums for singletons : Simplify singleton design by using enums.
Use atomic variables instead of locks : For single variables, atomic classes are simpler and faster.
Use ThreadLocal variables with care : Avoid misuse to prevent memory leaks and unnecessary complexity.
Avoid Java serialization : Serialization introduces many risks; consider safer alternatives like JSON or Protocol
Buffers.
Prefer alternatives to clone : Use copy constructors or factory methods instead of clone.
Use interfaces for types : Always prefer interfaces for type definition and flexibility.
Avoid raw types : Use generics for type-safe and maintainable code.
Prefer immutability : Immutable objects are simpler, safer, and easier to use in multithreaded environments.
You can’t perform that action at this time.