Java继承的概念

来自CloudWiki
跳转至: 导航搜索

继承的概念

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

例子一: 动物类别的属性

Java5-100.jpg


兔子和羊属于食草动物类,狮子和豹属于食肉动物类。

食草动物和食肉动物又是属于动物类。

所以继承需要符合的关系是:is-a,父类更通用,子类更具体。

虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。

例子二: 汽车界的继承

Ai20-4-31.png

什么叫做汽车的平台化 ?两个产品的有些系统,子系统或零件是一样的,特别是不可见部分;而对于可见部分则根据不同地区,针对不同市场及不同用户群进行个性化设计。这种相同的系统,子系统及零部件的组成,被称之为平台,也就是说,平台是由一些共用件组成,在一个平台上,通过针对市场的个性化设计,可形成多个产品。

平台化- 很多人常挂在嘴边,表征就是很多零部件是通用的,做的最厉害的是大众,有人说大众只有一款车:高尔夫。拉长就是帕萨特,改名就叫迈腾,减掉一个后座就是CC,再拉长就叫辉腾,拍成方的就是途安,加多三个后座就是夏朗,加高底盘就是途观,再撑大点就是途锐,拍扁就是尚酷,加个屁股就是速腾,缩短点就是POLO,掰弯搓圆就是甲壳虫这说明大众平台化做的多么成功,通用件多。

继承在开发中的应用

假设开发动物类,其中动物分别为企鹅以及老鼠,要求如下:

   企鹅:属性(姓名,id),方法(吃,睡,自我介绍)
   老鼠:属性(姓名,id),方法(吃,睡,自我介绍)

企鹅类:

public class Penguin { 
    private String name; 
    private int id; 
    public Penguin(String myName, int  myid) { 
        name = myName; 
        id = myid; 
    } 
    public void eat(){ 
        System.out.println(name+"正在吃"); 
    }
    public void sleep(){
        System.out.println(name+"正在睡");
    }
    public void introduction() { 
        System.out.println("大家好!我是"         + id + "号" + name + "."); 
    } 
}

老鼠类:

public class Mouse { 
    private String name; 
    private int id; 
    public Mouse(String myName, int  myid) { 
        name = myName; 
        id = myid; 
    } 
    public void eat(){ 
        System.out.println(name+"正在吃"); 
    }
    public void sleep(){
        System.out.println(name+"正在睡");
    }
    public void introduction() { 
        System.out.println("大家好!我是"         + id + "号" + name + "."); 
    } 
}

仔细观察,发现企鹅类和老鼠类有很多相同的字段和方法,考虑到我们还有许多小动物需要开发,我们能不能集约一下,不要写这么多重复的代码?

这个时候,继承就派上用场了。

继承是面向对象编程中非常强大的一种机制,它首先可以复用代码。我们可以设计一个父类Animal类,让它具有企鹅、老鼠等小动物的普遍属性,然后企鹅类和老鼠类继承这个类之后,就获得了Person的所有功能,我们只需要为企鹅和老鼠类编写新增的功能,其他已有的功能可以全部复用自父类Animal。

Java使用extends关键字来实现继承:

父类Animal:

public class Animal { 
    private String name;  
    private int id; 
    public Animal(String myName, int myid) { 
        name = myName; 
        id = myid;
    } 
    public void eat(){ 
        System.out.println(name+"正在吃"); 
    }
    public void sleep(){
        System.out.println(name+"正在睡");
    }
    public void introduction() { 
        System.out.println("大家好!我是"         + id + "号" + name + "."); 
    } 
}

这个Animal类就可以作为一个父类,然后企鹅类和老鼠类继承这个类之后,就具有父类当中的属性和方法,子类就不会存在重复的代码,维护性也提高,代码也更加简洁,提高代码的复用性(复用性主要是可以多次使用,不用再多次写同样的代码) 继承之后的代码:

企鹅类:

public class Penguin extends Animal { 
     // 不要重复name和id字段/方法,
    // 只需要定义新增color字段/方法:
    String type;
    public Penguin(String myName, int myid) { 
        super(myName, myid);
        this.type="type1"; 
    } 
}

老鼠类:

public class Mouse extends Animal { 
    // 不要重复name和id字段/方法,
    // 只需要定义新增color字段/方法:
    String color;
    public Mouse(String myName, int myid) { 
        super(myName, myid); 
        this.color="white";
    } 
}

可见,通过继承,Mouse只需要编写额外的功能,不再需要重复代码。

在OOP的术语中,我们把Animal称为超类(super class),父类(parent class),基类(base class),把Mouse称为子类(subclass),扩展类(extended class)。


继承的含义和格式

  • 继承就是子类继承父类的特征和行为,使得代码能够复用;
  • 声明继承之后,子类就可以使用父类所有的变量和方法;
  • 如果有变量和方法父类当中不存在,子类可以自己定义。

继承的格式

在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:

类的继承格式

class 父类 {
}

class 子类 extends 父类 {
}


继承举例

【实例5-3】使用继承思想实现汽车类,以及公交车和卡车类。(重型汽车、洒水车、专用车等;软件公司有一个产品衍生的多款产品等)

public class Car {//定义父类
	  public String bound;//汽车牌子
	  public int count;//汽车载人数
	  public void showInfo(){//显示汽车基本信息
	    System.out.print("车的牌子是:"+bound+";车载人数:"+count);
	  }
}
public class Bus extends Car{//Car的子类Bus
	  public String number;//子类自己属性-几路公交车
	  protected void showStation(String station){//子类自己方法-报站名
		System.out.println("你到"+station);
	  }
}
public class Truck extends Car{//Car的子类Truck
	  public double weight;//子类自己属性-载重
	  public void loading(String things){//子类自己的方法-装货
		System.out.println("车里装"+things);
	  }
}

