紫薇星上的Java——继承

之前在JavaOO(7)中讲到过继承,不过因为篇幅关系讲的比较浅,有些需要注意的地方没有重点提到,所以今天我们就来开一篇专门来讲一下继承~


由于继承的基础点都在紫薇星上的Java——JavaOO(7)这篇文章中讲过,感兴趣的同学可以去看一下,这篇文章会开始补充细节和需要注意的地方。

1.引出继承

  • 继承的概念

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类,继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

  • 继承的特点

继承的主要特点在于:可以扩充已有类的功能。

  • 引出继承问题

所谓良好的代码指的是结构性合理、适合于维护、可重用性很高的代码。但如果现在只是按照之前学到的概念来进行程序的编写,那么不可避免地需要面对重复的问题,比如我们有两个类:

//企鹅类
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 + "."); 
    } 
}

大家可以看到,虽然实例化对象后我们知道这两个对象是不一样的,但在这两个类中出现了重复的代码结构,导致程序臃肿,增加维护成本,这种情况下重复的代码我们就可以使用继承来解决:

//公共父类
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 { 
    public Penguin(String myName, int myid) { 
        super(myName, myid); 
    } 
}
 
//老鼠类
public class Mouse extends Animal { 
    public Mouse(String myName, int myid) { 
        super(myName, myid); 
    } 
}

这样就极大的减少了代码量,提高了代码效率。

2.继承实现

  • 继承的特性
  1. 子类拥有父类非 private 的属性、方法。
  2. 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
  3. 子类可以用自己的方式实现父类的方法。
  4. Java 的继承是单继承,但可以多重继承。单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
  5. 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
  • 继承关键字

继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。

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{ 
}

在这个例子中,子类Penguin类没有任何的方法,仅仅简单继承了父类Animal类的方法,但是我们在实例化对象,调用父类方法时都没有问题。

implements关键字

使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口。

public interface A {
    public void eat();
    public void sleep();
}
 
public interface B {
    public void show();
}
 
public class C implements A,B {
}

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();
  }
}

3.子类对象实例化例程

从正常的逻辑来讲,没有父类就没有子类,所以我们对于子类对象实例化前一定要实例化好父类对象。

现在我们来举一个例子,首先我们编写下列代码:

class Person{
	public Person() {
		System.out.println("【Person父类】一个新的Person父类对象实例化成功了!");
	}
}

class Student extends Person{
	public Student() {
		System.out.println("【Student子类】一个新的Student子类对象实例化成功了!");
	}
}

public class first {
	public static void main( String args[] ){
		new Student();
	}
}

在上述代码中,我们创建了两个类:Person父类、Student子类,这两个类中只有一个构造方法,用于打印一条语句,现在我们仅仅简单的实例化一下子类对象,编译后运行结果如下:

【Person父类】一个新的Person父类对象实例化成功了!
【Student子类】一个新的Student子类对象实例化成功了!

可以看到,实例化子类对象前同时实例化了父类对象,说明我们即使没有调用父类实例化对象,系统也会自动的调用父类的构造方法,这就相当于在子类的构造方法中隐含了一个“super()”方法。

现在我们在子类的构造方法中添加一条super()语句:

class Student extends Person{
	public Student() {
		super();//写与不写此语句效果一样
		System.out.println("【Student子类】一个新的Student子类对象实例化成功了!");
	}
}

再次运行,结果如下:

【Person父类】一个新的Person父类对象实例化成功了!
【Student子类】一个新的Student子类对象实例化成功了!

super()表示的就是子类调用父类构造方法的语句,该语句子只允许放在子类构造方法的首行。在默认情况下的实例化处理时,子类只会调用父类中的无参构造方法,所以写与不写“super()”区别不大。但是如果父类中没有提供无参构造,这个时候就必须利用“super()”来调用有参构造

我们现在在Person类中定义一个有参构造,并将Student类中方法注释掉:


class Person{
	private String name;
	private int age;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
		System.out.println("【Person父类】一个新的Person父类对象实例化成功了!");
	}
}

class Student extends Person{
//	public Student() {
//		super();
//		System.out.println("【Student子类】一个新的Student子类对象实例化成功了!");
//	}

}

public class first {
	public static void main( String args[] ){
		new Student();
	}
}

上述代码运行结果如下:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
	Implicit super constructor Person() is undefined for default constructor. Must define an explicit constructor

	at My_Demo/com.zijun.wang1.Student.<init>(first.java:27)
	at My_Demo/com.zijun.wang1.first.main(first.java:37)

可以看到明显的报错,现在我们将Student类中的构造方法加进去,并使用super(),再添加一个属性school,完成了继承的使命——扩展功能,代码如下:

class Person{
	private String name;
	private int age;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
		System.out.println("【Person父类】一个新的Person父类对象实例化成功了!");
	}
}

