本文鏈接地址:https://www.cnblogs.com/oberon-zjt0806/p/11632528.html
No enclosing instance of type
SomeClassis accessible. Must qualify the allocation with an enclosing instance of typeSomeClass(e.g.x.new A()wherexis an instance ofSomeClass).
這是怎么發現的??
拿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的內類。
然后錯誤就發生了……
為什么會這樣呢??
如果已經了解了靜態的內容,請自行跳轉至“靜態成員說完了,那跟這你遇到的這個問題有什么關系呢??”子節
好吧,其實說到底都是main的static修飾惹的禍。
我們知道static的成員也好,類也好,所謂的“靜態”其實說的都是“為整個程序所共享”的意思,說的直白一點,static的東西都非常的“大公無私”。
當然,其實我寫C#寫的比較多,對Java的規則還不是很了解,似乎Java並不像C#那樣允許public static class SomeClass,所以對於Java而言更多的是針對靜態成員討論的。
作為一個大公無私的內容,static的成員里面除了自己再構造一些臨時對象以外,直接援引的類、函數都不能是為某一對象所特有的。
不能為某一對象所特有??但是既然是定義到類里的屬性,那類的所有對象不是都有這個屬性么??
不,這里說的“特有”並不是這個意思,或許我換個詞語會更好一些——對象“個性”的屬性。
個性化……但這是什……
打住,我知道你要問這件事,所以我這里舉個糖炒栗子:
比如說類Gold被定義為:
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是“個性”的,這個結果當然也就可能彼此不同。
而Rise和Reduce這兩個函數,它們要修改的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的,那么也就意味着這不是一種“個性”的行為,而是“共性”的行為,這也就是說:
這種行為並不能由某一個體單獨執行,必須賭上全種族的命運,以種族的名義,去吧!!!
所以,如果不希望金價被某一個金塊干擾的話,Rise和Reduce也得是static的:
public static void Rise(float price) //提升黃金單價,增加price元
{
xau += price;
}
public static void Reduce(float price) // 降低黃金單價,減少price元
{
xau -= price;
}
這樣一來,Rise和Reduce就必須以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());
就可以了。
