Java面向对象(高级)-- 抽象类与抽象方法(或abstract关键字)

文章目录

  • 一、抽象类的由来
    • (1)举例1
    • (2)举例2
  • 二、案例引入
    • (1)抽象类
    • (2)抽象方法
    • (3)补充1
    • (4)补充2
    • (5)举例
      • 1. 举例1
      • 2. 举例2
  • 三、 抽象类与抽象方法
    • (1)语法格式
    • (2)使用说明
    • (3) 注意事项
      • 1. abstract不能使用的场景
    • (4) 应用举例
      • 1. 举例1 航运
      • 2. 举例2 模板方法设计模式
        • 2.1 描述
        • 2.2 案例1
        • 2.3 案例2
        • 2.4 案例3
        • 2.5 案例4
  • 四、练习
    • (1)练习1
    • (2)练习2

一、抽象类的由来

(1)举例1

随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。

类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类
image.png

将父类加上abstract标识之后,就不用用它造对象。

💬既然父类抽象了,功能也并不具体,子类也需要重写它的方法才好具体使用。那为啥还需要父类呢?

1、父类中的一些方法,子类确实会重写,但也会存在一些方法子类直接拿去用。若没有父类,每个子类就都需要自己写。

2、父类的属性子类可以直接拿来用,若没有父类,子类还需要自己去声明。

3、多态的使用前提,基于继承性,比如一个方法的形参就是Person类型的,真正调用方法的时候,传进来的却是具体子类的对象。不管传什么子类对象,都赋给了Person。若是没有父类,那就麻烦了,一堆方法需要一个个写,不靠谱。

所以继承关系还是需要的,在继承的前提下,这个抽象类只是表明父类不想实例化了

(2)举例2

我们声明一些几何图形类:圆、矩形、三角形类等,发现这些类都有共同特征:求面积、求周长。

那么这些共同特征应该抽取到一个共同父类:几何图形类中。

但是这些方法在父类中无法给出具体的实现(因为不知道具体什么图形),而是应该交给子类各自具体实现。

那么父类在声明这些方法时,就只有方法签名,没有方法体(具体的需要子类去重写),我们把没有方法体的方法称为抽象方法

Java语法规定,包含抽象方法的类必须是抽象类

二、案例引入

(1)抽象类

先将继承关系表示出来:

【Person.java】

