多态
一、内容回顾
在Java中,一个引用类型变量可以指向多种不同类型的对象(包括父类型的引用可以指向子类型的对象,或者接口类型的引用可以指向实现该接口的类的实例)称为多态。多态作为面向对象编程的核心特征之一,能够有效提高程序的可扩展性。在这一部分将对多态涉及到的Java抽象类和接口技术进行分析介绍。
(一)抽象类
用关键字 abstract 修饰的类叫做抽象类。
抽象类有如下特点:
(1)含有抽象方法的类必须被声明为抽象类。
(2)抽象类必须被继承,抽象方法必须被重写。
(3)抽象类不能被实例化。
(4)抽象方法只需要声明,而不需要实现。
(5)如果某个类是抽象类,那么这个类既可以包含抽象方法,也可以包含具体的方法(有声明,有实现)。
在Java中,与抽象类类似,接口是实现多态的关键技术。之前对接口进行了介绍,因此本次主要介绍接口与抽象类的差异。
(二) 抽象类与接口的不同
(1)抽象类里可以有构造方法,而接口内不能有构造方法。
(2)抽象类中可以有普通成员变量,而接口中不能有普通成员变量。
(3)抽象类中可以包含非抽象的普通方法,而接口中所有的方法必须是抽象的,不能有非抽象的普通方法。
(4)抽象类中的抽象方法的访问类型可以是public ,protected和默认类型,但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
(5)抽象类中可以包含静态方法,接口内不能包含静态方法。
(6)抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型不受限制,但接口中定义的变量只能是public static final类型,并且默认为public static final类型。
(7)一个类可以实现多个接口,但只能继承一个抽象类。
(8)接口更多的是在系统框架设计方面发挥作用,主要定义模块之间的通信,而抽象类主要在代码实现方面发挥作用,可以实现代码的重用。
二、典型实例
例1 本例定义一个抽象类Shapes,并在类Shapes的两个子类Square和Circle中分别给出了getArea()和getPerimeter()的实现。
// AbstractClassTest.java
abstract class Shapes {
public String getInfo(String shape) {
return shape+" informations are:perimeter is "+getPerimeter()+",area is "+getArea();
}
abstract double getArea();
abstract double getPerimeter();
}
class Circle extends Shapes {
public double r;
public double getArea() {
return (r * r * Math.PI);
}
public double getPerimeter() {
return (2 * Math.PI * r);
}
public Circle(int radius) {
r = radius;
}
}
class Square extends Shapes {
public int width , height;
public double getArea() {
return (width * height);
}
public double getPerimeter() {
return (2 * width + 2 * height);
}
public Square(int x, int y) {
width = x;
height = y;;
}
}
public class AbstractClassTest{
public static void main(String[] args) {
Shapes shap1 = new Circle(4);
Shapes shap2 = new Square(6,9);
System.out.println(shap1.getInfo("circle"));
System.out.println(shap2.getInfo("sqaure"));
}
}
运行结果如图1所示。
图1 例1执行结果
例2
// InterfaceTest.java
abstract class Duck{
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck(){}
public abstract void display();
public void swim(){
System.out.println("All duck can swim!");
}
public void performFly(){
flyBehavior.fly();
}
public void performQuack(){
quackBehavior.quack();
}
}
interface FlyBehavior{
public void fly();
}
interface QuackBehavior{
public void quack();
}
class Quack implements QuackBehavior{
public void quack(){
System.out.println("Quack!");
}
}
class MuteQueak implements QuackBehavior{
public void quack(){
System.out.println("Silence!");
}
}
class FlyWithWings implements FlyBehavior{
public void fly(){
System.out.println("I can fly!");
}
}
class FlyNoWay implements FlyBehavior{
public void fly(){
System.out.println("I can not fly!");
}
}
class MallardDuck extends Duck{
public MallardDuck (){
flyBehavior = new FlyWithWings ();
quackBehavior = new Quack();
}
public void display(){
System.out.println("I am MallardDuck!");
}
}
class DecoyDuck extends Duck{
public DecoyDuck() {
flyBehavior = new FlyNoWay ();
quackBehavior = new MuteQueak();
}
public void display(){
System.out.println("I am DecoyDuck!");
}
}
public class InterfaceTest{
public static void main(String[] args) {
Duck duck1 = new MallardDuck();
Duck duck2 = new DecoyDuck();
duck1.performQuack();
duck1.performFly();
duck1.display();
duck2.performQuack();
duck2.performFly();
duck2.display();
}
}
运行结果如图2所示。
说明:
①本例中的MallardDuck和DecoyDuck分别代表两种不同类型的鸭子。而FlyWithWings,FlyNoWay, Quack, MuteQueak这四个类分别通过实现FlyBehavior和QuackBehavior这两个接口来实现不同的“飞”和“叫”的动作。
②本例将“飞”和“叫”的动作用接口的方式实现的好处在于,其他类型的鸭子可以重复利用“飞,叫”的不同实现,从而达到代码复用的目的。另外,如果“飞”和“叫”的实现发生改变,则只需要修改实现FlyBehavior和QuackBehavior这两个接口的类,而不会将这种改变传递给不同类型的鸭子类,从而达到了代码易扩展,易维护的目的。
图2 例2执行结果
三、实验设计
(一)实验一
(1)实验目的
让学生体验抽象类和多态的应用。
(2)实验要求 设计3个类,分别是学生类Student,本科生类Undergaduate,研究生类Postgraduate,其中Student类是一个抽象类,它包含一些基本的学生信息,如姓名、所学课程、课程成绩等。Student类和主类代码如Student.java 和CourseGrade.java 所示。而Undergraduate类和Postgraduate都是Student类的子类,它们之间的主要差别是计算课程成绩等级的方法有所不同,研究生的标准要比本科生的标准高一些,如表1。所示假设某班级里既有本科生也有研究生,请编写程序统计出全班学生的成绩等级并显示出来。
表1 课程成绩等级表
本科生标准 | 研究生标准 | ||
80--100 | 优秀 | 90—100 | 优秀 |
70--80 | 良好 | 80—90 | 良好 |
60--70 | 一般 | 70—80 | 一般 |
50--60 | 及格 | 60—70 | 及格 |
50以下 | 不及格 | 60以下 | 不及格 |
// Student.java
abstract class Student {
final static int CourseNo = 3;
String type;//本科生或者研究生
String name;//学生姓名
int[] courses;//课程成绩数组
String courseGrade;//成绩等级
public Student(String name) {
this.name = name;
courses = new int[CourseNo];
courseGrade = null;
}
public abstract void calculateGrade();
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int[] getCourses() {
return courses;
}
public void setCourses(int[] courses) {
this.courses = courses;
}
public int getCourseScore(int courseNumber) {
return courses[courseNumber];
}
public void setCourseScore(int courseNumber,int courseScore) {
this.courses[courseNumber] = courseScore;
}
public String getCourseGrade() {
return courseGrade;
}
public void setCourseGrade(String courseGrade) {
this.courseGrade = courseGrade;
}
}
// CourseGrade.java
public class CourseGrade {
public static void main(String[] args) {
Student[] students=new Student[5];
students[0]=new Undergraduate("李明");
students[1]=new Undergraduate("张红");
students[2]=new Postgraduate("赵刚");
students[3]=new Undergraduate("周伟");
students[4]=new Postgraduate("刘凯");
for(int i=0;i<5;i++){
students[i].setCourseScore(0, 87);
students[i].setCourseScore(1, 90);
students[i].setCourseScore(2, 78);
}
for(int i=0;i<5;i++){
students[i].calculateGrade();
}
System.out.println("姓名"+" 类型"+" 成绩");
System.out.println("----------------------");
for(int i=0;i<5;i++){
System.out.println(students[i].getName()+" "+
students[i].getType()+" "+
students[i].getCourseGrade());
}
}
}
实验代码
//CouseGrade.java
abstract class Student{
final static int CourseNo = 3;
String type;//本科生或者研究生
String name;//学生姓名
int[] courses;//课程成绩数组
String courseGrade;//成绩等级
public Student(String name){
this.name = name;
courses = new int[CourseNo];
courseGrade = null;
}
public abstract void calculateGrade();
public String getType(){
return type;
}
public void setType(String type){
this.type = type;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int[] getCourses(){
return courses;
}
public void setCourses(int[] courses){
this.courses = courses;
}
public int getCourseScore(int courseNumber){
return courses[courseNumber];
}
public void setCourseScore(int courseNumber,int courseScore){
this.courses[courseNumber] = courseScore;
}
public String getCourseGrade(){
return courseGrade;
}
public void setCourseGrade(String courseGrade){
this.courseGrade = courseGrade;
}
}
class Undergraduate extends Student{
public Undergraduate(String name){ //类型和名字
super(name);
type = "本科生";
}
public void calculateGrade(){
int total = 0;
double average = 0;
for(int i=0;i<CourseNo;i++){
total += courses[i];
}
average = total/CourseNo; //计算各门成绩之和求平均值
if(average>=80&&average<100) courseGrade = "优秀";
else if(average>=70&&average<80) courseGrade = "良好";
else if(average>=60&&average<70) courseGrade = "一般";
else if(average>=50&&average<60) courseGrade = "及格";
else courseGrade = "不及格";
}
}
class Postgraduate extends Student{
public Postgraduate(String name){
super(name);
type = "研究生";
}
public void calculateGrade(){
int total = 0;
double average = 0;
for(int i=0;i<CourseNo;i++){
total += courses[i];
}
average = total/CourseNo;//计算各门成绩之和再求平均值
if(average>=90&&average<100) courseGrade = "优秀";
else if(average>=80&&average<90) courseGrade = "良好";
else if(average>=70&&average<80) courseGrade = "一般";
else if(average>=60&&average<70) courseGrade = "及格";
else courseGrade = "不及格";
}
}
public class CourseGrade{
public static void main(String[] args){
Student[] students = new Student[5];
students[0] = new Undergraduate("李明");
students[1] = new Undergraduate("张红");
students[2] = new Postgraduate("赵刚");
students[3] = new Undergraduate("周伟");
students[4] = new Postgraduate("刘凯");
for(int i=0;i<5;i++){
students[i].setCourseScore(0,87);
students[i].setCourseScore(1,90);
students[i].setCourseScore(2,78);
}
for(int i=0;i<5;i++){
students[i].calculateGrade();
}
System.out.println("姓名"+" 类型"+" 成绩");
System.out.println("----------------------");
for(int i=0;i<5;i++){
System.out.println(students[i].getName()+" "+
students[i].getType()+" "+
students[i].getCourseGrade());
}
}
}
运行结果
(3)实验效果展示
执行后的结果如图3所示。
图3 实验一效果展示
(4)实验指导
本实验的关键是在子类(Undergraduate和Postgraduate)中实现calculateGrade()。实现的方式为将int[] courses中的成绩求和然后取平均值,最后根据表1来判断成绩等级。
(二)实验二
(1)实验目的
让学生体验抽象类和多态的应用。
(2)实验要求
如MainBoard.java和Assemblers.java所示,MainBoard为主板类,方法 usePCICard 用来启动和停止PCI插槽上的板卡。Assembler为主类,通过主板启动和停止网卡和声卡。要求以实现接口的形式实现网卡类和声卡类。
// MainBoard.java
class MainBoard {
public void usePCICard(PCI p){
p.start();
p.stop();
}
}
// Assemblers.java
public class Assembler{
public static void main(String [] args){
MainBoard mb=new MainBoard();
NetworkCard nc=new NetworkCard();
mb.usePCICard(nc);
SoundCard sc=new SoundCard();
mb.usePCICard(sc);
}
}
实验代码
//Assembler.java
class MainBoard{
public void usePCICard(PCI p){
p.start();
p.stop();
}
}
interface PCI{ //声明一个PCI接口
void start();
void stop();
}
class NetworkCard implements PCI{ //网卡类实现接口
public void start(){
System.out.println("Send ...");
}
public void stop(){
System.out.println("Network Stop.");
}
}
class SoundCard implements PCI{ //声卡类实现接口
public void start(){
System.out.println("Du du...");
}
public void stop(){
System.out.println("Sound Stop.");
}
}
public class Assembler{
public static void main(String[] args){
MainBoard mb = new MainBoard();
NetworkCard nc = new NetworkCard();
mb.usePCICard(nc);
SoundCard sc = new SoundCard();
mb.usePCICard(sc);
}
}
运行结果
(3)实验效果展示
执行后的结果如图4所示。
图4 实验二效果展示
(4)实验指导
定义一个PCI接口,让网卡类和声卡类来实现这个接口。