文章目录
- 一、抽象类的由来
- (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
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。
类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
将父类加上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();}
}
输出结果:
现在在现有的class Person
前面加上abstract
。
Person
类就叫做抽象类。意味着它不能够实例化了。
上一节说了final
,final也可以用来修饰Person,表示不能有子类。
现在这个可以有子类的,只是不能造对象了。
可以看到,这里就报错了:
既然不能造方法,那么此时方法也自然不能调用了。
单例模式只造一次对象,抽象类是禁止造对象。
(2)抽象方法
随着Person提供的子类越来越丰富,Person里面的eat()
方法都不用指明具体怎么吃了,具体的子类会指明。
所以在Person里面,只需要声明了就行。
表示有这个功能,具体怎么吃不确定,需要子类重写它。
比如,现在在eat()方法前面加上abstract
,如下:
此时会报错,因为抽象方法不能有方法体。(代码连着花括号一块都不能要)
这样即可:(表明此方法是抽象方法)
既然这个抽象方法没有方法体了,那么它也就不应该被调用了。
在子类Student中将父类Person中的eat()方法重写了,可以发现这里的符号变了,抽象方法的重写换了一个词儿,叫Implements
(实现),而之前的重写叫Overrides
(重写)。
这里就不讲究那么多了,都叫重写也没什么毛病。
所以,在子类中,将父类的抽象方法给重写了,不耽误后面子类正常调用。
Java语法规定,包含抽象方法的类必须是抽象类。
刚才我们先说的是抽象类,表示该类不能被实例化了。然后说的是抽象方法,不能有方法体。没有任何问题。
但现在方法是抽象的,若是该类不是抽象的,就会报错,如下:
什么原因呢?
抽象方法没有方法体,那么这个方法就不应该被调用。
现在的方法是非静态方法,按道理说拿对象是可以调用它的。
现在既然不给调用抽象方法,那么就别有对象了。
如何保证这个类没有对象?删掉构造器?不行,就算没有写构造器,也会有默认的空参构造器,没有意义。那就限制该类呗,那就抽象,抽象类没有对象。
所以,抽象方法所属的类也是抽象类,因为这样就不能造对象了。
抽象方法存在的原因,主要是在多态应用中,对象只能调用父类中已有的方法,子类自己写的方法不能调用,所以要在抽象父类中声明抽象方法。
(3)补充1
若此时,我们将sleep()方法也抽象化:
重新建一个Worker类,继承于Person类,会发现报错,如下:
以前是构造器的问题,但现在构造器没有问题。(有空参构造器)
看报错信息可知,要么将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
,然后点击“实现方法”:
将两个都选中,点击“确定”:
然后就可以自动生成啦:
当然,只重写了一个方法也会报错,如下:
(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()也给重写一下才不会报错。
如下:
现在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()也可以(不会报错)。
对于Worker类来说,它有三个方法,从Person类中继承来的(Person两个,Creature一个)。此时只重写了一个,剩下两个还是抽象的,所以Worker还只能是抽象类。
【Worker.java】
package yuyi08;public abstract class Worker extends Person{ //只重写了一个父类抽象方法,还是抽象类@Overridepublic void eat() {System.out.println("工人很辛苦,多吃饭");}}
既然Worker类是抽象类,那么就不能去造对象。如下:
(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
修饰方法:
> 此方法即为抽象方法。
> 抽象方法只有方法的声明,没有方法体。
> 抽象方法其功能是确定的(通过方法的声明即可确定),只是不知道如何具体实现(体现为没有方法体)。
> 子类必须重写父类中的所有的抽象方法之后,方可实例化。否则,此子类仍然是一个抽象类。
注意:抽象方法没有方法体。
抽象方法虽然没有声明,但是这个方法的功能已经确定了,只不过没有方法体(不知道功能该如何实现)而已。
代码举例:
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)使用说明
- 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
- 抽象类中,也有构造方法,是供子类创建对象时,初始化父类成员变量使用的。
理解:子类的构造方法中,有默认的super()或手动的super(实参列表),需要访问父类构造方法。
- 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
- 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
(3) 注意事项
1. abstract不能使用的场景
<1> abstract
不能修饰哪些结构?
属性、构造器、代码块等。
<2> abstract
不能与哪些关键字共用?(自洽)
不能用abstract修饰私有方法、静态方法、final的方法、final的类。
①私有方法不能重写
若abstract
修饰了一个方法,这个方法一定要让子类去重写的,否则就造不了对象了。
②静态方法不能重写
避免静态方法使用类进行调用
一般我们是这样来写的,如下:
此时将method()方法私有化是不可以的,如下:
那用static
修饰呢?
静态方法(static)本来就可以通过类来调;而抽象方法(abstract)没有方法体,这个方法就不能被调。
所以不能用static和abstract组合。
抽象方法
只有声明,没有方法体,这个方法不能被调。
调用方法的有“类.
”和“对象.
”两种方法。若此时不让调方法,那就让这两件事做不了。
不让对象去调用,就让类为抽象类,它就没有对象了,自然就不能用对象去调用什么方法;不让类去调用,这个方法就不要是静态方法,就不能拿类去调用了。
所以刚才就不能静态,因为静态(用static修饰)了,它就可以被类调用,而此时此方法又被abstract修饰,意味着它是抽象类不能被调用。矛盾呐。
静态方法可以直接用类名调用,抽象方法不能调用。
③final的方法不能被重写
abstract
修饰的方法一定要被重写,否则就不能造对象。
所以final与abstract水火不容。
④final修饰的类不能有子类
abstract
修饰的类一定要有子类,否则也造不了对象,而且自己也造不了,那有何用呢?
(4) 应用举例
1. 举例1 航运
在航运公司系统中,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)
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
解决的问题:
- 当功能内部一部分实现是确定的(非抽象),另一部分实现是不确定的(抽象化)。这时可以把不确定的部分暴露出去,让子类去实现。 (父类中有方法体的都是确定的,没有方法体的都是不确定的)
- 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
类比举例:英语六级模板
制作月饼的模板:
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);}}}
}
👻输出结果(部分)
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万美元!!");}
}
👻输出结果
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()
,当时还纠结返回什么,现在来看根本不需要提供它的方法体。因为不确定,所以可以抽象化。同样,当前类也是抽象类了。
【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()
方法了,所以后续不用改了。
在抽象这块,一定要用多态。
🍺输出结果
(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();}
}
🍺输出结果
以后在开发中,会见到一些抽象类,见到抽象类,第一反应它可能有抽象方法(抽象类里面不一定有抽象方法,但是一般都有),若想用抽象类,需要找它的子类,因为它不能实例化。