服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

引用造型protected和private的困惑


  或许大家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的对象?d在这里就是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

扫描关注微信公众号