Java中的static(1)【持續更新】——關於Eclipse的No enclosing instance of type ... 錯誤的理解和改正

Java中的static(1)【持續更新】——關於Eclipse的No enclosing instance of type ... 錯誤的理解和改正

本文連接地址:http://www.javashuo.com/article/p-zenshsfg-kh.htmlhtml

No enclosing instance of type SomeClass is accessible. Must qualify the allocation with an enclosing instance of type SomeClass (e.g. x.new A() where x is an instance of SomeClass).java

這是怎麼發現的??

拿Eclipse編寫Java的AWT/Swing程序時,編寫了一個public class MainFrame extends JFrame而後在其中放入一個主函數寫入了以下內容:面試

public static void main(String args[])
{
    MainJFrame frmBase = new MainJFrame("Hello Swing");
    frmBase.setBackground(Color.white);
    frmBase.setVisible(true);
    frmBase.addWindowListener(new MainJWindowListener());    // <--- Error occured here !!!
}

也就是我在主函數建立了窗口以後想要繼續在主函數裏爲frmBase這個JFrame窗口添加窗口偵聽器(class MainJWindowListener implements WindowListener),其中MainJWindowListener做爲MainFrame內類
而後錯誤就發生了……eclipse

爲何會這樣呢??

若是已經瞭解了靜態的內容,請自行跳轉至「靜態成員說完了,那跟這你遇到的這個問題有什麼關係呢??」子節函數

好吧,其實說到底都是mainstatic修飾惹的禍。
咱們知道static的成員也好,類也好,所謂的「靜態」其實說的都是「爲整個程序所共享」的意思,說的直白一點,static的東西都很是的「大公無私」
固然,其實我寫C#寫的比較多,對Java的規則還不是很瞭解,彷佛Java並不像C#那樣容許public static class SomeClass,因此對於Java而言更多的是針對靜態成員討論的。this

做爲一個大公無私的內容,static的成員裏面除了本身再構造一些臨時對象之外,直接援引的類、函數都不能爲某一對象所特有的。code

不能爲某一對象所特有??可是既然是定義到類裏的屬性,那類的全部對象不是都有這個屬性麼??

不,這裏說的「特有」並非這個意思,或許我換個詞語會更好一些——對象「個性」的屬性。orm

個性化……但這是什……

打住,我知道你要問這件事,因此我這裏舉個糖炒栗子:
好比說類Gold被定義爲:htm

public class Gold
{
    // Properties
    private static float xau = 1504.35f;  // 金單價,也就是每盎司黃金在國際市場的價格,我隨便編的數請別在乎……
    private float oz;    // 該黃金實例(理解成金塊金錠金碎末金戒指金項鍊均可以)的質量(盎司數)
    
    // Constructor
    public Gold(float oz)
    {
        this.oz = oz;
    }
    // Methods ... 
}

咱們看到金單價xau是靜態的,由於不管那個金子多沉,長成什麼樣子,金單價對全部金子都是同樣的,並且當xau變更的時候,全部Gold的價格也都會隨着xau變更。因此這種屬性就不具有「個性」,由於你用我用你們一塊兒用,也就是說,這是全體的「共性」。對象

而重量oz就不同了,一座金山(Kingsoft)可能有1e+xxx的oz,而一個金粒可能只有1e-xxx的oz,我可能用一個new Gold(1e+500)表示我構建了一個黃金星球,我也可能用new Gold(1e-500)表示我構造了一個金分子之類的。
但總之,他們都是Gold,他們都有oz,但是每一個人的oz都不同,這就是個性

你有Freestyle嘛

咱們接下來再看看它的方法想定義些什麼(但我如今暫時不定義):

public class Gold
{
    // Properties ...
    
    // Constructor ...

    // Methods
    // GetPrice() 可以得到該金塊的具體價格,按金單價*盎司數計算
    // Rise(float price) 提高黃金單價,增長price元
    // Reduce(float price) 下降黃金單價,減小price元
    // ...
}

先看這些函數,很明顯咱們能看出一些規律:
GetPrice這種函數,它的結果是「個性」的,xau*oz當中oz是「個性」的,這個結果固然也就可能彼此不一樣。
RiseReduce這兩個函數,它們要修改的xau是共性的,並且僅僅牽涉到「共性」,也就是說,它們是可以影響整個黃金國度每個金子的命運的函數。

嗨,要是我嘛,全都定義成日常的就能夠了吧

啊,確實能夠,由於非靜態的成員可以訪問類內全部的其餘成員,無所謂靜態和非靜態。
固然,對於GetPrice來講,這無所謂,並且其結果自己就是「個性」的:

public class Gold
{
    // Properties ...
    
    // Constructor ...

    // Methods
    public float GetPrice() // 可以得到該金塊的具體價格,按金單價*盎司數計算
    {
        return xau*oz;
    }
    public void Rise(float price) //提高黃金單價,增長price元
    {
        xau += price;
    }
    public void Reduce(float price) // 下降黃金單價,減小price元
    {
        xau -= price;
    }
    // ...
}

這麼寫的話固然沒有問題,不犯任何語法錯誤並且編譯是經過的,並且你只要實例化一個金子你也確實可以正確地調用這些函數並獲得正確的結果。
像這樣:

public void main(String args[]) //主函數,在哪一個類裏就別管了
{
    Gold gNugget = new Gold(0.01f);
    System.out.println(String.format("%.2f", gNugget.GetPrice()));
    gNugget.Rise(10.0f);
    System.out.println(String.format("%.2f", gNugget.GetPrice()));
    gNugget.Reduce(20.0f);
    System.out.println(String.format("%.2f", gNugget.GetPrice()));
}

輸出結果:

15.04
15.14
14.94

結果確實沒問題

……固然你也能夠弄好多個金子這麼玩……

只不過……

gNugget.Rise()或者gMountain.Reduce()被調用的時候,全部金子(固然包含它們自身)都是受到影響的。
想像一下:

gNugget.Reduce(1000.0f);

結果一個小小的金粒致使了黃金市場遭受了慘重的金融危機,你不以爲這個小金粒的能耐忒大了點麼??
固然,這也就同將Rise()/Reduce()改爲public也差很少了。

好吧,是挺危險的,那我不但願這樣,該怎麼辦呢

一個普通的,不是static的函數,意味着它是一種「個性」的行爲,這也就是說:

這種行爲誰均可以作,想來就來我行我秀u can u up

固然,若是一個函數被聲明爲static的,那麼也就意味着這不是一種「個性」的行爲,而是「共性」的行爲,這也就是說:

這種行爲並不能由某一個體單獨執行,必須賭上全種族的命運,以種族的名義,去吧!!!

因此,若是不但願金價被某一個金塊干擾的話,RiseReduce也得是static的:

public static void Rise(float price) //提高黃金單價,增長price元
    {
        xau += price;
    }
    public static void Reduce(float price) // 下降黃金單價,減小price元
    {
        xau -= price;
    }

這樣一來,RiseReduce就必須以Gold.Rise()Gold.Reduce()調用,而不能讓Gold的實例來調用,也就是說,這一舉措會致使下面的語句出錯:

gNugget.Rise(10.0f);
gNugget.Reduce(20.0f);

由於這兩個函數是「共性」的,想漲價降價你一個小小的gNugget說了不算的,必須是以Gold全體的名義來講:Gold.Rise()

固然,共性行爲也會有這樣的約束,好比,共性的行爲不容許引入個性的內容(書面上來講就是靜態方法中不容許調用非靜態的屬性和方法和內部類),由於若是一個行爲牽扯到個性的因素,那麼它就不能以這個類全體的名義去作,好比我把GetPrice改爲static的就會出錯:

public static float GetPrice() // 不行!!!!
    {
        return xau*oz;
    }

由於並不存在屬於Gold全體的oz,每一個金子都有本身的oz,因此這個時候調用Gold.GetPrice(),程序本身也不知道該返回誰的oz,因此這樣是不能夠的。

靜態成員說完了,那跟這你遇到的這個問題有什麼關係呢??

