Static Initialization Blocks

前端 未结 14 1251
暗喜
暗喜 2020-11-22 01:56

As far as I understood the \"static initialization block\" is used to set values of static field if it cannot be done in one line.

But I do not understand why we ne

相关标签:
14条回答
  • 2020-11-22 02:22

    Here's an example:

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

    The code in the "static" section(s) will be executed at class load time, before any instances of the class are constructed (and before any static methods are called from elsewhere). That way you can make sure that the class resources are all ready to use.

    It's also possible to have non-static initializer blocks. Those act like extensions to the set of constructor methods defined for the class. They look just like static initializer blocks, except the keyword "static" is left off.

    0 讨论(0)
  • 2020-11-22 02:22
    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");
        } 
    }
    
    0 讨论(0)
  • 2020-11-22 02:24

    If they weren't in a static initialization block, where would they be? How would you declare a variable which was only meant to be local for the purposes of initialization, and distinguish it from a field? For example, how would you want to write:

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

    If first and second weren't in a block, they'd look like fields. If they were in a block without static in front of it, that would count as an instance initialization block instead of a static initialization block, so it would be executed once per constructed instance rather than once in total.

    Now in this particular case, you could use a static method instead:

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

    ... but that doesn't work when there are multiple variables you wish to assign within the same block, or none (e.g. if you just want to log something - or maybe initialize a native library).

    0 讨论(0)
  • 2020-11-22 02:28

    It is a common misconception to think that a static block has only access to static fields. For this I would like to show below piece of code that I quite often use in real-life projects (copied partially from another answer in a slightly different context):

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

    Here the initializer is used to maintain an index (ALIAS_MAP), to map a set of aliases back to the original enum type. It is intended as an extension to the built-in valueOf method provided by the Enum itself.

    As you can see, the static initializer accesses even the private field aliases. It is important to understand that the static block already has access to the Enum value instances (e.g. ENGLISH). This is because the order of initialization and execution in the case of Enum types, just as if the static private fields have been initialized with instances before the static blocks have been called:

    1. The Enum constants which are implicit static fields. This requires the Enum constructor and instance blocks, and instance initialization to occur first as well.
    2. static block and initialization of static fields in the order of occurrence.

    This out-of-order initialization (constructor before static block) is important to note. It also happens when we initialize static fields with the instances similarly to a Singleton (simplifications made):

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

    What we see is the following output:

    Static Block 1
    Constructor
    Static Block 2
    In Main
    Constructor
    

    Clear is that the static initialization actually can happen before the constructor, and even after:

    Simply accessing Foo in the main method, causes the class to be loaded and the static initialization to start. But as part of the Static initialization we again call the constructors for the static fields, after which it resumes static initialization, and completes the constructor called from within the main method. Rather complex situation for which I hope that in normal coding we would not have to deal with.

    For more info on this see the book "Effective Java".

    0 讨论(0)
  • 2020-11-22 02:29

    As supplementary, like @Pointy said

    The code in the "static" section(s) will be executed at class load time, before any instances of the class are constructed (and before any static methods are called from elsewhere).

    It's supposed to add System.loadLibrary("I_am_native_library") into static block.

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

    It will guarantee no native method be called before the related library is loaded into memory.

    According to loadLibrary from oracle:

    If this method is called more than once with the same library name, the second and subsequent calls are ignored.

    So quite unexpectedly, putting System.loadLibrary is not used to avoid library be loaded multi-times.

    0 讨论(0)
  • 2020-11-22 02:31

    You can execute bits of code once for a class before an object is constructed in the static blocks.

    E.g.

    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());
      }
    }
    
    0 讨论(0)
提交回复
热议问题