鍍金池/ 問答/Java  PHP/ Java編譯時提示非法向前引用

Java編譯時提示非法向前引用

問題背景:
想了解Java類非靜態(tài)成員變量以及靜態(tài)成員變量的初始化過程。在代碼塊中使用System.out.println輸出語句,輸出成員變量的值,但是出現(xiàn)非法引用的提示。具體代碼如下。

具體代碼:

class A {
    public A() {
       System.out.println("父類A的構造方法");
        System.out.println("靜態(tài)成員變量 = " + staticStr + ", 非靜態(tài)成員變量 = " + str);    
    }

    {
        //System.out.println(str); //錯誤: 非法前向引用
        str = "123";
        System.out.println("父類A的構造代碼塊0");
    }

    static {
        staticStr = "1234";
        //System.out.println(staticStr);
        System.out.println("父類A的靜態(tài)代碼塊0");
    }
    
    private static String staticStr = iniStaticStr();


    static {
        System.out.println(staticStr);
        System.out.println("父類A的靜態(tài)代碼塊");
    }

    private String str = iniStr();

    {
        System.out.println(str);
        System.out.println("父類A的構造代碼塊");
    }


    private static String iniStaticStr() {
        System.out.println("staticStr = " + staticStr);
        System.out.println("靜態(tài)成員變量顯示初始化");
        return "iniStaticStr";
    }

    private String iniStr() {
        System.out.println("str = " + str);
        System.out.println("非靜態(tài)成員變量顯示初始化");
        return "iniStr"; 
    }

    {
        //System.out.println(str);//錯誤: 非法前向引用
        System.out.println("父類A的構造代碼塊1");
    }

    static {
        System.out.println("父類A的靜態(tài)代碼塊1");
    }

}

class B extends A {
    static {
        System.out.println("子類B的靜態(tài)代碼塊");
    }

    public B() {
        System.out.println("子類B的構造方法");
    }

    {
        System.out.println("子類B的構造代碼塊");
    }
}

public class JavaTest1 {
    public  static void main(String[] args) {
        new B();
    }

}

輸出結果:
父類A的靜態(tài)代碼塊0
staticStr = 1234
靜態(tài)成員變量顯示初始化
iniStaticStr
父類A的靜態(tài)代碼塊
父類A的靜態(tài)代碼塊1
子類B的靜態(tài)代碼塊
父類A的構造代碼塊0
str = 123
非靜態(tài)成員變量顯示初始化
iniStr
父類A的構造代碼塊
父類A的構造代碼塊1
父類A的構造方法
靜態(tài)成員變量 = iniStaticStr, 非靜態(tài)成員變量 = iniStr
子類B的構造代碼塊
子類B的構造方法

具體問題描述:
1、代碼塊與成員變量的執(zhí)行順序是怎樣的?如果代碼塊先于成員變量執(zhí)行,那此時的成員變量并沒用被聲明,那為什么能對成員變量進行賦值?
2、假如是成員變量先于代碼塊執(zhí)行,為什么代碼塊中使用System語句輸出操作會提示非法向前引用?
3、成員變量的初始化過程是否是一下過程?

1)默認初始化
2)顯示初始化(包括代碼塊中的顯示初始化)
3)構造初始化
回答
編輯回答
憶當年

這個問題我讀書的時候遇到過:https://www.cnblogs.com/iamzh...
樓主可以看下我當時寫的這個博客

2018年2月7日 05:28
編輯回答
解夏

這種情況:

{
    System.out.println("6666");
}
     int a=10;
執(zhí)行順序由代碼順序決定。

這種情況:

{
        a=10;
    }
     int a;
和
int a;
{
    a=10;
}
等價,字節(jié)碼指令一樣
2018年7月31日 13:13
編輯回答
不討喜

首先,關于執(zhí)行順序問題。主要有以下幾個要點:

  1. 變量的聲明在任意代碼執(zhí)行前發(fā)生(類似于private String str這樣的聲明并不是可執(zhí)行代碼)
  2. 接下來是靜態(tài)塊、靜態(tài)變量的聲明時賦值語句,會被合并在一起執(zhí)行,執(zhí)行順序就是它們在代碼中的書寫順序
  3. 接下來是實例塊、實例變量的聲明時賦值語句、以及構造方法,前兩者按照書寫順序執(zhí)行,構造方法最后執(zhí)行

關于執(zhí)行順序,你可以對照你程序的打印輸出來看,應該就能明白了。

下面再來說說非法向前引用這個錯誤。

前面說過,所有變量的聲明都是在任意代碼執(zhí)行前發(fā)生的,那么按道理來說并不存在“向前引用”一說,因為任一句代碼執(zhí)行時變量肯定已經存在了才對。

那么這個錯誤究竟是怎么出現(xiàn)的呢?答案是:

這是Java編譯器強制進行的一個檢查

其目的是避免循環(huán)初始化和其他非正常的初始化行為。

所以,雖然你的代碼看起來沒有問題,但是卻無法通過編譯器的強制檢查,所以報錯。

那么為什么類似于staticStr = "1234";這樣的代碼可以呢?這是因為Java對其中的某些情況做了“特許”,其中有一條就是“通過簡單名稱引用的變量可以出現(xiàn)在左值位置,但不能出現(xiàn)在右值的位置”,所以前面的代碼可以,但System.out.println(staticStr);不行,因為這是一個右值引用。

最后再簡單提一下什么是循環(huán)引用,看一下下面這個例子:

private int i = j;
private int j = i;

如果沒有前面說的強制檢查,那么這兩句代碼就會通過編譯,但是很容易就能看得出來,ij并沒有被真正賦值,因為兩個變量都是未初始化的(Java規(guī)定所有變量在使用之前必須被初始化),而這個就是最簡單的循環(huán)引用的例子。

2018年3月10日 10:33