任务描述
相关知识
继承的基本概念
继承的特性
子类对象的实例化过程
编程要求
测试说明
任务描述
本关任务:掌握继承的基本概念以及怎么使用继承。
相关知识
为了完成本关任务,你需要掌握:1.继承的基本概念;2.继承的特性;3.子类对象的实例化过程。
继承的基本概念
所谓继承:是指可以让某个类型的对象获得另一个类型的对象的属性的方法。
兔子和羊属于食草动物类,狮子和豹属于食肉动物类。
食草动物和食肉动物又是属于动物类。
所以继承需要符合的关系是:is-a,父类更通用,子类更具体。
虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。
在讲解继承的基本概念之前,读者可以先想一想这样一个问题:现在假设有一个Person类,里面有name与age两个属性,而另外一个Student类,需要有name、age、school三个属性,如图所示,从这里可以发现Person中已经存在有name和age两个属性,所以不希望在Student类中再重新声明这两个属性,这个时候就需要考虑是不是可以将Person类中的内容继续保留到Student类中,也就是引出了接下来所要介绍的类的继承概念。
在这里希望Student类能够将 Person类的内容继承下来后继续使用:
Java类的继承,可用下面的语法来表示:
class 父类 // 定义父类
{
...
}
class 子类 extends 父类 // 用extends关键字实现类的继承
{
...
}
范例:
public class TestPersonStudentDemo {
public static void main(String[] args) {
Student s = new Student();
// 访问Person类中的name属性
s.name = "张三";
// 访问Person类中的age属性
s.age = 18;
// 访问Student类中的school属性
s.school = "哈佛大学";
System.out.println("姓名:" + s.name + ",年龄:" + s.age + ",学校:" + s.school);
}
}
class Person {
String name;
int age;
}
class Student extends Person {
String school;
}
输出结果:
姓名:张三,年龄:18,学校:哈佛大学
由上面的程序可以发现,在Student类中虽然并未定义name与age属性,但在程序外部却依然可以调用name或age,这是因为Student类直接继承自Person类,也就是说Student类直接继承了Person类中的属性,所以Student类的对象才可以访问到父类中的成员。
继承的特性
子类拥有父类非private的属性和方法;
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展;
子类可以用自己的方式实现父类的方法;
在Java中只允许单继承,而不允许多重继承,也就是说一个子类只能有一个父类,但是Java中却允许多层继承,多层继承就是,例如类C继承类B,类B继承类A,所以按照关系就是类A是类B的父类,类B是类C的父类,这是Java继承区别于C++继承的一个特性;
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系)。
多重继承:
class A{
...
}
class B{
...
}
class C extends A,B{
...
}
由上面可以发现类C同时继承了类A与类B,也就是说类C同时继承了两个父类,这在Java中是不允许的。
多层继承:
class A{
...
}
class B extends A{
...
}
class C extends B{
...
}
由上面可以发现类B继承了类A,而类C又继承了类B,也就是说类B是类A的子类,而类C则是类A的孙子类。
子类对象的实例化过程
既然子类可以继承直接父类中的方法与属性,那父类中的构造方法呢?请看下面的范例:
public class TestPersonStudentDemo1 {
public static void main(String[] args) {
Student s = new Student();
}
}
class Person {
String name;
int age;
// 父类的构造方法
public Person() {
System.out.println("1.public Person(){}");
}
}
class Student extends Person {
String school;
// 子类的构造方法
public Student() {
System.out.println("2.public Student(){}");
}
}
输出结果:
1.public Person(){}
2.public Student(){}
从程序输出结果中可以发现,虽然程序第3行实例化的是子类的对象,但是程序却先去调用父类中的无参构造方法,之后再调用了子类本身的构造方法。所以由此可以得出结论,子类对象在实例化时会默认先去调用父类中的无参构造方法,之后再调用本类中的相应构造方法。
实际上在本范例中,在子类构造方法的第一行默认隐含了一个super()语句,上面的程序如果改写成下面的形式,也是可以的:
class Student extends Person{
String school ;
// 子类的构造方法
public Student(){
super() ; //实际上在程序的这里隐含了这样一条语句
System.out.println("2.public Student(){}");
}
}
继承条件下构造方法调用规则如下:
如果子类的构造方法中没有通过super显示调用父类的有参构造方法,也没有通过this显示调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。在这种情况下写不写super()语句效果都是一样;
如果子类的构造方法中通过super显示调用父类的有参构造方法,那将执行父类相应构造方法,而不执行父类无参构造方法;
如果子类的构造方法中通过this显示调用自身的其他构造方法,在相应构造方法中应用以上两条规则;
特别注意的是,如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类Object类的无参构造方法为止。
编程要求
根据提示,在右侧编辑器Begin-End处补充代码:
声明一个Animal类,将属性name和age封装起来,提供对外的公共访问方法;
声明一个Cat类和Dog类,都继承Animal类,分别定义各自的voice方法和eat方法;
在main方法中分别实例化一个Cat对象和Dog对象,设置各自的属性并调用这两个方法,再打印出名字和年龄信息;
具体具体输出要求请看测试说明。
测试说明
测试输入:无
预期输出:
大花猫喵喵叫
大花猫吃鱼
大花猫6岁
大黑狗汪汪叫
大黑狗吃骨头
大黑狗8岁
开始你的任务吧,祝你成功!
package case2;
public class extendsTest {
public static void main(String args[]) {
// 实例化一个Cat对象,设置属性name和age,调用voice()和eat()方法,再打印出名字和年龄信息
/********* begin *********/
Cat c=new Cat();
c.setname("大花猫");
c.setage(6);
c.voice();
c.eat();
System.out.println(c.getname()+c.getage()+"岁");
/********* end *********/
// 实例化一个Dog对象,设置属性name和age,调用voice()和eat()方法,再打印出名字和年龄信息
/********* begin *********/
Dog d=new Dog();
d.setname("大黑狗");
d.setage(8);
d.voice();
d.eat();
System.out.println(d.getname()+d.getage()+"岁");
/********* end *********/
}
}
class Animal {
/********* begin *********/
private String name;
private int age;
public void setage(int age)
{
this.age=age;
}
public void setname(String name)
{
this.name=name;
}
public String getname()
{
return name;
}
public int getage()
{
return age;
}
/********* end *********/
}
class Cat extends Animal {
// 定义Cat类的voice()和eat()方法
/********* begin *********/
public void voice()
{
System.out.println(getname()+"喵喵叫");
}
public void eat()
{
System.out.println(getname()+"吃鱼");
}
/********* end *********/
}
class Dog extends Animal {
// 定义Dog类的voice()和eat()方法
/********* begin *********/
public void voice()
{
System.out.println(getname()+"汪汪叫");
}
public void eat()
{
System.out.println(getname()+"吃骨头");
}
/********* end *********/
}