我一直在想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()
更好。有没有理由说第二种不是主流方式呢?
答案 0 :(得分:2)
第一个版本在第一次实际需要时创建实例,而第二个版本(更短版本)在类is initialized
之后立即运行构造函数类或接口类型T将在紧接之前初始化 首次出现以下任何一种情况:
- T是一个类,创建了一个T实例。
- T是一个类,调用T声明的静态方法。
- 分配由T声明的静态字段。
- 使用由T声明的静态字段,该字段不是常量 变量(§4.12.4)。
- T是顶级课程(第7.6节)和断言声明(第14.10节) 在词典中嵌套在T(§8.1.3)中执行。 [...]
在类Class和in中调用某些反射方法 package java.lang.reflect也会导致类或接口初始化。
首次使用时的初始化是一种性能改进,如果构造函数中的代码进行昂贵的操作,可能会加速应用程序的启动。另一方面,第二个版本很容易阅读,并且自动是线程安全的。
无论如何,最先进的技术不是以任何方式创建单例:对于一堆KB,你可以获得依赖注入库,使其适合你,并处理更复杂的场景(例如看看Spring和AOP) - 背部注射)。
注意:第一个版本在粘贴的代码段中不是线程安全的
答案 1 :(得分:1)
您首次描述的方式称为延迟实例化,即只有在首次调用时才会创建对象。此方法不是线程安全的,因为第二个线程可以创建第二个实例。
如果您阅读以下书籍:
Joshua Bloch的有效Java
他解释说,单例模式的最佳实现是使用Enum
:
public enum Singleton {
INSTANCE;
public void doSomething() {
...
}
}
然后你可以通过Enum
调用你的单身人士,如下所示:
public class Test {
public void test(){
Singleton.INSTANCE.doSomething();
}
}
这非常适合你所说的,它看起来更好更短,但也保证永远不会有第二个实例。
答案 2 :(得分:1)
我可以想到两个原因:
第一个是封装:在您的类暴露给客户端代码之后,您可能会想到如何以及何时初始化单例。初始化方法为您以后更改策略提供了更多自由。例如,根据运行时的另一个静态变量值,您可能会改变主意并决定使用两个不同的构造函数而不是一个构造函数。使用您的解决方案,您只需在将类加载到内存时使用一个构造函数,而使用getInstance()
,您可以更改初始化逻辑,而不会影响客户端代码的接口。
第二个是延迟初始化:使用传统的单例实现,MyClass
对象仅在客户端代码第一次需要时才加载到内存中。如果客户端代码根本不需要它,则可以节省应用程序分配的内存。请注意,在程序运行之前,可能无法确定是否需要单例。例如,它可能取决于用户与程序的交互。
然而,懒惰初始化不是你可能想要的东西。例如,如果您正在编写交互式系统并且单例的初始化非常耗时,那么在程序加载时初始化它可能实际上更好,而不是在用户已经与它进行交互时,因为后者可能在第一次调用getInstance()
时导致系统响应延迟。但在这种情况下,您可以使用公共方法初始化您的实例,如:
private static MyClass instance = getInstance();
答案 3 :(得分:0)
同步线程的最佳方法是使用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;
}
}
答案 4 :(得分:0)
反射:反射可能会破坏单例 单例类的属性,如以下示例所示:
// 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
}
}