JAVA面向对象4:类的继承

类的继承

1、继承的概念

在现实生活中,继承一般指的是子女继承父辈的财产。在程序中,继承描述的是事物之间的所属关系,通过继承可以是多种事物之间形成一种关系体系。例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物,同理,波斯猫和巴厘猫继承自猫,而沙皮狗和斑点狗继承自狗。这些动物之间会形成一个继承体系。

在Java中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类被称作子类,现有类被称作父类,子类会自动拥有父类所有可继承的属性和方法。在程序中,如果想声明一个类继承另一个类,需要使用extends关键字,接下来通过一个案例来学习子类是如何继承父类的。
案例1:

//定义Animal类
class Animal {
    String name;                         //定义name属性
    //定义动物叫的方法
    void shout() {
        System.out.println("动物发出叫声");
    }
}
//定义Dog类继承Animal类
class Dog extends Animal {
    //定义一个打印name的方法
    public void printName() {
        System.out.println("name="+name);
    }
}
//定义测试类
public class Example1 {
    public static void main(String[] args) {
        Dog dog=new Dog();                //创建一个Dog类的实例对象
        dog.name="沙皮狗";                 //为Dog类的name属性进行赋值
        dog.printName();                  //调用Dog类的printName()方法
        dog,shout();                      //调用Dog类继承来的shout()方法
    }
}

在案例1中,Dog类通过extends关键字继承了Animal类,这样Dog类便是Animal类的子类。从运行结果不难看出,子类虽然没有定义name属性和shout()方法,但是却能访问这两个成员。这就说明,子类在继承父类的时候,会自动拥有父类所有的成员。
在类的继承中需要注意一些问题:

1.在Java中,类只支持单继承,不允许多重继承,也就是说一个类只能有一个直接父类。

class A {}
class B {}
class C extends A,B {}                 //C类不可以同时继承A类和B类

2.多个类可以继承一个父亲,例如下面这种情况是允许的。

class A {}
class B extends A {}
class C extends A {}                   //类B和类C都可以继承类A

3.在Java中,多层继承是可以的,即一个类的父亲可以再去继承另外的父亲,例如C类继承自B类,而B类又可以去继承A类,这时C类也可称作A类的子类。下面这种情况是允许的。

class A {}
class B extends A {}                  //类B继承类A,类B是类A的子类
class C extends B {}                  //类C继承类B,类C是类B的子类,同时也是类A的子类

4.在Java中,子类和父类是一种相对概念,也就是说一个类是某个类父亲的同时,也可以是另一个类的子类。例如上面示例中的类B,类B是类A的子类,同时又是类C的父类。


2、重写父类方法

在继承关系中,子类会自动继承父类中定义的方法,但有时在子类中需要对继承的方法进行一些修改,即对父类的方法进行重写。需要注意的是,在子类中重写的方法需要和父类被重写的方法具有相同的方法名、参数列表以及返回值类型。
在案例1中,Dog类从Animal类继承了shout()方法,该方法在被调用时会打印“动物发出叫声”,这明显不能描述一种具体动物的叫声,Dog类对象表示犬类,发出的叫声应该是“汪汪”。为了解决这个问题,可以再Dog类中重写父类Animal中的shout()方法。
案例2:

//定义Animal类
class Animal {
    //定义动物叫的方法
    void shout() {
        System.out.println("动物发出叫声");
    }
}
//定义Dog类
class Dog extends Animal {
    //定义狗叫的方法
    void shout() {
        Syetem.out.println("汪汪……");
    }
}
//定义测试类
class Example2 {
    public static void main(String[] args) {
        Dog dog=new Dog();            //创建Dog类的实例对象
        dog.shout();                  //调用dog重写的shout()方法
    }
}

案例2中,定义了Dog类并且继承自Animal类。在子类Dog中定义了一个shout()方法对父类的方法进行重写。从运行结果可以看出,在调用Dog类对象的shout()方法时,只会调用子类重写的该方法,并不会调用父类的shout()方法。

注意!!!
子类重写父类方法时,不能使用比父类中被重写的方法更严格的访问权限,如父类中的方法时public的,子类的方法就不能是private的,关于访问权限中更多的只是,我们将在后面描述,这里先有个印象就行。


3、super关键字

从案例2中可以看出,当子类重写父类的方法后,子类对象将无法访问父类被重写的方法,为了解决这个问题,在Java中专门提供了一个super关键字用于访问父类的成员。例如访问父类的成员变量、成员方法和构造方法。

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

super.成员变量
super.成员方法([参数1,参数2...])

案例3:

//定义Animal类
class Animal {
    String name="动物";
    //定义动物叫的方法
    void shout() {
        System.out.println("动物发出叫声");
    }
}
//定义Dog类继承动物类
class Dog extends Animal {
    String name="犬类";
    //重写父类的shout()方法
    void shout() {
        super.shout();                            //访问父类的成员方法
    }
    //定义打印name的方法
    printName() {
        System.out.println("name="+super.name);   //访问父类的成员变量
    }
}
//定义测试类
public class Example3 {
    public static void main(String[] args) {
        Dog dog=new dog();                       //创建一个dog对象
        dog.shout();                             //调用dog对象重写的shout()方法
        dog.printName();                         //调用dog对象的printName()方法
    }
}

案例3中,定义了一个Dog类继承Animal类,并重写了Animal类的shout()方法。在子类Dog的shout()方法中使用“super.name”访问副类的成员变量。子类通过super关键字可以成功地访问父类的成员变量和成员方法。

2.使用super关键字调用父类的构造方法。

super.{[参数1,参数2...]}

案例4:

//定义Animal类
class Animal {
    //定义Animal类有参的构造方法
    public Animal(String name) {
        System.out.println("我是一只"+name);
    }
}
//定义Dog类继承Animal类
class Dog extends Animal {
    public Dog() {
        super("沙皮狗");                //调用父类有参的构造方法
    }
}
//定义测试类
public class Example4 {
    public static void main(String[] args) {
        Dog dog=new Dog();             //实例化子类Dog对象
    }
}

根据前面所学的知识,案例4在实例化Dog对象时一定会调用Dog类的构造方法。从结果可以看出,Dog类的构造方法被调用时父类的构造方法也被调用了。需要注意的是,通过super调用父类构造方法的代码必须位于子类构造方法的第一行,并且只能出现一次。
将案例4第11行代码去掉,再次编译程序会报错。
出错的原因是,在子类的构造方法中一定会调用父类的某个构造方法。这时可以在子类的构造方法中通过super指定调用父类的那个构造方法,如果没有指定,在实例化子类对象时,会自动调用父类无参的构造方法。

为了解决上述程序的编译错误,可以在子类中显式地调用父类中已有的构造方法,当然也可以选择在父类中定义无参的构造方法。
案例5:

//定义Animal类
class Animal {
    //定义Animal无参的构造方法
    public Animal() {
        System.out.println("我是一只动物");
    }
    //定义Animal有参的构造方法
    public Animal(String[] args) {
        System.out.println("我是一只"+name);
    }
}
//定义Dog类,继承自Animal类
class Dog extends Animal {
    //定义Dog类无参的构造方法
    public Dog() {
        //方法体中无代码
    }
}
//定义测试类
public class Example5 {
    public static void main(String[] args) {
        Dog dog=new Dog();           //创建Dog类的实例对象
    }
}

从结果可以看出,子类在实例化时默认调用了父类无参的构造方法。通过这个案例还可以得出一个结论,那就是在定义一个类时,如果没有特殊的需求,尽量在类中定义一个无参的构造方法,避免被继承时出现错误。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值