Java'da tek tonlu bir desen uygulamanın etkili bir yolu nedir? [kapalı]


812

Java'da tek tonlu bir desen uygulamanın etkili bir yolu nedir?


1
"Java'da tek tonlu bir desen uygulamanın etkili bir yolu nedir?" lütfen verimli tanımlayın.
Marian Paździoch

medium.com/@kevalpatel2106/… . Bu, tekli desende iplik, yansıma ve serileştirme güvenliğinin nasıl elde edileceğine dair eksiksiz bir makaledir. Singleton sınıfının faydalarını ve sınırlarını anlamak için iyi bir kaynaktır.
Keval Patel

Joshua Bloch'un Etkili Java'da belirttiği gibi, enum singleton en iyi yoldur. Burada çeşitli uygulamaları tembel / istekli vb. Olarak sınıflandırdım
isaolmez

Yanıtlar:


782

Bir numaralandırma kullanın:

public enum Foo {
    INSTANCE;
}

Joshua Bloch bu yaklaşımı Google I / O 2008'deki Etkili Java Reloaded konuşmasında açıkladı : video bağlantısı . Ayrıca sunumunun 30-32. Slaytlarına bakın ( effect_java_reloaded.pdf ):

Serileştirilebilir Singleton Uygulamanın Doğru Yolu

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

Düzenleme: Bir "Etkili Java" online kısmı diyor ki:

"Bu yaklaşım, kamusal alan yaklaşımına işlevsel olarak eşdeğerdir, ancak daha özlü olması, serileştirme makinelerini ücretsiz olarak sağlar ve karmaşık serileştirme veya yansıma saldırıları karşısında bile birden fazla somutlaştırmaya karşı zırhlı bir garanti sağlar. ancak geniş çapta benimsenmesi nedeniyle, tek elementli bir enum tipi, bir singletonu uygulamanın en iyi yoludur . "


203
Bence insanlar sadece bir özelliğe sahip bir sınıf olarak enum'lara bakmaya başlamalı. sınıfınızın örneklerini derleme zamanında listeleyebiliyorsanız, bir numaralandırma kullanın.
Amir Arad

7
Şahsen tekil kalıbı doğrudan kullanma ihtiyacını çoğu zaman bulamıyorum. Bazen baharın bağımlılık enjeksiyonunu singleton olarak adlandırdığı şeyi içeren bir uygulama bağlamıyla kullanırım. Yardımcı sınıflarım yalnızca statik yöntemler içeriyor ve bunların herhangi bir örneğine ihtiyacım yok.
Stephen Denne

3
Merhaba, Herkes bana bu tür singleton alay ve test durumlarda nasıl test edilebilir söyleyebilir. Bu tür için sahte singleton örneğini değiştirmeye çalıştım ama yapamadım.
Ashish Sharma

29
Sanırım mantıklı, ama yine de hoşlanmıyorum. Başka bir sınıfı genişleten bir singletonu nasıl yaratırsınız? Eğer bir numaralandırma kullanırsan yapamazsın.
chharvey

11
@bvdb: Çok fazla esneklik istiyorsanız, ilk etapta bir singleton uygulayarak berbat ettiniz. İhtiyacınız olduğunda bağımsız bir örnek oluşturma yeteneği kendi başına oldukça pahalıdır.
cHao

233

Kullanıma bağlı olarak birkaç "doğru" cevap vardır.

Java5 yapmanın en iyi yolu bir numaralandırma kullanmaktır:

public enum Foo {
   INSTANCE;
}

Java5 öncesi, en basit durum:

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

Kodun üzerinden geçelim. İlk olarak, sınıfın final olmasını istiyorsunuz. Bu durumda, finalanahtar kelimeyi kullanıcıların nihai olduğunu bildirmek için kullandım . Ardından, kullanıcıların kendi Foo'larını oluşturmasını önlemek için yapıcıyı özel yapmanız gerekir. Yapıcıdan bir istisna atmak, kullanıcıların ikinci bir Foo oluşturmak için yansıma kullanmasını önler. Sonra private static final Footek örneği tutmak için bir alan ve public static Foo getInstance()döndürmek için bir yöntem oluşturun. Java belirtimi, kurucunun yalnızca sınıf ilk kullanıldığında çağrılmasını sağlar.

Çok büyük bir nesneniz veya ağır inşaat kodunuz varsa VE ayrıca bir örneğe ihtiyaç duyulmadan önce kullanılabilecek diğer erişilebilir statik yöntemlere veya alanlara sahip olduğunuzda, ancak ancak o zaman tembel başlatma kullanmanız gerekir.

Örneği private static classyüklemek için a kullanabilirsiniz . Kod daha sonra şöyle görünecektir:

public final class Foo {

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

Çizgi private static final Foo INSTANCE = new Foo();yalnızca FooLoader sınıfı gerçekten kullanıldığında yürütüldüğünden, bu tembel örneklemeyi halleder ve iş parçacığı güvenli olması garanti edilir.

Nesnenizi serileştirmek istediğinizde, serileştirmenin bir kopya oluşturmayacağından emin olmanız gerekir.

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Yöntem readResolve(), nesnenin programınızın önceki bir çalıştırmasında serileştirilmiş olsa bile, tek örneğin döndürülmesini sağlar.


32
Yansıtma kontrolü işe yaramaz. Diğer kodlar ayrıcalıklarda yansıma kullanıyorsa, bu Oyun Bitti. Böyle bir yanlış kullanım altında düzgün çalışmayı denemek için hiçbir neden yoktur. Ve eğer denerseniz, zaten eksik bir "koruma" olacak, sadece çok boşa kod.
Wouter Coekaerts

5
> "İlk olarak, sınıfın nihai olmasını istersiniz". Birisi bu konuyu biraz açıklayabilir mi?
PlagueHammer

2
Deserializasyon koruması tamamen bozuldu (Sanırım bu Etkili Java 2nd Ed'de geçiyor).
Tom Hawtin - tackline

9
-1 Bu kesinlikle en basit durum değil , tartışmalı ve gereksiz derecede karmaşık. Tüm vakaların% 99,9'unda yeterli olan en basit çözüm için Jonathan'ın cevabına bakın.
Michael Borgwardt

2
Bu, singletonunuzun bir üst sınıftan miras alması gerektiğinde yararlıdır. Bu durumda enum singleton desenini kullanamazsınız, çünkü enumlar bir üst sınıfa sahip olamazlar (ancak arayüzleri uygulayabilirler). Örneğin, numaralandırma tek tonlu modeli bir seçenek olmadığında Google Guava statik bir son alan kullanır: code.google.com/p/guava-libraries/source/browse/trunk/guava/src/…
Etienne Neveu

139

Feragatname: Tüm harika cevapları özetledim ve sözlerime yazdım.


Singleton uygularken 2 seçeneğimiz var
1. Tembel yükleme
2. Erken yükleme

Tembel yükleme biraz ek yük (dürüst olmak gerekirse) ekler, bu yüzden sadece çok büyük bir nesneniz veya ağır inşaat kodunuz olduğunda kullanın ve ayrıca bir örneğe ihtiyaç duyulmadan önce kullanılabilecek diğer erişilebilir statik yöntemlere veya alanlara sahip olun. Aksi takdirde erken yüklemeyi seçmek iyi bir seçimdir.

Singleton'u uygulamanın en basit yolu

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }
}

Erken yüklü singleton hariç her şey iyidir. Tembel yüklü singleton'u deneyelim

class Foo {

    // Our now_null_but_going_to_be sole hero 
    private static Foo INSTANCE = null;

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT  
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

Şimdiye kadar çok iyi ama kahramanımız, kahramanımızın birçok örneğini isteyen çok sayıda kötü iş parçacığı ile tek başına savaşırken hayatta kalmayacak. Böylece onu kötü çok iş parçacığından koruyalım

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

ama kahramanı korumak için yeterli değil, Gerçekten !!! Bu, kahramanımıza yardım etmek için yapabileceğimiz / yapmamız gereken en iyi şey

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

Buna "Çift Denetimli Kilitleme deyimi" denir. Uçucu ifadeyi unutmak ve neden gerekli olduğunu anlamak zor.
Ayrıntılar için: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Şimdi kötü iplik konusunda eminiz ama acımasız serileştirmeye ne dersiniz? Seri ayırma işlemi sırasında bile yeni bir nesne oluşturulmadığından emin olmalıyız

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // Rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

Yöntem readResolve(), nesnenin programımızın bir önceki çalışmasında serileştirilmiş olsa bile, tek örneğin döndürülmesini sağlar.

Son olarak, iş parçacıklarına ve serileştirmeye karşı yeterli koruma ekledik ancak kodumuz hantal ve çirkin görünüyor. Kahramanımıza bir makyaj yapalım

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    // Damn you serialization
    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Evet bu aynı kahramanımız :)
Çizgi private static final Foo INSTANCE = new Foo();sadece sınıf FooLoadergerçekten kullanıldığında yürütüldüğünden , bu tembel örneklemeyi halleder,

ve ipliğin güvenli olacağı garanti edilir.

Ve şimdiye kadar geldik, yaptığımız her şeye ulaşmanın en iyi yolu mümkün olan en iyi yol

 public enum Foo {
       INSTANCE;
   }

Hangi dahili olarak tedavi edilecek

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

Bu kadar! Artık serileştirme, iş parçacığı ve çirkin kod korkusu yok. Ayrıca ENUMS singletonu tembel olarak başlatılır .

Bu yaklaşım, kamusal alan yaklaşımıyla işlevsel olarak eşdeğerdir, ancak daha özlü olması, serileştirme makinelerini ücretsiz olarak sağlaması ve sofistike serileştirme veya yansıma saldırıları karşısında bile çoklu somutlaşmaya karşı zırhlı bir garanti sağlar. Bu yaklaşım henüz geniş çapta benimsenmese de, tek elemanlı bir enum tipi bir singletonu uygulamanın en iyi yoludur.

-Joshua Bloch "Etkili Java"

Şimdi ÇETELELER Singleton ve sabrınız için teşekkür uygulamak için en iyi yol olarak kabul edilir neden fark olabilir :)
benim onu Güncelleme blogda .


3
Sadece bir açıklama: enum kullanılarak uygulanan singletonlar tembel olarak başlatılır. Ayrıntılar burada: stackoverflow.com/questions/16771373/…

2
mükemmel cevap. son bir şey, istisna atmak için klon yöntemini geçersiz kılar.
riship89

2
@xyz güzel açıklamalar, gerçekten zevk aldım ve çok kolay öğrendim ve umarım bunu asla unutmadım
Premraj

1
Stackoverflow üzerinde şimdiye kadar kırmızı var en iyi cevaplardan biri. Teşekkürler!
Shay Tsadok

1
Orada olan bir singleton olarak çeteleler kullanarak bir seri hale sorun: Her üye alan değerleri vardır değil tefrika ve bu nedenle geri yüklenmez. Bkz. Java Nesnesi Serileştirme Belirtimi, sürüm 6.0 . Başka bir sorun: Hiçbir sürüm - tüm enum türlerinin belirli bir adres serialVersionUIDarasında 0L. Üçüncü sorun: Özelleştirme yok: Sınıflandırma ve serileştirme sırasında enum türleri tarafından tanımlanan herhangi bir sınıfa özgü writeObject, readObject, readObjectNoData, writeReplace ve readResolve yöntemleri yok sayılır.
Basil Bourque

124

Stu Thompson tarafından yayınlanan çözüm Java5.0 ve sonrasında geçerlidir. Ama bunu kullanmamayı tercih ederim çünkü hata eğilimli olduğunu düşünüyorum.

Uçucu ifadeyi unutmak ve neden gerekli olduğunu anlamak zor. Uçucu olmadan bu kod, çift kontrollu kilitleme antipatterninden dolayı artık güvenli değildir. Bununla ilgili daha fazla bilgiyi Uygulamada Java Eşzamanlılığı'nın 16.2.4. Paragrafına bakınız . Kısacası: Bu kalıp (Java5.0'dan önce veya geçici ifade olmadan) Bar nesnesine (hala) yanlış durumda olan bir başvuru döndürebilir.

Bu model performans optimizasyonu için icat edildi. Ama bu artık gerçek bir endişe değil. Aşağıdaki tembel başlatma kodunun hızlı ve daha da önemlisi okunması daha kolaydır.

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}

1
Yeterince adil! Ben sadece uçucu ile rahatım ve kullanımı. Oh, ve JCiP için üç alkış.
Stu Thompson

1
Oh, görünüşe göre FindBugz şöhretinden William Pugh tarafından savunulan yaklaşım bu.
Stu Thompson

4
Etkili Java (2001 telif hakkı) ilk baskısında @Stu öğenin 48. altında bu desen detayları
Pascal Thivent

9
@Bno: Yapıcıyı özel yapmaya ne dersiniz?
xyz

2
@ AlikElzin-kilaka Pek değil. Örnek, BarHolder için sınıf yükleme aşamasında oluşturulur ve bu ilk ihtiyaç olana kadar ertelenir. Bar'ın yapıcısı istediğiniz kadar karmaşık olabilir, ancak ilkine kadar çağrılmaz getBar(). (Ve getBar"çok erken" olarak adlandırılırsa, singleonların nasıl uygulandığına bakılmaksızın aynı sorunla karşılaşırsınız.) Yukarıdaki kodun tembel sınıf yüklemesini burada görebilirsiniz: pastebin.com/iq2eayiR
Ti Strga

95

Java 5 ve sonraki sürümlerde güvenli iş parçacığı:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

EDIT : volatileBuradaki değiştiriciye dikkat edin . :) Önemli çünkü onsuz, diğer iş parçacıklarının JMM (Java Bellek Modeli) tarafından değerindeki değişiklikleri görmesi garanti edilmez. Senkronizasyon bununla ilgilenmez - sadece bu kod bloğuna erişimi serileştirir.

DÜZENLEME 2 : @Bno'nun cevabı, Bill Pugh (FindBugs) tarafından önerilen yaklaşımı detaylandırır ve daha iyi tartışılabilir. Git ve cevabını oyla.


1
Uçucu değiştirici hakkında nereden daha fazla bilgi edinebilirim?
eleven81


2
Bence yansıma saldırılarından bahsetmek önemlidir. Çoğu geliştiricinin endişelenmesi gerekmediği doğrudur, ancak bu gibi örneklerin (Enum tabanlı tek tonlar üzerinden) birden fazla örnekleme saldırısına karşı koruma sağlayan kod içermesi veya basitçe bu olasılıkları gösteren bir feragatname içermesi gerektiği görülmektedir.
luis.espinal

2
Burada geçici anahtar kelimeye gerek yoktur - senkronizasyon hem karşılıklı dışlama hem de bellek görünürlüğü sağlar.
Hemant

2
Neden tüm bunları Java 5+ ile rahatsız ettiniz? Anladığım kadarıyla, enum yaklaşımı hem iş parçacığı güvenliği hem de tembel başlatma sağlar. Aynı zamanda çok daha basit ... Dahası, bir enumdan kaçınmak istiyorsanız, yine de iç içe geçmiş statik sınıf yaklaşımını
Alexandros

91

Tembel başlatmayı unutun , çok sorunlu. Bu en basit çözüm:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}

21
singleton örnek değişkeni de son haline getirilebilir. örneğin, özel statik final A singleton = yeni A ();
jatanp

19
Statik singleton sınıf yükleninceye kadar başlatılmayacak ve sınıf gerekli olana kadar yüklenmeyecek (getInstance () yöntemine ilk başvuruda bulunduğunuz zaman doğru olacaktır) için bu etkili bir şekilde tembel başlatmadır.
Dan Dyer

7
A sınıfı, statikin somutlaştırılmasını istemeden önce yüklenirse, sınıf başlatılmasını ayırmak için statik bir statik sınıfta sarım yapabilirsiniz.
Tom Hawtin - tackline

3
Bu cevabın en basit olduğu konusunda hemfikirim ve Anirudhan, örneğin nihai olarak beyan edilmesine gerek yok. Statik üyeler başlatılırken başka hiçbir iş parçacığı sınıfa erişemez. bu derleyici tarafından garanti edilir, diğer bir deyişle, tüm statik başlatma senkronize bir şekilde yapılır - sadece bir iş parçacığı.
inor

4
Bu yaklaşımın bir sınırlaması vardır: Yapıcı bir istisna atamaz.
wangf

47

Gerçekten ihtiyacınız olduğundan emin olun. Buna karşı bazı argümanları görmek için "tekil anti-desen" için bir google yapın. Doğal olarak yanlış bir şey yok sanırım ama bu sadece bazı küresel kaynak / verileri ortaya çıkarmak için bir mekanizma yani bu en iyi yol olduğundan emin olun. Özellikle bağımlılık enjeksiyonunu özellikle birim testler kullanıyorsanız daha yararlı buldum çünkü DI, alay konusu kaynakları test amacıyla kullanmanıza izin veriyor.


