1.类的继承

1.1 继承的定义

在程序中,一个类想要继承另一个类,需要使用关键字extends,其基本语法如下:

    [修饰符] class 子类名 extends 父类名{
        //程序核心代码
    }

上述语法中,类的修饰符是可选的,用来指定类的访问权限,可以使用public或者省略不写,子类名和父类名都是必选的,并且子类和父类之间要使用extends关键字实现继承关系。

class Animal{
    public String name;
    //private String name;
    void shout(){
        System.out.println("动物发出叫声");
    }
}
class Dog extends Animal{
    public void printName(){
        System.out.println("name:"+name);
    }
}
public class test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name="赖皮狗";
        dog.printName();
        dog.shout();
    }
}

alt

如果我们将上述第二行代码换成第三行,那么将会显示报错,错误如下图,这就是封装的重要性。

alt

那么如何进行修改了?

class Animal{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    void shout(){
        System.out.println("动物发出叫声");
    }
}
class Dog extends Animal{
    private String name;
    public void printName(){
        System.out.println("name:"+getName());
    }
}
public class test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setName("赖皮狗");
        dog.printName();
        dog.shout();
    }
}

alt

由上述代码所知,子类在继承父类的时候,会自动拥有父类所有的公共成员。

在类的继承中,需要注意以下问题:

(1)在Java中,类只支支持单继承,不允许多继承,也就是说一个类只能有一个直接父类,下面这种情况是不合法的。

class A()
class B()
class C extends A,B()

(2)多个类可以继承同一个父类,如下:

class A()
class B extends A()
class C extends A()

(3)在Java中,多层继承是可以的,即一个类可以再去继承另外的父类,例如:

class A()
class B extends A()
class C extends B()

1.2 重写父类的方法

在继承关系中,子类会自动继承父类中公共的方法,但有时在子类中需要对继承的方法进行一些修改,即对父类的方法进行重写。需要注意的是,子类中重写的方法需要和父类被重写的方法具有相同的方法名,参数列表以及返回值类型。

class Animal{
    public void shout(){
        System.out.println("动物发出叫声");
    }
}
class Dog extends Animal{
    @Override
    public void shout() {
        System.out.println("汪汪汪。。。");
    }
}
public class test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.shout();
    }
}

alt

注意: 子类重写父类方法时,不能使用比父类中被重写的方法更严格的访问权限,如父类中的方法访问权限是public,子类重写父类该方法的访问权限就不能是private。

1.3 super关键字

从上述程序运行结果可以看出,当子类重写父类的方法后,子类对象将无法直接访问父类被重写的方法。为了解决这个问题,在Java中专门提供了一个关键字super来访问父类的成员,例如访问父类的成员变量,成员方法和构造方法。具体用法如下:

(1)使用super关键字调用父类的成员变量和成员方法。

super.成员变量
super.成员方法([参数1,参数2...])
class Animal{
    public String name="动物";
    public void shout(){
        System.out.println("动物发出叫声");
    }
}
class Dog extends Animal{
    public String name="犬类";
    @Override
    public void shout() {
        super.shout();
    }
    public void printName(){
        System.out.println("name = " + super.name);
    }
}
public class test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.shout();
        dog.printName();
    }
}

alt

(2)使用super关键字调用父类的构造方法,形式如下

super([参数1,参数2...])
class Animal{
    public Animal(String name) {
        System.out.println("我是一只"+name);
    }
}
class Dog extends Animal{
    public Dog(){
        super("癞皮狗");
    }
}
public class test {
    public static void main(String[] args) {
        Dog dog = new Dog();
    }
}

alt

注意: 通过super调用父类构造方法的代码必须位于子类构造方法的第一行,并且只能出现一次,否则编译就会报错。

class Animal{
    public Animal(String name) {
        System.out.println("我是一只"+name);
    }
}
class Dog extends Animal{
    public Dog(){
    }
}
public class test {
    public static void main(String[] args) {
        Dog dog = new Dog();
    }
}

alt

从上述图片中可以看出,程序编译出现错误。出错的原因是,在子类的构造方法中,一定会调用父类的某个构造方法。这时可以在子类的构造方法中通过super关键字指定调用父类的哪个构造方法,如果没有指定,在实例化子类对象时,会默认调用父类的无参构造函数,而在上述代码中,父类中只定义了有参构造函数,未定义无参构造函数,所以在子类默认调用父类无参构造方法时就会出错。

为解决上述的编译错误,可以在子类中显式的调用父类中已有的构造方法,或者在父类中定义无参构造函数。

class Animal{
    public Animal() {
        System.out.println("我是一只动物");
    }

    public Animal(String name) {
        System.out.println("我是一只"+name);
    }
}
class Dog extends Animal{
    public Dog(){
    }
}
public class test {
    public static void main(String[] args) {
        Dog dog = new Dog();
    }
}

alt

通过上述代码可以得出一个结论:在定义一个类时,如果没有特殊需求,当定义了有参构造方法后,尽量在类中在显式地定义一个无参构造方法,这样就可以避免该类在继承时出现错误。