拿Eclipse編寫Java的AWT/Swing程序時,編寫了一個public class MainFrame extends JFrame而後在其中放入一個主函數寫入了以下內容:

public static void main(String args[])
{
  MainJFrame frmBase = new MainJFrame("Hello Swing");
  frmBase.setBackground(Color.white);
  frmBase.setVisible(true);
  frmBase.addWindowListener(new MainJWindowListener()); // <--- Error occured here !!!
}

也就是我在主函數建立了窗口以後想要繼續在主函數裏爲frmBase這個JFrame窗口添加窗口偵聽器(class MainJWindowListener implements WindowListener),其中MainJWindowListener做爲MainFrame內類
而後錯誤就發生了……

請注意畫線部分!!!

Java裏面main函數強制具有這些修飾:public static void,也就是說main理所應當的是個靜態成員。
可是,請注意,main裏面試圖引入了MainJWindowListener這個內類並試圖調用構造函數進行構造,但是,MainJWindowListener只是個缺省訪問級別的普通動態內類,這就是問題所在了。

那該怎麼解決呢??

這裏基於使用了Swing這個事實,解決方案有兩種,其中一種是推薦的,而另一種是通用的。

推薦的方法是用於根治在Swing下發生這種問題的一種重構代碼的思路,這種方法規避了在靜態成員中進行動態內類(這裏面動態內類主要指事件偵聽器)的引用。

通用的方法是針對咱們遇到的這個問題,經過修改代碼使得靜態成員可以調用動態內類,這裏時刻注意一點,內類由於不是靜態的,這意味着內類的構造必須在一個外部類(一個非靜態類)實例的範疇下進行。

推薦的方法

考慮到我這裏的情景,我是要給窗口註冊一下窗口關閉的動做,也就是爲關閉按鈕添加事件偵聽器。
可是實際狀況是,基本上只要是這個MainJFrame窗口,我都但願它註冊這個關閉按鈕的事件偵聽器。因此這個過程徹底能夠不在主函數裏進行。

而一個非靜態類,不管長成什麼樣子,其構造函數永遠也不會是static,因此事件偵聽的註冊徹底能夠放到構造函數裏去作

public MainJFrame(String caption)
{
    super(caption);
    //....Some initial actions...//
    this.addWindowListener(new MainJWindowListener());
}

而後main函數只須要負責構造之就能夠了:

public static void main(String args[])
{
    //..Others..//
    MainJFrame frmBase = new MainJFrame();
    //..Others..//
}

這是個很是一勞永逸且合乎邏輯的作法,由於就算有多個MainJFrame,咱們通常都會但願對用戶行爲的反饋是一致的,也就是說實際上全部的MainJFrame都會被配備相同的事件偵聽器,既然如此的話放到構造函數統一隨着構造的時候執行是再好不過的了。

通用的方法

推薦的方法裏面規避了在靜態成員中調用這個內部類,轉到一個非靜態成員中進行,並且事實上證實「效果拔羣」

可是說到底「效果拔羣」也只是對這類情形「拔羣」,假使咱們非要這麼作,好比說我非要在多個MainJFrame作不一樣的事件偵聽處理,那就不該該寫在構造函數當中,由於這不是統一的構造流程,無法寫進一個函數裏。

也就是說,咱們非得要在main函數裏作偵聽綁定,由於main函數強制具備static性質,而MainJWindowListener不是static的因此像這樣是不能夠的:

public static void main(String args[])
{
    MainJFrame frmBase = new MainJFrame("Hello Swing");
    frmBase.setBackground(Color.white);
    frmBase.setVisible(true);
    
     // 不行!!!MainJWindowListener不是靜態的!!!
    frmBase.addWindowListener(new MainJWindowListener()); 
}

也就是說,MainJWindowListener必須跟着一個MainJFrame的實例裏面才能夠構造,那麼咱們非要找一個MainJFrame實例mjfinstance不可,而後再new mjfinstance.MainJWindowListener()……

……巧了,這不是有麼??frmBase不就是一個MainJFrame麼??因此把這一句改爲:

frmBase.addWindowListener(new frmBase.MainJWindowListener());

就能夠了。

相關文章
相關標籤/搜索