继承的特点
Java继承通过extends关键字来实现,实际上就是扩展,从一个公共类中扩展一个类出来,这个类既有自己的特色,也有其父类的一些东西。
子类只能从被扩展的父类获得成员变量,方法,和内部类,不能获得构造器和初始化块。
如果一个类在定义的时候并没有显示指定其直接父类,那么默认其父类为Java.lang.Object。子类扩展,父类派生,描述的是同一个动作,只是角度不同而已。
方法重写
继承就是扩展,会继承父类的方法,但有可能父类的方法并不适合子类,因此需要对一些方法进行改动,这就叫方法重写(Override),也可以称为方法覆盖。
package ObjectOriented;
public class Bird {
protected void fly(){
System.out.println("鸟儿飞翔");
}
}
class Ostrich extends Bird{
public void fly(){ //方法重写,对父类方法实现了覆盖
System.out.println("我现在还不能飞!");
}
public static void main(String[] args) {
var o = new Ostrich();
o.fly();
}
}
方法重写遵循“两同两小一大”的原则。这里着重讲两同和一大,两同是指方法名同,形参列表同,一大是指子类方法的访问权限应该大于或者等于父类方法的访问权限。
尤其要注意的是:方法重写要求覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法,不能一个是类方法,一个是实例方法。
当子类覆盖了父类的方法后,子类对象无法访问父类中被覆盖的方法,但是子类的方法重可以调用父类中被覆盖的方法。可以使用super(被覆盖的是实例方法)或者父类类名(被覆盖的是类方法)来调用父类中被覆盖的方法。如果父类的方法的访问权限是private,那么这时子类是无法重写的。
super限定
如果要在子类方法中调用父类被覆盖的方法,则可以使用super来限定,比如为上述的Ostrich增加一个方法,就可以实现对父类方法的调用。
public void call_override_method(){
super.fly();
}
super用于限定该对象调用它从父类继承得到的实例方法或者变量。和this关键字类似,不能出现在static修饰的方法中,因为super和this其实都是指代的对象,this指本对象,super也一样,只是super能调用的方法或者变量是从父类继承而来的。
package ObjectOriented;
class BaseClass{
public int a = 5;
}
public class SubClass extends BaseClass {
public int a = 7;
public void accessOwner(){
System.out.println(this.a);
}
public void accessBase(){
System.out.println(super.a); //访问从父类继承过来的实例变量
}
public static void main(String[] args) {
var sc = new SubClass();
sc.accessBase();
sc.accessOwner();
}
}
如果被覆盖的是类变量,则通过类名来访问。
当程序创建一个子类对象时,系统不仅会为该类中定义的实例变量分配内存,还会为从父类继承得到的所有实例变量分配内存,即使子类中定义了与父类中同名的实例变量
总结一下:为了在子类中访问父类中定义的且被隐藏的实例变量或者被覆盖的(OverRide)的方法,可以通过super来调用。如果是类变量或者类方法,则通过类名访问即可。
调用父类构造器
子类不会获得父类的构造器,但是子类构造器内可以调用父类构造器的初始化代码。就像一个构造器调用另一个重载的构造器一样。
在一个构造器内调用另一个重载的构造器使用this,而在子类构造器中调用父类构造器使用super。使用super调用父类构造器必须出现在子类构造器执行体的第一行,而this也有同样的规定,因此super和this调用不会同时出现。当调用子类构造器来初始化子类对象时,父类构造器总是会在子类构造器之前执行。
子类构造器调用父类构造器分如下情况:
- 子类构造器执行体的第一行使用super显式调用父类构造器,系统将根据super调用里传入的实参列表调用父类对应的构造器。
- 子类构造器执行体的第一行代码是使用this显式调用本类中重载的构造器,系统将根据this调用里传入的实参列表调用本类中的另一个构造器。执行本类中的另一个构造器时也会先调用父类构造器。
- 子类构造器执行体中既没有super调用,也没有this调用,系统将会在执行子类构造器执行体之前,隐式调用父类无参数的构造器。
package ObjectOriented;
class Creature{
public Creature(){
System.out.println("Creature无参数的构造器");
}
public Creature(String x){
System.out.println(x);
}
}
class Animal extends Creature{
public Animal(String name){
System.out.println("Animal带一个参数的构造器" + "该动物的name为" + name);
}
public Animal(String name, int age){
this(name);
System.out.println("Animal带两个参数的构造器" + "其age为" + age);
}
}
public class Wolf extends Animal{
public Wolf(){
super("灰太狼",3);
System.out.println("wolf无参数的构造器");
}
public static void main(String[] args) {
new Wolf();
}
}
输出:
Creature无参数的构造器
Animal带一个参数的构造器该动物的name为灰太狼
Animal带两个参数的构造器其age为3
wolf无参数的构造器