对此的简单回答是,像new BufferedReader() 这样的实例化总是 每次调用它时都会创建一个不同的实例;调用像 System.console() 这样的方法可能会也可能不会给你不同的实例。
最终,所有对象都通过new 实例化;您可能在代码中看不到它。
以下是System.console() 可能的几种实现方式(完全简化,实际上并不像它那样):
// (1) Returns new instance each time
class System {
static Console console() {
return new Console();
}
}
或
// (2) Returns same instance each time
class System {
private static final Console CONSOLE = new Console();
static Console console() {
return CONSOLE;
}
}
(实现的方法还有无限多,这只是两个例子。你可以通过查看源代码看到way it is implemented in OpenJDK - 它类似于(2),因为每次都返回相同的实例,只是还有一些我不想在这里描述的复杂情况)
在 (1) 中,如果您调用 System.console() 两次,您将返回两个不同的 Console 实例:
System.console() != System.console()
在(2)中,如果你调用System.console()两次,你会得到Console的同一个实例:
System.console() == System.console()
我在这里要问的问题是我是否需要关心我返回的是不同的实例还是相同的实例?答案是可能不需要,如果 API 设计者已经完成了合理的工作。
关于是否公开创建新的Console 的决定是由编写类的人做出的。有很多原因导致他/她可能不希望您在每次调用该方法时创建不同的实例,例如:
- 您正在创建的东西可能非常昂贵(速度慢、占用大量资源等),因此您不想创建太多;
- 你想要的东西在逻辑上只有一个实例(它是一个单例)。
他/她可能希望您在每次调用该方法时创建一个单独的实例的原因有很多,例如:
- 您不希望使用该实例的所有地方都共享状态。共享可变类的实例时,您必须担心线程安全等问题。
您可能不希望用户直接调用构造函数的原因有很多:
-
new Console() 创建Console 的实例完全正确;诸如控制台之类的东西通常与平台相关,因此您实际上可能希望在 Windows、MacOS 等上运行时返回WindowsConsole、MacConsole 等实例。如果WindowsConsole 和MacConsole 扩展Console,要么其中可以从System.console() 方法返回。
-
在 Java 7 中引入菱形运算符 <> 之前,有必要在 new 语句中包含完整的泛型参数,例如ArrayList<HashMap<String, List<String>>> list = new ArrayList<HashMap<String, List<String>>>();;但是,通用方法允许将其写为 ArrayList<HashMap<String, List<String>>> list = newList():
<T> List<T> newList() { return new ArrayList<T>(); }
(有时候,你需要很多参数传递给构造函数,使用Builder Pattern很方便。这与问题中的案例无关,但这是一个原因不直接调用构造函数。)
问题是这些是内部实现细节,应该封装:作为Console 类的用户,你不需要关心创建它的成本有多大,或者是否有共享状态:你只需要一个Console。
这种封装是通过提供像System.console()这样的方法来实现的:您不需要知道该方法是像上面的(1)还是(2)(或任何其他方法)那样实现的。
此外,如果该类最初是像 (1) 那样编写的,并且证明是有问题的,那么它的实现可以更改为 (2),而您作为 System 类的用户不需要更新您的代码.
这对于初学者来说可能有点太详细了,我可以尝试帮助您了解更多;总而言之,有时不直接创建实例会更好。