geleneksel yöntemle de sahte değerler enjekte edebilirsiniz ama onun standart / srping yolu değil, bu yüzden sadece eski kod olmak kazanç ile ekstra çalışma ...
tgkprog

21

DI'yi singleton kullanmaya alternatif olarak öneren cevaplardan bazılarına gizem duyuyorum; bunlar ilgisiz kavramlardır. DI'yi tekil veya tekil olmayan (örn. İplik başına) örnekleri enjekte etmek için kullanabilirsiniz. En azından Spring 2.x kullanıyorsanız bu doğrudur, diğer DI çerçeveleri için konuşamam.

Bu yüzden OP'ye cevabım (en önemsiz örnek kod hariç hepsi):

  1. Spring gibi bir DI çerçevesi kullanın,
  2. Bağımlılıklarınızın tek ton, istek kapsamı, oturum kapsamı veya başka bir şey olup olmadığını DI yapılandırmanızın bir parçası yapın.

Bu yaklaşım, bir tektonun kullanılıp kolayca geri döndürülebilen bir uygulama ayrıntısı olduğu güzel bir ayrıştırılmış (ve dolayısıyla esnek ve test edilebilir) bir mimari sunar (kullandığınız tektonların iş parçacığı güvenli olması şartıyla).


4
Belki insanlar sana katılmıyorlar. Seni küçümsemedim, ama katılmıyorum: DI, singletonların aynı problemleri çözmek için kullanılabilir. Bu, “tekton” un, belki de biraz hileli olan “tek bir örneği olan bir nesne” yerine “tek bir örnekle doğrudan erişilen bir nesne” anlamına gelmesini anlamaya dayanır.
Tom Anderson

2
Bunu biraz genişletmek için, TicketNumbererhangisinin tek bir global örneği olması gerektiğini ve TicketIssuerbir kod satırı içeren bir sınıf yazmak istediğinizi düşünün int ticketNumber = ticketNumberer.nextTicketNumber();. Geleneksel singleton düşüncesinde, önceki kod satırı gibi bir şey olmalıdır TicketNumberer ticketNumberer = TicketNumberer.INSTANCE;. DI düşüncesinde, sınıfın bir yapıcısı olurdu public TicketIssuer(TicketNumberer ticketNumberer) { this.ticketNumberer = ticketNumberer; }.
Tom Anderson

2
Ve bu kurucu olarak adlandırmak başkasının sorunu haline gelir. Bir DI çerçevesi bunu bir tür küresel harita ile yapar; elle oluşturulmuş bir DI mimarisi bunu yapardı çünkü uygulamanın mainyöntemi (veya minyonlarından biri) bağımlılığı yaratıp kurucuyu çağırırdı . Esasen, global bir değişkenin (veya global bir yöntemin) kullanımı, korkulan hizmet konumlandırıcı modelinin basit bir şeklidir ve tıpkı o modelin diğer tüm kullanımları gibi bağımlılık enjeksiyonu ile değiştirilebilir.
Tom Anderson

@TomAnderson İnsanların neden servis bulucu kalıbını neden 'korktukları' konusunda kafam karıştı. Sanırım çoğu durumda aşırıya kaçıyor ya da en iyi şekilde gerekli değil, ancak görünüşe göre faydalı durumlar var. Daha az sayıda parametre ile DI kesinlikle tercih edilir, ancak 20+ düşünün. Kodun yapılandırılmadığını söylemek geçerli bir argüman değildir, çünkü bazen parametre grupları mantıklı değildir. Ayrıca, birim test perspektifinden bakıldığında, hizmeti test etmeyi umursamıyorum, sadece iş mantığı ve doğru kodlanmışsa, bu kolay olurdu. Bu ihtiyacı sadece çok büyük ölçekli projelerde gördüm.
Brandon Ling

20

Yazmadan önce neden bir singletona ihtiyacınız olduğunu düşünün. Java'da Google singletonları yaparsanız, bunları kolayca kullanabileceğiniz yarı dini bir tartışma var.

Şahsen ben pek çok nedenden ötürü mümkün olduğunca sık tekillerden kaçınmaya çalışıyorum. Çoğu zaman singletonların istismar edildiğini hissediyorum, çünkü herkes tarafından anlaşılması kolay, bir OO tasarımına "küresel" veri almak için bir mekanizma olarak kullanılıyorlar ve kullanılıyorlar çünkü nesne yaşam döngüsü yönetimini atlatmak kolay (veya gerçekten A'yı B içinden nasıl yapabileceğinizi düşünmek). Güzel bir orta alan için Inversion of Control (IoC) veya Dependency Injection (DI) gibi şeylere bakın.

Gerçekten birine ihtiyacınız varsa, wikipedia tek bir uygun bir uygulama iyi bir örneğine sahiptir.


Kabul. Daha çok uygulamanızın geri kalanını başlatan temel bir sınıftır ve çoğaltılmışsa, tam bir kaosla karşılaşırsınız (yani bir kaynağa tek başına erişim veya zorlayıcı güvenlik). Uygulamanızın her yerine global veri iletmek büyük bir kırmızı bayraktır. Gerçekten ihtiyacınız olduğunu kabul ettiğinizde kullanın.
Salvador Valencia

16

3 farklı yaklaşım aşağıdadır

1) Numaralandırma

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

2) Çift kontrol Kilitleme / Tembel yükleme

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private static volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public static DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

3) statik fabrika yöntemi

/**
* Singleton pattern example with static factory method
*/

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

13

Tek çerçevelerimi yönetmek için Spring Framework'ü kullanıyorum. Sınıfın "singleton-ness" i zorunlu kılmaz (eğer birden fazla sınıf yükleyici varsa gerçekten yapamazsınız), ancak farklı nesne türleri oluşturmak için farklı fabrikalar oluşturmak ve yapılandırmak için gerçekten kolay bir yol sağlar.


