2010-10-21 100 views
69

我们可以将代码放入构造函数或方法或初始化块中。初始化块的用途是什么?每个Java程序都必须拥有它吗?什么是初始化块?

+0

你的意思是一个init()函数在constructoe之后调用?或者在任何方法之外声明的sttatic块? – atk 2010-10-21 12:31:04

+0

我看不清楚你的问题,也许标题有点误导 – 2010-10-21 12:47:00

+0

可能重复[在Java中使用初始化与构造函数](http://stackoverflow.com/questions/804589/use-of-initializers -vs-constructors-in-java) – 2010-10-21 13:18:31

回答

135

首先,有两种类型的initialization blocks

  • 实例初始化块,并
  • 静态初始化块

此代码应说明它们的用途以及以何种顺序它们被执行:

public class Test { 

    static int staticVariable; 
    int nonStaticVariable;   

    // Static initialization block: 
    // Runs once (when the class is initialized) 
    static { 
     System.out.println("Static initalization."); 
     staticVariable = 5; 
    } 

    // Instance initialization block: 
    // Runs each time you instantiate an object 
    { 
     System.out.println("Instance initialization."); 
     nonStaticVariable = 7; 
    } 

    public Test() { 
     System.out.println("Constructor."); 
    } 

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

打印:如果你想有一些代码

Static initalization. 
Instance initialization. 
Constructor. 
Instance initialization. 
Constructor. 

实例itialization块是有用的无论使用哪个构造函数或者是否想为匿名类执行一些实例初始化,都可以运行。

+6

目前看起来它们是按代码中的外观顺序执行的。这个例子可以通过代码顺序与实际执行顺序不同的方式得到改进。另外:可以有几个初始化块,然后按照外观顺序执行它们(但仍然在构造函数之前)。 – 2014-12-02 11:39:19

+0

@Prier所以你可以在有多个构造函数时使用公共代码,而不必使用init()方法(更新类可能会忘记调用它) – pablisco 2016-08-19 10:28:48

0

无论何时初始化类并在调用构造函数之前都会执行初始化块。它们通常放置在大括号内的构造函数之上。将它们包含在你的课堂中是没有必要的。

它们通常用于初始化引用变量。这page给出了一个很好的解释

+1

根据@Biman,来自超类的构造函数在init块之前运行**。 – 2014-06-07 20:55:45

0

这个问题并不完全清楚,但下面简要描述了您可以在对象中初始化数据的方式。假设您有一个包含对象列表的类A.

1)把初始值的字段声明:

class A { 
    private List<Object> data = new ArrayList<Object>(); 
} 

2)在构造函数中分配初始值:

class A { 
    private List<Object> data; 
    public A() { 
     data = new ArrayList<Object>(); 
    } 
} 

这些都假设您不想通过“数据”作为构造函数的参数。

如果将重载的构造函数与上面的内部数据混合使用,事情会变得有点棘手。考虑:

class B { 
    private List<Object> data; 
    private String name; 
    private String userFriendlyName; 

    public B() { 
     data = new ArrayList<Object>(); 
     name = "Default name"; 
     userFriendlyName = "Default user friendly name"; 
    } 

    public B(String name) { 
     data = new ArrayList<Object>(); 
     this.name = name; 
     userFriendlyName = name; 
    } 

    public B(String name, String userFriendlyName) { 
     data = new ArrayList<Object>(); 
     this.name = name; 
     this.userFriendlyName = userFriendlyName; 
    } 
} 

请注意,有很多重复的代码。您可以通过构造函数调用对方解决这个问题,或者你可以有每个构造函数调用私有初始化方法:

class B { 
    private List<Object> data; 
    private String name; 
    private String userFriendlyName; 

    public B() { 
     this("Default name", "Default user friendly name"); 
    } 

    public B(String name) { 
     this(name, name); 
    } 

    public B(String name, String userFriendlyName) { 
     data = new ArrayList<Object>(); 
     this.name = name; 
     this.userFriendlyName = userFriendlyName; 
    } 
} 

class B { 
    private List<Object> data; 
    private String name; 
    private String userFriendlyName; 

    public B() { 
     init("Default name", "Default user friendly name"); 
    } 

    public B(String name) { 
     init(name, name); 
    } 

    public B(String name, String userFriendlyName) { 
     init(name, userFriendlyName); 
    } 

    private void init(String _name, String _userFriendlyName) { 
     data = new ArrayList<Object>(); 
     this.name = name; 
     this.userFriendlyName = userFriendlyName; 
    } 
} 

两者是(或多或少)等同。

我希望给你一些关于如何初始化对象中的数据的提示。我不会谈论静态初始化块,因为目前这可能有点进步。

编辑:我把你的问题解释为“我该如何初始化我的实例变量”,而不是“初始化块如何工作”,因为初始化块是一个相对先进的概念,并且从看起来像你的问题的语气'正在问起更简单的概念。我可能是错的。

+1

即使您将问题解释为“如何初始化我的实例变量?”,您的答案并未提及可以使用初始化工具完成。 – 2014-12-02 11:33:16

73

想添加到@ aioobe的回答

的执行顺序:

  1. 超类的静态初始化块

  2. 实例的静态初始化块超级类的初始化块

  3. 构造超类

  4. 类的实例初始化块

  5. 构造方法的类的。

了一些额外的点要牢记(点1 @ aioobe的回答重申):

  1. 在静态初始化块中的代码将在类加载时被执行(是的,这意味着每个类的负载只有一次),在构造类的任何实例之前以及在调用任何静态方法之前。

  2. 实例初始化块实际上被Java编译器复制到类所具有的每个构造函数中。所以每次在实例初始化块中的代码在构造函数中的代码之前执行刚好

+1

所以如果我创建了10个SomeClass实例,步骤1和2只能执行一次,直到导致类被卸载(只有我能想到的是重新启动程序,但如果有其他事情可能会导致这种情况,我想知道)。 – 2017-04-20 01:24:40

+3

@GlenPierce在这里你去:https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.7 – 2017-04-20 18:11:18

3

这里被认可为答案的示例代码是正确的,但我不同意它。它不显示正在发生的事情,我要告诉你一个很好的例子,了解JVM的实际工作原理:

