Java继承容易掉进的坑

1.覆盖私有方法会怎样

私有方法对子类不可见,因此并没有覆盖私有方法一说,当然,如果你强行写一个签名相同的方法在子类里,假装在覆盖私有方法——并不会起到覆盖的作用,编译不会报错,而是会将他作为子类的方法。

譬如以下的例子:

[java]
public class Father {
private void name() {
System.out.println("method in Father");
}

public static void main(String[] args) {
Father a = new Son();
a.name();
}
}

class Son extends Father {
public void name() {
System.out.println("method in Son");
}
}
[/java]

输出

[text]
method in Father
[/text]

本来,上面这个例子中的Father中的name方法如果是public的话,那么输出就是“method in Son”了。
因此,我们应该:

  1. 应该避免在子类中创建和父类中私有方法同名的方法,因为这没有什么意义,还容易出现语义上的错误。
  2. 对想要覆盖的方法使用@Override注解。让编译器检查是否覆盖成功。

2.只有方法是多态的,而域不是的。

要在方法内调用父类的域,需要使用super关键字,super.field进行调用。

3.静态方法不是多态的。

譬如

[java]
public class Start {
public static void main(String[] args) {
A a=new B();
a.m();
}
}
class A{
public static void m(){
System.out.println("method in A");
}
}
class B extends A{
public static void m(){
System.out.println("method in B");
}
}
[/java]

本来,多态的目标是,对向上转型后A类型的a对象调用B.m()方法,即输出“method in B”,然而事实却是输出了“method in A”,证实了静态方法不会被覆盖。又一次提醒我们想覆盖时使用@Override注解。

4.继承时子类的构造器中会发生些什么?有何特殊?

子类的构造器确实比较特殊,因为如果子类的构造器的第一句话没有显式的指定调用基类的某个构造器,将会产生一个隐式调用:

调用基类的默认构造器

且这个调用会优先于子类构造器中所有其他语句而被执行。

如果没意识到上面这一个过程的存在,就可能犯错。此外,如果上述过程发生了,但基类没有默认构造器,会编译报错。

譬如:

[java]
class A{
A(int n){

}
}
class B extends A{

}
[/java]

会直接报错,因为B没写构造器,将调用B的默认构造器,B的默认构造器的第一句话会调用A的默认构造器,而A没有默认构造器(因为A中已经有了有参构造器且没有显式的无参构造器),所以报错。

5.父类构造器调用了一个方法,这个方法在子类中存在覆盖,那么究竟会调用哪一个方法?

答:调用子类的方法。

这个问题非常容易让人迷惑,譬如下面的例子:

假设我们有一个论坛,论坛里有一个IT版块,想要进入IT版块必须要首先登录论坛,再登录进入IT版块。设计如下,在对象创建时就进行登录。

[java]
class BBS{
BBS(){
this.login();//这个login()方法究竟会调用哪个login()?
}
void login(){
System.out.println("登录论坛");
}

}
class BBSITSection extends BBS{
BBSITSection(){
super();
this.login();
}
void login(){
System.out.println("登录IT版块");
}
}

public class Start {
public static void main(String[] args) {
BBSITSection bbsitSection=new BBSITSection();
}
}

[/java]

以上程序,本意打算BBSITSection创建时,首先调用父类BBS的构造器,BBS的构造器会自己处理论坛登录,然后再进行IT版块的登录。

然而输出却是:

[text]
登录IT版块
登录IT版块
[/text]

可见父类的login方法从未被调用过,而是父类也调用了子类的login方法。

因此,总结一下:

  1. 问题结论是:父类构造器调用了一个方法,这个方法在父类中有,且在子类中被覆盖,最终将会调用子类的方法。
  2. 启示:永远避免在父类构造器中调用子类具有覆盖的方法,除非你知道自己在做什么。

6.继承,抽象类,接口什么关系,如何选择?

继承->抽象类->接口 【抽象等级越来越高】

区别就在于对“实现”的要求

继承必须包含所有实现。

抽象类允许没有具体实现的方法存在。

接口不允许实现的存在(Java8增加的默认方法,只是为了增加接口的向上兼容性)

共有 2 条评论

padeoe进行回复 取消回复

电子邮件地址不会被公开。