如何在java中为外部库类创建单例对象?

问题描述 投票:0回答:2

有没有更好的方法为外部类创建单例对象?我正在这样做,但是有更好的方法吗?

import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;

public class GetAWSCredentials {
    private static AWSCredentialsProvider credentials;

    public static synchronized AWSCredentialsProvider getInstance() {
        if (credentials == null) {
            credentials = new DefaultAWSCredentialsProviderChain();
        }
        return credentials;
    }
}
java design-patterns
2个回答
0
投票

在这种情况下,您的外部库类不是单例,因为首先它是一个接口。即使不是,任何人都可以从此类外部创建一个新实例。您无法将不是单例的外部类转变为一个。

现在,如果您想为自己的类 GetAWSCredentials 找到创建单例的最简洁方法,那么您可以尝试 effective-java 建议


0
投票

是的,有。简单来说就是:

public class GetAwsCredentials {
    private static final AwsCredentialsProvider credentials = new GetAwsCredentials();

    public static AwsCredentialsProvider getInstance() {
        return credentials;
    }
}

您可能会想:哦不,那不行!我不希望在 JVM 启动时创建此实例,仅在需要时创建!

但是..这已经是java的工作原理了。直到需要时才会按原样加载类。在某种访问方法中创建实例实际上是没有意义的;只需在类负载上进行即可。在 99% 需要单例的情况下,如果你要提到相关的类,你很快就会调用

getInstance()
方法。

在极不可能的情况下,您确实需要这样做(并且如果该构造函数做了相当多的繁重工作,则这才算数),您可以使用此构造:

public class GetAwsCredentials {
  private static class AwsCredentialsHolder {
    static final AwsCredentialsProvider provider = new AwsCredentialsProvider();
  }

  public static AwsCredentialsProvider getInstance() {
    return AwsCredentialsHolder.provider;
  }
}

无需使用

volatile
synchronized
。也就是说:

  • 这些东西要么“太重”(例如
    synchronized
    ),要么试图减轻重量,例如使用双重检查锁定,“太轻” - 它们似乎可以工作,但在适当的负载下它们会失败(从某种意义上说,您最终会得到2个实例)。
  • 上面使用了 java itself 具有内置锁定机制的事实:保证类永远不会加载多次,因此,通过立即初始化
    static final
    字段,我们可以搭载它,这是该 java 提供的最有效的机制。
  • 第二个片段之所以有效,是因为在有人调用
    .getInstance()
    之前,该内部类从未被触及。只要该内部类未被触及,它就不会被初始化。即使
    new AwsCredentialsProvider()
    需要一个小时,直到有人打电话给
    .getInstance()
    才花掉这个小时。
© www.soinside.com 2019 - 2024. All rights reserved.