public class BedAndBreakfast { // initialize to 10 public static int capacity = 10; // initialize to false private boolean full = false;}
:pencil2: As you have seen, you can often provide an initial value for a field in its declaration.
正如上面你所看到的,你可以在属性声明的时候就给它供应一个 初始值 (Initial Value)
:pencil2: This works well when the initialization value is available and the initialization can be put on one line.
当初始值可用或比较大略,以至于可以一行书写的时候,这样写是很好的。
:pencil2: However, this form of initialization has limitations because of its simplicity.
然而,由于其 大略性 (simplicity),这种形式的初始化也有其 局限性 (limitation)
:pencil2: If initialization requires some logic (for example:error handling or a for loop to fill a complex array), simple assignment is inadequate.
如果初始化须要一些逻辑处理(如:缺点处理,或用 for 循环为繁芜数组填充数据),那么大略的赋值是完备不足的。
:pencil2: Instance variables can be initialized in constructors, where error handling or other logic can be used. To provide the same capability for class variables , the Java programming language includes static initialization blocks .
实例变量 (Instance Variable)可以在布局方法中初始化属性,缺点处理或其他逻辑也可以在布局方法中完成。为了给 类变量 (Class Variable)供应相同的功能,Java 措辞供应了 静态初始化块 。
public class BedAndBreakfast { public static int capacity = 10; / whatever code is needed for initialization goes here 初始化所需的任何代码都放在这里 / static { capacity = 12; } public static void main(String[] args) { // 12 System.out.println(capacity); }}
:pencil2: A static initialization block is a normal block of code enclosed in braces{ }, and preceded by the static keyword.
如上所示: 静态初始化块 便是一个普通的代码块,它包含在花括号{ }中,花括号的前方有一个 static 关键字。
:pencil2: A class can have any number of static initialization blocks , and they can appear anywhere in the class body. The runtime system guarantees that static initialization blocks are called in the order that they appear in the source code .
一个类中可以有任意数量的 静态初始化块 ,静态初始化块可以涌如今类体的任意位置。运行时系统会按照静态初始化块在源代码中涌现的顺序挨个调用它们。
:pencil2: There is an alternative to static blocks — you can write a private static method.
你也可以编写一个 私有的静态方法 来进行 类变量的初始化 ,以此来替代静态初始化块【如下所示】
public class Whatever { public static Double money = initClassVar(); / 该静态方法用于初始化某个类变量 / private static Double initClassVar() { // initialization code goes here // 初始化代码放这儿 return Math.pow(10, 5); } public static void main(String[] args) { // 100000.0 System.out.println(money); }}
:pencil2: The advantage of private static methods is that they can be reused later if you need to reinitialize the class variable .
私有静态方法 初始化类变量的好处是:如果你须要重新为类变量进行初始化的时候,私有静态方法可以被重复利用
二、初始化实例成员(官方教程):pencil2: Normally, you would put code to initialize an instance variable in a constructor. There are two alternatives to using a constructor to initialize instance variables : initializer blocks and final methods.
为了初始化一个 实例变量 ,常日情形下,你可能会把代码放在布局方法中。有两种可选的办法可用于在布局方法中初始化实例变量:初始化块和 final 方法。
:pencil2: Initializer blocks for instance variables look just like static initializer blocks , but without the static keyword.
实例变量的初始化块看起来和静态初始化块有点一样,只是没有 static 关键字罢了。
public class Whatever { public int money = 666; { // 初始化实例变量的代码都放这儿 money = 9999; } public static void main(String[] args) { // 9999 System.out.println(new Whatever().money); }}
:pencil2: The Java compiler copies initializer blocks into every constructor.
Java 编译器会把 初始化块 (把稳:是初始化块,不是静态初始化块)拷贝到每个布局方法中。
:pencil2: Therefore, this approach can be used to share a block of code between multiple constructors.
因此, 初始化块 可以实现在多个布局方法之间共用代码块的功能。
:pencil2: A final method cannot be overridden (重写) in a subclass. Here is an example of using a final method for initializing an instance variable .
被 final 关键字润色的方法不能被子类重写。下面是一个用 final 方法 初始化实例变量的例子。
public class Whatever { private double money = initInstanceVar(); / protected: 担保子类也可以重复利用该方法 / protected final double initInstanceVar() { // initialization code goes here // 初始化代码放这儿 return Math.pow(Math.PI 10, 3); } public static void main(String[] args) { // 31006.276680299816 System.out.println(new Whatever().money); // 31006.276680299816 System.out.println(new CuteSon().getSalary()); }}class CuteSon extends Whatever { private double salary = initInstanceVar(); public double getSalary() { return salary; }}
:pencil2: This is especially useful if subclasses might want to reuse the initialization method .
该办法非常有用,尤其是人类须要重复利用初始化方法的时候。
三、初始化块和静态初始化块:black_nib: 初始化块:Initializer block
:black_nib: 静态初始化块:Static Initialization Block
:pencil2: 编译器会按照 初始化块 在源代码中的涌现顺序把初始化块拷贝到 每个 布局方法的方法体的最前面( 先实行 初始化块 中的代码,后实行布局器中的代码 )
:pencil2: 在源代码中越靠前的 初始化块 放在布局器方法体的越前面
public class Whatever { private double money = 9; private int age = 9; { money = 1; age = 3; } public static void main(String[] args) { // 6.0 System.out.println(new Whatever().money); // 666 System.out.println(new Whatever().age); } { money = 0; } private Whatever() { age = 666; } { money = 7; age = 2; } { money = 6; }}
:pencil2: 初始化块会被拷贝到每个布局器方法体的最前面,以是 每当创建一个工具,初始化块中的代码就会被实行一次
public class Whatever { // 记录 Whatever 工具的个数 public static int instanceNum; { System.out.println("第" + ++instanceNum + "次调用初始化块"); } public static void main(String[] args) { Whatever whatever1 = new Whatever(); Whatever whatever2 = new Whatever(); Whatever whatever3 = new Whatever(); Whatever whatever4 = new Whatever(); Whatever whatever5 = new Whatever(); / 第1次调用初始化块 第2次调用初始化块 第3次调用初始化块 第4次调用初始化块 第5次调用初始化块 / }}
public class Whatever { private static double money = 9; private int age = 9; { System.out.println("2.Initializer 初始化块"); money = 2; age = 888; } private Whatever() { System.out.println("3.Constructor 布局器"); } static { System.out.println("1.Static 静态初始化块"); money = 1; // ERROR: Non-static field cannot be referenced from a static context // 非静态变量不能再在静态(static)环境中被引用 // age = 7; } public static void main(String[] args) { Whatever whatever = new Whatever(); // 6.0 System.out.println("4." + whatever.money); // 666 System.out.println("5." + whatever.age); } / 1.Static 静态初始化块 2.Initializer 初始化块 3.Constructor 布局器 4.2.0 5.888 /}
从上面的代码中可以知道:
:pencil2: 1. 静态初始化块只能对类变量做初始化操作;初始化块可以对实例变量和类变量做初始化操作
:pencil2: 2. 静态初始化块、初始化块、布局器三者的实行顺序是:① 静态初始化块;② 初始化块;③ 布局器【静态初始化块与静态初始化块之间按照在源代码中的涌现顺序依次实行,初始化块和初始化块之间按照在源代码中的涌现顺序依次实行】
:pencil2: 当 类被加载 的时候,静态初始化块会被 调用 。 静态初始化块中的代码只会被实行一次 (由于类加载只有一次)
:question: 类什么时候被加载 :question:
:black_nib: ① 创建本类工具的时候(new)
:black_nib: ② 利用本类的静态成员(静态变量、静态方法)的时候
:black_nib: ③ 创建子类工具或利用子类的静态成员
:question:【调用】这个词一样平常用在方法上,但官方教程中对初始化块也利用了【调用】这个词:
类什么时候会被加载?
public class Whatever { private static final double PI = 3.14; private static String poem = "人生自古谁无去世?"; private int num; static { System.out.println("static{}: 静态初始化块在被类被加载的时候被调用一次(仅一次)"); } { System.out.println("{}: 创建第 " + ++num + " 个 Whatever 工具"); } public static void staticMethod() { System.out.println("call staticMethod()"); } public static void main(String[] args) { / static{}: 静态初始化块在被类被加载的时候被调用一次(仅一次) call staticMethod() / // staticMethod(); / static{}: 静态初始化块在被类被加载的时候被调用一次(仅一次) 3.14 / // System.out.println(PI); / static{}: 静态初始化块在被类被加载的时候被调用一次(仅一次) 人生自古谁无去世? / // System.out.println(poem); / static{}: 静态初始化块在被类被加载的时候被调用一次(仅一次) {}: 创建第 1 个 Whatever 工具 HandsomeSon() / // new HandsomeSon(); / static{}: 静态初始化块在被类被加载的时候被调用一次(仅一次) 大头儿子 / // System.out.println(HandsomeSon.sonName); / static{}: 静态初始化块在被类被加载的时候被调用一次(仅一次) 调用了子类的静态方法 / // System.out.println(HandsomeSon.sonStaticMethod()); / static{}: 静态初始化块在被类被加载的时候被调用一次(仅一次) {}: 创建第 1 个 Whatever 工具 / new Whatever(); }}class HandsomeSon extends Whatever { public static String sonName = "大头儿子"; public HandsomeSon() { System.out.println("HandsomeSon()"); } public static String sonStaticMethod() { return "调用了子类的静态方法"; }}
静态初始化块中的代码只会在类被加载的时候被实行一次(静态初始化块只会在类被加载的时候被调用一次)
public class Whatever { private static final double PI = 3.14; private static String poem = "人生自古谁无去世?"; private int num; static { System.out.println("static{}: 静态初始化块在被类被加载的时候被调用一次(仅一次)"); } { System.out.println("{}: 创建第 " + ++num + " 个 Whatever 工具"); } public static void staticMethod() { System.out.println("call staticMethod()"); } public static void main(String[] args) { / 下面的代码都会致使类被加载 静态代码块只会在类被加载的时候被实行一次 / staticMethod(); System.out.println(PI); System.out.println(poem); new HandsomeSon(); System.out.println(HandsomeSon.sonName); System.out.println(HandsomeSon.sonStaticMethod()); new Whatever(); } / static{}: 静态初始化块在被类被加载的时候被调用一次(仅一次) call staticMethod() 3.14 人生自古谁无去世? {}: 创建第 1 个 Whatever 工具 HandsomeSon() 大头儿子 调用了子类的静态方法 {}: 创建第 1 个 Whatever 工具 /}class HandsomeSon extends Whatever { public static String sonName = "大头儿子"; public HandsomeSon() { System.out.println("HandsomeSon()"); } public static String sonStaticMethod() { return "调用了子类的静态方法"; }}
:pencil2: 初始化块会被编译器拷贝到每一个布局方法的最前部,随着布局方法被调用而实行【只是利用类的静态成员并不会导致初始化块中的代码被实行】
四、创建工具调用顺序(1) 先容本节标题:question: 创建一个类的工具的时候,类中的静态初始化块、 静态变量初始化 、初始化块、 实例变量初始化 、布局器的调用顺序…
① 静态变量初始化 :在本篇文章第一节先容过,可以编写一个 私有的静态方法 来进行类变量的初始化,以此来替代静态初始化块
② 实例变量初始化 :在本篇文章第二节先容过,可以利用被 final 润色的实例方法实现实例变量的初始化(如下图)
(2) 结论和测试
:pencil2: ① 调用静态初始化块和 静态属性初始化 (当有多个静态初始化块和多个静态变量初始化的时候,按照它们在源代码中的顺序依次调用)
public class Whatever { static { System.out.println("静态初始化块 Three"); } // getCount() 是静态属性初始化 private static double count = getCount(); private static double getCount() { System.out.println("静态属性初始化_getCount()"); return 1; } static { System.out.println("静态初始化块 One"); } public static void main(String[] args) { System.out.println(count); // 3.14159 / 静态初始化块 Three 静态属性初始化_getCount() 静态初始化块 One 静态初始化块 Two 3.14159 / } static { System.out.println("静态初始化块 Two"); count = 3.14159; }}
public class Whatever { private static double getCount1() { System.out.println("静态属性初始化_getCount()1"); return 12580; // 【幺儿我帮你】 } static { System.out.println("静态初始化块 Three"); } static { System.out.println("静态初始化块 One"); } public static void main(String[] args) { System.out.println(count); // 209420.0 / 静态初始化块 Three 静态初始化块 One 静态属性初始化_getCount()1 静态初始化块 Two 静态属性初始化_getCount2() 209420.0 / } private static double getCount2() { System.out.println("静态属性初始化_getCount2()"); return 209420; // 【爱你便是爱你】 } // getCount() 是静态属性初始化 private static double count = getCount1(); static { System.out.println("静态初始化块 Two"); count = 3.14159; // 【山巅一寺一壶酒】 // 静态属性初始化 count = getCount2(); }}
:pencil2: It is not necessary to declare fields at the beginning of the class definition, although this is the most common practice. It is only necessary that they be declared and initialized before they are used .
在类定义的开头声明字段并不是必须的,它只是一种常见的做法而已。必须的是:在利用它们之前,必须声明和初始化。
:pencil2: ② 调用初始化块和实例变量初始化(当有多个 初始化块 和多个 实例变量初始化 的时候,按照它们在源代码中的顺序依次调用)
public class Whatever { public int getNum1() { System.out.println("getNum1()"); return 6; } private int num = getNum1(); // getNum1 初始化块1 初始化块2 getNum2 getNum1 { System.out.println("初始化块1"); num = 2; } public static void main(String[] args) { // num = 528 System.out.println("num = " + new Whatever().num); / getNum1() 初始化块1 初始化块2 getNum2() getNum1() num = 528 / } { System.out.println("初始化块2"); num = getNum2() + getNum1() + num; // 520 + 6 + 2 } public int getNum2() { System.out.println("getNum2()"); return 520; }}
:pencil2: ③ 调用布局方法
思考下面代码的打印结果:
public class Whatever { private int num; private int getNum1() { System.out.println("getNum1()"); return 1; } static { System.out.println("静态初始化块1"); brand = "小米"; } private static String brand = getBrand(); static { System.out.println("静态初始化块2"); brand = "三星"; } public Whatever() { System.out.println("public Whatever()"); } { num = 2; } public static void main(String[] args) { / 静态初始化块1 getBrand() 静态初始化块2 public Whatever() 8 三星 / System.out.println(new Whatever().num); System.out.println(Whatever.brand); } private int getNum2() { return (int) Math.pow(num, 3); } private static String getBrand() { System.out.println("getBrand()"); return "华为"; } { num = getNum2(); }}
五、有继续关系的时候的调用顺序
:pencil2: 布局器方法体的最顶部隐含了 super() 和初始化块
:pencil2: 静态初始化块和静态属性初始化在类加载时实行完毕
class Apple { public Apple() { // (1) 隐含 super(); // (2) 隐含初始化块 System.out.println("public Apple() { }"); }}
思考下面代码的实行结果:
public class Whatever { public static int n = getN(); // n = 27 public Whatever() { // 1.super() // 2.初始化块 n = getN() + 3; // n = 30 System.out.println("4.Whatever_n_" + n); System.out.println("5.public Whatever()"); } private static int getN() { return (int) Math.pow(3, 3); } { System.out.println("3.愿万事顺心"); cnt = 56; } static { System.out.println("1.n = getN() + n"); n = getN() + n; // n = 27 + 27 = 54 } private int cnt = 2; public static void main(String[] args) { new Apple(); }}class Apple extends Whatever { // num = 5 private static int num = 5; { System.out.println("6.Apple 初始化块"); } private int count; public static int getNum(int n) { return n; } public Apple() { // 1.super() // 2.初始化块 System.out.println("7." + (getCount() + num + count + n)); // 31 + 5 + 2 + 30 = 68 System.out.println("8.public Apple()"); } { count = 1; } static { System.out.println("2.getNum(6 + num)"); getNum(6 + num); } public int getCount() { return n + count++; }}
磻 自己总结的过程:
① 父静 态初始化块(静态属性初始化)
② 子静 态初始化块(静态属性初始化)
③ 父初 始化块(实例属性初始化)
④ 父构 造器
⑤ 子初 始化块(实例属性初始化)
⑥ 子构 造器
父静 子静 父初 父构 子初 子构
父静、子静、父初、父构、子初、子构 【谐音】 父进、子进、父出、父go、子出、子go
看着上面的图片,并想象一个场景:
:black_nib: 父 亲先 进 入电梯 ( 父 类的 静 态初始化块和 静 态属性初始化)
:black_nib: 孩 子 后 进 入电梯 ( 子 类的 静 态初始化块和 静 态属性初始化)
:black_nib: 父 亲先 出 电梯 ( 父 类的 初 始化块和实例属性 初 始化)
:black_nib: 父 亲 go 了 ( 父 类的 构 造器)
:black_nib: 孩 子 后 出 电梯 ( 子 类的 初 始化块和实例属性 初 始化)
:black_nib:孩 子 也 go 了 ( 子 类的 构 造器)
六、Exercise(1) 第一题思考下面代码的打印结果:
public class Whatever { public static void main(String[] args) { / 1.getVal1() 2.A 静态初始化块1 3.getVal3() 4.B 的静态初始化块1 5.A 初始化块1 6.getVal2() 7.A 布局器 8.getVal4() 9.B 的初始化块1 10.B 的布局器 / new B(); }}/ 父类 /class A { private static int n1 = getVal1(); static { System.out.println("2.A 静态初始化块1"); } { System.out.println("5.A 初始化块1"); } public int n3 = getVal2(); public static int getVal1() { System.out.println("1.getVal1()"); return 10; } public static int getVal2() { System.out.println("6.getVal2()"); return 10; } public A() { System.out.println("7.A 布局器"); }}/ 子类 /class B extends A { private static int n3 = getVal3(); static { System.out.println("4.B 的静态初始化块1"); } public int n5 = getVal4(); { System.out.println("9.B 的初始化块1"); } public static int getVal3() { System.out.println("3.getVal3()"); return 10; } public static int getVal4() { System.out.println("8.getVal4()"); return 10; } public B() { // super(); // 初始化块 System.out.println("10.B 的布局器"); }}
(2) 第二题
看下面的代码,思考打印结果;
public class Whatever { public static void main(String[] args) { // Static Initialization Block // total: 8989 System.out.println("total: " + Money.total); // total: 8989 System.out.println("total: " + Money.total); }}class Money { public static int total; static { total = 8989; System.out.println("Static Initialization Block"); }}
利用类中的静态成员会导致类加载,进而静态初始化块中的代码会实行
静态初始化块中的代码会在类加载的时候实行 一次
(3) 第三题public class Whatever { public static void main(String[] args) { // 【output:】 // Money(String): StaticMoney // Static Initialization Block // Money(String): Money // House() new House(); }}class House { Money money1 = new Money("Money"); static Money money2 = new Money("StaticMoney"); static { System.out.println("Static Initialization Block"); if (money2 == null) System.out.println("money is null"); } House() { // super() // 初始化块 System.out.println("House()"); }}class Money { Money(String s) { System.out.println("Money(String): " + s); } Money() { System.out.println("Money()"); }}
再见!
如有缺点,请不吝见教
原文链接:
https://blog.csdn.net/m0_54189068/article/details/126913489?utm_source=tuicool&utm_medium=referral