11

Wikipedia, Java'da da bazı tekil örneklere sahiptir . Java 5 uygulaması oldukça eksiksiz görünüyor ve iş parçacığı için güvenli (çift denetimli kilitleme uygulandı).


11

Versiyon 1:

public class MySingleton {
    private static MySingleton instance = null;
    private MySingleton() {}
    public static synchronized MySingleton getInstance() {
        if(instance == null) {
            instance = new MySingleton();
        }
        return instance;
    }
}

Tembel yükleme, tıkanma ile güvenli iplik, düşük performans synchronized.

Versiyon 2:

public class MySingleton {
    private MySingleton() {}
    private static class MySingletonHolder {
        public final static MySingleton instance = new MySingleton();
    }
    public static MySingleton getInstance() {
        return MySingletonHolder.instance;
    }
}

Tembel yükleme, tıkanmasız iplik güvenliği, yüksek performans.


10

Tembel yüklemeye ihtiyacınız yoksa, sadece deneyin

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() { return Singleton.INSTANCE; }

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

Tembel yükleme yapmak ve Singleton'unuzun iş parçacığı için güvenli olmasını istiyorsanız, çift kontrol desenini deneyin

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

        public static Singleton getInstance() { 
              if(null == instance) {
                  synchronized(Singleton.class) {
                      if(null == instance) {
                          instance = new Singleton();
                      }
                  }
               }
               return instance;
        }

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

Çift kontrol deseninin çalışması garanti edilmediğinden (derleyicilerle ilgili bazı sorunlar nedeniyle, bunun hakkında daha fazla bir şey bilmiyorum.), Ayrıca tüm getInstance yöntemini senkronize etmeyi veya tüm Singletons için bir kayıt defteri oluşturmayı deneyebilirsiniz.


2
İlk sürüm en iyisidir. Sınıfın tek birton sağlamaktan başka bir şey yapmadığı varsayılarak, tembel sınıf yüklemesi nedeniyle tipik olarak ikinci versiyondaki ile aynı noktada başlatılacaktır.
Dan Dyer

1
Statik için çift kontrol anlamsızdır. Ve neden korunan klon yöntemini halka açtınız?
Tom Hawtin - tackline

1
-1 çift kontrollü kilitleme sürümünüz bozuk.
assylias

5
Ayrıca singleton değişkeninizi yapmanız gerekirvolatile
MyTitle

İlk sürümü olan tembel ve iş parçacığı güvenli.
Miha_x64

9

Enum singleton diyebilirim

Java'da enum kullanan singleton genellikle enum singleton'u bildirmenin bir yoludur. Enum singleton örnek değişkeni ve örnek yöntemi içerebilir. Basitlik açısından, eğer herhangi bir örnek yöntemi kullanıyorsanız, bu yöntemin nesnenin durumunu etkiliyorsa, iplik güvenliğini sağlamanız gerektiğini unutmayın.

Bir enumun kullanımı çok kolaydır ve diğer şekillerde atlanması gereken serileştirilebilir nesnelerle ilgili herhangi bir dezavantajı yoktur.

/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //perform operation here
        }
}

Buna Singleton'da Singleton.INSTANCEarama getInstance()yönteminden çok daha kolay erişebilirsiniz .

1.12 Enum Sabitlerinin Serileştirilmesi

Enum sabitleri sıradan serileştirilebilir veya dışsallaştırılabilir nesnelerden farklı şekilde serileştirilir. Bir numaralandırma sabitinin serileştirilmiş formu yalnızca adından oluşur; sabitin alan değerleri formda mevcut değildir. Bir enum sabitini serileştirmek için enum sabitinin ObjectOutputStreamname yöntemi tarafından döndürülen değeri yazar. Bir enum sabitinin serisini ObjectInputStreamkaldırmak için sabit adı akıştan okur; serileştirilmiş sabit, daha sonra sabit çağrının java.lang.Enum.valueOfenum türünü, alınan sabit adla birlikte argümanlar olarak geçirerek yöntemin çağrılmasıyla elde edilir . Diğer serileştirilebilir veya dışsallaştırılabilir nesneler gibi, enum sabitleri de sonradan serileştirme akışında görünen geri referansların hedefleri olarak işlev görebilir.

Enum sabitleri serileştirildiğini süreçtir özelleştirilemiyor: herhangi bir sınıf özgü writeObject, readObject, readObjectNoData, writeReplace, ve readResolveenum türleri tarafından tanımlanan yöntem seri ve seri kaldırma sırasında göz ardı edilir. Benzer şekilde, herhangi bir serialPersistentFieldsveya serialVersionUIDalan bildirimi de yoksayılır - tüm numaralandırma türlerinin sabit bir serialVersionUIDdeğeri vardır 0L. Gönderilen veri türünde herhangi bir değişiklik olmadığı için numaralandırma türleri için serileştirilebilir alanları ve verileri belgelemek gereksizdir.

Oracle dokümanlarından alıntılanmıştır

Geleneksel Singletons'la ilgili bir başka sorun, Serializablearabirimi uyguladıktan sonra artık tek başına kalmamasıdır çünkü readObject()yöntem her zaman Java'da yapıcı gibi yeni bir örnek döndürür. Bu, readResolve()aşağıdaki gibi singleton ile değiştirilerek yeni oluşturulan örnek kullanılarak ve atılarak önlenebilir

 // readResolve to prevent another instance of Singleton
 private Object readResolve(){
     return INSTANCE;
 }

Bu, Singleton Sınıfınızın durumunu koruduğunda, geçici hale getirmeniz gerektiğinden daha da karmaşık hale gelebilir, ancak Enum Singleton'da, Serileştirme JVM tarafından garanti edilir.


