繁体   English   中英

如何在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 :在编辑问题之前, sdfSimpleDateFormat ,它不是线程安全的。 我不知道什么是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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM