Java中的静态初始化器
我的问题是关于static关键字的一个特定用法。 可以使用static
关键字来覆盖不属于任何函数的类中的代码块。 例如下面的代码编译:
public class Test { private static final int a; static { a = 5; doSomething(a); } private static int doSomething(int x) { return (x+5); } }
如果你删除static
关键字,它会抱怨,因为variablesa
是final
。 但是可以删除final
和static
关键字并进行编译。
这两个方面让我感到困惑。 我应该如何有一个不属于任何方法的代码部分? 怎么可能调用它? 一般来说,这种用法的目的是什么? 或者更好,我在哪里可以find有关这方面的文档?
带有静态修饰符的代码块表示一个类初始化程序; 没有静态修饰符的代码块是一个实例初始值设定项。
当类被加载时(实际上,当它被parsing,但是这是一个技术性),类初始化符按照它们被定义的顺序(从上到下,就像简单的variables初始化符一样)被执行。
实例初始值设定项是在实例化类的时候执行的,紧接在构造函数代码执行之前,在超级构造函数调用之后立即执行。
如果从int a
移除static
,它将成为一个实例variables,您无法通过静态初始化程序块访问该实例variables。 这将无法编译错误“非静态variables不能从静态上下文中引用”。
如果你也从初始化块中移除static
,那么它将成为一个实例初始化器,所以int a
在构造时被初始化。
UFF! 什么是静态初始化器?
静态初始化器是java类中的static {}
代码块,在调用构造函数或main方法之前只运行一次。
好! 告诉我更多…
- 是任何java类中的一个代码块
static { ... }
。 并在调用类时由虚拟机执行。 - 没有
return
语句支持。 - 不支持参数。
- 没有
this
或super
的支持。
嗯我可以在哪里使用它?
可以在任何地方使用你感觉OK :)这很简单。 但是我在数据库连接,API初始化,日志logging等方面大部分时间都是使用它。
不要只吠! 哪里是例子?
package com.example.learnjava; import java.util.ArrayList; public class Fruit { static { System.out.println("Inside Static Initializer."); // fruits array ArrayList<String> fruits = new ArrayList<>(); fruits.add("Apple"); fruits.add("Orange"); fruits.add("Pear"); // print fruits for (String fruit : fruits) { System.out.println(fruit); } System.out.println("End Static Initializer.\n"); } public static void main(String[] args) { System.out.println("Inside Main Method."); } }
输出???
内部静态初始化器。
苹果
橙子
梨
结束静态初始化器。
内部主要方法。
希望这可以帮助!
static
块是一个“静态初始化器”。
它在加载类时自动调用,并且没有其他方法可以调用它(除了可能通过Reflection?)。
我个人只有在编写JNI代码时才使用它:
class JNIGlue { static { System.loadLibrary("foo"); } }
这是直接从http://www.programcreek.com/2011/10/java-class-instance-initializers/
1.执行顺序
看看下面的课,你知道哪一个先被执行?
public class Foo { //instance variable initializer String s = "abc"; //constructor public Foo() { System.out.println("constructor called"); } //static initializer static { System.out.println("static initializer called"); } //instance initializer { System.out.println("instance initializer called"); } public static void main(String[] args) { new Foo(); new Foo(); } }
输出:
调用静态初始化器
实例初始化器调用
构造函数调用
实例初始化器调用
构造函数调用
2. Java实例初始化程序如何工作?
上面的实例初始化器包含一个println语句。 为了理解它是如何工作的,我们可以把它当作一个variables赋值语句,例如b = 0
。 这可以使其更加明显的理解。
代替
int b = 0
,你可以写
int b; b = 0;
因此,实例初始化器和实例variables初始化器几乎是一样的。
3.实例初始化程序何时有用?
实例初始值设定项的使用很less见,但在以下情况下,它仍然是实例variables初始值设定项的有用替代项:
(1)初始化代码必须处理exception(2)执行无法用实例variables初始值设定项表示的计算。
当然,这样的代码可以写在构造函数中。 但是,如果一个类有多个构造函数,则必须在每个构造函数中重复该代码。
使用实例初始化程序,只需编写一次代码即可,无论使用何种构造函数创build对象,都会执行该代码。 (我想这只是一个概念,并不经常使用。)
另一个实例初始化器很有用的例子是匿名内部类,它根本不能声明任何构造函数。 (这是一个放置日志function的好地方吗?)
感谢Derhein。
另请注意,实现接口[1]的匿名类没有构造函数。 因此实例初始化器需要在构造时执行任何types的expression式。
“final”保证在对象初始化代码结束之前variables必须被初始化。 类似的,“static final”保证了一个variables会被类初始化代码的末尾初始化。 从初始化代码中省略“静态”将其转换为对象初始化代码; 因此你的variables不再满足其保证。
您不会将代码写入需要在程序中的任何位置调用的静态块。 如果代码的目的是被调用,那么你必须把它放在一个方法中。
你可以编写静态初始化块来初始化静态variables,当这个类被加载时,这个代码可能更复杂。
一个静态初始化块看起来像一个没有名字,没有参数,也没有返回types的方法。 既然你永远不叫它,它不需要一个名字。 被调用的唯一时间是虚拟机加载类时。
静态代码块可用于实例化或初始化类variables(与对象variables相反)。 因此,声明“a”静态意味着只有一个静态方法是所有Test对象共享的,并且静态代码块仅在初次加载Test类时初始化“a”,无论创build了多less个Test对象。
当开发人员使用初始化程序块时,Java编译器将初始化程序复制到当前类的每个构造函数中。
例:
下面的代码:
class MyClass { private int myField = 3; { myField = myField + 2; //myField is worth 5 for all instance } public MyClass() { myField = myField * 4; //myField is worth 20 for all instance initialized with this construtor } public MyClass(int _myParam) { if (_myParam > 0) { myField = myField * 4; //myField is worth 20 for all instance initialized with this construtor //if _myParam is greater than 0 } else { myField = myField + 5; //myField is worth 10 for all instance initialized with this construtor //if _myParam is lower than 0 or if _myParam is worth 0 } } public void setMyField(int _myField) { myField = _myField; } public int getMyField() { return myField; } } public class MainClass{ public static void main(String[] args) { MyClass myFirstInstance_ = new MyClass(); System.out.println(myFirstInstance_.getMyField());//20 MyClass mySecondInstance_ = new MyClass(1); System.out.println(mySecondInstance_.getMyField());//20 MyClass myThirdInstance_ = new MyClass(-1); System.out.println(myThirdInstance_.getMyField());//10 } }
相当于:
class MyClass { private int myField = 3; public MyClass() { myField = myField + 2; myField = myField * 4; //myField is worth 20 for all instance initialized with this construtor } public MyClass(int _myParam) { myField = myField + 2; if (_myParam > 0) { myField = myField * 4; //myField is worth 20 for all instance initialized with this construtor //if _myParam is greater than 0 } else { myField = myField + 5; //myField is worth 10 for all instance initialized with this construtor //if _myParam is lower than 0 or if _myParam is worth 0 } } public void setMyField(int _myField) { myField = _myField; } public int getMyField() { return myField; } } public class MainClass{ public static void main(String[] args) { MyClass myFirstInstance_ = new MyClass(); System.out.println(myFirstInstance_.getMyField());//20 MyClass mySecondInstance_ = new MyClass(1); System.out.println(mySecondInstance_.getMyField());//20 MyClass myThirdInstance_ = new MyClass(-1); System.out.println(myThirdInstance_.getMyField());//10 } }
我希望我的例子能被开发者理解。
静态块的定义来自: java中的静态关键字
静态块是一个java类内部的一个语句块,当一个类首次被加载到JVM中时被执行,例如在代码中第一次引用这个类的时候它被初始化。
通常情况下,静态块用于初始化静态类variables,它们与构造函数不同,静态块在JVM加载和初始化时只执行一次,而每次创build类的新实例时调用构造函数。
PS:你不能在静态块中使用实例variables或调用实例方法。
使用静态块的一个典型例子是当你支持通过它的值检索一个Enum实例时,为了做到这一点,你需要定义一个HashMap作为一个静态variables,将每个值映射到相应的Enum实例,映射被初始化并填充到一个静态在应用程序中使用Enum之前阻止。
public enum ErrorCodes { BUSINESS_ERROR(100), SERVER_ERROR(500), NETWORK_ERROR(1000); private int errorCode; // This field maps each error code numeric value to a corresponding Enum instance. private static Map<Integer, ErrorCodes> errorCodeByErrorNumber = new HashMap<Integer, ErrorCodes>(); static { for (ErrorCodes errorCode : ErrorCodes.values()) { errorCodeByErrorNumber.put(errorCode.getErrorCode(), errorCode); } } private ErrorCodes(int errorCode) { this.errorCode = errorCode; } public int getErrorCode() { return errorCode; } public static ErrorCodes getErrorCodeByNumber(Integer dayNumber) { return errorCodeByErrorNumber.get(dayNumber); } }