靜態與匿名區塊執行順序
Prerequisites:
靜態屬性與靜態區塊的執行順序
靜態屬性與靜態區塊的執行順序根據程式碼的前後順序。
1
2
3
4
5
6
7
8
9
10
class StaticTest {
static int i = getValue();
static {
System.out.println("進到靜態區塊");
}
private static int getValue() {
System.out.println("static 屬性 初始化");
return 10;
}
}
1
2
3
4
5
public class Test2 {
public static void main(String[] args) {
System.out.println(StaticTest.i);
}
}
static 屬性 初始化
進到靜態區塊
10
靜態區塊使用還未宣告屬性
以下程式碼,靜態區塊先,裡面的i屬性可以不宣告就先使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class StaticTest {
static {
System.out.println("進到靜態區塊");
i = 100;
}
static int i = getValue();
private static int getValue() {
System.out.println("static 屬性 初始化");
return 10;
}
}
public class Test2 {
public static void main(String[] args) {
System.out.println(StaticTest.i);
}
}
進到靜態區塊
static 屬性 初始化
10
靜態屬性與成員屬性的執行順序
以下程式碼靜態屬性與靜態區塊一定先執行,因為是類別要先載入,靜態屬性與靜態區塊是屬於類別。
類別載入完畢,才會執行物件相關。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class StaticTest {
private int i = getValue();
static {
System.out.println("進到靜態區塊");
si = 100;
}
static int si = getSValue();
private static int getSValue() {
System.out.println("static 屬性 初始化");
return 10;
}
private static int getValue() {
System.out.println("成員 屬性 初始化");
return 100;
}
}
public class Test2 {
public static void main(String[] args) {
new StaticTest();
}
}
進到靜態區塊
static 屬性 初始化
成員 屬性 初始化
建構子與成員屬性執行順序
根據程式碼的前後順序,以下是成員屬性先,建構子後。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class StaticTest {
private int i = getValue();
StaticTest() {
System.out.println("建構子 初始化");
}
private static int getValue() {
System.out.println("成員 屬性 初始化");
return 100;
}
}
public class Test2 {
public static void main(String[] args) {
new StaticTest();
}
}
成員 屬性 初始化
建構子 初始化
匿名區塊與建構子執行順序
匿名區塊一定優先建構子。
1
2
3
4
5
6
7
8
9
10
11
12
13
class StaticTest {
StaticTest() {
System.out.println("建構子 初始化");
}
{
System.out.println("匿名區塊 初始化");
}
}
public class Test2 {
public static void main(String[] args) {
new StaticTest();
}
}
匿名區塊 初始化
建構子 初始化
匿名區塊與成員屬性執行順序
執行順序根據程式碼的前後順序。
成員屬性在前
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class StaticTest {
private int i = getValue();
StaticTest() {
System.out.println("建構子 初始化");
}
{
System.out.println("匿名區塊 初始化");
}
private static int getValue() {
System.out.println("成員 屬性 初始化");
return 100;
}
}
public class Test2 {
public static void main(String[] args) {
new StaticTest();
}
}
成員 屬性 初始化
匿名區塊 初始化
建構子 初始化
匿名區塊在前
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class StaticTest {
StaticTest() {
System.out.println("建構子 初始化");
}
{
System.out.println("匿名區塊 初始化");
}
private int i = getValue();
private static int getValue() {
System.out.println("成員 屬性 初始化");
return 100;
}
}
public class Test2 {
public static void main(String[] args) {
new StaticTest();
}
}
匿名區塊 初始化
成員 屬性 初始化
建構子 初始化
靜態與建構子初始化順序
請問以下程式碼執行順序?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class StaticTest {
StaticTest() {
System.out.println("建構子 初始化");
}
{
System.out.println("匿名區塊 初始化");
}
private int i = getI();
private int getI() {
System.out.println("成員屬性 初始化");
return 55;
}
static int count = getValue();
static {
System.out.println("靜態 區塊 初始化");
}
private static int getValue() {
System.out.println("靜態 屬性 初始化");
return 100;
}
}
public class Test2 {
public static void main(String[] args) {
new StaticTest();
}
}
靜態 屬性 初始化
靜態 區塊 初始化
匿名區塊 初始化
成員屬性 初始化
建構子 初始化
由執行結果可以發現,靜態一定先執行。
只使用靜態屬性,建構子會執行嗎?
以下程式碼執行結果是什麼?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Test2 {
public static void main(String[] args) {
System.out.println(StaticTest.count);
}
}
class StaticTest {
StaticTest() {
System.out.println("建構子 初始化");
}
{
System.out.println("匿名區塊 初始化");
}
private int i = getI();
private int getI() {
System.out.println("成員屬性 初始化");
return 55;
}
static int count = getValue();
static {
System.out.println("靜態 區塊 初始化");
}
private static int getValue() {
System.out.println("靜態 屬性 初始化");
return 100;
}
}
靜態 屬性 初始化
靜態 區塊 初始化
100
從結果可以發現只有靜態相關的會執行,而建構子與匿名區塊不會執行。
靜態屬性是建構子
以下程式碼,靜態屬性為本身類別的建構子,靜態屬性也是成員屬性,只是儲存靜態屬性的位置是同一個記憶體位址,記憶體位址是共享。
先前提過靜態屬性與靜態區塊的執行順序是根據程式碼前後。
所以以下程式碼會先執行建構子,才執行靜態區塊。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class StaticTest {
static StaticTest instance = new StaticTest();
public StaticTest() {
System.out.println("建構子初始化");
}
static {
System.out.println("靜態區塊初始化");
}
}
public class Test2 {
public static void main(String[] args) {
System.out.println(StaticTest.instance);
}
}
建構子初始化
靜態區塊初始化
static_.StaticTest@681a9515
靜態屬性與靜態區塊只會被執行一次
以下程式碼,不管執行幾次,建構子與靜態區塊,只有執行一次。
靜態屬性instance的hashCode都是一模一樣的,hashCode代表一個物件的身份證,是根據記憶體位址換算出來的數字。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class StaticTest {
static StaticTest instance = new StaticTest();
public StaticTest() {
System.out.println("建構子初始化");
}
static {
System.out.println("靜態區塊初始化");
}
}
public class Test2 {
public static void main(String[] args) {
System.out.println(StaticTest.instance.hashCode());
System.out.println(StaticTest.instance.hashCode());
System.out.println(StaticTest.instance.hashCode());
System.out.println(StaticTest.instance.hashCode());
}
}
建構子初始化
靜態區塊初始化
1746572565
1746572565
1746572565
1746572565
繼承與靜態屬性
父類別有靜態屬性count,靜態區塊。
1
2
3
4
5
6
class Father {
static int count = 0;
static {
System.out.println("Father static init");
}
}
子類別有靜態屬性count,靜態區塊。
1
2
3
4
5
6
class Child extends Father{
static int count = 5;
static {
System.out.println("Child static init");
}
}
呼叫子類別的靜態屬性,沒有new,請問靜態區塊如何呼叫嗎?
1
2
3
4
5
public class Test2 {
public static void main(String[] args) {
System.out.println(Child.count);
}
}
Father static init
Child static init
5
由執行結果可以發現,父類別先呼叫靜態區塊。
接著呼叫子類別靜態區塊。
繼承與子類別方法覆寫。
父類別
1
2
3
4
5
6
7
8
9
10
11
12
13
class Father {
int i = getI();
int getI() {
System.out.println("Father 屬性初始化");
return 20;
}
Father() {
System.out.println("Father建構子初始化");
}
{
System.out.println("Father 匿名初始化");
}
}
子類別
1
2
3
4
5
6
7
8
9
10
11
12
13
class Child extends Father{
int i = getI();
int getI() {
System.out.println("Child 屬性初始化");
return 20;
}
Child() {
System.out.println("Child建構子初始化");
}
{
System.out.println("Child 匿名初始化");
}
}
執行測試
1
2
3
4
5
public class Test2 {
public static void main(String[] args) {
new Child();
}
}
Child 屬性初始化 <- 居然先執行?
Father 匿名初始化
Father建構子初始化
Child 屬性初始化
Child 匿名初始化
Child建構子初始化
為什麼是Child 屬性初始化
先執行?因為子類覆寫父類別的getI()方法,所以會執行getI(),實際上是呼叫子類別的getI()。
1
2
3
4
5
6
7
class Father {
int i = getI(); // 實際上是呼叫子類別的getI
int getI() {
System.out.println("Father 屬性初始化");
return 20;
}
}
若程式碼修正如下,執行結果也就如預期正常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Father {
int i = getFI();
int getFI() {
System.out.println("Father 屬性初始化");
return 20;
}
Father() {
System.out.println("Father建構子初始化");
}
{
System.out.println("Father 匿名初始化");
}
}
class Child extends Father{
int i = getI();
int getI() {
System.out.println("Child 屬性初始化");
return 20;
}
Child() {
System.out.println("Child建構子初始化");
}
{
System.out.println("Child 匿名初始化");
}
}
Father 屬性初始化
Father 匿名初始化
Father建構子初始化
Child 屬性初始化
Child 匿名初始化
Child建構子初始化
由執行結果可以發現會先執行父類別的屬性->匿名區塊->建構子,然後才執行子類別。
建立物件就呼叫匿名區塊
靜態區塊只會執行一次,匿名區塊跟建構子一樣,每建立一次物件就呼叫一次。
1
2
3
4
5
6
7
8
9
10
11
12
13
class StaticTest {
public StaticTest() {
System.out.println("建構子初始化");
}
{
System.out.println("匿名區塊初始化");
}
static {
System.out.println("靜態區塊初始化");
}
}
建立二次物件。
1
2
3
4
5
6
public class Test2 {
public static void main(String[] args) {
new StaticTest();
new StaticTest();
}
}
靜態區塊初始化
匿名區塊初始化
建構子初始化
匿名區塊初始化
建構子初始化
由執行結果可以發現,靜態區塊只執行一次,匿名區塊與建構子分別執行二次。
執行順序: 靜態區塊 -> 匿名區塊 -> 建構子