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。
在课上案例“根据周长计算不同形状图形的面积”基础上,编写正六边形类继承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); } }
参考答案:
抽象类的意义在于:
1. 为其子类提供一个公共的父类型,避免该类被实例化;
2. 封装子类中的重复内容(成员变量和方法);
3. 定义公共抽象方法,由子类提供不同的实现。
在课上案例“银行卡系统(实现银联接口)”的基础上,编写建设银行接口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("密码错误"); } } }
A. 抽象类和接口都不能实例化。
B. 接口里只能包含抽象方法,抽象类则可以包含普通方法。
C. 接口里不包含构造器,抽象类里可以包含构造器。
D. 一个类只能实现一个接口
参考答案:
选项ABC是正确的。
选项A,抽象类和接口都不能实例化对象,用于被其他类继承和实现。
选项B,接口里只能包含抽象方法,抽象类则可以包含普通方法和抽象方法。
选项C,接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
选项D,一个类只能有一个直接父类;但一个类可以直接实现多个接口,实现多继承。