İyi okuma

  1. Tekli Desen
  2. Numaralamalar, Tek Tonlar ve Diziselleştirme
  3. Çift kontrollu kilitleme ve Singleton deseni

8
There are 4 ways to create a singleton in java.

1- eager initialization singleton

    public class Test{
        private static final Test test = new Test();
        private Test(){}
        public static Test getTest(){
            return test;
        }
    

2- lazy initialization singleton (thread safe)

    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;
    


3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)

    public class Test {

        private Test(){}

        private static class TestHolder{
            private static final Test test = new Test();
        }

        public static Test getInstance(){
            return TestHolder.test;
        }
    }

4- enum singleton
      public enum MySingleton {
        INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}

(1) istekli değildir, JVM sınıfı yükleme mekanizması nedeniyle tembeldir.
Miha_x64

@ Miha_x64 ne zaman istekli yükleme dedim, istekli başlatma dedim.Her ikisinin de aynı olduğunu düşünüyorsanız, o zaman istekli yükleme o zaman.
Dheeraj Sachan

Etkili Java harika bir kitaptır, ancak kesinlikle düzenleme gerektirir.
Miha_x64

@ Miha_x64 ne istekli yükleme, u örnekle açıklayabilir
miyim

'İstekli bir şekilde' bir şey yapmak 'mümkün olan en kısa sürede' anlamına gelir. Örneğin, Hazırda Beklet, açıkça isteniyorsa yükleme ilişkilerini hevesle destekler.
Miha_x64

8

Bu konuda oyuna biraz geç olabilir, ancak singletonu uygulamak konusunda çok fazla nüans var. Tutucu modeli birçok durumda kullanılamaz. Uçucu kullanırken IMO - yerel bir değişken de kullanmalısınız. En baştan başlayalım ve sorunu tekrarlayalım. Ne demek istediğimi göreceksin.


İlk deneme şöyle görünebilir:

public class MySingleton {

     private static MySingleton INSTANCE;

     public static MySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }

        return INSTANCE;
    }
    ...
}

Burada INSTANCE adlı özel bir statik üyeye ve getInstance () adlı bir genel statik yönteme sahip MySingleton sınıfımız var. GetInstance () yöntemi ilk çağrıldığında INSTANCE üyesi null olur. Akış daha sonra oluşturma koşuluna girecek ve MySingleton sınıfının yeni bir örneğini oluşturacaktır. GetInstance () öğesine yapılan sonraki çağrılar, INSTANCE değişkeninin önceden ayarlandığını görür ve bu nedenle başka bir MySingleton örneği oluşturmaz. Bu, tüm getInstance () çağrıları arasında paylaşılan yalnızca bir MySingleton örneği olmasını sağlar.

Ancak bu uygulamanın bir sorunu var. Çok iş parçacıklı uygulamalar, tek bir örneğin oluşturulması konusunda bir yarış durumuna sahip olacaktır. Birden çok yürütme iş parçacığı getInstance () yöntemini aynı anda (veya çevresinde) vurursa, her biri INSTANCE üyesini null olarak görür. Bu, her iş parçacığının yeni bir MySingleton örneği oluşturmasına ve ardından INSTANCE üyesini ayarlamasına neden olur.


private static MySingleton INSTANCE;

public static synchronized MySingleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new MySingleton();
    }

    return INSTANCE;
}

Burada getInstance () yöntemini senkronize etmek için yöntem imzasında senkronize edilmiş anahtar kelimeyi kullandık. Bu kesinlikle yarış durumumuzu düzeltir. Konular şimdi engellenecek ve yöntemi birer birer girecektir. Ama aynı zamanda bir performans sorunu yaratıyor. Bu uygulama yalnızca tek eşgörünümün oluşturulmasını senkronize etmekle kalmaz, okumalar dahil tüm çağrıları getInstance () öğesine senkronize eder. INSTANCE değerini döndürdüğü için okumaların senkronize edilmesi gerekmez. Okumalar çağrılarımızın çoğunu oluşturacağından (hatırlayın, örnekleme yalnızca ilk çağrıda gerçekleşir), tüm yöntemi senkronize ederek gereksiz bir performans isabetine gireceğiz.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronize(MySingleton.class) {
            INSTANCE = new MySingleton();
        }
    }

    return INSTANCE;
}

Burada senkronizasyonu yöntem imzasından MySingleton örneğinin oluşturulmasını saran senkronize bir bloğa taşıdık. Peki bu sorunumuzu çözüyor mu? Artık okumaları engellemiyoruz, ancak geriye doğru da bir adım attık. Birden çok iş parçacığı aynı anda veya çevresinde getInstance () yöntemine çarpar ve hepsi INSTANCE üyesini null olarak görür. Daha sonra, kilidi elde edip örneği oluşturacak olan senkronize bloğa çarparlar. Bu iş parçacığı bloktan çıktığında, diğer iş parçacıkları kilit için yarışacak ve her iş parçacığı tek tek bloktan düşecek ve sınıfımızın yeni bir örneğini oluşturacaktır. Yani başladığımız yere geri döndük.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Burada bloğun İÇİNDEN başka bir kontrol daha veriyoruz. INSTANCE üyesi önceden ayarlanmışsa, başlatmayı atlayacağız. Buna çift denetimli kilitleme denir.

