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 {
- public Hexagon(double c) {
- this.c = c;
- }
- /**
- * 计算正六边形的面积
- */
- public double area() {
- return 0.0721 * c * c;
- }
- }
步骤三:在测试类TestShape类中添加测试代码
在TestShape类中,构造Hexagon类型的对象,并将其放入数组中,代码如下所示:
- 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);
- }
- }
步骤四:运行
运行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;
- }
- @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;
- }
- }
以上四个方法的实现逻辑为:
取钱功能的实现为在当前余额的基础上减去要取的金额,如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接口的完整代码如下所示:
CCB接口的完整代码如下所示:
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,一个类只能有一个直接父类;但一个类可以直接实现多个接口,实现多继承。