快捷搜索:

对Java多态性综合运用的探讨

或许大年夜家java的多态问题,对上溯,下溯造型有了必然的观点,对protect和private大年夜家想必也很清楚,然则,这几个个结合在一路,每每令人孕育发生很多利诱,在这里,我举一个例子,大年夜家或许会发觉这篇文章对你来说照样很故意义的:

例子一共有两个class. 可能呈现利诱的地方我都邑在后面逐一解释。

A是一个父类,B承袭A,并且实现了protectedTest(Object obj)措施.如下面所示:

B.java的源代码:

package cn.org.matrix.test;

import cn.org.matrix.test.A;

/**

* <p>Title: protect, private and upcasting </p>

* <p>Description: email:chris@matrix.org.cn</p>

* <p>Copyright: Matrix Copyright (c) 2003</p>

* <p>Company: Matrix.org.cn</p>

* @author chris

* @version 1.0,who use this example pls remain the declare

*/

public class B extends A

{

protected int protectedb = 0;

protected int protectedab = 0;

protected void protectedTest(Object obj)

{

System.out.println("in B.protectedTest(Object):" + obj);

}

}

A.java的源代码:

package cn.org.matrix.test;

import cn.org.matrix.test.B;

/**

* <p>Title: protect, private and upcasting </p>

* <p>Description: email:chris@matrix.org.cn</p>

* <p>Copyright: Matrix Copyright (c) 2003</p>

* <p>Company: Matrix.org.cn</p>

* @author chris

* @version 1.0,who use this example pls remain the declare

*/

public class A

{

protected int protecteda = 0;

protected int protectedab = 0;

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 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 ?

}

}

下面,我来逐个解释每一处利诱的地方:

利诱1:

这里着实便是子类自动上溯造型到父类A。这里a2着实是指向了一个B类型的工具. 我们平日都可以这样作: A a2=b1, 这样作的意思实际上便是让a2指向了一个类型B的工具-在这里便是b1了。

在java里面,关于跨类引用,有两条规则应该记着:

1. 假如a是类A的一个引用,那么,a可以指向类A的一个实例,或者说指向类A的一个子类。

2. 假如a是接口A的一个引用,那么,a必须指向实现了接口A的一个类的实例。

以是,根据这两个规则,我们就不难理解例子中的A a2 = b1是什么意思了。

利诱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));

}

}

System.out.println(A.two(3));这行编译掉足,显然,由于private措施不能在这个类之外被造访。

而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"的掉足信息.

利诱4:

b1.protectedTest(helloObj); 输出的是"in B.protectedTest(Object):…" 这到底是为什么呢? 为什么jvm能够确定是输出B的措施而不是A的措施? 这就和jvm的运行机制有关系了. 我们上面提到了,a1是一个A类型的引用,然则指向了一个B类型的实例. 在这里,假如jvm根据引用的类型-在这里便是A 来定义调用哪个措施的话,那么应该是调用A的protectedTest(helloObj).

然后实际上不是这样的,由于jvm的动态编译能力,jvm会在run-time来抉择调用哪一个method,而不是在compile time. 也便是所谓的late-binding(run-time)和early-binding(compile-time).

利诱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的差错了.

利诱6:

a2.protectedTest(helloObj);

输出结果分手是:"in B.protectedTest(Object).." 颠末上面的解释,想必大年夜家都能很清楚的知道为什么会有这两个输出结果了:a2.protectedTest(helloObj);由于jvm的late-binding,以是在run-time的时刻,调用了B类的措施,虽然在编译时代a2只是一个父类A的引用类型。

利诱7:

a2.protectedTest(helloStr); 为什么这里会输出" in A.protectedTest(Object)…"。为什么这里不会编译掉足?为什么b1. protectedTest(helloStr)会掉足而a2. protectedTest(helloStr)会掉足?我调用了a2.equals(b1)和a2==b1获得的结果都是true啊?然则为什么这里出这个差错?

在这里,这个问题是最关键的,也是我们放到着末来解答的缘故原由。

首先,回首一下equals()和==的在java里面的观点,记得有一道scjp的题目:

题目:下面的哪些论述为真。

A. equals()措施鉴定引用值是否指向同一工具。

B. == 操作符鉴定两个分立的工具的内容和类型是否同等。

C. equals()措施只有在两个工具的内容同等时返回true。

D. 类File重写措施equals()在两个分立的工具的内容和类型同等时返回true。

谜底是AD,严格来说这个问题的谜底是不确定的,由于equals()措施是可以被重载的,然则假如新类没有重写equals(),则该措施在两个变量指向同一工具时返回真. 实际上java也是保举的是应用equals()措施来判断两个工具的内容是否一样,就像String类的equals()措施所做的那样,鉴定两个String工具的内容是否相同。而==操作符返回true的独一前提是两个变量指向同一工具。

在这里,我们不再深入的评论争论关于equals()和==的差别和观点。我们只必要知道,在我们的例子里面,无论是equals()和==都是一个含义-便是引用值是否指向同一个工具(由于我们并没有重写equals()).

显然,我们在进行了a2=b1.这一步之后,a2和b1都是指向同一个工具。

既然指向同一个工具,为什么还要差别对待a2和b1?为什么a2就没有编译差错,而b1就要碰到reference ambiguous差错?

我们现看看jvm规范里的一段话:

"The Java Virtual Machine does not require any particular internal

structure for objects. In Sun´s current implementation of the Java Virtual Machine, a reference to a class instance is a pointer to a handle that is itself a pair of pointers: one to a table containing the methods of the object and a pointer to the Class object that represents the type of the object, and the other to the memory allocated from the Java heap for the object data."

实际上便是说:在java虚拟机中,类实例的引用便是指向一个句柄(handle)的指针,这个句柄是一对指针:一个指针指向一张表格,实际上这个表格也有两个指针:一个指针指向一个包孕了工具的措施表,别的一个指向类工具;另一个指针指向一块从java堆中为分配出来内存空间。

那么,在a2=b1的时刻,到底发生了什么?

实际上,在a2=b1的时刻,仍旧是存在两个句柄,a2和b1,然则a2和b1拥有同一块数据内存块和不合的函数表。以是在a2.protectedTest(helloStr)的时刻,jvm会从a2的函数表里找到protectedTest(String Str)措施,然则b1.protectedTest(helloStr)却会呈现编译差错,由于jvm从b1的函数表里找不到,然后就选择自己上溯造型照样参数上溯造型呈现了ambiguous。这也是我们对这个问题的正确解释。

摘自:天极网   光阴:2003年9月11日

您可能还会对下面的文章感兴趣: