【问题标题】:Java: When is a static initialization block useful?Java:静态初始化块什么时候有用?
【发布时间】:2012-03-11 20:58:00
【问题描述】:

static 块内的初始化有什么区别:

public class staticTest {

    static String s;
    static int n;
    static double d;

    static {
        s = "I'm static";
        n = 500;
        d = 4000.0001;
    }
    ...

以及单独的静态初始化:

public class staticTest {

    static String s = "I'm static";
    static int n    = 500;
    static double d = 4000.0001;

    ....

【问题讨论】:

  • 您只在静态初始化块中使用赋值,所以当然可以使用静态变量赋值来完成。如果您需要执行非赋值语句,您是否尝试过看看会发生什么?
  • 是加载类或者加载原生库的好地方。
  • 请注意,应避免使用静态变量,因此静态初始化块通常不是一个好主意。如果您发现自己经常使用它们,那么可能会遇到麻烦。

标签: java static-initialization


【解决方案1】:

在您的示例中,没有区别;但通常初始值比在单个表达式中轻松表达的更复杂(例如,它是一个 List<String>,其内容最好用 for-loop 表达;或者它是一个可能不存在的 Method,所以例外-handlers 是必需的),和/或静态字段需要按特定顺序设置。

【讨论】:

    【解决方案2】:

    有时您想做的不仅仅是为静态变量赋值。由于不能在类主体中放置任意语句,因此可以使用静态初始化块。

    【讨论】:

      【解决方案3】:

      静态初始化块允许更复杂的初始化,例如使用条件:

      static double a;
      static {
          if (SomeCondition) {
            a = 0;
          } else {
            a = 1;
          }
      }
      

      或者当不仅仅需要构造时:使用构建器创建实例时,除了创建静态字段之外,还需要进行异常处理或其他工作。

      静态初始化块也在内联静态初始化器之后运行,因此以下内容有效:

      static double a;
      static double b = 1;
      
      static {
          a = b * 4; // Evaluates to 4
      }
      

      【讨论】:

      • 正在执行“b = a * 4;”仅当 b 在 a 之前声明时,内联才会成为问题,而在您的示例中并非如此。
      • @GeorgeHawkins 我只是试图说明静态初始化程序在内联初始化程序之后运行,而不是不能内联完成等效的初始化程序。但是,我同意您的观点,并已将示例更新为(希望)更清晰。
      • 只是为了好玩,我可能会指出您的第一个示例可以很容易地是“static double a=someCondition?0:1;”并不是说你的例子不好,我只是说...... :)
      • 所以将变量初始化为静态,然后在静态块中使用它们是可行的,但是如果我在静态块中将变量初始化为静态 int B 等,为什么会出现错误?
      • 嗨@AmitAmola,感谢您的评论/问题-如果我理解正确,这将是因为在代码块中声明类变量是无效的(例如,在代码块中将变量声明为静态是'无效的Java,它们只能在类中声明为静态),但如果我误解了,值得将其作为堆栈溢出的单独问题与您的代码一起发布。 HTH
      【解决方案4】:

      从技术上讲,没有它你可以逃脱。有些人更喜欢多行初始化代码进入静态方法。我很高兴使用静态初始化器进行相对简单的多语句初始化。

      当然,我几乎总是让我的静态 final 指向一个不可修改的对象。

      【讨论】:

        【解决方案5】:

        典型用法:

        private final static Set<String> SET = new HashSet<String>();
        
        static {
            SET.add("value1");
            SET.add("value2");
            SET.add("value3");
        }
        

        没有静态初始化器你会怎么做?

        【讨论】:

        • 回答:Guava :) +1
        • 另一个没有额外库的答案:创建一个封装SET初始化的静态方法,并使用变量初始化器(private final static Set&lt;String&gt; SET = createValueSet())。如果您有 5 个集合和 2 个地图,您会将它们全部转储到一个 static 块中吗?
        【解决方案6】:

        静态代码块可以用多个指令初始化字段,以不同的声明顺序初始化字段,也可以用于条件初始化。

        更具体地说,

        static final String ab = a+b;
        static final String a = "Hello,";
        static final String b = ", world";
        

        不会起作用,因为 a 和 b 是在 ab 之后声明的。

        但是我可以使用静态初始化。块来克服这个。

        static final String ab;
        static final String a;
        static final String b;
        
        static {
          b = ", world";
          a = "Hello";
          ab = a + b;
        }
        
        static final String ab;
        static final String a;
        static final String b;
        