package test; 

    class A { 
     A() { 
      print(); 
     } 

     void print() { 
      System.out.println("A"); 
     } 
    } 

    class B extends A { 
     static int staticVariable2 = 123456; 
     static int staticVariable; 

     static 
     { 
      System.out.println(staticVariable2); 
      System.out.println("Static Initialization block"); 
      staticVariable = Math.round(3.5f); 
     } 

     int instanceVariable; 

     { 
      System.out.println("Initialization block"); 
      instanceVariable = Math.round(3.5f); 
      staticVariable = Math.round(3.5f); 
     } 

     B() { 
      System.out.println("Constructor"); 
     } 

     public static void main(String[] args) { 
      A a = new B(); 
      a.print(); 
      System.out.println("main"); 
     } 

     void print() { 
      System.out.println(instanceVariable); 
     } 

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

之前开始评论的源代码,我给你一个简短的说明一类静态变量:

首先是它们被称为类变量,它们属于类而不是该类的特定实例。该类的所有实例共享此静态(类)变量。每个变量都有一个默认值,取决于原始类型或引用类型。另一件事是,当你在类的某些成员(初始化块,构造函数,方法,属性)中重新分配静态变量,并且这样做时,你正在改变静态变量的值而不是特定实例,所以你正在改变它实例。为了总结静态部分,我会说一个类的静态变量不是在你首次实例化类时创建的,它们是在你定义你的类时创建的,它们存在于JVM中,而不需要任何实例。因此,从外部类(它们未定义的类)的静态成员的正确访问是通过使用点后面的类名称,然后使用要访问的静态成员(模板:<CLASS_NAME>.<STATIC_VARIABLE_NAME>)。

现在让我们来看看上面的代码:

入口点的主要方法 - 目前只有三行代码。我想参考当前批准的示例。根据它,打印“静态初始化块”后必须打印的第一件事是“初始化块”,这里是我的不同意见,非静态初始化块不在构造函数之前调用,它在构造函数的任何初始化之前调用在其中定义了初始化块的类。类的构造函数是创建对象(类的实例)时涉及的第一件事,然后当您输入构造函数时,第一部分调用的是隐式(默认)超级构造函数或显式超级构造函数或显式调用另一个重载构造函数(但在某些情况下,如果存在重载构造函数的链,则最后一个隐式或显式地调用超构造函数)。

有一个对象的多态创建,但在进入B类及其主要方法之前,JVM初始化所有类(静态)变量,然后通过静态初始化块(如果存在的话),然后进入类B并开始执行主要方法。它转到类B的构造函数,然后立即(隐式地)使用多态性调用类A的构造函数,在类A的构造函数体中调用的方法(重写方法)是在类B中定义的方法,在这种情况下在重新初始化之前使用名为instanceVariable的变量。在关闭B类的构造函数之后,线程返回到B类的构造函数,但在打印“构造函数”之前,它首先转到非静态初始化块。为了更好地理解,使用一些IDE进行调试,我更喜欢Eclipse。

+7

** TL; DR ** OP只要求对初始化块的解释,而不是对静态变量,构造函数或IDE偏好的基本原理的冗长解释。 – 2014-08-29 07:19:47

+0

有时,这些长期的解释可能会出乎意料地受欢迎。如果那些提出原始问题的人真的需要一个长期的解释来使他们的基础变得直白。或者如果人们自己阅读答案,就好像它是一个给定主题的博客。在这种情况下,我认为它既不是。 – user7610 2017-04-14 20:08:54

+0

@nenito,我认为你对接受答案的评论是有误导性的。我鼓励你把它改为更像“我可能感兴趣的更细致的解释”。接受的答案看起来确实是正确的,只是不如你的详细。 – 2017-04-20 01:32:01

5

好的答案通过aioobe 增加几个百分点

public class StaticTest extends parent { 
    static { 
     System.out.println("inside satic block"); 
    } 

    StaticTest() { 
     System.out.println("inside constructor of child"); 
    } 

    { 
     System.out.println("inside initialization block"); 
    } 

    public static void main(String[] args) { 
     new StaticTest(); 
     new StaticTest(); 
     System.out.println("inside main"); 
    } 
} 

class parent { 
    static { 
     System.out.println("inside parent Static block"); 
    } 
    { 
     System.out.println("inside parent initialisation block"); 
    } 

    parent() { 
     System.out.println("inside parent constructor"); 
    } 
} 

这给

inside parent Static block 
inside satic block 
inside parent initialisation block 
inside parent constructor 
inside initialization block 
inside constructor of child 
inside parent initialisation block 
inside parent constructor 
inside initialization block 
inside constructor of child 
inside main 

其想说明明显,但似乎更加清楚一点。

0
public class StaticInitializationBlock { 

    static int staticVariable; 
    int instanceVariable; 

    // Static Initialization Block 
    static { 
     System.out.println("Static block"); 
     staticVariable = 5; 

    } 

    // Instance Initialization Block 
    { 

     instanceVariable = 7; 
     System.out.println("Instance Block"); 
     System.out.println(staticVariable); 
     System.out.println(instanceVariable); 

     staticVariable = 10; 
    } 


    public StaticInitializationBlock() { 

     System.out.println("Constructor"); 
    } 

    public static void main(String[] args) { 
     new StaticInitializationBlock(); 
     new StaticInitializationBlock(); 
    } 

} 

输出:

Static block 
Instance Block 
5 
7 
Constructor 
Instance Block 
10 
7 
Constructor 
0

一个初始化块类中定义的,而不是作为方法的一部分。它为每个为类创建的对象执行。在下面的例子中,类Employee限定初始化代码块:

class Employee { 
{ 
System.out.println("Employee:initializer"); 
} 
} 
1
初始化器

块包含每当创建一个实例是始终执行的代码。它用于声明/初始化类的各种构造函数的公共部分。

初始化构造函数和初始化块的顺序无关紧要,初始化块始终在构造函数之前执行。

如果我们想为一个类的所有对象执行一次代码,该怎么办?

我们在Java中使用静态块。