class Student extends Person{
	private String school;
	public Student(String name, int age, String school) {
		super(name, age);//明确调用父类构造
		this.school = school;
		System.out.println("【Student子类】一个新的Student子类对象实例化成功了!");
	}

}

public class first {
	public static void main( String args[] ){
		new Student("紫郡",19,"哈佛大学");
	}
}

上述代码编译运行结果如下:

【Person父类】一个新的Person父类对象实例化成功了!
【Student子类】一个新的Student子类对象实例化成功了!

可以看到成功实例化了父类对象与子类对象,所以在我们写类的时候,无参构造是非常重要的。

4.继承的相关限制

需要注意的是 Java 不支持多继承,但支持多重继承

继承类型描述代码格式
单继承B类继承A类

public class A{...}

public class B extends A{...}

多重继承C类继承B类,B类继承A类

public class A{...}

public class B extends A{...}

public class C extends B{...}

不同类继承同一个类C类继承A类,B类继承A类

public class A{...}

public class B extends A{...}

public class C extends A{...}

多继承(不支持)C类继承A类,C类继承B类

public class A{...}

public class B{...}

public class C extends A,B{...}

继承的主要目的是扩展已有类的功能,但多重继承的目的是希望可以同时继承多个类中的方法,而面对多继承的要求应该将范围限定在同一类中。如果说使用了多重继承,在上述表格中,C类也可以继承B类的父类的方法,但应该有个限度,我们在编写代码的时候,理论上层次不应该超过三层。

在进行继承关系定义的时候,实际上子类可以继承父类中的所有操作结构。但是对于私有操作属于隐式继承,而所有非私有操作属于显式继承。继承一旦发生了,父类中所有的操作在子类中就都能操作了。

5.实际案例分析

下面我们来写一个实际案例,建立一个Person类和一个Student类,功能要求如下:

  1. Person类中包含4个私有型数据成员变量name,addr,sex,age,分别对应Person对象的姓名,地址,性别,年龄。一个4参构造方法,一个2参构造方法,一个无参构造方法。
  2. Student类继承Person类,并增加成员变量math,english,用来存放数学和英语的成绩。一个6参构造方法,一个2参构造方法,一个无参构造方法和重写输出方法显示6中属性。

正常来讲我们先不考虑之类的问题,我们先来实现Person类:

class Person{
	private String name;
	private int age;
	private char sex;
	private String addr;

	public Person() {}

	public Person(String name, String addr) {
		this(name, 18, '男', addr);
	}

	public Person(String name, int age, char sex, String addr) {
		this.name = name;
		this.age = age;
		this.sex = sex;
		this.addr = addr;
	}
	public String getInfo() {
		return " [姓名:" + this.name + ", 年龄:" + this.age + ", 性别:" 
+ this.sex + ", 地址:" + this.addr + "]";
	}
}

这里我们一共有三个构造方法,无参构造,2参构造,4参构造,其中4参是主要方法,2参构造引用了4参中的this属性。

接下来我们来编写Student类:

class Student extends Person{
	private double math;
	private double english;

	public Student() {}

	public Student(String name, String addr) {
		super(name, addr);
	}

	public Student(String name, int age, char sex, String addr, double math, double english) {
		super(name, age, sex, addr);
		this.math = math;
		this.english = english;
	}
	
	public String getInfo() {
		return super.getInfo() + "\t|- 数学成绩:" + this.math + ", 英语成绩:" + this.english;
	}
}

由于是继承Person类,所以编写上简单了很多。其中一共有三个构造方法,无参构造,2参构造,6参构造,其中6参是主要方法,使用了super()来继承父类的属性。

接下来我们实例化几个对象来看一下:

public class first {
	public static void main( String args[] ){
		Student stuA = new Student("紫薇一号", 23, '男', "上海", 98, 76);
		Student stuB = new Student("紫薇二号", "北京");
		System.out.println(stuA.getInfo());
		System.out.println(stuB.getInfo());
	}
}

我们随便实例化了两个对象,其中一个是6参,一个是2参,这里参数要与我们程序中设置的参数相同,运行结果如下:

 [姓名:紫薇一号, 年龄:23, 性别:男, 地址:上海]
	|- 数学成绩:98.0, 英语成绩:76.0
 [姓名:紫薇二号, 年龄:18, 性别:男, 地址:北京]
	|- 数学成绩:0.0, 英语成绩:0.0

继承问题我刚学Java的时候写过一次,只是当时还太年轻,简单几句写给我自己看;上次在JavaOO系列中也系统的写过,不过因为要留篇幅给其他知识点,一些重要的知识点比如super()没有详细说明,今天终于算稍微详细的说了一下,当然随着知识的长进我还会再写几次,每写一次都是让我对继承有更好的认识。今天就到这里,我们下次见👋

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值