[英]How to initialize a static field as an object of the same class in Java?
[英]How to initialize a constant object field for a class in java
我想知道为类初始化复杂对象类型的常量字段的最佳方法是什么,哪种方法性能最高?
1)内联初始化
public class TopClass {
private static final ComplexObject sdf = new ComplexObject();
public TopClass (
}
}
2)初始化方法
public class TopClass {
private static final ComplexObject sdf = initializeComplexObject();
private static ComplexObject initializeComplexObject(){
return sdf == null ? new ComplexObject() : sdf;
}
public TopClass (
}
}
3)在构造函数中初始化,4)静态初始值设定项块或您建议采用的其他方法...
每次创建新的TopClass类时,sdf都会初始化吗? 我希望sdf字段在应用程序生存期内仅初始化一次。
静态初始化器的性能几乎无关紧要(特别是对于这种琐碎的初始化),因为对于一个类,它只完成一次。
2)这种特定的方法方法是多余的,因为在初始化类时会立即调用它; 当静态初始值设定项调用该方法时, sdf
始终为 null,并且您无需再次调用该方法(至少不是为了给sdf
提供不同的值)。 由于您有意读取未初始化的final字段,因此它也很鲁ro。
因此,只需要删除条件,就可以有效地使用内联初始化方法,并间接调用方法。
例如,如果您想在格式化程序上进行其他配置,例如设置时区,则该方法很有用。
3)不要在构造函数中初始化静态成员。 构造函数用于初始化实例成员。
特别是,这要求您将静态字段设为非最终值。 这意味着您必须担心该字段更新的可见性,以避免多个线程初始化该字段,因为它们看到空值
4)在声明字段时初始化字段只是声明静态初始化程序的简写方式。 问题中的第一个代码块在语义上与此相同:
private static final ComplexObject sdf;
static {
sdf = new ComplexObject();
}
如果您不能避免,则明确地执行此操作没有任何好处。
静态初始值设定项类似于匿名方法。 Google的内部Java实践建议在可能的情况下使用方法而不是显式的静态初始化程序块,部分原因是您可以显式调用它们进行测试,也因为它们必然会迫使您仅初始化一个字段。 (我在很大程度上同意这是一个很好的建议,但是请注意,您会丢失方法中的明确赋值检查(如上所述),这可能有助于捕获某些类型的错误)。
结论:使用private static final ComplexObject sdf = new ComplexObject();
,因为您不需要任何更复杂的操作。
这里更大的问题是正确性,即确保线程之间不共享sdf
:在编辑问题之前, sdf
是SimpleDateFormat
,它不是线程安全的。 我不知道什么是ComplexObject
,但是您需要确保它是线程安全的,或者以线程安全的方式访问它。 在进行微优化之前,请担心此类事情。
就像另一个答案所说的那样:性能绝对不是问题。 启动JVM时,它可能必须加载数千甚至数十万个类。 在此过程中如何初始化单个常量根本不重要。 最好,我们将纳秒用于不同的方法。
因此,唯一可以指导决策的就是:干净的编码思想,例如:什么是人类最容易理解/理解的方式。
我认为:如果可能,您会选择选项1。如果表达式不是太复杂,并且可以简单地看一下并理解SOME_CONSTANT = some expression
,为什么SOME_CONSTANT = some expression
增加方法调用/初始化程序块的复杂性呢?
但是,当然可以:当表达式已经“复杂”了,并且您很想写一条注释来解释为什么它以特定方式运行时,那么使用辅助方法是个好主意。 一个有用的方法名称可能会解释需要解释的内容(省去您的注释!)
换句话说:始终专注于编写最少的代码,这也易于阅读和理解。 您不会使用初始化器方法是因为可以使用,但是因为这样做会使事情更容易理解(在您的情况下,事实并非如此)。 初始化程序块(恕我直言)更糟,仅因为它们如此罕见。 在我眼中,它们是异常的,因为如今您甚至可以将“地图”,“列表”等创建为“文字”。
@Andy Turner的答案几乎可以回答这个问题,但是我想添加一个额外的设计注意事项,因为您的示例对象是ComplexObject
并且您似乎专注于性能方面。
如果您只想实例化对象一次,但是初始化非常昂贵( ComplexObject
可能会提出这样的建议),则您可能只想在对象实际使用后即第一次访问对象时进行初始化( 惰性初始化和延迟加载 )。
以其simplext形式,您基本上将您的ComplexObject
隐藏在静态getter private static ComplexObject getComplexObject()
后面。 然后,您可以使用某些习惯用法:
如果您不希望使用其他方法,则可以定义一个Lazy
类,例如:
class Lazy<T> implements Supplier<T> {
private T value;
private final Supplier<T> initializer;
public Lazy(final Supplier<T> initializer) {
this.initializer = initializer;
}
@Override
public T get() {
if (value == null) {
synchronized (this) {
if (value == null) {
value = initializer.get();
}
}
}
return value;
}
}
public class TopClass {
private static final Lazy<ComplexObject> sdf = new Lazy<>(ComplexObject::new);
public void exampleMethod() {
ComplexObject o = sdf.get();
}
}
如果您不希望使用get()
的其他方法调用,并且可以将ComplexObject
的行为封装在接口中,则可以使用代理模式 。 然后,代理应该将 ComplexObject
所有方法调用委托给真实实例(在这种情况下,我使用先前的代码通过扩展Lazy
来创建实例,该Lazy
通过get()
访问,但是您可以根据需要实现lazy init。在您的代理中)。
interface ComplexObject {}
class ComplexObjectImpl implements ComplexObject {}
class ComplexObjectProxy extends Lazy<ComplexObjectImpl> implements ComplexObject {
public ComplexObjectProxy() {
super(ComplexObjectImpl::new);
}
}
public class TopClass {
private static final ComplexObject sdf = new ComplexObjectProxy();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.