package yuyi08;/*** ClassName: Person* Package: yuyi08* Description:** @Author 雨翼轻尘* @Create 2023/11/23 0023 13:13*/
public class Person {String name;int age;//构造器public Person(){}public Person(String name, int age) {this.name = name;this.age = age;}//方法public void eat(){System.out.println("人吃饭");}public void sleep(){System.out.println("人睡觉");}}

【Student.java】

package yuyi08;/*** ClassName: Student* Package: yuyi08* Description:** @Author 雨翼轻尘* @Create 2023/11/23 0023 13:15*/
public class Student extends Person{String school;public Student() {}public Student(String name, int age, String school) {super(name, age);this.school = school;}public void eat(){System.out.println("学生多吃有营养的食物");}public void sleep(){System.out.println("学生不要熬夜");}
}

【AbstractTest.java】

package yuyi08;/*** ClassName: AbstractTest* Package: yuyi08* Description:** @Author 雨翼轻尘* @Create 2023/11/23 0023 13:17*/
public class AbstractTest {public static void main(String[] args) {Person p1=new Person();p1.eat();Student s1=new Student();s1.eat();}
}

输出结果:

image.png

现在在现有的class Person前面加上abstract

Person类就叫做抽象类。意味着它不能够实例化了。

上一节说了final,final也可以用来修饰Person,表示不能有子类

现在这个可以有子类的,只是不能造对象了。

可以看到,这里就报错了:

image.png

既然不能造方法,那么此时方法也自然不能调用了。

单例模式只造一次对象,抽象类是禁止造对象。

(2)抽象方法

随着Person提供的子类越来越丰富,Person里面的eat()方法都不用指明具体怎么吃了,具体的子类会指明。

所以在Person里面,只需要声明了就行

表示有这个功能,具体怎么吃不确定,需要子类重写它。

比如,现在在eat()方法前面加上abstract,如下:

image.png

此时会报错,因为抽象方法不能有方法体。(代码连着花括号一块都不能要)

这样即可:(表明此方法是抽象方法)

image.png

既然这个抽象方法没有方法体了,那么它也就不应该被调用了。

在子类Student中将父类Person中的eat()方法重写了,可以发现这里的符号变了,抽象方法的重写换了一个词儿,叫Implements(实现),而之前的重写叫Overrides(重写)。

image.png

这里就不讲究那么多了,都叫重写也没什么毛病。

所以,在子类中,将父类的抽象方法给重写了,不耽误后面子类正常调用。

Java语法规定,包含抽象方法的类必须是抽象类

刚才我们先说的是抽象类,表示该类不能被实例化了。然后说的是抽象方法,不能有方法体。没有任何问题。

但现在方法是抽象的,若是该类不是抽象的,就会报错,如下:

image.png

什么原因呢?

抽象方法没有方法体,那么这个方法就不应该被调用。

现在的方法是非静态方法,按道理说拿对象是可以调用它的。

现在既然不给调用抽象方法,那么就别有对象了。

如何保证这个类没有对象?删掉构造器?不行,就算没有写构造器,也会有默认的空参构造器,没有意义。那就限制该类呗,那就抽象,抽象类没有对象。

所以,抽象方法所属的类也是抽象类,因为这样就不能造对象了。

抽象方法存在的原因,主要是在多态应用中,对象只能调用父类中已有的方法,子类自己写的方法不能调用,所以要在抽象父类中声明抽象方法。

(3)补充1

若此时,我们将sleep()方法也抽象化:

image.png

重新建一个Worker类,继承于Person类,会发现报错,如下:

image.png

以前是构造器的问题,但现在构造器没有问题。(有空参构造器)

看报错信息可知,要么将Worker类声明为抽象类,要么实现Person中的抽象方法eat()

因为子类Worker将父类中的两个抽象方法继承过来了。

所以,

第一种解决方法:将Worker类声明为抽象类

public abstract class Worker extends Person{}

第二种解决方法:实现Person中的抽象方法eat()

public  class Worker extends Person{@Overridepublic void eat() {}@Overridepublic void sleep() {}}

【小Tips】

将光标放在Worker上,Alter+Enter,然后点击“实现方法”:

image.png

将两个都选中,点击“确定”:

image.png

然后就可以自动生成啦:

image.png

当然,只重写了一个方法也会报错,如下:

image.png

(4)补充2

现在【Worker.java】代码写成这样的:

package yuyi08;public  abstract class Worker extends Person{   //只重写了一个父类抽象方法,还是抽象类@Overridepublic void eat() {System.out.println("工人很辛苦,多吃饭");}/*@Overridepublic void sleep() {}*/}

再写一个Creature类:

package yuyi08;public abstract class Creature { //生物类public abstract void breath();  //呼吸}

现在将Person类继承于Creature,此时Person里面就包含Creature的抽象方法breath()

此时会报错!

//报错了
public abstract class Person extends Creature {  //抽象类String name;int age;//构造器public Person(){}public Person(String name, int age) {this.name = name;this.age = age;}//方法public abstract void eat(); //抽象方法public abstract void sleep();}

学生类Student继承于Person的,除了将Person类中的抽象方法重写了,要想实例化,此时必须将breath()也给重写一下才不会报错

如下:

image.png

现在Student类中,将直接父类Person和间接父类Creature的抽象方法都做了重写,如下:

package yuyi08;public class Student extends Person{String school;public Student() {}public Student(String name, int age, String school) {super(name, age);this.school = school;}public void eat(){System.out.println("学生多吃有营养的食物");}public void sleep(){System.out.println("学生不要熬夜");}@Overridepublic void breath() {System.out.println("学生要多呼吸新鲜空气");}
}

那么就可以用Student类去造对象了。

当然,若是父类Person已经将breath()方法重写了,在Student类中不重写breath()也可以(不会报错)。

image.png


对于Worker类来说,它有三个方法,从Person类中继承来的(Person两个,Creature一个)。此时只重写了一个,剩下两个还是抽象的,所以Worker还只能是抽象类。

【Worker.java】

package yuyi08;public  abstract class Worker extends Person{   //只重写了一个父类抽象方法,还是抽象类@Overridepublic void eat() {System.out.println("工人很辛苦,多吃饭");}}

既然Worker类是抽象类,那么就不能去造对象。如下:

image.png

(5)举例

抽象类需要有子类,自己不能造对象,写了一些方法(抽象方法和非抽象方法),这些抽象方法想要用就需要造对象,自己造不了对象了,就只能造子类对象啦。言外之意就是抽象类必须有子类

1. 举例1

举例1:GeometricObject-Circle-Rectangle

父类【GeometricObject.java】

abstract class GeometricObject{  //几何图形//求面积 (只能考虑提供方法的声明,而没有办法提供方法体。所以,此方法适合声明为抽象方法)//求周长(只能考虑提供方法的声明,而没有办法提供方法体。所以,此方法适合声明为抽象方法)
}

子类1【Circle.java】

class Circle extends GeometricObject{//求面积 (必须重写(或实现)父类中的抽象方法)//求周长(必须重写(或实现)父类中的抽象方法)
}

子类2【Rectangle.java】

子类要么是抽象类,要么就需要重写父类中的抽象方法

class Rectangle extends GeometricObject{//求面积 (必须重写(或实现)父类中的抽象方法)//求周长(必须重写(或实现)父类中的抽象方法)
}

2. 举例2

举例2:Account-SavingAccount-CheckAcount

父类【Account.java】

abstract class Account{double balance;//余额//取钱 (声明为抽象方法)//存钱 (声明为抽象方法)}

子类1【SavingAccount.java】

class SavingAccount extends Account{ //储蓄卡//取钱 (需要重写父类中的抽象方法)//存钱(需要重写父类中的抽象方法)
}

子类2【CheckAccount.java】

class CheckAccount extends Account{ //信用卡//取钱(需要重写父类中的抽象方法)//存钱(需要重写父类中的抽象方法)
}
//....

三、 抽象类与抽象方法

(1)语法格式

abstract的概念:抽象的

abstract可以用来修饰:方法

  • 抽象类:被abstract修饰的
  • 抽象方法:被abstract修饰没有方法体的方法

抽象类的语法格式

[权限修饰符] abstract class 类名{}
[权限修饰符] abstract class 类名 extends 父类{}

abstract修饰
> 此类称为抽象类
> 抽象类不能实例化(不能造对象)。
> 抽象类中是包含构造器的,因为子类对象实例化时,需要直接或间接的调用到父类的构造器。
> 抽象类中可以没有抽象方法(仅仅表明抽象类不能造对象)。反之,抽象方法所在的类,一定是抽象类

构造器是用来对对象进行初始化的,即为实例变量初始化,赋初值,而不是用来创建对象的,创建对象用new关键字。(子类在造对象的时候,还是要加载父类的结构–调用构造器的时候加载)

抽象方法的语法格式

[其他修饰符] abstract 返回值类型 方法名([形参列表]);

abstract修饰方法
> 此方法即为抽象方法
> 抽象方法只有方法的声明,没有方法体
> 抽象方法其功能是确定的(通过方法的声明即可确定),只是不知道如何具体实现(体现为没有方法体)。
> 子类必须重写父类中的所有的抽象方法之后,方可实例化否则,此子类仍然是一个抽象类

注意:抽象方法没有方法体。

抽象方法虽然没有声明,但是这个方法的功能已经确定了,只不过没有方法体(不知道功能该如何实现)而已。

image.png

代码举例:

public abstract class Animal {public abstract void eat();
}
public class Cat extends Animal {public void eat (){System.out.println("小猫吃鱼和猫粮"); }
}
public class CatTest {public static void main(String[] args) {// 创建子类对象Cat c = new Cat(); // 调用eat方法c.eat();}
}

此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法

基于继承以后,将代码改造了一下,父类不能实例化,里面有抽象方法。将造对象的事情交给子类,让子类继承父类之后,要是没有将父类中的抽象方法都给重写,就会报错。(要么全部重写了,要么也变成抽象类)

(2)使用说明

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

理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。

  1. 抽象类中,也有构造方法,是供子类创建对象时,初始化父类成员变量使用的。

理解:子类的构造方法中,有默认的super()或手动的super(实参列表),需要访问父类构造方法。

  1. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。

  1. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。

理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。

(3) 注意事项

1. abstract不能使用的场景

<1> abstract 不能修饰哪些结构

属性构造器代码块等。

<2> abstract 不能与哪些关键字共用?(自洽)

不能用abstract修饰私有方法静态方法final的方法final的类

①私有方法不能重写

abstract修饰了一个方法,这个方法一定要让子类去重写的,否则就造不了对象了。

②静态方法不能重写

避免静态方法使用类进行调用

一般我们是这样来写的,如下:

image.png

此时将method()方法私有化是不可以的,如下:

image.png

那用static修饰呢?

静态方法(static)本来就可以通过类来调;而抽象方法(abstract)没有方法体,这个方法就不能被调。

所以不能用static和abstract组合。

image.png

抽象方法只有声明,没有方法体,这个方法不能被调。

调用方法的有“类.”和“对象.”两种方法。若此时不让调方法,那就让这两件事做不了。

不让对象去调用,就让类为抽象类,它就没有对象了,自然就不能用对象去调用什么方法;不让类去调用,这个方法就不要是静态方法,就不能拿类去调用了。

所以刚才就不能静态,因为静态(用static修饰)了,它就可以被类调用,而此时此方法又被abstract修饰,意味着它是抽象类不能被调用。矛盾呐。

静态方法可以直接用类名调用,抽象方法不能调用。

③final的方法不能被重写

abstract修饰的方法一定要被重写,否则就不能造对象。

所以final与abstract水火不容。

④final修饰的类不能有子类

abstract修饰的类一定要有子类,否则也造不了对象,而且自己也造不了,那有何用呢?

(4) 应用举例

1. 举例1 航运

image.png

在航运公司系统中,Vehicle类需要定义两个方法分别计算运输工具的燃料效率行驶距离

问题:卡车(Truck)和驳船(RiverBarge)的燃料效率和行驶距离的计算方法完全不同。Vehicle类不能提供计算方法,但子类可以。

解决方案:Java允许类设计者指定:超类声明一个方法但不提供实现,该方法的实现由子类提供。这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。

//Vehicle是一个抽象类,有两个抽象方法。
public abstract class Vehicle{public abstract double calcFuelEfficiency();	//计算燃料效率的抽象方法public abstract double calcTripDistance();	//计算行驶距离的抽象方法
}public class Truck extends Vehicle{public double calcFuelEfficiency( )   { //写出计算卡车的燃料效率的具体方法   }public double calcTripDistance( )    {  //写出计算卡车行驶距离的具体方法   }
}public class RiverBarge extends Vehicle{public double calcFuelEfficiency( ) { //写出计算驳船的燃料效率的具体方法  }public double calcTripDistance( )  {  //写出计算驳船行驶距离的具体方法}
}

2. 举例2 模板方法设计模式

2.1 描述

应用举例2:模板方法设计模式(TemplateMethod)

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

解决的问题

  • 当功能内部一部分实现是确定的(非抽象),另一部分实现是不确定的(抽象化)。这时可以把不确定的部分暴露出去,让子类去实现。 (父类中有方法体的都是确定的,没有方法体的都是不确定的)
  • 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。

类比举例:英语六级模板

image.png

制作月饼的模板:

image.png

2.2 案例1

🌱计算代码运行时间

package yuyi09;/*** ClassName: TemplateTest* Package: yuyi09* Description:*      抽象应用案例:模板方法的设计模式* @Author 雨翼轻尘* @Create 2023/11/24 0024 10:03*/
public class TemplateTest {public static void main(String[] args) {PrintPrimeNumber p=new PrintPrimeNumber();p.spendTime();  //现在想算一下代码花费了多长时间,调用spendTime()是父类中的方法,在执行code()时是子类重写的方法(已经将原来父类中的覆盖了)}
}//父类
abstract class Template{//计算某段代码的执行,需要花费的时间public void spendTime(){    //花费的时间long start= System.currentTimeMillis(); //记录开始时间code(); //代码执行完long end=System.currentTimeMillis();    //记录结束时间System.out.println("花费时间为:"+(end-start));}//代码不确定,不妨将它写到方法中public abstract void code();
}//子类
class PrintPrimeNumber extends Template{//输出质数@Overridepublic void code() {for (int i = 2; i <=100000 ; i++) {boolean isFlag=true;for (int j = 2; j <=Math.sqrt(i) ; j++) {if(i%j==0){isFlag=false;break;}}if(isFlag){System.out.println(i);}}}
}

👻输出结果(部分)

image.png

2.3 案例2

🌱银行相关模板

package yuyi09;
//抽象类的应用:模板方法的设计模式//测试类
public class TemplateMethodTest {public static void main(String[] args) {BankTemplateMethod btm = new DrawMoney();	//若想取钱,就new一个抽象类的子类,可以赋给父类(也可以不赋)btm.process();	//new的子类也继承了父类的process()方法,其中办理业务的transact()方法是子类重写的方法,其他的继承父类中的System.out.println();	//换个行喽BankTemplateMethod btm2 = new ManageMoney();btm2.process();}
}//父类--模板
abstract class BankTemplateMethod {// 具体方法public void takeNumber() {System.out.println("取号排队");}public abstract void transact(); // 办理具体的业务,不确定每个人所办理的业务,将它抽象化public void evaluate() {System.out.println("反馈评分");}// 模板方法,把基本操作组合到一起,子类一般不能重写public final void process() {	//流程   加了final,不要重写了this.takeNumber();	//取号this.transact();// 办理业务,每个人不一样(像个钩子,具体执行时,挂哪个子类,就执行哪个子类的实现代码)this.evaluate();	//反馈评分}
}//取钱--子类
class DrawMoney extends BankTemplateMethod {//将父类中的抽象方法重写public void transact() {System.out.println("我要取款!!!");}
}//理财--子类
class ManageMoney extends BankTemplateMethod {//将父类中的抽象方法重写public void transact() {System.out.println("我要理财!我这里有2000万美元!!");}
}

👻输出结果

image.png

2.4 案例3
abstract class Template {public final void getTime() {long start = System.currentTimeMillis();code();long end = System.currentTimeMillis();System.out.println("执行时间是:" + (end - start));}public abstract void code();
}class SubTemplate extends Template {public void code() {for (int i = 0; i < 10000; i++) {System.out.println(i);}}
}
2.5 案例4
package com.atguigu.java;
//抽象类的应用:模板方法的设计模式
public class TemplateMethodTest {public static void main(String[] args) {BankTemplateMethod btm = new DrawMoney();btm.process();BankTemplateMethod btm2 = new ManageMoney();btm2.process();}
}
abstract class BankTemplateMethod {// 具体方法public void takeNumber() {System.out.println("取号排队");}public abstract void transact(); // 办理具体的业务 //钩子方法public void evaluate() {System.out.println("反馈评分");}// 模板方法,把基本操作组合到一起,子类一般不能重写public final void process() {this.takeNumber();this.transact();// 像个钩子,具体执行时,挂哪个子类,就执行哪个子类的实现代码this.evaluate();}
}class DrawMoney extends BankTemplateMethod {public void transact() {System.out.println("我要取款!!!");}
}class ManageMoney extends BankTemplateMethod {public void transact() {System.out.println("我要理财!我这里有2000万美元!!");}
}

模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的影子,比如常见的有:

  • 数据库访问的封装
  • Junit单元测试
  • JavaWeb的Servlet中关于doGet/doPost方法调用
  • Hibernate中模板程序
  • Spring中JDBCTemlate、HibernateTemplate等

四、练习

(1)练习1

🌋题目描述

针对多态性的练习题1:GeometricObject等类进行升级,体现抽象的使用。

【Circle.java】

package yuyi10;/*** ClassName: Circle* Description:** @Author 雨翼轻尘* @Create 8:51* @Version 1.0*/
public class Circle extends GeometricObject {private double radius;//半径public Circle(String color, double weight, double radius) {super(color, weight);this.radius = radius;}public double getRadius() {return radius;}public void setRadius(double radius) {this.radius = radius;}@Overridepublic double findArea() {return 3.14 * radius * radius;}
}

【GeometricObject.java】

package yuyi10;/*** ClassName: GeometricObject* Description:** @Author 雨翼轻尘* @Create 8:47* @Version 1.0*/
public class GeometricObject {protected String color;protected double weight;protected GeometricObject(String color, double weight) {this.color = color;this.weight = weight;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public double getWeight() {return weight;}public void setWeight(double weight) {this.weight = weight;}public double findArea(){return 0.0;}
}

【MyRectangle.java】

package yuyi10;/*** ClassName: MyRectangle* Description:** @Author 雨翼轻尘* @Create 8:53* @Version 1.0*/
public class MyRectangle extends GeometricObject {private double width;//宽private double height;//高public MyRectangle(String color, double weight, double width, double height) {super(color, weight);this.width = width;this.height = height;}public double getWidth() {return width;}public void setWidth(double width) {this.width = width;}public double getHeight() {return height;}public void setHeight(double height) {this.height = height;}@Overridepublic double findArea() {return width * height;}
}

【GeometricTest.java】

package yuyi10;/*** ClassName: GeometricTest* Description:*      编写equalsArea方法测试两个对象的面积是否相等(注意方法的参数类型),*      编写displayGeometricObject方法显示对象的面积(注意方法的参数类型)。* @Author 雨翼轻尘* @Create 8:55* @Version 1.0*/
public class GeometricTest {public static void main(String[] args) {GeometricTest test = new GeometricTest();Circle c1 = new Circle("red",1.0,2.3);Circle c2 = new Circle("red",1.0,3.3);test.displayGeometricObject(c1);test.displayGeometricObject(c2);boolean isEquals = test.equalsArea(c1,c2);if(isEquals){System.out.println("面积相等");}else{System.out.println("面积不相等");}//使用匿名对象test.displayGeometricObject(new MyRectangle("blue",1.0,2.3,4.5));}/*** 比较两个几何图形的面积是否相等* @param g1* @param g2* @return true:表示面积相等   false:面积不相等*/public boolean equalsArea(GeometricObject g1, GeometricObject g2){return g1.findArea() == g2.findArea();}/*** 显示几何图形的面积* @param g*/public void displayGeometricObject(GeometricObject g){ //GeometricObject g = new Circle("red",1.0,2.3);System.out.println("几何图形的面积为:" + g.findArea()); //动态绑定  <---> 静态绑定}
}

🌴分析

当初在写几何图形GeometricObject的时候,其中有一个求面积的方法findArea(),当时还纠结返回什么,现在来看根本不需要提供它的方法体。因为不确定,所以可以抽象化。同样,当前类也是抽象类了。

image.png

【GeometricObject.java】

public abstract class GeometricObject {protected String color;protected double weight;protected GeometricObject(String color, double weight) {this.color = color;this.weight = weight;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public double getWeight() {return weight;}public void setWeight(double weight) {this.weight = weight;}public abstract double findArea();
}

因为之前子类都重写过findArea()方法了,所以后续不用改了。

在抽象这块,一定要用多态。

🍺输出结果

image.png

(2)练习2

🌋题目描述

编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个Employee对象的生日,则将该雇员的工资增加100元。

实验说明:

(1)定义一个Employee类,该类包含:

private成员变量name,number,birthday,其中birthday 为MyDate类的对象;

abstract方法earnings();

toString()方法输出对象的name,number和birthday。

(2)MyDate类包含:

private成员变量year,month,day ;

toDateString()方法返回日期对应的字符串:xxxx年xx月xx日

(3)定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。

该类包括:private成员变量monthlySalary;

实现父类的抽象方法earnings(),该方法返回monthlySalary值;

toString()方法输出员工类型信息及员工的name,number,birthday。

(4)参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理。该类包括:

private成员变量wage和hour;

实现父类的抽象方法earnings(),该方法返回wage*hour值;

toString()方法输出员工类型信息及员工的name,number,birthday。

(5)定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各类雇员对象的引用。

利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday,以及该对象生日。

当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息。

//提示:
//定义People类型的数组People c1[]=new People[10];
//数组元素赋值
c1[0]=new People("John","0001",20);
c1[1]=new People("Bob","0002",19);
//若People有两个子类Student和Officer,则数组元素赋值时,可以使父类类型的数组元素指向子类。
c1[0]=new Student("John","0001",20,85.0);
c1[1]=new Officer("Bob","0002",19,90.5);

🌱代码

【Employee.java】

package yuyi11;/*** ClassName: Employee* Package: yuyi11* Description:*      private成员变量name,number,birthday,其中birthday 为MyDate类的对象;*      提供必要的构造器;*      abstract方法earnings(),返回工资数额;*      toString()方法输出对象的name,number和birthday。** @Author 雨翼轻尘* @Create 2023/11/24 0024 16:21*/
public abstract class Employee {private String name;private int number;private MyDate birthday;public Employee() {}public Employee(String name, int number, MyDate birthday) {this.name = name;this.number = number;this.birthday = birthday;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getNumber() {return number;}public void setNumber(int number) {this.number = number;}public MyDate getBirthday() {return birthday;}public void setBirthday(MyDate birthday) {this.birthday = birthday;}public abstract double earnings();public String toString(){return "name= "+name +",number= "+number+",birthday= "+birthday.toDateString(); //注意birthday的调用}}

【MyDate.java】

package yuyi11;/*** ClassName: MyDate* Package: yuyi11* Description:*      private成员变量year,month,day;*      提供必要的构造器;*      toDateString()方法返回日期对应的字符串:xxxx年xx月xx日** @Author 雨翼轻尘* @Create 2023/11/24 0024 16:22*/
public class MyDate {private int year;private int month;private int day;public MyDate() {}public MyDate(int year, int month, int day) {this.year = year;this.month = month;this.day = day;}public int getYear() {return year;}public void setYear(int year) {this.year = year;}public int getMonth() {return month;}public void setMonth(int month) {this.month = month;}public int getDay() {return day;}public void setDay(int day) {this.day = day;}public String toDateString(){return year +"年" +month +"月"+day+"日";}
}

【SalariedEmployee.java】

package yuyi11;/*** ClassName: SalariedEmployee* Package: yuyi11* Description:*      定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。*      该类包括:private成员变量monthlySalary;*      提供必要的构造器;*      实现父类的抽象方法earnings(),该方法返回monthlySalary值;*      toString()方法输出员工类型信息及员工的name,number,birthday。*      比如:SalariedEmployee[name = '',number = ,birthday=xxxx年xx月xx日]** @Author 雨翼轻尘* @Create 2023/11/25 0025 8:34*/
public class SalariedEmployee extends Employee{private double monthlySalary;   //月工资public SalariedEmployee(){}@Overridepublic double earnings() {return monthlySalary;}public SalariedEmployee(String name,int number,MyDate birthday,double monthlySalary){super(name,number,birthday);this.monthlySalary=monthlySalary;}public void setMonthlySalary(double monthlySalary){this.monthlySalary=monthlySalary;}/*public double getMonthlySalary(){return monthlySalary;}*/public String toString(){return "SalariedEmployee[" + super.toString() +"]"; //注意这里要加super}}

【HourlyEmployee.java】

package yuyi11;/*** ClassName: HourlyEmployee* Package: yuyi11* Description:*      参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理。该类包括:*      private成员变量wage和hour;*      提供必要的构造器;*      实现父类的抽象方法earnings(),该方法返回wage*hour值;*      toString()方法输出员工类型信息及员工的name,number,birthday。** @Author 雨翼轻尘* @Create 2023/11/25 0025 8:45*/
public class HourlyEmployee extends Employee{private double wage;    //单位小时工资private int hour;    //月工作的小时数public HourlyEmployee() {}public HourlyEmployee(String name, int number, MyDate birthday, double wage, int hour) {super(name, number, birthday);this.wage = wage;this.hour = hour;}public double getWage() {return wage;}public void setWage(double wage) {this.wage = wage;}public int getHour() {return hour;}public void setHour(int hour) {this.hour = hour;}@Overridepublic double earnings() {return wage * hour;}public String toString(){return "HourlyEmployee[" + super.toString() +"]"; //注意这里要加super}
}

【PayrollSystem.java】

package yuyi11;import java.util.Scanner;/*** ClassName: PayrollSystem* Package: yuyi11* Description:*      定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各类雇员对象的引用。*      利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday,以及该对象生日。*      当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息。** @Author 雨翼轻尘* @Create 2023/11/25 0025 8:51*/
public class PayrollSystem {public static void main(String[] args) {Scanner scan=new Scanner(System.in);//abstract类型的不能创建对象,但是可以创建抽象类数组,然后将子类实例化对象放进去Employee[] emps=new Employee[2];    //这里并没有实例化,只是在堆内存开辟了数组空间emps,用来加载Employee类emps[0]=new SalariedEmployee("张三",1001,new MyDate(2002,9,1),15000); //new的是子类对象--多态emps[1]=new HourlyEmployee("李四",1002,new MyDate(2001,10,5),240,100);System.out.println("请输入当前的月份:");int month=scan.nextInt();for (int i = 0; i < emps.length; i++) {System.out.println(emps[i].toString());System.out.println("工资为: "+emps[i].earnings());if(month==emps[i].getBirthday().getMonth()){System.out.println("生日快乐!加薪100");}}scan.close();}
}

🍺输出结果

image.png

以后在开发中,会见到一些抽象类,见到抽象类,第一反应它可能有抽象方法(抽象类里面不一定有抽象方法,但是一般都有),若想用抽象类,需要找它的子类,因为它不能实例化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/217133.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

incast,拥塞控制,内存墙的秘密

数据中心 incast&#xff0c;广域网拥塞&#xff0c;内存墙都是一类问题。 我接触 incast 很久了&#xff0c;大多是帮忙查问题&#xff0c;也解过几例。 我记得有一次在业务几乎总是(在统计学上&#xff0c;几乎和总是属同义强调) tail latency 很大时&#xff0c;我建议在 …

系列九、声明式事务(xml方式)

一、概述 声明式事务(declarative transaction management)是Spring提供的对程序事务管理的一种方式&#xff0c;Spring的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明&#xff0c;是指在配置文件中声明&#xff0c;用在Spring配置文件中声明式的处理事务来…

【Linux】匿名管道+进程池

文章目录 前置知识一、管道的原理二、管道的特性三、管道的接口四、使用管道实现简单的进程池解决进程池的一个小问题 前置知识 一个进程在创建时&#xff0c;会默认打开三个文件&#xff0c;分别是&#xff1a;stdin&#xff0c;stdout&#xff0c;stderr 进程中有一个维护进…

基于opencv+ImageAI+tensorflow的智能动漫人物识别系统——深度学习算法应用(含python、JS、模型源码)+数据集(三)

目录 前言总体设计系统整体结构图系统流程图 运行环境爬虫模型训练实际应用 模块实现1. 数据准备1&#xff09;爬虫下载原始图片2&#xff09;手动筛选图片 2. 数据处理1&#xff09;切割得到人物脸部2&#xff09;重新命名处理后的图片3&#xff09;添加到数据集 3. 模型训练及…

深入解析Selenium动作链:精通点击、拖拽、切换等操作

背景&#xff1a; 一些交互动作都是针对某个节点执行的。比如&#xff0c;对于输入框&#xff0c;我们就调用它的输入文字和清空文字方法&#xff1b;对于按钮&#xff0c;就调用它的点击方法。其实&#xff0c;还有另外一些操作&#xff0c;它们没有特定的执行对象&#xff0…

JAVA创建线程方式有几种

方式1&#xff1a;继承Thread类 步骤&#xff1a; 创建一个继承于Thread类的子类重写Thread的run()方法创建当前Thread子类的对象通过实例对象调用start()方法&#xff0c;启动线程----》JAVA虚拟机会调用run()方法 实现&#xff1a; public class TestMyThread {public sta…

html实现各种瀑布流(附源码)

文章目录 1.设计来源1.1 动态响应瀑布流1.2 分页瀑布流1.3 响应瀑布流 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/134613121 html实现各种瀑布流(附源码)&#xff0c;…

pwn:[NISACTF 2022]ReorPwn?

题目 按正常方式走&#xff0c;发现指令被反着输出

十大排序之计数排序、桶排序、基数排序(详解)

文章目录 &#x1f412;个人主页&#x1f3c5;算法思维框架&#x1f4d6;前言&#xff1a; &#x1f380;计数排序 时间复杂度O(nk)&#x1f387;1. 算法步骤思想&#x1f387;2.动画实现&#x1f387; 3.代码实现 &#x1f380;桶排序&#x1f387;1. 算法步骤思想&#x1f38…

list的总结

目录 1.什么是list 1.1list 的优势和劣势 优势&#xff1a; 劣势&#xff1a; 2.构造函数 2.1 default (1) 2.2 fill (2) 2.3 range (3) 2.4 copy (4) 3.list iterator的使用 3.1. begin() 3.2. end() 3.3迭代器遍历 4. list容量函数 4.1. empty() 4.2. siz…

开源vs闭源,处在大模型洪流中,向何处去?

文章目录 一、开源和闭源的优劣势比较1.1 开源优势1.2 闭源的优势 二、开源和闭源对大模型技术发展的影响2.1 数据共享2.2 算法创新2.3 业务拓展2.4 安全性和隐私2.5 社会责任和伦理 三、开源与闭源的商业模式比较3.1 盈利模式3.2 市场竞争3.3 用户生态3.4 创新速度 四&#xf…

基于厨师算法优化概率神经网络PNN的分类预测 - 附代码

基于厨师算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于厨师算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于厨师优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络的光滑…