本例中主要描述了关于汽车的继承关系。其中Bus和Truck分别代表公交车和货车(实体),它们都是一种汽车Car(概念)。因此Car作为了父类,Bus和Truck分别是由Car派生出来的子类。

  • 继承就是子类继承父类的特征和行为,使得代码能够复用;
  • 声明继承之后,子类就可以使用父类所有的变量和方法;
  • 如果有变量和方法父类当中不存在,子类可以自己定义。


【经 验】 我们可以看出父类都是概念性的类别词汇,例如:汽车、电灯、风扇。而汽车又可分为公交车、货车等;电灯又分为台灯、日光灯、彩灯等;风扇又可分为吊扇、台扇等。Java是面向对象程序设计语言,来形容实际存在的实体对象,所以编程前对程序需求分析应从对象入手,总结多个对象之间的相同点和不同点,把相同点抽象出来组成一个概念性的父类,把不同点作为子类自己独有的性质。因此通常情况父类是没有实例化的必要。

技术要点

  • 如何写一个子类?
  • 子类如何继承父类的私有变量?
  • 子类何时需要调用父类的构造方法?
  • 子类和父类如何相互转化 ?

如何写一个子类?

extends关键字

在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。

public class Animal { 
    private String name;   
    private int id; 
    public Animal(String myName, String myid) { 
        //初始化属性值
    } 
    public void eat() {  //吃东西方法的具体实现  } 
    public void sleep() { //睡觉方法的具体实现  } 
} 
 
public class Penguin  extends  Animal{ 
}

如何引用父类的变量

protected关键字

继承有个特点,就是子类无法访问父类的private字段或者private方法。例如,Student类就无法访问Person类的name和age字段:

class Person {
    private String name;
    private int age;
}
class Student extends Person {
    public String hello() {
        return "Hello, " + name; // 编译错误:无法访问name字段
    }
}

这使得继承的作用被削弱了。为了让子类可以访问父类的字段,我们需要把private改为protected。用protected修饰的字段可以被子类访问:

class Person {
    protected String name;
    protected int age;
}
class Student extends Person {
    public String hello() {
        return "Hello, " + name; // OK!
    }
}

因此,protected关键字可以把字段和方法的访问权限控制在继承树内部,一个protected字段和方法可以被其子类,以及子类的子类所访问,后面我们还会详细讲解。

super 与 this 关键字

super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

this关键字:指向自己的引用。

实例

class Animal {
  void eat() {
    System.out.println("animal : eat");
  }
}
 
class Dog extends Animal {
  void eat() {
    System.out.println("dog : eat");
  }
  void eatTest() {
    this.eat();   // this 调用自己的方法
    super.eat();  // super 调用父类方法
  }
}
 
public class Test {
  public static void main(String[] args) {
    Animal a = new Animal();
    a.eat();
    Dog d = new Dog();
    d.eatTest();
  }
}

输出结果为:

animal : eat
dog : eat
animal : eat


如何调用父类构造方法

子类不能继承父类的构造方法,但是为了使用方便,可以在自己的构造方法中调用它。

  • 如果父类没有无参数的构造方法,子类就必须显式地通过super关键字调用父类的构造器并配以适当的参数列表。
  • 如果父类有无参数的构造方法,则在子类的构造器中用super调用父类构造器不是必须的,因为如果我们这里不写,系统也会默默去调用下父类的构造方法。

我们来看一个例子:

Person类:

class Person {
    protected String name;
    protected int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

Student类:

class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
        this.score = score;
    }
}

测试类Main.java:

public class Main {
    public static void main(String[] args) {
        Student s = new Student("Xiao Ming", 12, 89);
    }
}

运行上面的代码,会得到一个编译错误,大意是在Student的构造方法中,无法调用Person的构造方法。

这是因为在Java中,任何class的构造方法,第一行语句必须是调用父类的构造方法。如果没有明确地调用父类的构造方法,编译器会帮我们自动加一句super();,所以,Student类的构造方法实际上是这样:

class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
        super(); // 自动调用父类的构造方法
        this.score = score;
    }
}

但是,Person类并没有无参数的构造方法,因此,编译失败。

解决方法是调用Person类存在的某个构造方法。例如:

class Student extends Person {

   protected int score;
   public Student(String name, int age, int score) {
       super(name, age); // 调用父类的构造方法Person(String, int)
       this.score = score;
   }

}

这样就可以正常编译了!


  • 如果父类有无参构造器,则在子类的构造器中用super调用父类构造器不是必须的,可以调用,也可以不调用。

Person类:

class Person {
    protected String name;
    protected int age;
    
    public Person( ){

    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

Student类:

class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
        this.score = score;
    }
}

</nowiki>


因此我们得出结论:如果父类没有默认的构造方法,子类就必须显式调用super()并给出参数以便让编译器定位到父类的一个合适的构造方法。

这里还顺带引出了另一个问题:即子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的。

继承的特性

  • 如果子类继承了父类,则子类自动具有父类的全部非私有的数据成员(数据结构)和成员方法(功能);
  • 子类可以定义自己的数据成员和成员函数, 同时也可以修改父类的数据成员或重写父类的方法;
  • Java的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如A类继承B类,B类继承C类,所以按照关系就是C类是B类的父类,B类是A类的父类,这是java继承区别于C++继承的一个特性。
  • Java中却允许多层继承。例如,子类A可以有父类B,父类B同样也可以再拥有父类C。因此子类都是“相对”的;
  • 在Java中,Object类为特殊超类或基类,所有的类都直接或间接地继承Object。


要点拾遗

父类变量和方法的调用

父类构造方法的调用

子类新建自己的变量和方法

子类重写父类的方法

参考文档: http://www.runoob.com/java/java-override-overload.html

返回 Java程序设计