中南林业科技大学Java实验报告七:子类与继承

简介: 中南林业科技大学Java实验报告七:子类与继承

实验7:子类与继承

7.1 实验目的

  1. 掌握类的继承的语法;
  2. 掌握在子类中用子类对象调用父类定义的成员方法;
  3. 学习子类与父类构造方法之间的关系;
  4. 掌握继承时方法的覆盖;
  5. 掌握抽象类的声明,以及子类中实现父类中的抽象方法;

7.2 实验内容

7.2.1 编写一个Java程序,声明一个Person类,成员变量为私有成员变量name,分别编写相应的set和get方法,声明一个Person类的子类Programmer,其私有成员变量为company(即所在公司),编写相应的set和get方法。新建一个test类,新建Programmer的对象,并利用set方法设置程序员的姓名和公司,利用get方法显示其姓名和公司。

【前提引入】

💬 谈谈继承

1️⃣ 介绍

继承可以解决代码复用,让编程更加靠近人类的思维,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,再弗雷中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来声明继承父类即可。

2️⃣ 基本语法

class 子类 extends 父类{
}

3️⃣ 注意事项

  • 类继承了父类所有的属性和方法,非私有的属性和方法可以在子类直接访问,但私有属性和方法需要通过父类提供的公共方法间接访问
  • Java所有类都是Object类的子类,Object类所有类的基类
  • Java中的继承是单继承机制,注意一下,这个单继承机制指的是子类只能有一个直接父类,即一个类只能直接继承另一个类,但是可以有多个超类。
    我们以本题为例子:Programmer的直接父类是Person,这满足单继承机制只有一个直接父类,但Programmer还有一个父类是Object类。无论你创建哪个类,都会给你默认继承Object这个顶级父类。我们可以证明它的继承:

  • 不能滥用继承,子类和父类之间必须满足is-a的关系,比如本题:Programmer是一个``Person`

稍微补充:接口与实现接口的类是 like-a 的关系

【核心类代码】

🌿 Person类

public class Person {
    /**
     * 姓名
     */
    private String name;
   /**
    * 无参构造器
    */
    public Person(){
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

🌿 Programmer类

public class Programmer extends Person {
    /**
     * 所在公司
     */
    private String company;
    /**
     * 无参构造器
     */
    public Programmer(){
    }
    public String getCompany() {
        return company;
    }
    public void setCompany(String company) {
        this.company = company;
    }
}

【运行流程】

public class Test {
    public static void main(String[] args) {
        Programmer programmer = new Programmer();
        //设置姓名
        programmer.setName("狐狸半面添");
        //设置公司
        programmer.setCompany("中南林业科技大学");
        System.out.println(programmer.getName() + ":" + programmer.getCompany());
    }
}

7.2.2 为Person类添加一个有参构造方法:设置姓名,为子类Programmer新增一个有参构造方法,对name和company变量进行赋值。

【前提引入】

  • 子类创建时,必须先调用父类的构造器,完成父类的初始化
  • 当创建子类构造器的时候,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,必须在子类的构造器中使用super去指定使用父类的哪个构造器完成父类的初始化工作,否则编译是不会通过的。
  • super在使用的时候必须放在子类构造器的第一行
  • 如果你不做任何处理,可以理解为在子类构造器的代码块中的第一行默认加上super();

【核心类代码】

🌿 为Person类添加的有参构造方法

/**
     * 给name赋值的有参构造器
     * @param name 姓名
     */
    public Person(String name){
        this.name = name;
    }

🌿 为Programmer类添加的有参构造器

/**
     * 有参构造器:给name和company进行赋值
     * 
     * @param name 姓名
     * @param company 公司
     */
    public Programmer(String name,String company){
        //这里就是调用父类的构造器,括号中的就是实参列表,对应父类构造器的形参
        super(name);
        this.company = company;
    }

7.2.3 为Person类中增加一个实例方法businessCard(),用以显示name,在test类中,增加一个Programmer对象的声明,调用businessCard()方法。

【前提引入】

子类继承了父类所有的属性和方法,因此可以在子类Pragrammer中使用父类Person的方法businessCard()方法

【核心类代码】

🌿 为Person类添加实例方法businessCard()方法

/**
     * 这是用于显示 name 的方法
     */
    public void businessCard(){
        System.out.println("name = " + this.name);
    }

【运行流程】

public static void main(String[] args) {
        Programmer programmer = new Programmer();
        programmer.setName("狐狸半面添");
        //调用 businessCard() 方法
        programmer.businessCard();
    }

7.2.4 Programmer类中重写businessCard()方法,显示姓名和公司,运行test类查看结果。

【前提引入】

这里涉及了两个知识点:方法重写/覆盖 与 动态绑定机制

1️⃣ 方法重写/覆盖

  • 简单来说,方法覆盖就是子类有一个方法,和父类的某个方法返回值类型、方法名、形参列表一样,那么我们就说子类的这个方法覆盖了父类的方法
  • 子类方法的返回值类型和父类的返回值类型要一样,或者是父类返回类型的子类
  • 不允许子类方法缩小父类的方法的访问权限与范围:
public > protected > default(默认) > private
  • 举个简单例子:父类有个方法 A 的访问修饰符是protected,那么重写这个方法时应该将重写方法的访问修饰符设置为protected或者访问权限更大的public

2️⃣ 动态绑定机制

这里其实还涉及到多态:多态就是方法或对象具有多种形态,是面向对象的第三大特点,建立在封装与继承的基础之上。方法多态就是方法重写和方法重载,对象多态就与向上转型、向下转型有关啦。这里不详说,简单聊聊动态绑定机制。

  • 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。

什么是运行类型,又什么是编译类型呢?(不细说)

举个例子:Person p = new Student();

我们说,Student类是student这个对象的运行类型,Person类是编译类型。你也可以简单记忆:编译类型看左边(左边是Person),运行类型看右边(右边是Student),这是在父类引用指向子类对象向上转型的情况下进行判定。

  • 如下面代码中,programmer.businessCard()我们调用的是Programmer类重写的那个方法,而不是Person类的方法,这是为什么呢?我们用动态绑定机制来解释,programmer这个对象的运行类型就是Programmer类,因此调用的是Programmer类重写的那个方法。
  • 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。

【核心类代码】

🌿 为Programmer类添加重写方法businessCard()方法

/**
     * 重写方法,显示姓名和公司
     */
    @Override
    public void businessCard() {
        System.out.println("姓名:" + getName() + "  公司:" + this.company);
    }

【运行流程】

public static void main(String[] args) {
        Programmer programmer = new Programmer();
        programmer.setName("狐狸半面添");
        programmer.setCompany("中南林业科技大学");
        //调用 businessCard() 方法
        programmer.businessCard();
    }

7.2.5 将Person类改变为抽象类,businessCard()方法改为抽象方法(注意抽象方法不能有方法体),运行test类查看结果。

【前提引入】

💬 聊聊抽象方法 与 抽象类

1️⃣ 为什么需要抽象方法与抽象类

当父类的某些方法需要声明,但是呢,有不确定这个方法该如何实现时,可以使用abstract关键字来修饰这个方法,即不做实现,交给我们子类去实现。用abstract修饰的类就是抽象类。

2️⃣ 语法

访问修饰符 abstract class 类名{}
//抽象方法没有代码体,写完形参列表直接以分号结尾
访问修饰符 abstract 返回值类型 方法名(形参列表);

3️⃣ 注意事项

  • 抽象类不能被实例化
  • 除非子类也是抽象类,不然父类的抽象方法必须在子类得到实现,否则编译报错,这是个强制的方法重写
  • 如果我们把一个方法声明为了抽象方法,那么这个方法所在的类我们必须声明为抽象类
  • 抽象类中一定要有抽象方法吗?是不一定要有的,但该类仍不能被实例化
  • 抽象方法不能使用 privatefinalstatic 来修饰,因为这些关键字都与方法重写想违背,抽象方法的实现也是方法重写

4️⃣ 抽象类最佳实践 —— 模板设计模式

抽象类体现的就是一种模板设计模式,抽象类作为多个子类的通用模板,子类在抽象类的基本上进行扩展与改造,但子类总体上会保留抽象类的行为方式。

做个小小补充:

我在做Javaweb原生项目的时候,有这样一个问题,一种请求对应一个Servlet,造成Servlet太多了,不利于管理。怎么办呢?我们设置一个BasicServlet抽象类,继承HttpServlet,其他类都来继承这个BasicServlet,同时配置Servlet路径,采用反射+模板设计模式+动态绑定实现了一种请求对应了一个方法,让归属一个业务的请求都对应一个继承BasicServlet的子类。妙啊!

【核心类代码】

🌿 新的Person类

public abstract class Person {
    /**
     * 姓名
     */
    private String name;
    /**
     * 无参构造器
     */
    public Person(){
    }
    /**
     * 给name赋值的有参构造器
     * @param name 姓名
     */
    public Person(String name){
        this.name = name;
    }
    /**
     * 这是用于显示 name 的方法 --> 现在我们做成了抽象方法
     */
    public abstract void businessCard();
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

🌿 新的Programmer类

public class Programmer extends Person {
    /**
     * 所在公司
     */
    private String company;
    /**
     * 无参构造器
     */
    public Programmer(){
    }
    /**
     * 有参构造器:给name和company进行赋值
     *
     * @param name 姓名
     * @param company 公司
     */
    public Programmer(String name,String company){
        super(name);
        this.company = company;
    }
    /**
     * 重写抽象方法,显示姓名和公司
     */
    @Override
    public void businessCard() {
        System.out.println("姓名:" + getName() + "  公司:" + this.company);
    }
    public String getCompany() {
        return company;
    }
    public void setCompany(String company) {
        this.company = company;
    }
}

【运行流程】

public static void main(String[] args) {
        Programmer programmer = new Programmer();
        programmer.setName("狐狸半面添");
        programmer.setCompany("中南林业科技大学");
        //调用 businessCard() 方法
        programmer.businessCard();
    }


相关文章
|
19天前
|
Java
Java中的继承和多态是什么?请举例说明。
Java中,继承让子类继承父类属性和方法,如`class Child extends Parent`,子类可重写父类方法。多态允许父类引用指向子类对象,如`Animal a = new Dog()`,调用`a.makeSound()`会根据实际对象类型动态绑定相应实现,增强了代码灵活性和可扩展性。
11 0
|
26天前
|
搜索推荐 Java
Java的面向对象特性主要包括封装、继承和多态
【4月更文挑战第5天】Java的面向对象特性主要包括封装、继承和多态
15 3
|
2月前
|
监控 数据可视化 Java
Java代码如何轻松实现实验数据监控
Java代码如何轻松实现实验数据监控
25 0
|
2月前
|
存储 算法 Java
Java代码表示实验数据处理系统
Java代码表示实验数据处理系统
12 0
|
6天前
|
安全 Java
Java基础&面向对象&继承&抽象类
Java基础&面向对象&继承&抽象类
|
6天前
|
Java
【Java基础】详解面向对象特性(诸如继承、重载、重写等等)
【Java基础】详解面向对象特性(诸如继承、重载、重写等等)
10 0
|
1月前
|
存储 Java 关系型数据库
实验设备管理系统【GUI/Swing+MySQL】(Java课设)
实验设备管理系统【GUI/Swing+MySQL】(Java课设)
11 0
|
2月前
|
Java
java中的继承
java中的继承
10 1
|
2月前
|
Java
Java类的继承
Java类的继承
8 0
|
2月前
|
Java
JAVA类的继承
JAVA类的继承
15 1