Java继承总结
1. 继承的结果
1.1 继承的限定词
在Java中只有公有继承,而不像C++那般复杂。可以简单的理解为子类所继承到的内容都被超类属性和方法的限定词所限定,只受其影响。
范围 | public | protected | default | private |
---|---|---|---|---|
同类 | √ | √ | √ | √ |
同包子类 | √ | √ | √ | |
非同包子类 | √ | √ | ||
非同包非子类 | √ |
tips1:注意protected和default的区别
tips2:private子类能不能继承到超类的属性将在下面详细介绍。
tips3:protected限定词用于属性时,子类的方法只用作用于自己继承而来的protected属性,而不能作用于其他的子类对象。protected限定词作用于方法时,无法通过对象进行调用,而应该是在其他方法中调用这个方法,
1.2 private限定词
先看代码:
public class test{
public static void main(String[] args){
father father = new father();
son son = new son();
System.out.println(son.getAge());
}
}
class father{
private String name;
private int age;
public father(){
this.name = "李华";
this.age = 18;
System.out.println("===father===");
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
}
class son extends father{
public son(){
System.out.println("===son===");
}
}
结果:
=== father===
=== father===
=== son===
18
这里会产生一个疑问,因为 son 是 father 的子类,而对于属性name、age来说,是不应该被son继承的,可为什么getAge方法是可以得出结果的呢?那不是就存在这样的属性了吗?
1.2.1 子类对象中的超类对象
对于这个问题我们可以先看程序运行结果的其他部分,可以发现 father 的无参构造函数运行了两次,而且是在son构造函数运行结束前先运行结束。从这里可以发现,如果在 son 构造函数运行的时候,没有name、age属性,那么father构造函数中的初始化操作就会报错,不存在怎么赋值的呢?其实这就证明,最起码在子类中是存在超类的属性的。
之所以会有这样的结果,我猜想(还未发现怎么证实)是子类中存在一个匿名的超类对象,即子类中套着一个超类,并且子类无法直接访问超类。所以子类对象确实拥有超类对象中所有的属性和方法,但是超类对象中的私有属性和方法,子类是无法访问到的,只是拥有,但不能使用。
按照这个思路,在子类构造时,会先构造超类,初始化name、age属性,函数也会存在于超类中,然后在继续子类构造。如果调用了setName()、getAge(),那么操作的是这时的this对象,也就是超类对象,所以是允许的。
1.2.2 超类对象的private方法
我们都知道private方法被隐含修饰为final,即无法继承。如果真的有超类对象,那么在public方法中应该可以调用它。
//在father中添加如下方法并调用son对象的sayHi()
public void sayHi(){
say();
}
private void say(){
System.out.println("hi!");
}
结果:
=== father===
=== father===
=== son===
18
hi!
很明显是可以的。同时这也验证了超类对象的存在,因为private方法是final方法,无法被继承。这说明虽然son对象有father类的方法和属性,但这并不是由继承而来的。
并且对于private修饰的变量,是被隐含修饰为final变量的,即无法被修改值。所以如果没有超类对象,超类属性被子类继承了,那么就应该无法用 setXXX 方法对其进行修改。
1.2.3 子类重写超类方法
子类重写了超类的方法,那么调用时使用的谁的方法呢?
/*如果在son类中添加如下代码会报错:
'sayHi()' in 'test.son' clashes with 'sayHi()' in 'test.father'; attempting to assign weaker access privileges ('private'); was 'public'
*/
private void sayHi(){
System.out.println("hello!");
}
报错的含义是访问权限出错。如果将其改为 public 则无这个问题,所以在重写超类的方法时,需要访问权限大于原来的权限,不然编译器无法分辨使用哪一个。
改为 public 则结果显而易见的为 son 类对象的方法。如果需要调用超类的方法,则可以通过super.XXX()
来调用。
1.2.4 子类和超类有同名属性
当子类覆盖超类的成员变量时,超类方法使用的是超类的成员变量,子类方法使用的是子类的成员变量。
- 子类覆盖父类的方法,必须有同样的参数返回类型,否则编译不能通过
- 子类覆盖父类的方法,在jdk1.5后,参数返回类可以是父类方法返回类的子类
- 子类覆盖父类方法,可以修改方法作用域修饰符,但只能把方法的作用域放大,而不能把public修改为private
- 子类方法能够访问父类的protected作用域成员,不能够访问默认的作用域成员,除非子类与父类同包
- 子类的静态方法不能隐藏同名的父类实例方法
2.其他
- 子类继承超类需要在子类构造函数里调用超类的构造函数,即super()。如果子类没有写,则会默认自动加上super()。此外如果写了有参构造函数却没写无参构造函数,则没有默认构造函数。
- 子类重写超类方法,只能方法作用域。