Statik Başlatma Blokları


265

Anladığım kadarıyla "statik başlatma bloğu", bir satırda yapılamazsa statik alanın değerlerini ayarlamak için kullanılır.

Ama bunun için neden özel bir bloğa ihtiyacımız olduğunu anlamıyorum. Örneğin, bir alanı statik olarak (değer ataması olmadan) bildiririz. Ve sonra yukarıda belirtilen statik alana bir değer üreten ve atanan kodun birkaç satırını yazın.

Neden bu satırlara aşağıdaki gibi özel bir blokta ihtiyacımız var static {...}?


6
Küçük geri bildirim, ancak varsayımlarınızı açıkça belirtmeniz ve böylece hangi cevabın doğru olduğunu açıklamanız yararlı olacaktır. sorunuzu ilk okuduğumda, yanlış anladım ve {...}vs arasındaki farkı bildiğinizi düşündüm static {...}. (bu durumda Jon Skeet sorunuza daha iyi cevap verdi)
David T.

1
Bu soru çok belirsiz; cevapları kastediyor ve ne demek istediğine dair uzun soluklu varsayımlar yapıyorsun. Aklınızda olan örnek statik başlatma bloğunu ve alternatifinizi açık bir şekilde yazmaya ne dersiniz, böylece insanların yanıtlayabileceği net bir şey olsun?
Don Hatch

Yanıtlar:


430

Statik olmayan blok:

{
    // Do Something...
}

Sınıfın her örneği oluşturulduğunda çağrılır . Statik blok sadece çağrılan bir kez sınıf kendisi başlatıldığında bu tür çok sayıda nesne oluşturup nasıl olursa olsun,.

Misal:

public class Test {

    static{
        System.out.println("Static");
    }

    {
        System.out.println("Non-static block");
    }

    public static void main(String[] args) {
        Test t = new Test();
        Test t2 = new Test();
    }
}

Bu yazdırır:

Static
Non-static block
Non-static block

107
Bu neden kabul edilen cevap? Soruyu bile cevaplamıyor.
Paul Bellora

43
Şu soruyu yanıtlar: "Bu, sınıf her oluşturulduğunda çağrılır. Bu türden kaç nesne oluşturursanız oluşturun, statik blok yalnızca bir kez çağrılır."
Adam Arold

83
Meraklı okuyucu için, statik olmayan blok aslında Java derleyicisi tarafından sınıfın ( kaynak ) sahip olduğu her kurucuya kopyalanır . Bu nedenle, alanları başlatmak hala kurucunun görevidir.
Martin Andersson

2
Kabul edilen cevap şu olmalıdır: stackoverflow.com/a/2420404/363573 . Bu cevap, statik bloklara ihtiyacınız olan gerçek bir yaşam örneği sunar.
Stephan

16
Bu cevap neden birden aşağıya düşüyor? Bunun kabul edilen cevap olduğuna katılmayabilirsiniz, ancak kesinlikle hiçbir şekilde yanlış veya yanıltıcı değildir. Basit bir örnekle bu dil yapılarının anlaşılmasına yardımcı olmaya çalışıyor.
Frederik Wordenskjold

133

Statik bir başlatma bloğunda olmasalardı, nerede olurlardı? Yalnızca başlatma amacıyla yerel olması gereken bir değişkeni nasıl bildirir ve onu bir alandan nasıl ayırırsınız? Örneğin, nasıl olur sen yazma istiyorum:

public class Foo {
    private static final int widgets;

    static {
        int first = Widgets.getFirstCount();
        int second = Widgets.getSecondCount();
        // Imagine more complex logic here which really used first/second
        widgets = first + second;
    }
}

Eğer firstve secondbir blokta değildi, onlar alanları benzeyecektin. Önlerinde olmayan bir blokta staticolsaydı, statik başlatma bloğu yerine örnek başlatma bloğu olarak sayılırlardı , bu yüzden toplamda bir kez değil, inşa edilen örnek başına bir kez yürütülürdü .

Şimdi bu özel durumda, bunun yerine statik bir yöntem kullanabilirsiniz:

public class Foo {
    private static final int widgets = getWidgets();

