public static void main (String[] args) { // Test A A a1 = new A(); a1.privateTest(); // Test B String helloStr = "Hello"; Object helloObj = helloStr; B b1 = new B(); A a2 = b1; // 這里發生了什么?困惑1 b1=a1; //編譯錯誤,困惑2 b1. privateTest(); //編譯錯誤,困惑3 b1.protectedTest(helloObj); //輸出結果?困惑4 b1.protectedTest(helloStr); //編譯錯誤,困惑5 a2.protectedTest(helloObj); //輸出結果? 困惑6 a2.protectedTest(helloStr); //輸出結果?困惑7 ? } }
困惑2: A a2 = b1是可以的,但是為什么b1=a1卻是不行? 在這里,我們依然可以套用上面的兩條規則,我們可以看到,b1是類B的一個引用,a1既不是類B的實例,也不是類B的子類的實例,所以直接b1=a1就出現了編譯錯誤. 假如確實需要進行這樣的轉化,我們可以這樣作:b1=(B)a1; 進行強制轉化,也就是下溯造型. 在java里面,上溯造型是自動進行的,但是下溯造型卻不是,需要我們自己定義強制進行.
困惑3: b1. privateTest();編譯不通過? 這是很顯然的,你可以回顧一下private的定義: 私有域和方法只能被定義該域或方法的類訪問. 所以,在這里,b1不能訪問A的方法privateTest(),即使b1是A的子類的實例. 請看下面的例子: public class A { private int two(int i) { return i; } } class Test extends A { public static void main(String[] args) { System.out.println(A.two(3)); } }
而protected則不同,我們回顧一下protected的定義: 被保護的域或方法只能被類本身、類的子類和同一 程序包中的類所訪問。 下面是一個錯誤使用protected的例子: package cn.org.matrix.test; public class ProtectedTest { protected void show() { System.out.println("I am in protected method"); } }
import cn.org.matrix.test.*; public class Test { public static void main (String[] args) { ProtectedTest obj = new ProtectedTest(); obj.show(); } } 因為訪問權限問題,你會得到”show() has protected access in test.ProtectedTest”的出錯信息.
困惑5: b1.protectedTest(helloStr); 這里為什么會出現編譯錯誤? 他可以調用類B的protectedTest(Object obj)方法啊,把helloStr上溯造型成一個object就行了啊..或者上溯造型到A然后調用A的protectedTest(helloStr)方法啊. 呵呵,問題的根源就在于此了,既然有兩種選擇,jvm應該選擇那一種?這種不確定性假如交給jvm來動態決定的話,勢必帶來程序的不確定性..雖然java在其他的一些地方也有類似的情形出現,比如static變量的循環定義造成的不確定性,但是,在這里,jvm還是在編譯階段就解決了這個問題. 所以,我們會在這一步碰到編譯錯誤: “reference to protectedTest is ambiguous; both method protectedTest(java.lang.String) in mytest.A and method protectedTest(java.lang.Object) in mytest.B match at line 46. 在這里,我們碰到的是顯式的reference ambiguous錯誤,但是,有時候,隱式的reference ambiguous卻往往是更加的危險. 在這里,我舉個例子: 父類的 源代碼: public super { private void test(int i, long j); { System.out.println(i+”and”+j); } } 子類的源代碼: public sub { private void test(long j, int i); { System.out.println(i+”and”+j); } }
子類和父類都用有相同名稱的方法test,參數類型不同而已.這種情況下,編譯可以被通過. 但是假如你在另外一個類中用到了如下代碼: Sub sb = new Sub(); sb.test(100, 3000); 你就會碰到編譯錯誤,因為沒有確定的指出3000的類型,所以造成reference ambiguous的錯誤了.