关于《Effective Java》中Joshua Bloch对依赖底层资源的单例及静态工具类使用建议的澄清
Great question—let's unpack exactly what Bloch is recommending here, because it's easy to misinterpret the line between "avoid singletons/static classes" and "use dependency injection".
First, let's anchor ourselves to Bloch's core point from Item 5:
Classes that depend on one or more underlying resources should not be implemented as singletons or static utility classes, and should not create these resources directly. Instead, use dependency injection.
The Key Distinction: State vs. Stateless
Bloch's advice isn't a blanket ban on singletons or static utility classes—it's a ban on using them for classes that rely on mutable external resources that affect their behavior. Let's break this down into two clear scenarios:
1. When You Should Avoid Singletons/Static Utility Classes
If your class has behavior that changes based on an external resource (e.g., a database connection, file system, or API client), do not use a singleton or static utility class. Here's why:
- Singletons/static classes enforce a single global instance, which means their dependencies are fixed at initialization. You can't easily swap out a production database connection for a test mock, or use different resource implementations in different contexts.
- Dependency injection works best with instances that are created per-context (or as needed), where the caller provides the required resources. Singletons undermine this flexibility by locking in dependencies.
For example: A DataImporter that relies on a DatabaseConnection shouldn't be a singleton. If it were, you couldn't test it with an in-memory database—you'd be forced to use the production connection everywhere. Instead, make it a regular class and inject the DatabaseConnection via the constructor:
public class DataImporter { private final DatabaseConnection connection; public DataImporter(DatabaseConnection connection) { this.connection = connection; } // Methods that use connection... }
This aligns perfectly with Bloch's recommendation: the class doesn't create its own resource, and it's flexible enough to work with any DatabaseConnection implementation.
2. When Singletons/Static Utility Classes Are Acceptable
Bloch has no issue with singletons or static classes if they're stateless and don't depend on external resources. These are classes where behavior is purely determined by their input parameters—no hidden state or external dependencies.
Examples include:
java.util.Collections(static utility methods that operate on collections, no external dependencies)- A singleton
MathUtilsclass with pure functions likecalculateHypotenuse(double a, double b)
In these cases, there's no need for dependency injection because there's nothing to inject—their behavior is consistent regardless of context.
The Bottom Line: What's Most Aligned With Bloch's Advice?
If your class has variables (dependencies) that affect its behavior:
Do not use singletons or static utility classes. Stick to regular classes with dependency injection (constructor injection is preferred, per Bloch's later advice in the book).
This is the strategy Bloch explicitly recommends. Singletons and static classes are only appropriate for stateless, dependency-free code.
内容的提问来源于stack exchange,提问作者Leo4343

