No Enclosing instance ...的原因和解決方法


      一個月前剛考研復試完,終於也從一個毫不相關的專業轉了碼。現在在畢設期間找機會摸魚學java。由於機試用的C,之前又沒有接觸過任何OOP語言,對Java的學習一度走了不少彎路。曾經寫的沙雕代碼也犯了不少錯誤(現在仍然在每天生產沙雕Java代碼)。比如下面這段:

public class Main {
    int a = 1;
    public class Test{
        public void test() {
            System.out.println("Hello World");
        }
    }
    public static void main(String[] args) {
        Test T = new Test();
T.test(); } }

     編譯器提示No enclosing instance of type Main is accessible. 也就是說沒有Main的實例。查相應的博客,一群人復讀“靜態類方法無法調用動態實例方法,需要把類修飾符改為static”,但詳細的原因卻很少有人去說明,這一度讓我非常難受,問題也沒得到解決,只得暫時擱置。

     直到最近正在讀《Java核心技術卷一》,才明白錯誤原因。下面進行相應的解釋:

     為了搞清這個問題,要明白兩個方面的知識:

1) public static void main方法

2) 內部類

     先解決第一個問題。首先我們要直到,任何的類都可以自定義一個main方法。在程序運行過程中只會調用唯一一個main方法,具體調用哪個就要看我們要加載哪一個類了。這樣我們也可以明白為什么牛客OJ要求把主類名定義為Main,因為OJ僅僅測試Main中的main方法的輸出結果,而其他的類中的main方法是不會被加載的。這種設計對於我們進行不同模塊的單元測試是十分有利的,只需要把測試代碼寫到不同類的main方法中,調試時加載相應類即可。

     雖然main方法是屬於類的,但當執行main方法的時候,main方法所屬的類剛開始並沒有被加載到內存中(先不考慮靜態類),必須要在代碼中new一個Main類,才會把Main的內容加載到內存中。

     這樣我們就可以明白為什么錯誤示例代碼中編譯器提示沒有加載Main實例了。由於沒有加載Main實例,自然沒有方法生成Test類,更不可能調用Test類中的方法了。因此我們實例化Main類,做以下修改:

public class Main {
    int a = 1;
    public class Test{
        public void test() {
            System.out.println("Hello World");
        }
    }
    public static void main(String[] args) {
        Main m = new Main();
        m.test();
    }
}

      emmmm.......編譯器的報錯更離譜了,啥都報了。這是因為第二個問題:內部類。

     下面解決第二個問題。內部類是Java的一種語法規則,也就是說,允許在類中定義類(之前寫這段demo時不知道這個知識點)。很明顯,Test類是在Main類中定義的內部類。而內部類的對象並不是在外部類實例化的時候就完成實例化的,也必須需要在代碼中完成內部類的實例化:

        Main M = new Main();
        Main.Test T = M.new Test(); //new關鍵字在.后面,小心別看錯了
        T.test();

    這樣我們完成了Main類的實例化和Main類的內部類Test類的實例化,終於可以調用Test類的test()方法了,修改后可運行的代碼如下:

public class Main {
    public class Test {
        public int a = 1;
        public void test() {
            System.out.println("Hello World");
        }
    }
    public static void main(String[] args) {
        Main M = new Main();
        Main.Test T = M.new Test();
        T.test();
    }
}

  

      這樣問題就解決了。但是顯然,內部類並不是我們想要的結果,它看起來太難受了,如何優雅的解決這個問題呢?

      第一種方法,將Test類修飾為static,即將Test類變為靜態類也就是大多數人復讀的方法在這種情況下,運行main方法時,雖然並沒有實例化Test對象,但是Test類的靜態方法和靜態屬性已經加載到內存中了,自然可以直接使用,不會提示No Enclosing XXX的內容。

public class Main {
    public static class Test {
        public int a = 1;
        public void test() {
            System.out.println("Hello World");
        }
    }
    public static void main(String[] args) {
        Test t = new Test();
        t.test();
    }
}

      第二種方法更簡單,將算法的實現方法和操縱的類分開定義,將包含算法的main定義在Main類中,將類定義在另一個class中,這樣我們直接選擇實例化Test類就可以了,相應的修改代碼如下:

public class Main { // 在被加載的類Main中定義算法過程
    public static void main(String[] args) {
        Test T = new Test();
        T.test();
    }
}

class Test {   //把使用的類拿到外面定義
    public int a = 1;
    public void test() {
        System.out.println("Hello World");
    }
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM