乐趣区

关于java:JavaSE第10篇面向对象之继承

本篇咱们将持续学习面向对象编程,在之前咱们曾经学习过了面向对象之封装,晓得了如何定义一个规范的类及如何创立和应用对象。面向对象有三大特色,封装、继承、多态。那么接下来,咱们将会学习面向对象编程的继承。

第一章:继承

1.1- 继承概述(理解)

什么是继承

继承,指的是事物与事物之间的关系。

在生活中,咱们所了解的“继承”更多的是“子承父业”,就是儿子与父亲之间存在“继承”关系。

在 Java 编程中,咱们用类来形容事物,那么在面向对象编程中,“继承”指的是类与类之间的关系,通常是 子类 父类 之间的关系,子类能够继承父类中的非公有成员(属性和办法)。

为什么要有继承

继承有什么益处呢?

在生活中,很显然,儿子继承父亲的财产,能够少奋斗几十年甚至几辈子。

在编程中,继承有什么作用呢?咱们先来看一个需要,需要如下:

  • 用面向对象的形式,形容几种动物,狗、猫、牛

    • 狗,有名字、年龄、性别、毛色、爱吃食物、会看门
    • 猫,有名字、年龄、性别、毛色、爱吃食物、会撒娇
    • 牛,有名字、年龄、性别、毛色、爱吃食物、会耕地

此时面临这个需要时,咱们通常会定义三品种来形容,形容如下:

咱们察看能够发现,会存在这样一种景象:猫、狗、牛,存在一些共性的成员,这些共性都别离在不同的类中定义了。

问题:这样在编程中,就呈现的代码冗余(反复),若是当前有更多的动物品种呈现,还是要从新定义它们的共性成员,在整个工程看来,代码将会变得越来越冗余,越来越臃肿,不易于程序前期的保护。那如何解决呢?

解决:此时就能够应用继承机制解决代码的冗余问题,抽取共性,让共性复用。抽取的形式就是,形象出更高级的类(超类、父类),把共性定义在父类中,让其余也要领有这些共性成员的类作为子类继承父类。

对于上述需要,咱们能够形象出它们的父类,动物类。它们都是动物,动物都有属性 - 名字、年龄、毛色、性别,都是吃货,最终类的定义形容如下:

所以,在面向对象编程中,继承的作用:

  • 进步代码的复用性,缩小代码冗余
  • 继承是多态的根底(后续解说)

总结

  1. 什么是继承:

    • 继承就是,子类与父类之间的关系,子类能够继承父类中的成员。
  2. 继承的作用:

    • 进步代码复用性,缩小代码冗余。

1.2- 继承的格局(记忆)

通过上述解说,咱们晓得了什么是继承以及继承的作用,接下来咱们来学习一下 Java 中定义继承的格局。

格局

关键字:extends

class 父类 {...}

class 子类 extends 父类 {...}

示例

父类:动物类

package www.penglei666.com.demo03;
/**
 * 动物类,父类
 */
public class Animal {
    String name;
    int age;
    String color;
    String gender;
    public void eat(){System.out.println("我是吃货!");
    }
}

子类:狗类

package www.penglei666.com.demo03;

/**
 * 狗类,子类,继承了 Animal
 */
public class Dog extends Animal {public void alert(){System.out.println("正告,我是看门专家");
    }
}

测试类:Test

package www.penglei666.com.demo03;

public class Test {public static void main(String[] args) {Dog wc = new Dog();
        // 能够应用父类中的属性:name、age、gender、color
        wc.name = "旺财";
        wc.age = 10;
        wc.gender = "公";
        wc.color = "yellow";
        // 能够调用父类中的办法:eat,也能够调用本人的办法
        wc.eat();   // 我是吃货!wc.alert(); // 正告,我是看门专家}
}

在上述代码中,Dog 类通过 extends 关键字继承了 Animal 类,这样 Dog 类便是 Animal 类的子类。

从运行后果不难看出,子类尽管没有定义 name、age、gender 等属性和 eat 办法,然而却能操作这几个成员。这就阐明,子类在继承父类的时候,会主动领有父类的成员。

1.3-super 关键字(记忆)

若是父类中的成员和子类中的成员重名,子类对象在调用重名的成员时,会使怎么的景象呢?

子类和父类成员重名时

父类:Animal

package www.penglei666.com.demo03;
/**
 * 动物类,父类
 */
public class Animal {int age = 10;}

子类:Dog

package www.penglei666.com.demo03;

/**
 * 狗类,子类,继承了 Animal
 */
public class Dog extends Animal {
    int age = 11;
    public void printAge(){System.out.println("年龄:" + this.age);
    }
}

测试类:Test

package www.penglei666.com.demo03;

public class Test {public static void main(String[] args) {Dog wc = new Dog();
        wc.printAge(); // 输入后果:年龄:11}
}

通过输入后果能够发现,输入的并不是父类中的后果值,而是子类本人的。

那么,如何在重名的状况下,如何拜访到父类中的成员呢?此时能够应用关键字:super

咱们之前,学习过 this 关键字,示意代表调用者自身的援用,而 super 关键字,示意父类的援用。

super 关键字的应用

应用格局:super. 父类成员变量名

批改子类代码:Dog 类

package www.penglei666.com.demo03;

/**
 * 狗类,子类,继承了 Animal
 */
public class Dog extends Animal {
    int age = 11;
    public void printAge(){System.out.println("父 age:" + super.age);
        System.out.println("子 age:" + this.age);
    }
}

测试类:Test

package www.penglei666.com.demo03;

public class Test {public static void main(String[] args) {Dog wc = new Dog();
        wc.printAge(); 
        /* 输入后果:父 age:10
            子 age:11
        */
    }
}

1.4-super 和 this 关键字(了解)

父类空间优先于子类对象的产生

在每次创立子类对象时,先初始化父类空间,再创立其子类对象自身。

目标在于子类对象中蕴含了其对应的父类空间,便能够蕴含其父类的成员,如果父类成员非 private 润饰,则子类能够随便应用父类成员。

代码体现在子类的构造方法调用时,肯定先调用父类的构造方法。了解图解如下:

super 和 this 的含意:

  • super:代表父类的 存储空间标识(能够了解为父亲的援用)。
  • this:代表 以后对象的援用(谁调用就代表谁)。

super 和 this 调用成员属性和办法

this. 成员变量        --    本类的
super. 成员变量        --    父类的

this. 成员办法名()      --    本类的    
super. 成员办法名()   --    父类的

super 和 this 调用构造方法

this(...)        --    本类的构造方法
super(...)       --    父类的构造方法

子类的每个构造方法中均有默认的 super(),调用父类的空参结构。

手动调用父类结构会笼罩默认的 super()。

super() 和 this() 都必须是在构造方法的第一行,所以不能同时呈现。

1.5- 子类可重写父类办法(了解)

办法重写 Override

如果子类父类中呈现 不重名 的成员办法,这时的调用是 没有影响的

对象调用办法时,会先在子类中查找有没有对应的办法,若子类中存在就会执行子类中的办法,若子类中不存在就会执行父类中相应的办法。代码如下:

父类:Fu

package www.penglei666.com.demo04;

public class Fu {public void fn1(){System.out.println("父类办法:fn");
    }
}

子类:Zi

package www.penglei666.com.demo04;

public class Zi extends Fu {
    @Override
    public void fn2() {System.out.println("Zi 类中的办法:fn");
    }
}

测试类:Test

package www.penglei666.com.demo04;

public class Test {public static void main(String[] args) {Zi zi = new Zi();
        zi.fn1(); // 输入后果:父类办法:fn
        zi.fn2(); // 输入后果:Zi 类中的办法:fn}
}

如果子类父类中呈现 重名 的成员办法,这时的拜访是一种非凡状况,叫做 办法重写 (Override)。

办法重写 :子类中呈现与父类截然不同的办法时(返回值类型,办法名和参数列表都雷同),会呈现笼罩成果,也称为重写或者复写。 申明不变,从新实现

代码如下:

父类:Fu

package www.penglei666.com.demo04;

public class Fu {public void fn(){System.out.println("父类办法:fn");
    }
}

子类:Zi

package www.penglei666.com.demo04;

public class Zi extends Fu {
    @Override
    public void fn() {System.out.println("Zi 类中的办法:fn");
    }
}

测试类:

package www.penglei666.com.demo04;

public class Test {public static void main(String[] args) {Zi zi = new Zi();
        zi.fn(); // 输入后果:Zi 类中的办法:fn}
}

办法重写的利用

子类能够依据须要,定义特定于本人的行为。

既因循了父类的性能名称,又依据子类的须要从新实现父类办法,从而进行扩大加强。

比方新的手机减少来电显示头像的性能,代码如下:

父类:Phone

package www.penglei666.com.demo05;

/**
 * 父类:Phone
 */
public class Phone {public void sendMessage(){System.out.println("发送短信");
    }
    public void call(){System.out.println("拨打电话");
    }
    public void showNum(){System.out.println("来电显示对方号码");
    }
}

子类:

package www.penglei666.com.demo05;

/**
 * 子类:NewPhone
 */
public class NewPhone extends Phone {
    // 重写父类的来电显示号码性能,并减少本人的显示姓名和图片性能
    public void showNum(){
        // 调用父类曾经存在的性能应用 super
        super.showNum();
        // 减少本人特有显示姓名和图片性能
        System.out.println("来电显示对方姓名");
        System.out.println("来电显示对方头像");
    }
}

测试类:Test

package www.penglei666.com.demo05;

public class Test {public static void main(String[] args) {NewPhone np = new NewPhone();
        np.showNum();
        /**
         * 输入后果:* 来电显示号码
         * 来电显示对方姓名
         * 来电显示对方头像
         */
    }
}

这里重写时,用到 super. 父类成员办法,示意调用父类的成员办法。

1.6- 子类的初始化过程(了解)

构造方法的作用是初始化成员变量的。所以 子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个super(),示意调用父类的构造方法,父类成员变量初始化后,才能够给子类应用。代码如下:

父类:

package www.penglei666.com.demo06;

public class Fu {public Fu(){System.out.println("Fu 类构造方法初始化");
    }
}

子类:

package www.penglei666.com.demo06;

public class Zi extends Fu {public Zi(){// super(); 默认调用 super
        System.out.println("Zi 类构造方法初始化");
    }
}

测试类:

package www.penglei666.com.demo06;

public class Test {public static void main(String[] args) {Zi zi = new Zi();
        /* 执行后果:Fu 类构造方法初始化
            Zi 类构造方法初始化
        */
    }
    
}

1.7- 继承的特点(了解)

特点 1:Java 只反对单继承,不反对多继承。

// 一个类只能有一个父类,不能够有多个父类。class C extends A{}     //ok
class C extends A,B...    //error

特点 2:Java 反对多层继承(继承体系)。

class A{}
class B extends A{}
class C extends B{}

第二章:抽象类

2.1- 抽象类概述(理解)

抽象类的由来

当编写一个类时,咱们往往会为该类定义一些办法,这些办法是用来形容该类的性能具体实现形式,那么这些办法都有具体的办法体。

剖析事物时,发现了共性内容,就呈现向上抽取(父类中定义)。

然而,可能会有这样一种非凡状况,就是办法性能申明雷同,但办法性能主体不同。

那么这时也能够抽取,但 只抽取办法申明,不抽取办法主体。那么此办法就是一个形象办法。

如:

  • 形容狗的行为:吃
  • 形容猫的行为:吃
  • 形容牛的行为:吃

狗、猫、牛之间有共性,能够进行向上抽取父类定义共性。

抽取它们的所属共性类型:动物。

因为狗、猫、牛都具备吃的性能,然而它们吃的食物不一样(比方:开篇咱们的需要狗、猫、牛都是吃货,都有吃的行为,然而吃的食物第不同的,狗啃骨头、猫吃鱼、牛吃草)。

这时在形容动物类时,发现了有些性能不可能具体形容,那么,这些不具体的性能,须要在类中标识进去,通过 java 中的关键字 abstract(形象) 润饰。当定义了形象办法的类也必须被 abstract 关键字润饰,被 abstract 关键字润饰的类是抽象类

总结

  • 形象办法:没有办法体的办法。
  • 抽象类:蕴含形象办法的类。

2.2- 抽象类、形象办法的定义和应用(记忆)

形象办法:

应用abstract 关键字润饰办法,该办法就成了形象办法,形象办法只蕴含一个办法名,而没有办法体。

定义格局:

修饰符 abstract 返回值类型 办法名 (参数列表);

示例代码:

public abstract void eat();

抽象类:

如果一个类蕴含形象办法,那么该类必须是抽象类。

定义格局:

public abstract class 类名字 {}

示例代码:

public abstract class Animal {public abstract void eat();
}

抽象类的应用:

继承抽象类的子类必须重写父类所有的形象办法。否则,该子类也必须申明为抽象类。最终,必须有子类实现该父类的形象办法,否则,从最后的父类到最终的子类都不能创建对象,失去意义。

父类:Animal

package www.penglei666.com.demo07;
public abstract class Animal {public abstract void eat();
}

子类:Dog

package www.penglei666.com.demo07;
public class Dog extends Animal {
    @Override
    public void eat() {System.out.println("我爱啃骨头");
    }
}

此时的办法重写,是子类对父类形象办法的实现实现,咱们将这种办法重写的操作,也叫做 实现办法

2.3- 注意事项(理解)

  1. 抽象类 不能创建对象,如果创立,编译无奈通过而报错。只能创立其非形象子类的对象。

    • 了解形式:假如创立了抽象类的对象,调用形象的办法,而形象办法没有具体的办法体,没有意义。
  2. 抽象类中,能够有构造方法,是供子类创建对象时,初始化父类成员应用的。

    • 了解形式:子类的构造方法中,有默认的 super(),须要拜访父类构造方法。
  3. 抽象类中,能够有成员变量。

    • 了解形式:子类的共性的成员变量 , 能够定义在形象父类中。
  4. 抽象类中,不肯定蕴含形象办法,然而有形象办法的类必然是抽象类。

    • 了解形式:未蕴含形象办法的抽象类,目标就是不想让调用者创立该类对象,通常用于某些非凡的类结构设计。
  5. 抽象类的子类,必须重写形象父类中 所有的 形象办法,否则,编译无奈通过而报错。除非该子类也是抽象类。

    • 了解形式:假如不重写所有形象办法,则类中可能蕴含形象办法。那么创建对象后,调用形象的办法,没有意义。

第三章:综合案例

案例需要:

某 IT 公司有多名员工,依照员工负责的工作不同,进行了部门的划分(研发部员工、保护部员工)。研发部依据所需研发的内容不同,又分为 JavaEE 工程师、Android 工程师;保护部依据所需保护的内容不同,又分为网络保护工程师、硬件保护工程师。

公司的每名员工都有他们本人的员工编号、姓名,并要做它们所负责的工作。

工作内容:

  • JavaEE 工程师:员工号为 xxx 的 xxx 员工,正在研发淘宝网站
  • Android 工程师:员工号为 xxx 的 xxx 员工,正在研发淘宝手机客户端软件
  • 网络保护工程师:员工号为 xxx 的 xxx 员工,正在查看网络是否畅通
  • 硬件保护工程师:员工号为 xxx 的 xxx 员工,正在修复打印机

请依据形容,实现员工体系中所有类的定义,并指定类之间的继承关系。进行 XX 工程师类的对象创立,实现工作办法的调用。

案例剖析:

根据上述部门的形容,得出如下的员工体系图

依据员工信息的形容,确定每个员工都有员工编号、姓名、要进行工作。则把这些独特的属性与性能抽取到父类中(员工类),对于工作的内容由具体的工程师来进行指定。

工作内容:

  • JavaEE 工程师:员工号为 xxx 的 xxx 员工,正在研发淘宝网站
  • Android 工程师:员工号为 xxx 的 xxx 员工,正在研发淘宝手机客户端软件
  • 网络保护工程师:员工号为 xxx 的 xxx 员工,正在查看网络是否畅通
  • 硬件保护工程师:员工号为 xxx 的 xxx 员工,正在修复打印机

创立 JavaEE 工程师对象,实现工作办法的调用。

实现代码:

员工类:Employee

public abstract class Employee {
    private String id;// 员工编号
    private String name; // 员工姓名

    public String getId() {returnid;}
    publicvoid setId(String id) {this.id = id;}
    public String getName() {returnname;}
    publicvoid setName(String name) {this.name = name;}
    
    // 工作办法(形象办法)public abstract void work();}

定义研发部员工类 Developer 继承 员工类 Employee

public abstract class Developer extends Employee {}

定义保护部员工类 Maintainer 继承 员工类 Employee

public abstract class Maintainer extends Employee {}

定义 JavaEE 工程师 继承 研发部员工类,重写工作办法

public class JavaEE extends Developer {
    @Override
    public void work() {System.out.println("员工号为" + getId() + "的" + getName() + "员工,正在研发淘宝网站");
    }
}

定义 Android 工程师 继承 研发部员工类,重写工作办法

public class Android extends Developer {
    @Override
    public void work() {System.out.println("员工号为" + getId() + "的" + getName() + "员工,正在研发淘宝手机客户端软件");
    }
}

定义 Network 网络保护工程师 继承 保护部员工类,重写工作办法

public class Network extends Maintainer {
    @Override
    public void work() {System.out.println("员工号为" + getId() + "的" + getName() + "员工,正在查看网络是否畅通");
    }
}

定义 Hardware 硬件保护工程师 继承 保护部员工类,重写工作办法

public class Hardware extends Maintainer {
    @Override
    public void work() {System.out.println("员工号为" + getId() + "的" + getName() + "员工,正在修复打印机");
    }
}

在测试类中,创立 JavaEE 工程师对象,实现工作办法的调用

public class Test {public static void main(String[] args) {
        // 创立 JavaEE 工程师员工对象
        JavaEE ee = new JavaEE();
        // 设置该员工的编号
        ee.setId("000015");
        // 设置该员工的姓名
        ee.setName("小明");
        // 调用该员工的工作办法
        ee.work();}
}

退出移动版