我正在考虑编写单例类的其他方法。那么这个类被视为单身类吗?
public class MyClass{
static Myclass myclass;
static { myclass = new MyClass();}
private MyClass(){}
public static MyClass getInstance()
{
return myclass;
}
}
因为静态块只运行一次。
答案 0 :(得分:17)
不,不是。您没有声明myClass
private static final
,getInstance()
也不是static
。代码也没有真正编译。
这是Singleton成语:
public class MyClass {
private static final MyClass myClass = new MyClass();
private MyClass() {}
public static MyClass getInstance() {
return myClass;
}
}
它应该是private
,以便其他人无法直接访问它。它应该是static
,因此只有一个。应为final
,以便无法重新分配。您还需要在声明期间直接将其实例化 ,这样您就不必担心(那么多)线程。
如果加载很昂贵而你更喜欢延迟加载Singleton,那么考虑Singleton holder成语,它按需进行初始化而不是在类加载期间:
public class MyClass {
private MyClass() {}
private static class LazyHolder {
private static final MyClass myClass = new MyClass();
}
public static MyClass getInstance() {
return LazyHolder.myClass;
}
}
但是,无论是否需要 Singleton ,都应该提出大问号。通常不需要它。 只需静态变量,枚举,工厂类和/或依赖注入通常是更好的选择。
答案 1 :(得分:14)
这是另一种方法:
public enum Singleton{
INSTANCE("xyz", 123);
// Attributes
private String str;
private int i;
// Constructor
Singleton(String str, int i){
this.str = str;
this.i = i;
}
}
根据Josh Bloch的Effective Java,这是在Java中实现Singleton的最佳方法。与涉及私有静态实例字段的实现不同,私有静态实例字段可以通过滥用反射和/或序列化进行多次实例化,枚举可以保证是单例。
枚举单例的主要限制是它们总是在类加载时实例化,不能懒惰地实例化。因此,例如,如果要使用运行时参数实例化单例,则必须使用不同的实现(最好使用双重检查锁定)。
答案 2 :(得分:2)
我是这样做的。它更快,因为它在创建实例时只需要synchronized
块。
public class MyClass
{
private static MyClass INSTANCE=null;
private MyClass()
{
}
public static MyClass getInstance()
{
if(INSTANCE==null)
{
synchronized(MyClass.class)
{
if(INSATCNE==null) INSTANCE=new MyClass();
}
}
return INSTANCE;
}
}
答案 3 :(得分:1)
使用您的示例并使用GoF的实现方式:
public class MyClass{
private static Myclass instance;
private MyClass(){
//Private instantiation
}
public static synchronized MyClass getInstance() //If you want your method thread safe...
{
if (instance == null) {
instance = new MyClass();
}
return instance;
}
}
希望这会有所帮助:
答案 4 :(得分:1)
您的课程(原始代码,编辑前):
public class MyClass {
Myclass myclass;
static { myclass = new MyClass();}
private MyClass(){}
public MyClass getInstance()
{
return myclass;
}
}
不是真正的单身人士:
myclass
不是私有的,可以从外部读取和更改(假设你有一个实现它的实例myclass
不是静态的,无法在静态构造函数中访问(编译错误)getInstance()
方法不是静态的,因此您需要一个实例来调用它
实际代码:
public class MyClass {
static Myclass myclass;
static { myclass = new MyClass();}
private MyClass(){}
public static MyClass getInstance()
{
return myclass;
}
}
仍然myclass
不是私有的(也不是最终的)......声明它是最终的将有助于防止无意中从班级内部改变。
private static final Myclass myclass;
答案 5 :(得分:1)
有三种方法可以在java中创建单例。
急切初始化单身人士
public class Test {
private static final Test test = new Test();
private Test() {
}
public static Test getTest() {
return test;
}
}
延迟初始化单例(线程安全)
public class Test {
private static volatile Test test;
private Test(){}
public static Test getTest() {
if(test == null) {
synchronized(Test.class) {
if(test == null){test = new Test();}
}
}
return test;
}
}
Bill Pugh Singleton with Holder Pattern(最好是最好的一个)
public class Test {
private Test(){}
private static class TestHolder {
private static final Test test = new Test();
}
public static Test getInstance() {
return TestHolder.test;
}
}
答案 6 :(得分:1)
public class singletonPattern {
private static singletonPattern obj;
public static singletonPattern getObject() {
return obj = (obj == null) ? new singletonPattern() : obj;
}
public static void main(String args[]) {
singletonPattern sng = singletonPattern.getObject();
}
}
答案 7 :(得分:0)
在这个游戏上可能会有点迟到,但基本的实现看起来像这样:
public class MySingleton {
private static MySingleton INSTANCE;
public static MySingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new MySingleton();
}
return INSTANCE;
}
...
}
这里我们有MySingleton类,它有一个名为INSTANCE的私有静态成员,以及一个名为getInstance()的公共静态方法。第一次调用getInstance()时,INSTANCE成员为null。然后,流将进入创建条件并创建MySingleton类的新实例。对getInstance()的后续调用将发现已经设置了INSTANCE变量,因此不会创建另一个MySingleton实例。这确保了只有一个MySingleton实例在getInstance()的所有调用者之间共享。
但是这个实现有一个问题。多线程应用程序将在创建单个实例时具有竞争条件。如果多个执行线程同时(或大约)同时命中getInstance()方法,它们将各自看到INSTANCE成员为null。这将导致每个线程创建一个新的MySingleton实例,然后设置INSTANCE成员。
private static MySingleton INSTANCE;
public static synchronized MySingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new MySingleton();
}
return INSTANCE;
}
这里我们使用方法签名中的synchronized关键字来同步getInstance()方法。这肯定会解决我们的竞争状况。线程现在将阻止并一次输入一个方法。但它也会产生性能问题。此实现不仅同步单个实例的创建,还将所有调用同步到getInstance(),包括读取。读取不需要同步,因为它们只返回INSTANCE的值。由于读取将构成我们调用的大部分(请记住,实例化仅在第一次调用时发生),我们将通过同步整个方法而产生不必要的性能损失。
private static MySingleton INSTANCE;
public static MySingleton getInstance() {
if (INSTANCE == null) {
synchronize(MySingleton.class) {
INSTANCE = new MySingleton();
}
}
return INSTANCE;
}
这里我们将同步从方法签名移动到包装MySingleton实例创建的同步块。但这能解决我们的问题吗?好吧,我们不再阻止阅读,但我们也向前退了一步。多个线程将同时或大约同时命中getInstance()方法,并且它们都将INSTANCE成员视为null。然后,他们将点击同步块,其中一个将获得锁并创建实例。当该线程退出该块时,其他线程将争用该锁,并且每个线程将逐个通过该块并创建该类的新实例。所以我们回到了我们开始的地方。
private static MySingleton INSTANCE;
public static MySingleton getInstance() {
if (INSTANCE == null) {
synchronized(MySingleton.class) {
if (INSTANCE == null) {
INSTANCE = createInstance();
}
}
}
return INSTANCE;
}
这里我们从INSIDE块发出另一张支票。如果已经设置了INSTANCE成员,我们将跳过初始化。这称为双重检查锁定。
这解决了我们的多实例化问题。但是,我们的解决方案又一次提出了另一项挑战。其他线程可能不会“看到”INSTANCE成员已更新。这是因为Java优化了内存操作。线程将变量的原始值从主存储器复制到CPU的缓存中。然后,将对值的更改写入该缓存并从中读取。这是Java的一项功能,旨在优化性能。但这给我们的单例实现带来了问题。第二个线程 - 由不同的CPU或核心使用不同的缓存处理 - 将不会看到第一个线程所做的更改。这将导致第二个线程将INSTANCE成员视为null,从而强制创建我们的单例的新实例。
private static volatile MySingleton INSTANCE;
public static MySingleton getInstance() {
if (INSTANCE == null) {
synchronized(MySingleton.class) {
if (INSTANCE == null) {
INSTANCE = createInstance();
}
}
}
return INSTANCE;
}
我们通过在INSTANCE成员的声明中使用volatile关键字来解决这个问题。这将告诉编译器始终读取和写入主内存,而不是CPU缓存。
但这种简单的改变需要付出代价。因为我们绕过CPU缓存,所以每次操作易失性INSTANCE成员时我们都会受到性能影响 - 我们会这样做4次。我们仔细检查存在(1和2),设置值(3),然后返回值(4)。有人可能认为这条路径是边缘情况,因为我们只在第一次调用方法时创建实例。也许创作的表现受到了影响。但即使是我们的主要用例read也会对volatile组件进行两次操作。一旦检查存在,再次返回其值。
private static volatile MySingleton INSTANCE;
public static MySingleton getInstance() {
MySingleton result = INSTANCE;
if (result == null) {
synchronized(MySingleton.class) {
result = INSTANCE;
if (result == null) {
INSTANCE = result = createInstance();
}
}
}
return result;
}
由于性能命中是由于直接在volatile成员上操作,让我们将局部变量设置为volatile的值,然后对local变量进行操作。这将减少我们对易失性操作的次数,从而回收我们失去的一些性能。请注意,当我们进入synchronized块时,我们必须再次设置本地变量。这可确保它在我们等待锁定时发生的任何更改都是最新的。
我最近写了一篇关于此事的文章。 Deconstructing The Singleton。您可以找到有关这些示例的更多信息以及"持有者"的示例。那里的模式。还有一个真实的例子展示了双重检查的volatile方法。希望这会有所帮助。
答案 8 :(得分:0)
Singloton类是每次都获得相同对象的类。 当你想限制一个类创建多个对象时,我们需要Singleton类。
例如:
public class Booking {
static Booking b = new Booking();
private Booking() { }
static Booking createObject() { return b; }
}
要创建此类的对象,我们可以使用:
Booking b1, b2, b3, b4;
b1 = Booking.createObject();
b2 = Booking.createObject();
Booking b1, b2, b3, b4;
b1 = Booking.createObject();
b2 = Booking.createObject();
b1
和b2
指的是同一个对象。
答案 9 :(得分:0)
在创建单例类时,您应该考虑以下属性
如果您的课程中没有Clone或Serialization接口,我认为以下课程最好作为单例课程。
public class JavaClass1 {
private static JavaClass1 instance = null;
private JavaClass1() {
System.out.println("Creating -------");
if (instance != null) { // For Reflection
throw new RuntimeException("Cannot create, please use getInstance()");
}
}
public static JavaClass1 getInstance() {
if (instance == null) {
createInstance();
}
return instance;
}
private static synchronized void createInstance() { // for multithreading
if (instance == null) {
instance = new JavaClass1();
}
}}