Java 单人公开静态决赛
我一直在想Java中的单例。按照惯例,单身人士是这样设置的:Java 单人公开静态决赛,java,singleton,Java,Singleton,我一直在想Java中的单例。按照惯例,单身人士是这样设置的: private static MyClass instance = null; public static MyClass getInstance(){ if (instance == null){ instance = new MyClass(); } return instance; } private MyClass(){} 最近,我改用以下方法: public static final
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将在 首次出现以下任何一种情况:
public static final MyClass instance = new MyClass();
private MyClass(){}
T是一个类,并且创建了T的一个实例。
T是一个类,调用由T声明的静态方法。
分配了一个由T声明的静态字段。
使用由T声明的静态字段,该字段不是常量
变量§4.12.4。
T是顶级类别§7.6,是断言声明§14.10
执行T§8.1.3中的词汇嵌套。[...]
在类和中调用某些反射方法
包java.lang.reflect也会导致类或接口初始化
首次使用时的初始化是一种性能改进,如果构造函数中的代码进行了昂贵的操作,则可以加快应用程序的启动。另一方面,第二个版本易于阅读,并且是自动线程安全的
无论如何,最先进的技术并不是以任何一种方式创建单例:对于一堆KB,您可以获得使其适合您的依赖注入库,并且还可以处理更复杂的场景,例如查看Spring和AOP支持的注入
注意:第一个版本在粘贴的代码段中不是线程安全的第一个版本在第一次实际需要时创建实例,而第二个版本越短,则在类完成后立即运行构造函数 类或接口类型T将在 首次出现以下任何一种情况:
public static final MyClass instance = new MyClass();
private MyClass(){}
T是一个类,并且创建了T的一个实例。
T是一个类,调用由T声明的静态方法。
分配了一个由T声明的静态字段。
使用由T声明的静态字段,该字段不是常量
变量§4.12.4。
T是顶级类别§7.6,是断言声明§14.10
执行T§8.1.3中的词汇嵌套。[...]
在类和中调用某些反射方法
包java.lang.reflect也会导致类或接口初始化
首次使用时的初始化是一种性能改进,如果构造函数中的代码进行了昂贵的操作,则可以加快应用程序的启动。另一方面,第二个版本易于阅读,并且是自动线程安全的
无论如何,最先进的技术并不是以任何一种方式创建单例:对于一堆KB,您可以获得使其适合您的依赖注入库,并且还可以处理更复杂的场景,例如查看Spring和AOP支持的注入
注意:在粘贴的代码段中,第一个版本不是线程安全的,您首先描述的方式称为延迟实例化,即仅在第一次调用对象时才会创建该对象。此方法不是线程安全的,因为第二个线程可以创建第二个实例 如果您阅读以下书籍: 约书亚·布洛赫的《有效的Java》 他解释说,单例模式的最佳实现是通过使用枚举: 然后,您可以通过Enum调用您的singleton,如下所示:
public class Test {
public void test(){
Singleton.INSTANCE.doSomething();
}
}
这很符合你所说的,因为它看起来写起来更漂亮、更短,但也保证不会有第二个实例 您首先描述的方式称为延迟实例化,即仅在第一次调用对象时才会创建该对象。此方法不是线程安全的,因为第二个线程可以创建第二个实例 如果您阅读以下书籍: 约书亚·布洛赫的《有效的Java》 他解释说,单例模式的最佳实现是通过使用枚举: 然后,您可以通过Enum调用您的singleton,如下所示:
public class Test {
public void test(){
Singleton.INSTANCE.doSomething();
}
}
这很符合你所说的,因为它看起来写起来更漂亮、更短,但也保证不会有第二个实例 同步线程的最佳方法是使用双重检查,以确保一次只有一个线程进入同步块,并避免每次执行代码时获得锁
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;
}
}
同步线程的最佳方法是使用Double Check,以确保一次只有一个线程进入同步块,并避免每次执行代码时获得锁
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;
}
}
我可以想到两个原因: 金融机构 rst是封装:在类暴露于客户机代码之后,您可能会重新考虑初始化单例的方式和时间。初始化方法为您以后更改策略提供了更多的自由。例如,您可能会改变主意,根据运行时另一个静态变量的值,决定使用两个不同的构造函数,而不是一个。对于您的解决方案,在将类加载到内存时,您必须只使用一个构造函数,而对于getInstance,您可以更改初始化逻辑,而不会影响到客户端代码的接口 第二种是延迟初始化:使用传统的单例实现,只有当客户端代码第一次需要时,MyClass对象才会加载到内存中。如果客户端代码根本不需要它,则可以节省应用程序分配的内存。请注意,在程序运行之前,可能无法确定是否需要单例。例如,它可能取决于用户与程序的交互 但是,延迟初始化并不是您一定想要的。例如,如果您正在编程一个交互式系统,并且单例的初始化非常耗时,那么在加载程序时初始化它可能比在用户已经与它交互时更好,因为后者可能会在第一次调用getInstance时导致系统响应延迟。但在这种情况下,您可以使用public方法初始化实例,如下所示:
private static MyClass instance = getInstance();
我可以想到两个原因: 第一个是封装:在类暴露于客户机代码之后,您可能会重新考虑初始化单例的方式和时间。初始化方法为您以后更改策略提供了更多的自由。例如,您可能会改变主意,根据运行时另一个静态变量的值,决定使用两个不同的构造函数,而不是一个。对于您的解决方案,在将类加载到内存时,您必须只使用一个构造函数,而对于getInstance,您可以更改初始化逻辑,而不会影响到客户端代码的接口 第二种是延迟初始化:使用传统的单例实现,只有当客户端代码第一次需要时,MyClass对象才会加载到内存中。如果客户端代码根本不需要它,则可以节省应用程序分配的内存。请注意,在程序运行之前,可能无法确定是否需要单例。例如,它可能取决于用户与程序的交互 但是,延迟初始化并不是您一定想要的。例如,如果您正在编程一个交互式系统,并且单例的初始化非常耗时,那么在加载程序时初始化它可能比在用户已经与它交互时更好,因为后者可能会在第一次调用getInstance时导致系统响应延迟。但在这种情况下,您可以使用public方法初始化实例,如下所示:
private static MyClass instance = getInstance();
反射:反射可以导致破坏单态 singleton类的属性,如下例所示:
// 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
}
}
反射:反射可以导致破坏单态 singleton类的属性,如下例所示:
// 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
}
}
您将单例和惰性实例化混为一谈。第一个版本不是线程安全的。第二个是@biziclop指出的:P.@VinodMadyalkar第二个例子有什么线程不安全的地方?但是如果提问者只是编写不应该是多线程的应用程序呢?那么我认为他对线程安全不太关心。线程安全甚至不是这个问题的重点@VinodMadyalkar@biziclop-谢谢你提醒我填满我的咖啡杯:你把单例和惰性实例化混为一谈了。第一个版本不是线程安全的。第二个是@biziclop指出的:P.@VinodMadyalkar第二个例子有什么线程不安全的地方?但是如果提问者只是编写不应该是多线程的应用程序呢?那么我认为他对线程安全不太关心。线程安全甚至不是这个问题的重点@VinodMadyalkar@biziclop-谢谢你提醒我装满咖啡杯:P你能详细说明这两种方法的优点吗?这取决于MyClass在其构造函数中做了什么。例如,如果从数据库加载数据以填充缓存,需要30秒,如果还有其他类似的类,则整个启动过程可能需要一些时间。这是可以优化的,尤其是在启动应用程序后,如果不是所有这些类都需要的话。如果可以立即实例化MyClass,那么您的第二个解决方案是绝对合适的
这两种方法的优点是什么?这取决于MyClass在其构造函数中执行的操作。例如,如果从数据库加载数据以填充缓存,需要30秒,如果还有其他类似的类,则整个启动过程可能需要一些时间。这是可以优化的,尤其是在启动应用程序后,如果不是所有这些类都需要的话。若可以立即实例化MyClass,那个么您的第二个解决方案是绝对合适的。最好的方法是使用枚举单例。DCL是90年代最流行的。最好的方法是使用枚举单例。DCL现在是90年代,这确实是最好的推荐方式。您可以免费获得延迟加载,但要保持机制极其简单。这确实是最好的推荐方式。您可以免费获得延迟加载,但保持机制极其简单。