        static {
          b = (...) ? ", world" : ", universe";
          a = "Hello";
          ab = a + b;
        }
        

        【讨论】:

        • 虽然你说的是真的,但它并没有证明静态初始化块的必要性。您可以将ab 声明移到b 声明下方。
        【解决方案7】:

        初始化期间的异常处理是另一个原因。例如:

        static URL url;
        static {
            try {
                url = new URL("https://blahblah.com");
            }
            catch (MalformedURLException mue) {
                //log exception or handle otherwise
            }
        }
        

        这对于像上面那样烦人地抛出检查异常的构造函数很有用,或者更复杂的初始化逻辑可能容易出现异常。

        【讨论】:

          【解决方案8】:

          static 块可用于初始化 singleton 实例,以防止使用 同步 getInstance() 方法。

          【讨论】:

            【解决方案9】:

            静态关键字(无论是变量还是块)属于类。所以当类被调用时,这些变量或块被执行。所以大部分初始化将在 static 关键字的帮助下完成。因为它属于类本身,所以类可以直接访问它,不需要创建类的实例。

            举个例子,有一个鞋类,里面有 几个变量,如颜色、尺寸、品牌等……如果鞋子在这里 制造公司只有一个品牌,我们应该将其初始化为 静态变量。所以,当鞋类被调用和不同的类型 鞋的制造(通过创建类的实例)在 每当新鞋上市时,颜色和尺码都会占据记忆 但在这里,品牌是所有鞋子的共同财产,因此它将 不管生产多少鞋,只占一次内存。

            示例:

                class Shoe {
                int size;
                String colour;
                static String brand = "Nike";
            
                public Shoe(int size, String colour) {
                    super();
                    this.size = size;
                    this.colour = colour;
                }
            
                void displayShoe() {
                    System.out.printf("%-2d %-8s %s %n",size,colour, brand);
                }
            
                public static void main(String args[]) {
                    Shoe s1 = new Shoe(7, "Blue");
                    Shoe s2 = new Shoe(8, "White");
            
                    System.out.println("=================");
                    s1.displayShoe();
                    s2.displayShoe();
                    System.out.println("=================");
                }
            }
            

            【讨论】:

              【解决方案10】:

              如果您希望在类首次使用之前初始化指定的类静态类型,则静态初始化块很有用。后续使用不会调用任何静态初始化块。它与初始化实例成员的实例初始化器正好相反。

              【讨论】:

                【解决方案11】:

                我们使用构造函数有条件地初始化我们的实例变量。

                如果你想有条件地初始化类/静态变量,并且想在不创建对象的情况下进行(构造函数只能在创建对象时调用),那么你需要静态块。

                static Scanner input = new Scanner(System.in);
                static int widht;
                static int height;
                
                static
                {
                    widht = input.nextInt();
                    input.nextLine();
                    height = input.nextInt();
                    input.close();
                
                    if ((widht < 0) || (height < 0))
                    {
                        System.out.println("java.lang.Exception: Width and height must be positive");
                    }
                    else
                    {
                        System.out.println("widht * height = " + widht * height);
                    }
                }
                

                【讨论】:

                • 在静态初始化程序中读取标准输入是一个非常糟糕的想法。而System.out.println("B * H"); 非常没用。答案本身非常模糊。 OP 没有提到构造函数或实例变量。
                • 这只是一个例子,展示了什么是静态初始化器以及它是如何使用的。 OP 没有要求构造函数或实例变量,但为了教他静态初始化程序与构造函数的区别,他需要知道这一点。否则他会说“我为什么不直接使用构造函数来初始化我的静态变量?”
                【解决方案12】:

                您可以在 static{} 中使用 try/catch 块,如下所示:

                MyCode{
                
                    static Scanner input = new Scanner(System.in);
                    static boolean flag = true;
                    static int B = input.nextInt();
                    static int H = input.nextInt();
                
                    static{
                        try{
                            if(B <= 0 || H <= 0){
                                flag = false;
                                throw new Exception("Breadth and height must be positive");
                            }
                        }catch(Exception e){
                            System.out.println(e);
                        }
                
                    }
                }
                

                PS:引用自this

                【讨论】:

                  【解决方案13】:

                  当您想在类加载时评估任何特定表达式时,您可以使用静态块,但请记住:

                  您必须在静态块中处理异常,这意味着您不能从静态块中抛出异常。

                  【讨论】:

                    猜你喜欢
                    • 2012-03-10
                    • 2012-02-01
                    • 2011-03-30
                    • 1970-01-01
                    • 2016-05-30
                    相关资源
                    最近更新 更多