    static int getWidgets() {
        int first = Widgets.getFirstCount();
        int second = Widgets.getSecondCount();
        // Imagine more complex logic here which really used first/second
        return first + second;
    }
}

... ancak aynı blok içinde atamak istediğiniz birden fazla değişken olduğunda veya hiçbiri olmadığında işe yaramaz (örneğin, yalnızca bir şeyi günlüğe kaydetmek istiyorsanız veya yerel bir kütüphaneyi başlatmak istiyorsanız).


1
Statik blok, statik değişkenler atanmadan önce mi sonra mı olur? private static int widgets = 0; static{widgets = 2;}
Weishi Zeng

1
Statik bloğun statik değişkenler atanmadan önce mi sonra mı olduğunu merak ediyordum. Örneğin private static int widgets = 0; static{widgets = 2;}, '=' atamasının sırayla gerçekleştiğini, yani ilk önce '=' koyulduğunu gösterir. Yukarıdaki örnek 'widget'lara 2 değerini verecektir. (PS yorumların sadece 5 dakikada düzenlenebileceğini bilmiyordu ...)
Weishi Zeng

@WeishiZeng: Evet, bu docs.oracle.com/javase/specs/jls/se8/html/… - madde 9'da belgelendiği gibidir
Jon Skeet

Ancak, statik başlatma bloğu ile tam olarak aynı koda sahip özel bir statik yöntem kullanamaz ve özel statik yönteme widget atayamaz mısınız?
Zachary Kraus

1
@Zachary: Değeri döndürmek ve yöntem çağrısının sonucunu atamak mı demek istediniz? Eğer öyleyse, evet - ne zaman edilir bloğun sonucunda tam olarak bir değişkene atama. Cevabımı ayrıntılarla yaklaşık 7 saat içinde düzenleyecek ...
Jon Skeet

103

İşte bir örnek:

  private static final HashMap<String, String> MAP = new HashMap<String, String>();
  static {
    MAP.put("banana", "honey");
    MAP.put("peanut butter", "jelly");
    MAP.put("rice", "beans");
  }

"Statik" bölüm (ler) deki kod, sınıfın herhangi bir örneği oluşturulmadan önce (ve başka bir yerden herhangi bir statik yöntem çağrılmadan önce) sınıf yükleme zamanında yürütülür. Bu şekilde sınıf kaynaklarının kullanıma hazır olduğundan emin olabilirsiniz.

Statik olmayan başlatıcı bloklarına sahip olmak da mümkündür. Bunlar, sınıf için tanımlanan yapıcı yöntemleri kümesinin uzantıları gibi davranır. "Statik" anahtar kelimesinin dışında bırakılması dışında, statik başlatıcı blokları gibi görünürler.


4
Bu özel örnek için bazen çift ​​ayraç deseni "istismar edilir" :)
BalusC

Suistimal edilebilir, ancak öte yandan bazı karışıklıkları temizler ve bazı kod türlerini biraz daha "sağlam" hale getirir. Erlang'da eğlenmek için programlıyorum ve yerel değişkenlere ihtiyaç duymadan bağımlı oluyorsunuz :-)
Pointy

1
<< "Statik" bölüm (ler) deki kod, sınıfın herhangi bir örneği oluşturulmadan önce (ve başka bir yerden herhangi bir statik yöntem çağrılmadan önce) sınıf yükleme zamanında yürütülür. Bu şekilde sınıf kaynaklarının kullanıma hazır olduğundan emin olabilirsiniz. >> (Yukarıdaki cevapta belirtilen "Sivri" ifadesi) bu, statik blok yürütme söz konusu olduğunda dikkat edilmesi gereken çok önemli bir noktadır.
öğrenen

AfterPropertiesSet yönteminden sonra InitializingBean kullanarak bunu yapabilir miyiz?
egemen

48

Değeri aslında herhangi bir şeye atamak istemediğinizde de faydalıdır, örneğin çalışma sırasında bir sınıfı sadece bir kez yüklemek gibi .

Örneğin

static {
    try {
        Class.forName("com.example.jdbc.Driver");
    } catch (ClassNotFoundException e) {
        throw new ExceptionInInitializerError("Cannot load JDBC driver.", e);
    }
}