Bu, çoklu örnekleme sorunumuzu çözer. Ama bir kez daha, çözümümüz başka bir zorluk getirdi. Diğer evreler INSTANCE üyesinin güncellendiğini "görmeyebilir". Bunun nedeni Java'nın bellek işlemlerini nasıl optimize ettiğidir. Konular değişkenlerin orijinal değerlerini ana bellekten CPU'nun önbelleğine kopyalar. Değerlerdeki değişiklikler daha sonra bu önbelleğe yazılır ve bu önbellekten okunur. Bu, performansı optimize etmek için tasarlanmış bir Java özelliğidir. Ancak bu, singleton uygulamamız için bir sorun yaratıyor. Farklı bir önbellek kullanarak farklı bir CPU veya çekirdek tarafından işlenen ikinci bir iş parçacığı, ilk işlem tarafından yapılan değişiklikleri görmez. Bu, ikinci iş parçacığının INSTANCE üyesini, singleton'umuzun yeni bir örneğini oluşturmaya zorlayacak şekilde görmesine neden olur.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Bunu, INSTANCE üyesinin beyanındaki geçici anahtar kelimeyi kullanarak çözüyoruz. Bu derleyiciye her zaman CPU önbelleğinden değil ana bellekten okumasını ve yazmasını sağlar.

Ancak bu basit değişikliğin bir bedeli vardır. CPU önbelleğini atladığımız için, geçici INSTANCE üyesinde her çalıştığımızda 4 kez yaptığımız bir performans isabeti alacağız. Varlığı (1 ve 2) iki kez kontrol eder, değeri (3) ayarlarız ve sonra değeri (4) döndürürüz. Bir örnek, yalnızca yöntemin ilk çağrısı sırasında örneği oluşturduğumuz için bu yolun saçak durumu olduğunu iddia edebilir. Belki de yaratılıştaki bir performans tolere edilebilir. Ancak, ana kullanım durumumuz bile, uçucu eleman üzerinde iki kez çalışacaktır. Bir kez varlığını kontrol etmek ve tekrar değerini döndürmek için.


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;
}

Performans isabeti doğrudan uçucu eleman üzerinde çalıştığından kaynaklandığı için, uçucunun değerine bir yerel değişken ayarlayalım ve bunun yerine yerel değişken üzerinde çalışalım. Bu, uçucu madde üzerinde kaç kez çalıştığımızı azaltacak ve böylece kayıp performansımızın bir kısmını geri kazanacaktır. Senkronize bloğa girdiğimizde yerel değişkenimizi tekrar ayarlamamız gerektiğini unutmayın. Bu, kilidi beklerken meydana gelen değişikliklerle güncel kalmasını sağlar.

Son zamanlarda bunun hakkında bir makale yazdım. Singleton'un Yapısından Çıkarılması . Bu örnekler hakkında daha fazla bilgi ve orada "tutucu" desen örneği bulabilirsiniz. Ayrıca, çift kontrol edilen uçucu yaklaşımı gösteren gerçek dünya örneği de vardır. Bu yardımcı olur umarım.


Neden açıklayabilir misiniz BearerToken instancesenin içinde makalesinde değildir static? Ne oldu result.hasExpired()?
Woland

Ve ne olacak class MySingleton- belki de olmalı final?
Woland

1
@Woland BearerTokenÖrnek, BearerTokenFactory belirli bir yetkilendirme sunucusuyla yapılandırılan - öğesinin parçası olduğu için statik değildir . Çok sayıda BearerTokenFactorynesne olabilir - her birinin BearerTokensüresi doluncaya kadar dağıttığı kendi “önbelleğe alınmış” olması. Üzerindeki hasExpired()yöntem, süresi dolmuş bir belirteci dağıtmadığından emin olmak BeraerTokeniçin fabrika get()yönteminde çağrılır . Süresi dolmuşsa, yetkilendirme sunucusundan yeni bir kod istenir. Kod bloğunu izleyen paragraf bunu daha ayrıntılı olarak açıklamaktadır.
Michael Andrews

4

Basit bir uygulama şu şekildedir singleton:

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */ 
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

Uygun şekilde tembel oluşturmak için singleton:

public class Singleton {
    // The constructor must be private to prevent external instantiation   
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /** 
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only 
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE 
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

İkisi de tembeldir, singleton'dan ihtiyacınız olan tek şey onun örneğidir.
Miha_x64

@ Miha_x64, JVM sınıfı başlattığında ilk vaka singletonu başlatırken, ikinci vaka çağrı sırasında singletonu sadece başlatır getInstance(). Ama aslında sınıfınızda başka statik yöntemler yoksa Singletonve sadece getInstance()gerçek bir fark olmadığını söylüyorsanız.
Nicolas Filotto

3

Bir sınıfın örnek değişkenini tembel olarak yüklemeniz gerekiyorsa, çift ​​denetleme deyimine ihtiyacınız vardır. Statik bir değişken veya bir tektonu tembel olarak yüklemeniz gerekiyorsa , talep sahibi deyiminde başlatmaya ihtiyacınız vardır .

Ayrıca, singleton'un seriliazble olması gerekiyorsa, singleton nesnesinin değişmezliğini korumak için diğer tüm alanların geçici olması ve readResolve () yönteminin uygulanması gerekir. Aksi takdirde, nesnenin serisini her silme işlemi gerçekleştirildiğinde, nesnenin yeni bir örneği oluşturulur. ReadResolve () işlevi, readObject () tarafından okunan yeni nesnenin yerine geçer; bu, yeni nesneyi, ona başvuran bir değişken olmadığından çöp toplanmaya zorlar.

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 

3

Singleton nesnesi yapmanın çeşitli yolları:

  1. Joshua Bloch'a göre - Enum en iyisi olurdu.

  2. ayrıca çift çek kilitlemeyi de kullanabilirsiniz.

  3. İç statik sınıf bile kullanılabilir.


3

Enum singleton

İş parçacığı açısından güvenli olan bir Singletonu uygulamanın en basit yolu bir Enum kullanmaktır

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

Bu kod Java 1.5 Enum tanıtımı beri çalışır

Çift kontrollü kilitleme

Çok iş parçacıklı bir ortamda (Java 1.5'ten başlayarak) çalışan bir “klasik” singletonu kodlamak istiyorsanız, bunu kullanmalısınız.

public class Singleton {

  private static volatile Singleton instance = null;

  private Singleton() {
  }

  public static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class){
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance ;
  }
}

Bu, uçucu anahtar kelimenin uygulanması farklı olduğundan 1.5'ten önce güvenli değildir.

Erken yükleme Singleton (Java 1.5'ten önce bile çalışır)

Bu uygulama, sınıf yüklendiğinde singleton'u başlatır ve iş parçacığı güvenliği sağlar.

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}

2

JSE 5.0 ve üstü için Enum yaklaşımını kullanın, aksi takdirde statik tekli tutucu yaklaşımı kullanın ((Bill Pugh tarafından tarif edilen tembel bir yükleme yaklaşımı). İkinci çözüm, özel dil yapılarına (uçucu veya senkronize) gerek duymadan da iş parçacığı için güvenlidir.


2

Singletons'a karşı sıkça kullanılan bir diğer argüman, test edilebilirlik problemleridir. Singletons test amacıyla kolayca taklit edilemez. Bu bir sorun haline gelirse, aşağıdaki küçük değişiklikleri yapmak istiyorum:

public class SingletonImpl {

    private static SingletonImpl instance;

    public static SingletonImpl getInstance() {
        if (instance == null) {
            instance = new SingletonImpl();
        }
        return instance;
    }

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

Eklenen setInstanceyöntem, test sırasında singleton sınıfının mockup uygulamasını ayarlamaya izin verir:

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Bu aynı zamanda erken başlatma yaklaşımlarıyla da çalışır:

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Bu, bu işlevselliği normal uygulamaya da maruz bırakmanın dezavantajına sahiptir. Bu kod üzerinde çalışan diğer geliştiriciler, belirli bir işlevi değiştirmek ve böylece tüm uygulama davranışını değiştirmek için ´setInstance´ yöntemini kullanmaya cazip gelebilir, bu nedenle bu yöntem, javadocunda en azından iyi bir uyarı içermelidir.

Yine de, mockup testi (gerektiğinde) olasılığı için, bu kod maruziyeti ödeme için kabul edilebilir bir fiyat olabilir.


1

en basit singleton sınıfı

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}

1
bu Jonathan'ın aşağıdaki cevabı ile aynı
inor

1
Kopyası Bu kardeş cevap tarafından Jonathan beş yıl önce yayınlanmıştır. İlginç yorumlar için bu cevaba bakınız.
Basil Bourque

0

Java 1.5'ten sonra hala enum mevcut en iyi singleton uygulaması olduğunu düşünüyorum çünkü aynı zamanda çok iş parçacıklı ortamlarda bile - sadece bir örnek oluşturulmasını sağlıyor.

public enum Singleton{ INSTANCE; }

ve işiniz bitti !!!


1
Bu, yıllar önce diğer cevaplarda zaten belirtilmiştir.
Pang

0

Bu gönderiye bir göz atın.

Java'nın çekirdek kütüphanelerindeki GoF Tasarım Kalıplarına örnekler

En iyi yanıtın "Singleton" bölümünden,

Singleton (her seferinde aynı örneği (genellikle kendi başına) döndüren yaratıcı yöntemlerle tanınabilir)

  • java.lang.Runtime # getRuntime ()
  • java.awt.Desktop # getDesktop ()
  • java.lang.System # getSecurityManager ()

Singleton örneğini Java yerel sınıflarından da öğrenebilirsiniz.


0

Şimdiye kadar gördüğüm en iyi singleton modeli Tedarikçi arayüzünü kullanıyor.

  • Genel ve tekrar kullanılabilir
  • Tembel başlatmayı destekler
  • Yalnızca başlatılıncaya kadar senkronize edilir, ardından engelleme tedarikçisi engellemeyen bir tedarikçi ile değiştirilir.

Aşağıya bakınız:

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}

-3

Bazen basit bir " static Foo foo = new Foo();" yeterli olmaz. Sadece yapmak istediğiniz bazı temel veri eklemeyi düşünün.

Öte yandan, singleton değişkenini bu şekilde başlatan herhangi bir yöntemi senkronize etmeniz gerekir. Senkronizasyon bu kadar kötü değildir, ancak performans sorunlarına veya kilitlemeye neden olabilir (çok nadir durumlarda bu örneği kullanır.

public class Singleton {

    private static Singleton instance = null;

    static {
          instance = new Singleton();
          // do some of your instantiation stuff here
    }

    private Singleton() {
          if(instance!=null) {
                  throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
          }
    }

    public static getSingleton() {
          return instance;
    }

}

Şimdi ne olacak? Sınıf, sınıf yükleyici aracılığıyla yüklenir. Sınıf bir bayt dizisinden yorumlandıktan hemen sonra, VM statik {} - bloğunu yürütür . sırrın tamamı budur: Statik blok yalnızca bir kez çağrılır, verilen paketin verilen sınıfının (adının) bu bir sınıf yükleyici tarafından yüklenmesi.


3
Doğru değil. statik değişkenler, sınıf yüklendiğinde statik bloklarla birlikte başlatılır. Deklarasyonu bölmeye gerek yok.
Craig P. Motlin

-5
public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton(){
    if (INSTANCE != null)
        throw new IllegalStateException (“Already instantiated...”);
}

    public synchronized static Singleton getInstance() { 
    return INSTANCE;

    }

}

Senkronize anahtar sözcüğünü getInstance'dan önce eklediğimizden, iki iş parçacığının aynı anda getInstance'ı çağırması durumunda yarış koşulundan kaçındık.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.