码迷,mamicode.com
首页 > 编程语言 > 详细

半深入理解Java属性继承

时间:2016-06-08 00:00:22      阅读:391      评论:0      收藏:0      [点我收藏+]

标签:

前几天在面试的时候又被问到了一个问题,“Java重写和重载有什么区别?”。这个问题在Java领域是一个老生常谈的问题了,事实上我认为这两个东东除了中文名长得很像以外(英文名好像也很像),基本就没半毛钱关系了。我们很难找出他们的共性,却一直要尝试找出他们之间的区别,呵呵。


然而本文的主题并非重写和重载,而是重写的的孪生兄弟,属性继承。
故事的开始,我们先看一段代码

 1 public class Parent{
 2 public String color;
 3 public Parent(){
 4 this.color="green";
 5 }
 6 public void printColor(){
 7 System.out.println(color);
 8 }
 9 public static void main(String args[]){
10 new Child().printColor();
11 }
12 }
13 class Child extends Parent{
14 public String color;
15 public Child(){
16 }
17 public void printColor(){
18 System.out.println(color);
19 }
20 }

 

这段代码很简单,我们先写了一个类Parent,给他添加了一个属性color,并在构造函数中初始化为“green”,同时提供一个打印函数将color的值输出。
然后我们编写了一个Child类继承Parent,重写color属性,重写打印方法。
我们在main函数里面创建一个Child对象,并调用printColor方法。
那么,输出的结果是什么呢?


答案是null。


很多人会问,你Child类构造函数都没有初始化color,怎么可能有值。
慢着慢着,我们把这段代码改改。

public class Parent{
public String color;
public Parent(){
this.color="green";
}
public void printColor(){
System.out.println(color);
}
public static void main(String args[]){
new Child().printColor();
}
}
class Child extends Parent{
public String color;
public Child(){
}

}

 

这段代码里面我们只是删掉了子类的printColor方法。运行,结果是green。 事情好像就不那么简单了。 事实上,事情的关键点有三个。

  • 父类的构造方法是否在子类对象创建时调用?
  • 父类和子类拥有相同属性时,是覆盖还是共存?
  • 父类和子类printColor方法中的color究竟是谁的?父类的还是子类的?

我们一个一个来,首先是继承时构造方法的调用顺序。
是这样的,在几乎所有语言里面,如果想要正确运行一段代码的,前提条件是这段代码的所有依赖都能正常运行。所以,在搞定一段代码之前,都要先搞定他的依赖关系。例如我们加载一个类的时候,要先加载完成类中所有引用到的其他类。对象的创建也是一样,当我们创建一个对象时,首要任务就是“创建父类对象”,父类的构造方法也就是在这个时候调用的。
当我们“创建父类对象”之后明显会碰到一个问题,父类对象创建并初始化所有属性后,子类又打算再创建一个相同的属性,此时会怎样呢?
答案是:而是再创建一个该属性。

于是一个子类对象里面就有了两个同名的属性,一个是父类创建并且初始化为green的color,一个是还没有被初始化的子类的color。那么在printColor方法里面的color到底是哪一个呢?
这里要先说两个规则:

1.“逐级查找,找到则停”。属性和方法都可以适用这条规则。他的大意是,当我们需要调用一个方法或者属性时,我们先看看自己有没有,没有的话,就去上级(父类)找找,直到找到为止。这也可以很好地解释方法重写是如何实现的。不过属性和方法有一点区别,当子类对象向上转型为父类对象后,调用同名方法调用的只可以是子类方法,调用属性则调用父类属性。
2.调用的是谁的方法,属性查找的起点就是谁。这条规则的重点是在于判断调用的究竟是谁的方法。当一个子类重写父类方法时,调用的必然是子类方法,反之则调用的必然是父类的方法。
以上两点看似非常容易理解,现在我们来结合前面的代码理解一下。
第一段代码,当我们调用printColor方法时。

  • 1. 先在child对象中查找printColor方法,很容易就在Child类中找到了,于是调用的方法必然是Child类中的方法。
  • 2. 查找color属性,因为Child类中已经有color属性,于是该属性率先被查找到,并停止继续查找,打印出来,为null。

第二段代码

  • 1.先找到printColor方法,发现找不到,于是去父类找。
  • 2.在父类中找到了printColor方法,停止查找。
  • 3.找到color属性,由于调用的是父类的方法,所有属性查找起点为父类对象,在父类对象中成功找到初始化为green的color
  • 4.打印出green

如果以上两个流程你都理解了,那么属性继承你基本上也就掌握了。总结一下关键点。

  1. 父类子类有相同属性时,是在父类基础上添加而非覆盖。
  2. 方法和属性调用时,是从当前类开始一直向上查找。找到就停止。
  3. 调用的是谁的方法,查找的起点就是谁。所以,准确判断是哪个类的方法很重要。

问题1:当我们处在在第一段代码中的情况,并且想调用父类的color属性打印green方法怎么办呢

使用super.color就可以访问

同样,使用super.printColor()可以调用父类的printColor()方法。

 

问题2:如果层级Parent还有一个父类PerParent也有相同属性,我们在Child该怎么才能调用到?
super.super.color?
明显不对。

此时我们只要向上转型即可。(Perparent)child.color;

不过这里要注意的是,方法重写后,使用(Parent)child.printColor()是无法访问到父类方法的。

文章的结尾,我留下两个习题,如果你看一眼能正确给出答案的话,我只能为你默默刷66个6666。

题1:

public class Parent{
public String color;
public Parent(){
this.color="green";
}
public void printColor(){
System.out.println(color);
}
public static void main(String args[]){
new Child().printColor();
}
}
class Child extends Parent{
//我们在这里删掉color属性
//public String color;
public Child(){
}
public void printColor(){
System.out.println(color);
}
}

 

题2:

public class Parent{
public String color;
public Parent(){
this.color="green";
}
//在这里删掉父类的printColor方法
/* public void printColor(){
System.out.println(color);
}*/
public static void main(String args[]){
new Child().printColor();
}
}
class Child extends Parent{
//我们在这里删掉color属性
//public String color;
public Child(){
}
public void printColor(){
System.out.println(color);
}
}

 

半深入理解Java属性继承

标签:

原文地址:http://www.cnblogs.com/levi1994/p/5568433.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!