【问题标题】:Using static in Java singletons?在 Java 单例中使用静态?
【发布时间】:2010-09-24 19:25:00
【问题描述】:

我正在用 Java 为 Android 构建一个单例服务:

final class MyService {

    private static Context context = null;
    // ...

    private MyService() {
        myObj = new MyObj(context);
            // ...  
    }                       
    private static class Singleton {
        private static final MyService INSTANCE = new MyService();
    }

    /**
     *  a singleton - do not use new(), use getInstance().
     */
    static synchronized myService getInstance(Context c) {
        if (context == null) context = c;
        return Singleton.INSTANCE;
    }

为了提高性能,我始终使用静态变量和方法。

我的问题:

  • 对于对象,new() 确保 初始化该对象的机会 在使用之前。有没有 相当于静态类?为了 示例:当静态方法依赖时 在较早的初始化时-例如 至于上面的“上下文” - 有吗 一种确保 初始化首先发生?我必须吗 如果没有就抛出异常?

  • 因为这都是静态的,我希望 只有类本身:是 根据定义,这是一个单身人士?是 上面推荐的代码只是 防止实例化的方法 对象?

提前致谢!

【问题讨论】:

  • android中的所有服务都是单例的,你想完成什么?
  • 我建议不要让你的单例有状态(添加上下文)。这两件事是两个严重冲突的反模式。
  • @Falmarri:我正在努力实现 1)确保某些方法的单一权威来源,2)最大化性能。我想这类似于 getSystemService() 但在单个进程中(因此不需要 AIDL)。到目前为止,我的方法是扩展 Service,然后直接从其他应用程序组件中调用它的方法(不使用意图)。
  • @Mark:我的服务跟踪事件并通过 getter 提供摘要。这显然是有状态的,并且似乎也是单例的可行候选者。这是一个例外 - 或者你能建议我在哪里可以了解更多关于反模式的信息吗?谢谢!
  • @Falmarri:我需要添加 .. 我的服务通过警报定期运行以收集事件数据并进行汇总。该数据需要随时可用——例如当一个小部件读取它时。我已经在内存中“永久”地为小部件提供服务,但我突然想到我应该将这些解耦:定期运行的服务将摘要数据写入内容提供者,然后由调用者读取。 /me 感到很困惑 :)

标签: java android static singleton


【解决方案1】:

这当然是创建单例的一种方法。我认为您可以取消静态助手;出于性能原因,静态化并不能节省太多,而且会混淆代码。

我认为您真正的问题是静态的构造是否可以延迟到context 不为空。答案取决于虚拟机;在大多数“vanilla”Java VM 中,INSTANCE 在第一次调用 GetInstance() 之前实际上不会被实例化;静态评估是为了获得更好的启动性能。这意味着只要context 在第一次调用GetInstance() 之前得到其他东西,你就很好。

但是,请原谅我,但我不知道这种行为在 Android VM 中是否有所不同,从技术上讲,它不是“真正的”Java VM(或者说是 Sun/Oracle)。为了安全起见,如果 context 在调用静态构造函数时没有实例,我会考虑在实例化 @987654327 之前使用静态工厂方法或简单的 IoC 在静态构造函数中解析一个实例@。

【讨论】:

  • 我正在组合对象,并在其中实例化并使用需要上下文的 android 类(例如 getSharedPreferences)。我使用的是单例“带静态”模型,因此无需事先调用 getInstance() 及其执行的初始化即可公开使用方法。看来我向前迈出的第一步是放弃“静态”,以便确保 getInstance() - 并研究何时适合使用它。非常感谢大家帮助调试我破碎的概念!
  • VM 规范对静态初始化的情况非常具体。虽然 Dalvik 绝对不是 Java(tm),但它确实尝试遵循规范中规定的规则。
【解决方案2】:

你可以为你的类添加一个静态初始化块

public class MySingleton {
    /* Default constructor */
    private MySingleton() {
    }

    static {
          //Static init stuff...
        }
        ...
    }
}

对于加载类的每个类加载器,静态初始化程序块将被调用一次 - 通常,在进程的生命周期内只调用一次。

最后,根据定义,您的单例只会被创建一次,因此使用静态助手来完成这项工作不会带来性能优势。

【讨论】:

  • Robert,根据我的 O'Reilly 的说法,“如果使用 final{or static] 修饰符声明方法,...编译器知道该方法只有一个版本,并且动态方法查找不是必需的......并且是内联的候选者......”也许这就是你所说的? :D
  • DJC - 当然可以,但是由于这种情况是对单例的初始化,JIT 不会费心内联调用,因为它只调用一次。
【解决方案3】:

避免过度设计。请改用enum

虽然这种方法尚未被广泛采用,但单元素枚举类型是实现单例的最佳方式。

约书亚·布洛赫

Joshua Bloch 在this post 中解释了该方法。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-18
  • 1970-01-01
  • 2011-12-20
  • 1970-01-01
相关资源
最近更新 更多