[英]singleton public static final
我一直想知道Java中的单例。 按照惯例,单例设置如下:
private static MyClass instance = null;
public static MyClass getInstance(){
if (instance == null){
instance = new MyClass();
}
return instance;
}
private MyClass(){}
最近,我切换到使用以下内容:
public static final MyClass instance = new MyClass();
private MyClass(){}
由于没有空检查,因此它更短,更快捷,并且键入MyClass.instance
对我来说比键入MyClass.getInstance()
更好。 有什么理由不能使第二种方法成为主流呢?
第一个版本在第一次实际需要时创建实例,而第二个版本(较短的版本)在初始化类后立即运行构造函数
类或接口类型T将在以下任何一种首次出现之前立即初始化:
- T是一个类,并创建T的实例。
- T是一个类,并调用T声明的静态方法。
- 分配由T声明的静态字段。
- 使用由T声明的静态字段,并且该字段不是常量变量(第4.12.4节)。
- T是顶级类(第7.6节),并执行词法嵌套在T中的断言(第14.10节)(第8.1.3节)。 [...]
调用类Class和包java.lang.reflect中的某些反射方法也会导致类或接口初始化。
如果构造函数中的代码进行了昂贵的操作,则首次使用时的初始化会提高性能,从而可以加快应用程序的启动速度。 另一方面,第二个版本易于阅读,并且自动具有线程安全性。
无论如何,最新技术不会以任何方式创建单例:对于一堆KB,您可以获得依赖注入库,使其适合您,并处理更复杂的场景(例如,查看Spring和AOP支持的注入)。 )。
注意:第一个版本在粘贴的代码段中不是线程安全的
您首先描述的方式称为延迟实例化,即仅在首次调用对象时才创建该对象。 此方法不是线程安全的,因为第二个线程可能创建第二个实例。
如果您阅读以下书籍:
Joshua Bloch撰写的有效Java
他解释说,单例模式的最佳实现是通过使用Enum
:
public enum Singleton {
INSTANCE;
public void doSomething() {
...
}
}
然后,您将通过Enum
调用您的单身人士,如下所示:
public class Test {
public void test(){
Singleton.INSTANCE.doSomething();
}
}
这与您所说的内容非常吻合,因为它看起来更好,更短,但也保证永远不会出现第二个实例。
我可以想到两个原因:
第一个是封装 :在类暴露给客户端代码之后,您可能会对如何以及何时初始化单例进行第二次思考。 初始化方法为您提供了以后更改策略的更多自由。 例如,您可能改变主意,并根据运行时另一个静态变量的值决定使用两个不同的构造函数,而不是一个。 使用您的解决方案,您必须在将类加载到内存时仅使用一个构造函数,而使用getInstance()
可以更改初始化逻辑,而不影响客户端代码的接口。
第二个是惰性初始化 :使用常规的单例实现,仅当客户端代码首次需要时, MyClass
对象才加载到内存中。 而且,如果根本不需要客户端代码,则可以节省应用程序分配的内存。 请注意,在程序运行之前,可能不确定是否需要单身人士。 例如,它可能取决于用户与程序的交互。
但是,惰性初始化不是您可能需要的东西。 例如,如果您正在编写一个交互式系统,并且单例的初始化很耗时,那么在加载程序时而不是在用户已经在与之交互时,初始化它实际上可能会更好,因为后者可能会导致首次调用getInstance()
时系统响应中的延迟。 但是在这种情况下,您可以使用public方法初始化实例,如下所示:
private static MyClass instance = getInstance();
同步线程的最佳方法是使用Double Checked(确保一次仅一个线程将进入同步块,并避免在每次执行代码时获得锁)。
public class DoubleCheckLocking {
public static class SearchBox {
private static volatile SearchBox searchBox;
// private attribute of this class
private String searchWord = "";
private String[] list = new String[]{"Stack", "Overflow"};
// private constructor
private SearchBox() {}
// static method to get instance
public static SearchBox getInstance() {
if (searchBox == null) { // first time lock
synchronized (SearchBox.class) {
if (searchBox == null) { // second time lock
searchBox = new SearchBox();
}
}
}
return searchBox;
}
}
反射:可能导致反射破坏单例类的单例属性,如以下示例所示:
// Java code to explain effect of Reflection
import java.lang.reflect.Constructor;
// Singleton class
class Singleton
{
// public instance initialized when loading the class
public static Singleton instance = new Singleton();
private Singleton()
{
// private constructor
}
}
public class GFG
{
public static void main(String[] args)
{
Singleton instance1 = Singleton.instance;
Singleton instance2 = null;
try
{
Constructor[] constructors =
Singleton.class.getDeclaredConstructors();
for (Constructor constructor : constructors)
{
// Below code will destroy the singleton pattern
constructor.setAccessible(true);
instance2 = (Singleton) constructor.newInstance();
break;
}
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("instance1.hashCode():- "
+ instance1.hashCode()); //366712642
System.out.println("instance2.hashCode():- "
+ instance2.hashCode()); //1829164700
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.