- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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 ofclone
. - 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.