实验三 Java类的继承与派生(头歌)
制作不容易,点个关注!给大家带来更多的价值!
目录
第1关:类的继承
相关知识
继承、封装和多态是Java面向对象的3大特性,它们是面向对象程序开发的重要环节。继承是面向对象实现软件复用的重要手段,当子类继承父类后,子类作为一种特殊的父类,将直接获得父类的属性和方法;封装指的是将对象的实现细节隐藏起来,然后通过一些公用方法来暴露该对象的功能;多态指的是子类对象可以直接赋给父类变量,但运行时依然表现出子类的行为特征,这意味着同一个类型的对象在运行时可能表现出不同的行为特征。
类的继承(extends)
继承可以理解为现实世界中的“是一种(is-a)”关系,实现继承是通过extends关键字在声明类的时候指定其父类,其声明格式如下:
[修饰符] class 类名 extends 父类名
继承某个父类而生成新的子类不但拥有父类的变量与方法,还可以为子类添加新的成员变量和成员方法,以增强父类的功能,也就是所谓的扩展。还可以使用super关键字引用父类的方法,然后再添加新的业务代码。甚至还可以在子类中为父类的某个方法定义多个重载方法,增加该类的灵活性。
通过类的继承,祖先类的所有成员均将成为子类拥冇的“财富”。但能否通过子类对象直接访问这些成员则取决于访问权限的设置。Object类是所有类的祖先。
构造方法与继承关系
(1) 构造方法不存在继承关系,子类通过调用父类的构造方法给父类的属性赋值。
(2) 在子类的构造方法的第1行可以通过super去调用父类的构造方法,如果没有 super调用,则默认调用父类的无参构造方法.所以,在父类中编写构造方法通常均要提供无参构造方法。
继承关系中对成员的访问
由于继承关系的存在,一个对象的属性和方法中有自己新定义的,也有从祖先类继承的。允许子类对父类定义的属性和方法重新定义。一个对象按最近匹配原则查找其属性和方法。
在子类中访问属性和方法时将优先査找自己定义的属性和方法。如果该成员在本 类存在,则使用本类的,否则,按照继承层次的顺序到其祖先类査找。
this关键字特指本类的对象引用,使用this访问成员则首先在本类中査找,如果没有,则到父类逐层向上找。
super特指访问父类的成员,使用super则首先到直接父类査找匹配成员,如果未找到,再逐层向上到祖先类査找。
编程要求
根据提示,在右侧编辑器补充代码。
定义一个Person类,包含姓名(name)、性别(sex)、年龄(age)等字段;
定义一个Teacher类继承Person类,增加职称(pro)、部门(department)等字段;
定义一个Student类继承Person类,增加学号(no)、入学时间(enter)、专业(major)等字段;
定义各类的构造方法和toString()方法,并分别创建对象进行测试。
测试说明
平台会对你编写的代码进行测试:
测试输入:无;
预期输出:
学生基本信息为:张三,男,20,202102088,2021,计算机科学与技术
教师的信息为:齐心,男,44,高级实验师,计算机工程与软件实验中心
开始你的任务吧,祝你成功!
代码如下:
package step1;
import java.util.Scanner;
class Person {
/********** Begin **********/
// 自行设计类的实现
protected String name;
protected String sex;
protected int age;
public Person(String name,String sex,int age){
this.name=name;
this.sex=sex;
this.age=age;
}
/********** End **********/
}
class Student extends Person {
/********** Begin **********/
// 自行设计类的实现
private String no;
private String enter;
private String major;
public Student(String name, String sex, int age, String no, String enter, String major) {
super(name, sex, age);
this.no = no;
this.enter = enter;
this.major = major;
}
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public String getEnter() {
return enter;
}
public void setEnter(String enter) {
this.enter = enter;
}
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
@Override
public String toString() {
return super.name+","+super.sex+","+this.age+","+this.no+","+this.enter+","+this.major;
}
/********** End **********/
}
class Teacher extends Person {
/********** Begin **********/
// 自行设计类的实现
private String pro;
private String department;
public Teacher(String name, String sex, int age, String pro, String department) {
super(name, sex, age);
this.pro = pro;
this.department = department;
}
public String getPro() {
return pro;
}
public void setPro(String pro) {
this.pro = pro;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
@Override
public String toString() {
return super.name+","+super.sex+","+this.age+","+this.pro+","+this.department;
}
/********** End **********/
}
public class Lab3_1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Student student = new Student(sc.next(), sc.next(), sc.nextInt(), sc.next(), sc.next(), sc.next());
Teacher teacher = new Teacher(sc.next(), sc.next(), sc.nextInt(), sc.next(), sc.next());
System.out.println("学生基本信息为:" + student);
System.out.println("教师的信息为:" + teacher);
sc.close();
}
}
第2关:上转型对象的使用,多态的特性
相关知识
对象的上转型对象
假设B类是A类的子类或间接子类,当我们用子类B创建一个对象,并把这个对象的引用放到A类的对象中时:A a = new B(); 称这个A类对象a是子类对象b的上转型对象。
上转对象不能操作子类新增的成员变量(失掉了这部分属性);不能使用子类新增的方法(失掉了一些功能)。
上转型对象可以操作子类继承或隐藏成员变量,也可以使用子类继承的或重写的方法。
上转型对象操作子类继承或重写的方法时,就是通知对应的子类对象去调用这些方法。因此,如果子类重写了父类的某个方法后,对象的上转型对象调用这个方法时,一定是调用了这个重写的方法。
可以将对象的上转型对象再强制转换到一个子类对象,这时,该子类对象又具备了子类所有属性和功能。
多态(polymophism)
多态性就是指父类的某个方法被其子类重写时,可以各自产生自己的功能行为。
当一个类有很多子类时,并且这些子类都重写了父类中的某个方法。那么当我们把子类创建的对象的引用放到一个父类的对象中时,就得到了该对象的一个上转型对象,那么这个上转的对象在调用这个方法时就可能具有多种形态。多态性的实现:通过方法的重载(overloading)、覆盖(overriding)和接口来实现。
方法重载是Java实现面向对象的多态性机制的一种方式。同一个类中多个方法有相同的名字,不同的参数列表,这种情况称为方法重载。返回类型不同并不足以构成方法重载。当重载方法被调用时,编译器根据参数的类型和数量来确定实际调用哪个重载方法的版本。
方法覆盖是Java实现多态性机制的另一种方式。在类层次结构中,如果子类中的一个方法与父类中的方法有相同的方法名并具有相同数量和类型的参数列表,这种情况称为方法覆盖。当一个覆盖方法通过父类引用被调用,Java根据当前被引用对象的类型来决定执行哪个版本的方法。可以通过super关键字调用直属父类中被覆盖的方法版本。
抽象类
抽象类的定义形式:
abstract class 类名称{
成员变量;
方法(){…} //定义一般方法
abstract方法(); //定义抽象方法
}
在抽象类中可以包含一般方法和抽象方法。抽象方法只需声明,而不需实现。抽象方法不能是静态方法,因为静态方法无须对象就能执行。
抽象类表示的是一个抽象概念,不能被实例化为对象。
继承抽象类的具体类必须将抽象类中抽象方法覆盖实现。
编程要求
根据提示,在右侧编辑器补充代码。
定义一个抽象(abstract)类,类名为Employee。Employee类有一个抽象(abstract)方法:public abstract double earnings();
定义Employee的子类:YearWorker、MonthWorker和WeekWorker。
YearWorker对象按年领取薪水(每年10万);
MonthWorker对象按月领取薪水(每月1万);
WeekWorker对象按周领取薪水(每周0.5万,设定一个月4周)。
子类需要重写父类的earnings()方法,给出各自领取每年报酬的具体方式。
定义一个Company类,该类用Employee数组作为成员,Employee数组的单元可以是YearWorker对象、MonthWorker对象、WeekWorker对象的上转型对象。
要求程序能输出Company对象一年需要支付的薪水总额。
测试说明
平台会对你编写的代码进行测试:
测试输入:无;
预期输出:
公司年工资总额:3120000.0
开始你的任务吧,祝你成功!
代码如下:
package step2;
abstract class Employee {
public abstract double earnings();
}
class YearWorker extends Employee {
//重写earnings()方法
/********** Begin **********/
@Override
public double earnings() {
return 100000.0;
}
/********** End **********/
}
class MonthWorker extends Employee {
//重写earnings()方法
/********** Begin **********/
@Override
public double earnings() {
return 120000.0;
}
/********** End **********/
}
class WeekWorker extends Employee {
//重写earnings()方法
/********** Begin **********/
@Override
public double earnings() {
return 5000.0*4*12;
}
/********** End **********/
}
class Company {
Employee[] employees;
double salaries = 0;
Company(Employee[] employees) {
this.employees = employees;
}
public double salariesPay() {
salaries = 0;
//计算salaries
/********** Begin **********/
for(Employee employee:employees) {
salaries+=employee.earnings();
}
/********** End **********/
return salaries;
}
}
public class HardWork {
public static void main(String[] args) {
Employee[] employees = new Employee[20];
for (int i = 0; i < employees.length; i++) {
if(i%3==0)
employees[i] = new WeekWorker();
else if(i%3==1)
employees[i] = new MonthWorker();
else if(i%3==2)
employees[i] = new YearWorker();
}
Company company = new Company(employees);
System.out.println("公司年工资总额:" + company.salariesPay());
}
}
第3关:接口的使用
相关知识
接口
接口是特殊的类,只有方法的原型,方法的实现在其子类中具体定义。Java不支持多继承性,即一个类只能有一个父类。单继承性使得Java简单,易于管理程序。为了克服单继承的缺点,Java使用了接口,一个类可以实现多个接口。
接口定义方法如下:
[public] interface 接口名 [extends 父接口名列表]{
域类型 域名 = 常量值; //常量域声明
返回类型 方法名(参数列表)[throw异常列表]; //抽象方法声明
)
接口只包括常量定义和抽象方法。
接口具有继承性,一个接口还可以继承多个父接口,父接口冋用逗号分隔。
系统默认接口中所有属性的修饰都是public static final
,也就是静态常量。
系统默认接口中所有方法的修饰都是public abstract
。
一个类通过使用关键字implements 声明自己使用一个或多个接口。如果使用多个接口,用逗号隔开接口名。如:
class A implements Printable,Addable
如果一个类使用了某个接口,那么这个类必须实现该接口的所有方法,即为这些方法提供方法体。接口只关心功能,并不关心功能的具体实现。
接口回调是指:可以把实现某一接口的类创建的对象的引用赋给该接口声明的接口变量中。那么该接口变量就可以调用被类实现的接口中的方法。实际上,当接口变量调用被类实现的接口中的方法时,就是通知相应的对象调用接口的方法。
内部类
Java支持在一个类中声明另一个类,这样的类称作内部类,而包含内部类的类成为内部类的外嵌类。 内部类的外嵌类的成员变量在内部类中仍然有效,内部类中的方法也可以调用外嵌类中的方法。内部类的类体中不可以声明类变量和类方法。外嵌类的类体中可以用内部类声明对象,作为外嵌类的成员。
匿名类
和类有关的匿名类
当使用类创建对象时,程序允许我们把类体与对象的创建组合在一起,也就是说,类创建对象时,除了构造方法还有类体,此类体被认为是该类的一个子类去掉类声明后的类体,称作匿名类。匿名类就是一个子类,由于无名可用,所以不可能用匿名类声明对象,但却可以直接用匿名类创建一个对象。
匿名类一定是内部类,匿名类可以继承父类的方法也可以重写父类的方法,匿名类可以访问外嵌类中的成员变量和方法,匿名类的类体中不可以声明static成员变量和static方法。匿名类的主要用途就是向方法的参数传值。
和接口有关的匿名类
Java允许直接用接口名和一个类体创建一个匿名对象,此类体被认为是实现了接口的类去掉类声明后的类体,称作匿名类。
编程要求
根据提示,在右侧编辑器补充代码。
定义一个接口(Display.java),其屮包含一个 display() 方法用于显示信息;
定义通知类(Inform)、汽车类(Car)、广告类(Adervise)均要实现该接口,以显示“通知内容”、“汽车油量”、“广告消息”。
试编程实现并测试类的设计,创建的对象用接口引用,并通过接口引用变量执行 display() 方法。
测试说明
平台会对你编写的代码进行测试:
测试输入:无;
预期输出:
通知内容
汽车油量
广告消息
开始你的任务吧,祝你成功!
代码如下:
package step3;
// 自行设计接口和实现类
/********** Begin **********/
interface Display{
void display();
}
class Adervise implements Display{
@Override
public void display() {
System.out.println("广告消息");
}
}
class Car implements Display{
@Override
public void display() {
System.out.println("汽车油量");
}
}
class Inform implements Display{
@Override
public void display() {
System.out.println("通知内容");
}
}
/********** End **********/
public class Lab3_3 {
public static void main(String[] args) {
Display[] arr = { new Inform(), new Car(), new Adervise() };
for (Display d : arr) {
d.display();
}
}
}
第4关:综合应用
编程要求
根据提示,在右侧编辑器补充代码。
编写一个学生信息管理系统,掌握接口的定义方法以及类的继承、封装和多态的特性。
图1 学生信息管理系统类图
1.定义学生类Student.java,该类有学号、姓名和系部三个成员变量,并用get和set方法进行属性的封装,重写该类的toString()方法,请参考类图1进行编写。
2.定义接口IStudentDAO,其中包含下列数据操作方法
// 插入学生信息记录
public void insertStudent(Student stu);
// 删除指定学号学生信息记录
public void deleteStudent(String sno);
// 更新指定学生信息记录
public void updateStudent(Student stu);
// 按学号查询指定学生信息记录
public Student findStudentBySno(String sno);
// 显示指定学号学生信息记录
public void displayStudentBySno(String sno);
// 显示所有学生信息记录
public void displayAllStudent();
3.定义接口IStudentDAO的实现类StudentDAOImpl,其中包含一个静态的集合对象用来保存学生信息记录,以及接口的所有实现方法,请参考类图1进行编写。
代码提示:
static List<Student> students = new ArrayList<Student>();
ArrayList类是实现了List接口的动态数组,数组元素可以动态增加和减少。常用方法如下:
public int size()
返回此列表中的元素数。
public boolean isEmpty()
如果此列表中没有元素,则返回 true;否则返回 false
public boolean add(E e)
将指定的元素添加到此列表的尾部。
public boolean remove(Object o)
移除此列表中首次出现的指定元素(如果存在)。
/**
* 显示所有学生信息记录参考代码
*/
@Override
public void displayAllStudent() {
if (students.size() > 0) {
for (Student stu : students) {
System.out.println(stu);
}
} else {
System.out.println("数据库中无学生记录!");
}
}
4.定义主类MainClass,实现应用程序的所有逻辑功能。代码如下:
import java.util.Scanner;
public class MainClass {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
StudentDAOImpl studentDAOImpl = new StudentDAOImpl();
//1. 插入学生信息
Student stu = new Student();
stu.setSno(scanner.next());
stu.setSname(scanner.next());
stu.setSdept(scanner.next());
studentDAOImpl.insertStudent(stu);
//2. 显示插入学生信息
System.out.println("1. 插入学生信息如下:");
studentDAOImpl.displayAllStudent();
//3. 更新学生信息
stu.setSname("李四");
stu.setSdept("计算机系");
studentDAOImpl.updateStudent(stu);
System.out.println("2. 更新后学生信息如下:");
System.out.println(studentDAOImpl.findStudentBySno(stu.getSno()));
//4. 删除指定学生信息
System.out.println("3. 删除当前学号学生信息:" + stu.getSno());
studentDAOImpl.deleteStudent(stu.getSno());
System.out.println("学生信息已删除!");
//2. 显示插入学生信息
System.out.println("5. 显示所有学生信息:");
studentDAOImpl.displayAllStudent();
scanner.close();
}
}
提示:
可以在【代码文件】处切换文件进行代码的编写。
测试说明
平台会对你编写的代码进行测试:
测试输入:
202102301 张三 电信系;
预期输出:
- 插入学生信息如下:
学号: 202102301 姓名: 张三 系部: 电信系 - 更新后学生信息如下:
学号: 202102301 姓名: 李四 系部: 计算机系 - 删除当前学号学生信息:202102301
学生信息已删除! - 显示所有学生信息:
数据库中无学生记录!
开始你的任务吧,祝你成功!
代码如下:
Student.java
package step4;
public class Student {
/********** Begin **********/
private String sno;
private String sname;
private String sdept;
public String getSno() {
return sno;
}
public void setSno(String sno) {
this.sno = sno;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getSdept() {
return sdept;
}
public void setSdept(String sdept) {
this.sdept = sdept;
}
@Override
public String toString() {
return "学号: "+sno+" 姓名: "+sname+" 系部: "+sdept;
}
/********** End **********/
}
接口IStudentDAO
package step4;
public interface IStudentDAO {
/********** Begin **********/
// 插入学生信息记录
public void insertStudent(Student stu);
// 删除指定学号学生信息记录
public void deleteStudent(String sno);
// 更新指定学生信息记录
public void updateStudent(Student stu);
// 按学号查询指定学生信息记录
public Student findStudentBySno(String sno);
// 显示指定学号学生信息记录
public void displayStudentBySno(String sno);
// 显示所有学生信息记录
public void displayAllStudent();
/********** End **********/
}
接口IStudentDAO的实现类StudentDAOImpl
package step4;
import java.util.ArrayList;
import java.util.List;
public class StudentDAOImpl implements IStudentDAO {
static List<Student> students = new ArrayList<Student>();
/********** Begin **********/
//自行实现所有其它方法
@Override
public void insertStudent(Student stu) {
// TODO Auto-generated method stub
students.add(stu);
}
@Override
public void deleteStudent(String sno) {
// TODO Auto-generated method stub
for(Student s:students) {
if(s.getSno()==sno) {
students.remove(s);
break;
}
}
}
@Override
public void updateStudent(Student stu) {
// TODO Auto-generated method stub
for(Student s:students) {
if(s.getSno()==stu.getSno()) {
s.setSname(stu.getSname());
s.setSdept(stu.getSdept());
}
}
}
@Override
public Student findStudentBySno(String sno) {
// TODO Auto-generated method stub
for(Student s:students) {
if(s.getSno()==sno) {
return s;
}
}
return null;
}
@Override
public void displayStudentBySno(String sno) {
// TODO Auto-generated method stub
}
/********** End **********/
/**
* 显示所有学生信息记录参考代码
*/
@Override
public void displayAllStudent() {
if (students.size() > 0) {
for (Student stu : students) {
System.out.println(stu);
}
}else {
System.out.println("数据库中无学生记录!");
}
}
}
定义主类MainClass
package step4;
import java.util.Scanner;
public class MainClass {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
StudentDAOImpl studentDAOImpl = new StudentDAOImpl();
//1. 插入学生信息
Student stu = new Student();
stu.setSno(scanner.next());
stu.setSname(scanner.next());
stu.setSdept(scanner.next());
studentDAOImpl.insertStudent(stu);
//2. 显示插入学生信息
System.out.println("1. 插入学生信息如下:");
studentDAOImpl.displayAllStudent();
//3. 更新学生信息
stu.setSname("李四");
stu.setSdept("计算机系");
studentDAOImpl.updateStudent(stu);
System.out.println("2. 更新后学生信息如下:");
System.out.println(studentDAOImpl.findStudentBySno(stu.getSno()));
//4. 删除指定学生信息
System.out.println("3. 删除当前学号学生信息:" + stu.getSno());
studentDAOImpl.deleteStudent(stu.getSno());
System.out.println("学生信息已删除!");
//2. 显示插入学生信息
System.out.println("5. 显示所有学生信息:");
studentDAOImpl.displayAllStudent();
scanner.close();
}
}