Java中引用,造型等问题解析

欢迎进入Java社区论坛,与200万技术人员互动交流 >>进入   作者:chris译     或许大家java的多态问题,对上溯,下溯造型有了一定的概念,对protected和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.p

任何的限制,都是从自己的内心开始的。

Java中引用,造型等问题解析

相关文章:

你感兴趣的文章:

标签云: