或許大家java的多態問題
對上溯
下溯造型有了一定的概念
對protected和private大家想必也很清楚
但是
這幾個個結合在一起
往往令人產生很多困惑
在這裡
我舉一個例子
大家或許會發覺這篇文章對你來說還是很有意義的:
例子一共有兩個class
可能出現困惑的地方我都會在後面一一解釋
A是一個父類
B繼承A
並且實現了protectedTest(Object obj)方法
如下面所示:
B
java的源代碼:
package
matrix
test;
import
matrix
test
A;
/**
* <p>Title: protect
private and upcasting </p>
* <p>Description: email:</p>
* <p>Copyright: Matrix Copyright (c)
</p>
* <p>Company: </p>
* @author chris
* @version
who use this example pls remain the declare
*/
public class B extends A
{
protected int protectedb =
;
protected int protectedab =
;
protected void protectedTest(Object obj)
{
System
out
println(
in B
protectedTest(Object):
+ obj);
}
}
A
java的源代碼:
package
matrix
test;
import
matrix
test
B;
/**
* <p>Title: protect
private and upcasting </p>
* <p>Description: email:</p>
* <p>Copyright: Matrix Copyright (c)
</p>
* <p>Company: </p>
* @author chris
* @version
who use this example pls remain the declare
*/
public class A
{
protected int protecteda =
;
protected int protectedab =
;
private void privateTest()
{
System
out
println(
in A
privateTest()
);
}
protected void protectedTest(Object obj)
{
System
out
println(
in A
protectedTest(Object):
+ obj );
}
protected void protectedTest( String str )
{
System
out
println(
in A
protectedTest(String):
+ str);
}
public static void main (String[] args)
{
// Test A
A a
= new A();
a
privateTest();
// Test B
String helloStr =
Hello
;
Object helloObj = helloStr;
B b
= new B();
A a
= b
; // 這裡發生了什麼?困惑
b
=a
; //編譯錯誤
困惑
b
privateTest(); //編譯錯誤
困惑
b
protectedTest(helloObj); //輸出結果?困惑
b
protectedTest(helloStr); //編譯錯誤
困惑
a
protectedTest(helloObj); //輸出結果? 困惑
a
protectedTest(helloStr); //輸出結果?困惑
?
}
}
下面
我來逐個解釋每一處困惑的地方:
困惑 這裡其實就是子類自動上溯造型到父類A
這裡a
其實是指向了一個B類型的對象
我們通常都可以這樣作: A a
=b
這樣作的意思實際上就是讓a
指向了一個類型B的對象—在這裡就是b
了
在java裡面
關於跨類引用
有兩條規則應該記住:
如果a是類A的一個引用
那麼
a可以指向類A的一個實例
或者說指向類A的一個子類
如果a是接口A的一個引用
那麼
a必須指向實現了接口A的一個類的實例
所以
根據這兩個規則
我們就不難理解例子中的A a
= b
是什麼意思了
困惑: A a
= b
是可以的
但是為什麼b
=a
卻是不行? 在這裡
我們依然可以套用上面的兩條規則
我們可以看到
b
是類B的一個引用
a
既不是類B的實例
也不是類B的子類的實例
所以直接b
=a
就出現了編譯錯誤
如果確實需要進行這樣的轉化
我們可以這樣作:b
=(B)a
; 進行強制轉化
也就是下溯造型
在java裡面
上溯造型是自動進行的
但是下溯造型卻不是
需要我們自己定義強制進行
困惑: b
privateTest();編譯不通過? 這是很顯然的
你可以回顧一下private的定義: 私有域和方法只能被定義該域或方法的類訪問
所以
在這裡
b
不能訪問A的方法privateTest()
即使b
是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(
));
}
}
System
out
println(A
two(
));這行編譯出錯
顯然
因為private方法不能在這個類之外被訪問
而protected則不同
我們回顧一下protected的定義: 被保護的域或方法只能被類本身
類的子類和同一 程序包中的類所訪問
下面是一個錯誤使用protected的例子:
package
matrix
test;
public class ProtectedTest {
protected void show() {
System
out
println(
I am in protected method
);
}
}
import
matrix
test
*;
public class Test {
public static void main (String[] args) {
ProtectedTest obj = new ProtectedTest();
obj
show();
}
}
因為訪問權限問題
你會得到
show() has protected access in test
ProtectedTest
的出錯信息
困惑: b
protectedTest(helloObj); 輸出的是
in B
protectedTest(Object):…
這到底是為什麼呢? 為什麼jvm能夠確定是輸出B的方法而不是A的方法? 這就和jvm的運行機制有關系了
我們上面提到了
a
是一個A類型的引用
但是指向了一個B類型的實例
在這裡
如果jvm根據引用的類型
在這裡就是A 來定義調用哪個方法的話
那麼應該是調用A的protectedTest(helloObj)
然後實際上不是這樣的
因為jvm的動態編譯能力
jvm會在run
time來決定調用哪一個method
而不是在compile time
也就是所謂的late
binding(run
time)和early
binding(compile
time)
困惑: b
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
在這裡
我們遇到的是顯式的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(
);
你就會遇到編譯錯誤
因為沒有確定的指出
的類型
所以造成reference ambiguous的錯誤了
困惑 a
protectedTest(helloObj);
輸出結果分別是
in B
protectedTest(Object)
經過上面的解釋
想必大家都能很清楚的知道為什麼會有這兩個輸出結果了
a
protectedTest(helloObj);因為jvm的late
binding
所以在run
time的時候
調用了B類的方法
雖然在編譯期間a
只是一個父類A的引用類型
困惑 a
protectedTest(helloStr); 為什麼這裡會輸出
in A
protectedTest(Object)…
為什麼這裡不會編譯出錯?為什麼b
protectedTest(helloStr)會出錯而a
protectedTest(helloStr)會出錯?我調用了a
equals(b
)和a
==b
得到的結果都是true啊?但是為什麼這裡出這個錯誤?
在這裡
這個問題是最關鍵的
也是我們放到最後來解答的原因
首先
回顧一下equals()和==的在java裡面的概念
記得有一道scjp
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19162.html