Hey, başka bir yararı daha var, istisnaları ele almak için kullanabilirsiniz. Düşünün getStuff()burada atar Exceptionhangi gerçekten bir catch bloğunda aittir:

private static Object stuff = getStuff(); // Won't compile: unhandled exception.

staticburada bir başlatıcı yararlıdır. Burada istisnayı kaldırabilirsiniz.

Başka bir örnek daha sonra atama sırasında yapılamayacak şeyler yapmaktır:

private static Properties config = new Properties();

static {
    try { 
        config.load(Thread.currentThread().getClassLoader().getResourceAsStream("config.properties");
    } catch (IOException e) {
        throw new ExceptionInInitializerError("Cannot load properties file.", e);
    }
}

JDBC sürücüsü örneğine geri dönmek için, iyi bir JDBC sürücüsünün kendisi de statickendisini kaydetmek için başlatıcıyı kullanır DriverManager. Ayrıca bu ve bu cevaba bakınız .


2
Burada tehlikeli voodoo yatıyor ... statik başlatıcılar örtülü olarak senkronize edilen sentetik clinit () yönteminde çalıştırılır . Bu, JVM'nin söz konusu sınıf dosyasında bir kilit alacağı anlamına gelir. Bu, iki sınıf birbirini yüklemeye çalışırsa ve her biri farklı bir iş parçacığında yüklenmeye başlarsa, çok iş parçacıklı ortamlarda kilitlenmeye yol açabilir. Bkz. Www-01.ibm.com/support/docview.wss?uid=swg1IV48872
Ajax

@Ajax: Bunun söz konusu JDBC sürücüsünde veya yüklenmesinden sorumlu uygulama kodunda bir hata olduğunu düşünürdüm. Genellikle, düzgün JDBC sürücüleri durumunda, uygulamanın başlangıcı sırasında uygulama genelinde yalnızca bir kez yüklediğiniz sürece, önemli bir şey yoktur.
BalusC

Ancak kesinlikle bir hata olurdu, ancak tamamen JDBC sürücüsünün hatası değil. Belki sürücünün masum bir şekilde kendi statik başlatıcıları vardır ve belki de bu sınıfı uygulamanızdaki diğerleriyle birlikte masumca başlatırsınız ve oh hayır, bazı beklenmedik sınıflar döngüsel olarak birbirini yükler ve şimdi uygulama kilitlenmeleri. Bunu java.awt.AWTEvent ve sun.util.logging.PlatformLogger arasındaki çıkmaz sayesinde keşfettim. AWTEvent'e sadece başsız çalışmasını söylemek için dokundum ve AWTEvent'in de yüklediği PlatformLogger'ı yükleyen diğer bazı lib'ler yaralandı.
Ajax

1
Her iki sınıf da farklı iş parçacıkları üzerinde senkronize olarak yaralandı ve derlemim yaklaşık 1/150 çalışma çıkardı. Bu yüzden, artık statik bloklarda sınıf yüklemesi konusunda çok daha dikkatli davranıyorum. Yukarıda bahsettiğim durumda, derhal bir geçici sağlayıcı sınıfı oluşturabildiğim (kilitlenme şansı olmadan) ertelenmiş bir sağlayıcı deseni kullanarak, alanı başlatın ve sonra gerçekten erişildiğinde (senkronize olmayan bir alan erişiminde), sonra aslında kilitlenmeye neden olabilecek sınıfları yüklerim.
Ajax

11

Ben static blocksadece sözdizimsel şeker olduğunu söyleyebilirim . staticBaşka bir şeyle değil, blokla yapabileceğiniz hiçbir şey yoktur.

Burada yayınlanan bazı örnekleri yeniden kullanmak için.

Bu kod parçası, staticbaşlatıcı kullanılmadan yeniden yazılabilir .

Yöntem # 1: İle static

private static final HashMap<String, String> MAP;
static {
    MAP.put("banana", "honey");
    MAP.put("peanut butter", "jelly");
    MAP.put("rice", "beans");
  }

Yöntem # 2: Olmadan static

private static final HashMap<String, String> MAP = getMap();
private static HashMap<String, String> getMap()
{
    HashMap<String, String> ret = new HashMap<>();
    ret.put("banana", "honey");
    ret.put("peanut butter", "jelly");
    ret.put("rice", "beans");
    return ret;
}

10

Var olmanın birkaç gerçek nedeni vardır:

  1. başlatılması static finalistisna oluşturabilecek üyelerin başlatılması
  2. static finalüyeleri hesaplanmış değerlerle başlatma

İnsanlar static {}, sınıfın çalışma zamanı içinde bağımlı olduğu şeyleri başlatmak için uygun bir yol olarak blokları kullanma eğilimindedir - örneğin, belirli bir sınıfın yüklenmesini sağlamak (ör. JDBC sürücüleri). Bu başka şekillerde yapılabilir; ancak, yukarıda bahsettiğim iki şey sadece static {}blok gibi bir yapı ile yapılabilir .


8

Statik bloklarda bir nesne oluşturulmadan önce bir sınıf için kod parçalarını bir kez yürütebilirsiniz.

Örneğin

class A {
  static int var1 = 6;
  static int var2 = 9;
  static int var3;
  static long var4;

  static Date date1;
  static Date date2;

  static {
    date1 = new Date();

    for(int cnt = 0; cnt < var2; cnt++){
      var3 += var1;
    }

    System.out.println("End first static init: " + new Date());
  }
}

7

Statik bir bloğun yalnızca statik alanlara erişimi olduğunu düşünmek yaygın bir yanlış anlamadır. Bunun için gerçek hayat projelerinde oldukça sık kullandığım kod parçasının altında göstermek istiyorum ( biraz farklı bir bağlamda kısmen başka bir cevaptan kopyalandı ):

public enum Language { 
  ENGLISH("eng", "en", "en_GB", "en_US"),   
  GERMAN("de", "ge"),   
  CROATIAN("hr", "cro"),   
  RUSSIAN("ru"),
  BELGIAN("be",";-)");

  static final private Map<String,Language> ALIAS_MAP = new HashMap<String,Language>(); 
  static { 
    for (Language l:Language.values()) { 
      // ignoring the case by normalizing to uppercase
      ALIAS_MAP.put(l.name().toUpperCase(),l); 
      for (String alias:l.aliases) ALIAS_MAP.put(alias.toUpperCase(),l); 
    } 
  } 

  static public boolean has(String value) { 
    // ignoring the case by normalizing to uppercase
    return ALIAS_MAP.containsKey(value.toUpper()); 
  } 

  static public Language fromString(String value) { 
    if (value == null) throw new NullPointerException("alias null"); 
    Language l = ALIAS_MAP.get(value); 
    if (l == null) throw new IllegalArgumentException("Not an alias: "+value); 
    return l; 
  } 

  private List<String> aliases; 
  private Language(String... aliases) { 
    this.aliases = Arrays.asList(aliases); 
  } 
} 

Burada başlatıcı, ALIAS_MAPbir dizi diğer adı orijinal numara türüne geri eşlemek için bir dizini ( ) korumak için kullanılır . EnumKendisi tarafından sağlanan yerleşik valueOf yönteminin bir uzantısı olarak tasarlanmıştır .

Gördüğünüz gibi, statik başlatıcı privatealana bile erişir aliases. staticBloğun zaten Enumdeğer örneklerine (ör. ENGLISH) Erişimi olduğunu anlamak önemlidir . Bunun nedeni, türler durumunda başlatmalar ve yürütme sırasınınEnum , tıpkı bloklar çağrılmadan static privateönce alanlar örneklerle başlatılmış gibi olmasıdır static:

  1. EnumÖrtük statik alanlardır sabitler. Bu, Enum yapıcısının ve örnek bloklarının ve örnek başlatmanın önce gerçekleşmesini gerektirir.
  2. static statik alanların oluşma sırasına göre bloklanması ve başlatılması.

Bu sıra dışı başlatma ( staticbloktan önce yapıcı ) not etmek önemlidir. Statik alanları bir Singleton örneğine benzer şekilde başlattığımızda da olur (basitleştirmeler yapıldı):

public class Foo {
  static { System.out.println("Static Block 1"); }
  public static final Foo FOO = new Foo();
  static { System.out.println("Static Block 2"); }
  public Foo() { System.out.println("Constructor"); }
  static public void main(String p[]) {
    System.out.println("In Main");
    new Foo();
  }
}

Gördüğümüz şu çıktı:

Static Block 1
Constructor
Static Block 2
In Main
Constructor

Açıktır ki, statik başlatma aslında kurucudan önce ve sonra olabilir:

Ana yöntemde sadece Foo'ya erişmek, sınıfın yüklenmesine ve statik başlatmanın başlamasına neden olur. Ancak Statik başlatmanın bir parçası olarak, tekrar statik alanlar için yapıcıları çağırırız, daha sonra statik başlatmayı sürdürür ve ana yöntemden çağrılan yapıcıyı tamamlar. Oldukça karmaşık bir durum için umarım normal kodlamada uğraşmak zorunda kalmayacağız.

Bu konuda daha fazla bilgi için " Etkili Java " kitabına bakınız .


1
Erişime sahip olmak aliases, statik bloğun statik olmayan üyelere erişebileceği anlamına gelmez. / static / yöntemi tarafından döndürülen değerlere aliaseserişilir . Bahsettiğiniz gibi, enum değişkenlerinin bu noktada zaten mevcut olması, olağan dışı bit - normal sınıfların statik olmayan üyelerine bu durumda erişilemez. Languagevalues()
Ignazio

Statik blok, yalnızca bu durumda nesne olan statik alanlara (enum ENGLISH, GERMAN, ... durumunda) erişmeye devam etmektedir. Statik alanlar nesnelerin kendileri olduğundan, statik nesnenin örnek alanına erişebilirsiniz.
Swami PR

1
class Foo { static final Foo Inst1; static final Foo Inst2; static{ Inst1 = new Foo("Inst1"); Inst2 = new Foo("Inst2"); } static { System.out.println("Inst1: " + Inst1.member); System.out.println("Inst2: " + Inst2.member); } private final String member; private Foo(String member){ this.member = member; } } Yukarıdaki kod enum örneğinden farklı değildir ve yine de statik bloğun içindeki örnek değişkenine erişime izin verir
Swami PR

@SwamiPR gerçekten benim için sürpriz derler ve kodun prensipte farklı olmadığını kabul etmeliyim. Java teknik özelliklerini yeniden okumam gerekiyor, kaçırdığım bir şey olduğunu hissediyorum. Geri dönüş iyi, teşekkürler.
YoYo

@SwamiPR Sorun şu ki bir kullanmalıyız Enum. Tekil örneklere işaret ettiğimizi garanti etmenin en iyi yolu '- buraya bakın . Ve puanlarınıza göre, birkaç güncelleme yaptım.
YoYo

3

Statik değişkenlerinizin çalışma zamanında ayarlanması gerekiyorsa, bir static {...}blok çok yardımcı olur.

Örneğin, statik üyeyi bir yapılandırma dosyasında veya veritabanında depolanan bir değere ayarlamanız gerekiyorsa.

Mapİlk üye bildiriminde bu değerleri ekleyemediğiniz için statik bir üyeye değer eklemek istediğinizde de yararlıdır .


3

Statik bir alanınız var (buna "sınıf değişkeni" de deniyor çünkü sınıfın bir örneğinden ziyade sınıfa ait; başka bir deyişle herhangi bir nesneyle değil sınıfla ilişkilendiriliyor) ve onu başlatmak istiyorsunuz. Bu nedenle, bu sınıfın bir örneğini oluşturmak istemiyorsanız ve bu statik alanı değiştirmek istiyorsanız, bunu üç şekilde yapabilirsiniz:

1- Değişkeni bildirdiğinizde başlatmanız yeterlidir:

static int x = 3;

2- Statik bir başlatma bloğuna sahip olun:

static int x;

static {
 x=3;
}

3- Sınıf değişkenine erişen ve onu başlatan bir sınıf yöntemine (statik yöntem) sahip olun: bu yukarıdaki statik bloğa alternatiftir; özel bir statik yöntem yazabilirsiniz:

public static int x=initializeX();

private static int initializeX(){
 return 3;
}

Şimdi neden statik yöntemler yerine statik başlatma bloğu kullanasınız?

Programınızda ihtiyacınız olan şey gerçekten. Ancak statik başlatma bloğunun bir kez çağrıldığını ve sınıf yönteminin tek avantajının, sınıf değişkenini yeniden başlatmanız gerektiğinde daha sonra tekrar kullanılabilmeleri gerektiğini bilmeniz gerekir.

diyelim ki programınızda karmaşık bir dizi var. Sen (kullanarak başlatmak için örneğin döngü) ve daha sonra bu dizideki değerleri program boyunca değişecek ama sonra bir noktada bunu yeniden başlatmak (ilk değere geri dönmek) istiyorum. Bu durumda özel statik yöntemi çağırabilirsiniz. Programınızda değerleri yeniden başlatmanız gerekmiyorsa, statik bloğu kullanabilirsiniz ve statik bir yönteme gerek yoktur, çünkü programda daha sonra kullanmayacaksınız.

Not: statik bloklar kodda göründükleri sırayla çağrılır.

Örnek 1:

class A{
 public static int a =f();

// this is a static method
 private static int f(){
  return 3;
 }

// this is a static block
 static {
  a=5;
 }

 public static void main(String args[]) {
// As I mentioned, you do not need to create an instance of the class to use the class variable
  System.out.print(A.a); // this will print 5
 }

}

Örnek 2:

class A{
 static {
  a=5;
 }
 public static int a =f();

 private static int f(){
  return 3;
 }

 public static void main(String args[]) {
  System.out.print(A.a); // this will print 3
 }

}

0

Ek olarak, @Pointy'nin dediği gibi

"Statik" bölüm (ler) deki kod, sınıfın herhangi bir örneği oluşturulmadan önce (ve başka bir yerden herhangi bir statik yöntem çağrılmadan önce) sınıf yükleme zamanında yürütülür.

System.loadLibrary("I_am_native_library")Statik bloğa eklenmesi gerekiyor .

static{
    System.loadLibrary("I_am_a_library");
}

İlgili kütüphane belleğe yüklenmeden önce hiçbir yerel yöntemin çağrılmayacağını garanti eder.

Yüke göre oracle'den kitap :

Bu yöntem aynı kitaplık adıyla birden çok kez çağrılırsa, ikinci ve sonraki çağrılar yok sayılır.

Bu nedenle, beklenmedik bir şekilde, System.loadLibrary'yi koymak, kitaplığın birden çok kez yüklenmesini önlemek için kullanılmaz.


0

Öncelikle, uygulama sınıflarınızın kendilerinin java.class.Classçalışma zamanı sırasında nesnelere somutlaştırıldığını anlamanız gerekir . Statik bloklarınız çalıştırıldığında budur. Yani aslında bunu yapabilirsiniz:

public class Main {

    private static int myInt;

    static {
        myInt = 1;
        System.out.println("myInt is 1");
    }

    //  needed only to run this class
    public static void main(String[] args) {
    }

}

ve konsola "myInt 1" yazdıracaktır. Herhangi bir ders başlatmadım.


0
static int B,H;
static boolean flag = true;
static{
    Scanner scan = new Scanner(System.in);
    B = scan.nextInt();
    scan.nextLine();
    H = scan.nextInt();

    if(B < 0 || H < 0){
        flag = false;
        System.out.println("java.lang.Exception: Breadth and height must be positive");
    } 
}

-1

statik blok, statik veri üyesini dinamik bir şekilde başlatmak için herhangi bir teknoloji için kullanılır veya statik veri üyesi statik bloğunun dinamik olarak başlatılması için kullanılabilir diyebiliriz. çünkü statik olmayan veri üyesi başlatma için yapıcıya sahibiz, ancak statik veri üyesini dinamik olarak başlatabileceğimiz herhangi bir yer

Eg:-class Solution{
         // static int x=10;
           static int x;
       static{
        try{
          x=System.out.println();
          }
         catch(Exception e){}
        }
       }

     class Solution1{
      public static void main(String a[]){
      System.out.println(Solution.x);
        }
        }

Şimdi benim statik int x dinamik olarak başlayacaktır ..Bcoz derleyici Solution.x gidecek zaman sınıf yükleme zamanında Solution Sınıfı ve statik blok yükü yükleyecektir .. Yani bu statik veri üyesi dinamik olarak başlatabilirsiniz ..

}

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.