首先,如果这是一个非常愚蠢的问题,请原谅我,我只是在努力学习这门语言的核心。我正在阅读 Effective Java,第一章讨论的是静态工厂方法与构造函数。他们的优点和缺点。让我感到困惑的几件事是:
- 静态工厂方法返回的对象的类是非公共的——它到底是什么意思?
- 与构造函数不同,静态工厂方法不需要在每次调用时都创建一个新对象——这是怎么发生的?我调用工厂方法只是为了获取一个新对象,我们是否在工厂方法中检查对象是否已经存在?
谢谢。
首先,如果这是一个非常愚蠢的问题,请原谅我,我只是在努力学习这门语言的核心。我正在阅读 Effective Java,第一章讨论的是静态工厂方法与构造函数。他们的优点和缺点。让我感到困惑的几件事是:
谢谢。
静态工厂方法返回的对象的类是非公共的——它到底是什么意思?
这意味着静态工厂方法返回的对象的实际类可以是声明类型的子类,并且该子类不必是公共的。这只是客户端代码不应该关心的另一个实现细节。
与构造函数不同,静态工厂方法不需要在每次被调用时都创建一个新对象——这是怎么发生的?我调用工厂方法只是为了获取一个新对象,我们是否在工厂方法中检查对象是否已经存在?
是的,这是可以做到的一种方式。但实际上,一切皆有可能。
首先,感谢您选择 Java-lit:Bloch 的书是一本出色的入门书。
要回答您的第二个问题('与构造函数不同,静态工厂方法不需要在每次调用时都创建一个新对象'),重要的是要意识到 Bloch 在这里所说的是,对于静态工厂,您可以选择:返回一个新对象或返回一个预先存在的对象。这一切都取决于你想做什么。
例如,假设您有一个非常简单的 Money 类型的值类。您的静态工厂方法可能应该返回一个新实例——即具有特定 Money 值的新对象。所以,像这样:
public class Money {
private Money(String amount) { ... } /* Note the 'private'-constructor */
public static Money newInstance(String amount) {
return new Money(amount);
}
}
但是,假设您有一些管理某些资源的对象,并且您希望通过某个 ResourceManager 类同步对该资源的访问。在这种情况下,您可能希望您的静态工厂方法将自身的相同实例返回给每个人——强制每个人都经过同一个实例,以便该 1 个实例可以控制该过程。这遵循单例模式。像这样的东西:
public ResourceManager {
private final static ResourceManager me = new ResourceManager();
private ResourceManager() { ... } /* Note the 'private'-constructor */
public static ResourceManager getSingleton() {
return ResourceManager.me;
}
}
上述方法强制您的用户只能使用单个实例,从而允许您精确控制谁(以及何时)可以访问您正在管理的任何内容。
要回答您的第一个问题,请考虑这个(诚然不是最好的例子,它非常特别):
public class Money {
private Money(String amount) { ... }
public static Money getLocalizedMoney( MoneyType localizedMoneyType, String amount ) {
switch( localizedMoneyType ) {
case MoneyType.US:
return new Money_US( amount );
case MoneyType.BR:
return new Money_BR( amount );
default:
return new Money_US( amount );
}
}
}
public class Money_US extends Money { ... }
public class Money_BR extends Money { ... }
请注意我现在如何做到这一点:
Money money = Money.getLocalizedMoney( user_selected_money_type );
saveLocalizedMoney( money );
同样,这是一个非常人为的示例,但希望它可以帮助您或多或少地了解 Bloch 在这一点上的意思。
其他答案很好——我只是认为,作为初学者,有时查看一些实际代码会有所帮助。
当您使用new
关键字时,作为开发人员的您知道 JDK 将创建该对象的新实例。作者的意思是,当您使用静态方法时,开发人员不再知道该方法是在创建新实例还是可能在做其他事情。其他方法可以是重用缓存数据、对象池、创建私有实现并返回该类的子类。
静态工厂方法返回的对象的类是非公共的
静态工厂方法通常会返回一个类型为接口的对象(最常见),或者有时会返回一些基类(不太常见)。无论哪种情况,您都不知道返回对象的确切类。
这样做的好处是获得一个您知道其行为的对象,而不必担心它实例化的类的混乱细节。
与构造函数不同,静态工厂方法不需要在每次调用时都创建一个新对象
要理解这一点,请考虑使用单例的情况。您可以在某些工厂类上调用 .getInstance() 来获取某个对象的单例实例。通常,如果它不存在,它会创建一个对象的实例,或者如果它已经存在,则为您提供现有实例。在任何一种情况下,您都会取回对象的副本。但是您不(也不会)知道是否必须创建这个单例,或者之前是否已经构建了一个。
这样做的好处是可以为您管理对象的生命周期和创建时间。
您的两个问题都可以通过查看一些利用静态工厂方法的这两个属性的代码来回答。我建议查看 Guava 的ImmutableList。
请注意无参数工厂方法如何of()
始终返回相同的实例(它不会每次都创建一个新实例)。如果你仔细看,你还会注意到它的copyOf(Iterable)
工厂方法实际上返回了传递给它的对象,如果该对象本身是一个ImmutableList
. 这两个都利用了一个ImmutableList
保证永远不会改变的事实。
还要注意其中的各种工厂方法如何返回不同的子类,例如EmptyImmutableList
、SingletonImmutableList
和RegularImmutableList
,而不暴露这些对象的类型。方法签名只是显示它们返回ImmutableList
,并且所有子类都ImmutableList
具有包私有(默认)可见性,使它们对库用户不可见。从用户的角度来看,这提供了多个实现类的所有优点,而不会增加任何复杂性,因为它们只允许ImmutableList
作为单一类型查看。
此外, GuavaImmutableList
中的大多数可实例化类都使用静态工厂方法。Guava 还体现了 Effective Java 中提出的许多原则(这并不奇怪,因为它是根据这些原则设计的,并且得到了 Josh Bloch 本人的指导),因此您可能会发现更多地了解它很有用。重新阅读这本书。