1 查看下列代码

abstract class Vehicle {
	public int speed() {
		return 0;
	}
}

class Car extends Vehicle {
	public int speed() {
		return 60;
	}
}

class RaceCar extends Car {
	public int speed() {
		return 150;
	}
}

public class TestCar {
	public static void main(String[] args) {
		RaceCar racer = new RaceCar();
		Car car = new RaceCar();
		Vehicle vehicle = new RaceCar();
		System.out.println(racer.speed() + ", " + car.speed() + ", "
	    + vehicle.speed());
	}
}

上述代码运行的结果是

A. 0,0,0

B. 150,60,0

C. 150,150,150

D. 抛出运行时异常

参考答案

C 选项正确。

这是因为,子类可以重写(覆盖)继承自父类的方法,即方法名和参数列表与父类的方法相同,但方法的实现不同。当子类对象的重写方法被调用时(无论是通过子类的引用调用还是通过父类的引用调用),运行的是子类的重写后的版本。因此,本题的正确答案为选项 C。

2 编写正六边形类继承Shape,实现其area方法

在课上案例“根据周长计算不同形状图形的面积”基础上,编写正六边形类继承Shape,实现其area方法。

注:正六边形的面积计算公式为:0.0721 * c * c,其中c为周长。

参考答案:

实现此案例需要按照如下步骤进行。

步骤一:定义类Hexagon

定义名为Hexagon的类,表示正六边形,使该类继承自Shape类,代码如下所示:

public class Hexagon extends Shape {
}

步骤二:实现 area方法

在area方法中,根据正六边形的面积公式计算面积,代码如下所示:

public class Hexagon extends Shape {
#cold_bold	public Hexagon(double c) {
#cold_bold		this.c = c;
#cold_bold	}
#cold_bold	/**
#cold_bold	 * 计算正六边形的面积
#cold_bold	 */
#cold_bold    public double area() {
#cold_bold        return 0.0721 * c * c;
#cold_bold   }
}

步骤三:在测试类TestShape类中添加测试代码

在TestShape类中,构造Hexagon类型的对象,并将其放入数组中,代码如下所示:

public class TestShape {
	public static void main(String[] args) {
#cold_bold		Shape[] shapes = new Shape[3];
		shapes[0] = new Circle(4);//数组中的第一个元素为圆形对象
		shapes[1] = new Square(4);//数组中的第二个元素为正方形对象
#cold_bold		shapes[2] = new Hexagon(4);//数组中的第三个元素为正六边形对象
		maxArea(shapes);
	}
public static void maxArea(Shape[] shapes) {
		double max = shapes[0].area();
		int maxIndex = 0;
		for (int i = 1; i < shapes.length; i++) {
			double area = shapes[i].area();
			if (area > max) {
				max = area;
				maxIndex = i;
			}
		}
		System.out.println("数组中索引为"+maxIndex+"的图形的面积最大,面积为:"+max);
	}
}

步骤四:运行

运行TestShape类,控制台输出结果如下所示:

数组中索引为0的图形的面积大,面积为:1.2736

观察上述输出结果,可以看出,shapes[0]表示的是圆形,周长相同的情况下,圆形的面积更大些。

本案例中,Hexagon类的完整代码如下所示:

public class Hexagon extends Shape {
	public Hexagon(double c) {
		this.c = c;
	}
	/**
	 * 计算正六边形的面积
	 */
    public double area() {
        return 0.0721 * c * c;
   }
}

TestShape类的完整代码如下所示:

public class TestShape {
	public static void main(String[] args) {
		Shape[] shapes = new Shape[3];
		shapes[0] = new Circle(4);//数组中的第一个元素为圆形对象
		shapes[1] = new Square(4);//数组中的第二个元素为正方形对象
		shapes[2] = new Hexagon(4);//数组中的第三个元素为正六边形对象
		maxArea(shapes);
	}
 public static void maxArea(Shape[] shapes) {
		double max = shapes[0].area();
		int maxIndex = 0;
		for (int i = 1; i < shapes.length; i++) {
			double area = shapes[i].area();
			if (area > max) {
				max = area;
				maxIndex = i;
			}
		}
		System.out.println("数组中索引为"+maxIndex+"的图形的面积最大,面积为:"+max);
	}

}

3 简述抽象类的意义

参考答案:

抽象类的意义在于:

1. 为其子类提供一个公共的父类型,避免该类被实例化;

2. 封装子类中的重复内容(成员变量和方法);

3. 定义公共抽象方法,由子类提供不同的实现。

4 编写建设银行接口CCB继承银联接口,并实现该接口

在课上案例“银行卡系统(实现银联接口)”的基础上,编写建设银行接口CCB。建设银行接口,用于描述中国建设银行发行的卡片功能,在满足银联接口的规则基础上,增加了支付燃气费的功能。

参考答案

实现此案例需要按照如下步骤进行。

步骤一: 定义建设银行接口

定义名为CCB的接口表示建设银行接口,用于描述中国建设银行发行的卡片功能,该接口需要满足银联接口的功能,因此,继承银联接口;该接口在具备银联接口的功能基础上,要求增加支付燃气费的功能,所以,在该接口中定义payGasBill方法,表示此功能,代码如下所示:

/**
 * 接口:用于描述建设银行发行的卡片功能,在满足
 * 银联的规则基础上,添加自己特有的功能
 */
public interface CCB extends UnionPay {
	/**增加的支付燃气费功能*/
	public void payGasBill(double number);
}

步骤二:定义建设银行接口的实现类

首先,定义名为CCBImpl的类 ,该类实现CCB接口;另外,分析问题中的取钱功能,需要对余额信息进行存储,插入卡片以后需要输入密码后才能进行取钱,因此,在CCBImpl类中定义double类型属性money表示账户余额以及String类型属性pwd表示卡片的密码,代码如下所示:

/**
 * 类:用于描述建设银行实际发行的卡片
 * 该卡片具有的功能来自于继承的已经符合银联规范的CCB接口
 */
public class CCBImpl implements CCB {
	private double money;
	private String pwd;
	
	public CCBImpl(double money,String pwd){
		this.money = money;
		this.pwd = pwd;
	}
}

步骤三:实现检查密码、获取余额、取款、支付燃气费功能

在CCBImpl类实现checkPwd方法、getBalance方法、drawMoney方法以及payGasBill方法,代码如下所示:

/**
 * 类:用于描述建设银行实际发行的卡片
 * 该卡片具有的功能来自于继承的已经符合银联规范的CCB接口
 */
public class CCBImpl implements CCB {
	private double money;
	private String pwd;
	
	public CCBImpl(double money,String pwd){
		this.money = money;
		this.pwd = pwd;
	}
	
#cold_bold	@Override
#cold_bold	public double getBalance() {		
#cold_bold		return money;
#cold_bold	}
#cold_bold
#cold_bold	@Override
#cold_bold	public boolean drawMoney(double number) {
#cold_bold		if(number <= money){
#cold_bold			money -=number;
#cold_bold			return true;
#cold_bold		}
#cold_bold		return false;
#cold_bold
#cold_bold	}
#cold_bold
#cold_bold	@Override
#cold_bold	public void payGasBill(double number) {
#cold_bold		if(number < money){
#cold_bold			money-=number;
#cold_bold		}
#cold_bold	}
#cold_bold
#cold_bold	@Override
#cold_bold	public boolean checkPwd(String input) {
#cold_bold		if(pwd.equals(input))
#cold_bold			return true;
#cold_bold		else
#cold_bold			return false;
#cold_bold	}
}

以上四个方法的实现逻辑为:

取钱功能的实现为在当前余额的基础上减去要取的金额,如drawMoney方法的实现。

支付燃气费功能的实现为当前余额的基础上减去要支付的金额,如payGasBill方法的实现。

检查密码功能的实现为检查卡的密码与用户输入的密码是否相等,如果相等返回true,否则返回false,如checkPwd方法的实现。

获取余额功能的实现为类CCBImpl中的money属性即表示余额,将其返回即可,如getBalance方法的实现。

步骤四:测试

在TestUnionPay类中,测试银联接口提供的方法,是否成功实现,代码如下所示:

import java.util.Scanner;
/**
 * 测试实现接口后的类的方法调用
 */
public class TestUnionPay {

	public static void main(String[] args) {
		//ICBCImpl icbc = new ICBCImpl(2000,"123456");
		//ICBC icbc = new ICBCImpl(2000,"123456");
		//UnionPay icbc = new ICBCImpl(2000,"123456");
		//UnionPay icbc = new ABCImpl(2000,"123456");
		UnionPay ccb = new CCBImpl(2000,"123456");
		Scanner input = new Scanner(System.in);
		System.out.println("请输入密码:");
		if(ccb.checkPwd(input.next())){
			System.out.println("请输入金额:");
			double num = Double.parseDouble(input.next());
			if(ccb.drawMoney(num)){
				System.out.println("取钱成功,卡余额为:"+icbc.getBalance());
			}
			else{
				System.out.println("取钱失败");
			}
		}else{
			System.out.println("密码错误");
		}
	}
}

查看上述代码,可以看出上述代码是采用CCBImpl类来实例化银联接口UnionPay的,运行后,实现了取款功能。

本案例中,UnionPay接口的完整代码如下所示:

/**
 * 接口:用于描述银联统一制定的规则
 */
public interface UnionPay {
	/**查看余额*/
	public double getBalance();	
	/**取钱*/
	public boolean drawMoney(double number);
	/**检查密码*/
	public boolean checkPwd(String input);
}

CCB接口的完整代码如下所示:

/**
 * 接口:用于描述建设银行发行的卡片功能,在满足
 * 银联的规则基础上,添加自己特有的功能
 */
public interface CCB extends UnionPay {
	/**增加的支付燃气费功能*/
	public void payGasBill(double number);
}

CCBImpl类的完整代码如下所示:

/**
 * 类:用于描述建设银行实际发行的卡片
 * 该卡片具有的功能来自于继承的已经符合银联规范的CCB接口
 */
public class CCBImpl implements CCB {
	private double money;
	private String pwd;
	
	public CCBImpl(double money,String pwd){
		this.money = money;
		this.pwd = pwd;
	}
	
	@Override
	public double getBalance() {		
		return money;
	}

	@Override
	public boolean drawMoney(double number) {
		if(number <= money){
			money -=number;
			return true;
		}
		return false;

	}

	@Override
	public void payGasBill(double number) {
		if(number < money){
			money-=number;
		}
	}

	@Override
	public boolean checkPwd(String input) {
		if(pwd.equals(input))
			return true;
		else
			return false;
	}
}

TestUnionPay类的完整代码如下所示:

import java.util.Scanner;
/**
 * 测试实现接口后的类的方法调用
 */
public class TestUnionPay {

	public static void main(String[] args) {
		//ICBCImpl icbc = new ICBCImpl(2000,"123456");
		//ICBC icbc = new ICBCImpl(2000,"123456");
		//UnionPay icbc = new ICBCImpl(2000,"123456");
		//UnionPay icbc = new ABCImpl(2000,"123456");
		UnionPay ccb = new CCBImpl(2000,"123456");
		Scanner input = new Scanner(System.in);
		System.out.println("请输入密码:");
		if(ccb.checkPwd(input.next())){
			System.out.println("请输入金额:");
			double num = Double.parseDouble(input.next());
			if(ccb.drawMoney(num)){
				System.out.println("取钱成功,卡余额为:"+icbc.getBalance());
			}
			else{
				System.out.println("取钱失败");
			}
		}else{
			System.out.println("密码错误");
		}
	}
}

5 关于接口和抽象类,下列说法正确的是

A. 抽象类和接口都不能实例化。

B. 接口里只能包含抽象方法,抽象类则可以包含普通方法。

C. 接口里不包含构造器,抽象类里可以包含构造器。

D. 一个类只能实现一个接口

参考答案:

选项ABC是正确的。

选项A,抽象类和接口都不能实例化对象,用于被其他类继承和实现。

选项B,接口里只能包含抽象方法,抽象类则可以包含普通方法和抽象方法。

选项C,接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。

选项D,一个类只能有一个直接父类;但一个类可以直接实现多个接口,实现多继承。