继承的意义
1 说出下面代码的输出结果,并解释原因
- //鸵鸟
- public class Ostrich extends Bird{
- public void fly(){
- System.out.println("我只能在地上奔跑...");
- }
- public static void main(String[] args) {
- Ostrich os=new Ostrich();
- os.fly();
- }
- }
- class Bird {
- public void fly(){
- System.out.println("我在天空里自由自在的飞翔...");
- }
- }
参考答案
上述代码的输出结果为:
- 我只能在地上奔跑...
执行上述程序,会发现执行os.fly()时,执行的不是Bird类的fly方法,而是执行Ostrich类的fly方法。这是因为,在Java中,如果子类覆盖了父类的某个方法,那么,当子类对象的重写方法被调用时(无论是通过子类的引用调用还是通过父类的引用调用),其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征,即,调用子类的方法。
2 说出下面代码的输出结果,并解释原因
- public class SlowPoint extends Point {
- public void move(int dx, int dy) {
- System.out.println("SlowPoint move parameter");
- move();
- }
- public static void main(String[] args) {
- SlowPoint sp=new SlowPoint();
- sp.move(10,20);
- }
- }
- class Point {
- public void move(int dx, int dy) {
- System.out.println("Point move parameter");
- }
- public void move(){
- System.out.println("Point move ");
- }
- }
参考答案
输出结果为:
- SlowPoint move parameter
- Point move
首先,介绍一下方法重载和重写的区别,方法重载的要求是两同一个不同,即同一类中方法名相同,参数列表不同。其中,同一类中是指两个方法可以是同一个类中声明的,或者是继承来的,抑或一个是声明的,另一个是继承来的;方法的重写要遵循“两同两小一大”规则,“两同”即方法名相同,形参列表相同;“两小”指的是子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;“一大”指的是子类方法的访问权限应比父类方法的访问权限更大或相等。
本题中,sp.move(10,20); 调用子类SlowPoint的带参数move方法,这是因为,子类SlowPoint重写了父类Point的带参数的move方法。在Java中,如果子类覆盖了父类的某个方法,那么,当子类对象的重写方法被调用时(无论是通过子类的引用调用还是通过父类的引用调用),其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征,即,调用子类的方法。接着又调用无参数的move方法,虽然,子类SlowPoint中没有无参数的move方法,但是,会从父类继承过来无参数的move方法和子类中带参数的move方法形成了重载,因此,调用此方法输出“Point move”。
3 关于package和import语句,下面说法错误的是:
A. package 提供了一种命名机制,用于管理类名空间
B. 定义类时,除了定义类的名称以外,必须要指定一个包名
C. import语句用于导入所需要的类
D. 同时使用不同包中相同类名的类,包名不能省略
参考答案
B 选项的说法错误。
这是因为,我们在定义类时,必须指定类的名称。但如果仅仅将类名作为类的唯一标识,则不可避免的会出现命名冲突的问题,因此,在Java语言中,用包(package)的概念来解决命名冲突的问题。但是,如果不考虑到命名冲突的问题,则可以不用指定包名,虽然这并不是一个好习惯。
4 关于public和private,下面说法错误的是:
A. private修饰的成员变量和方法仅仅只能在本类中访问
B. public修饰的成员变量和方法可以在任何地方访问
C. private修饰的成员变量和方法可以在本类和子类中访问
D. public 修饰的成员变量和方法只能在同一个包中访问
参考答案
选项C 和选项D 的说法错误。
这是因为,private修饰的成员变量和方法仅仅只能在本类中访问,即私有访问控制;而public修饰的内容是对外提供的可被调用的功能,可以在任何地方访问。
5 关于protected 关键字,下面说法错误的是:
A. 用protected修饰的成员变量和方法可以被子类及同一个包中的类使用
B. 使用protected 关键字修饰的访问控制,即默认访问控制
C. 默认访问控制的成员变量和方法可以被同一个包中的类访问
D. 用protected修饰的成员变量和方法只能被子类使用
参考答案
选项B和选项D的说法错误。
这是因为,用protected修饰的成员变量和方法可以被子类及同一个包中的类使用;而默认访问控制即不书写任何访问控制符,默认访问控制的成员变量和方法可以被同一个包中的类访问。
6 关于static 关键字,下面说法正确的是:
A. 用static修饰的成员变量是属于对象的数据结构
B. 在static方法中,可以访问非static成员(对象成员)
C. static成员变量存储在堆中
D. 一个类的static成员变量只有“一份”,无论该类创建了多少对象
参考答案
D 选项的说法正确,其他选项的说法均是错误的。
这是因为,static成员变量和类的信息一起存储在方法区,而不是在堆中。一个类的static成员变量只有“一份”无论该类创建了多少对象。用static修饰的成员变量是属于类的变量,并非属于对象的数据结构;在static方法中也不能访问非static成员(对象成员)。
7 关于final 关键字,下面说法正确的是:
A. final关键字如果用于修饰成员变量,那么该成员变量必须在声明时初始化
B. final关键字修饰的类只能被继承一次
C. final 关键字修饰的方法不可以被重写
D. final 关键字如果用于修饰方法,该方法所在的类不能被继承
参考答案
C 选项的说法正确,其他选项的说法均错误。
这是因为,final关键字修饰成员变量,意为初始化后不可改变(对象一旦创建即不可改变),那么该成员变量可以在声明时赋初始值,也可以在构造方法或是静态代码块中初始化。
另外,final关键字修饰的类不可以被继承,final关键字修饰的方法不可以被重写。如果一个类中的某个方法为 final所修饰,那么,仅仅该方法不能被重写,并不影响该方法所在的类。final修饰的方法可以被子类继承和使用。
8 关于声明静态常量,下面代码,正确的是:
A. public static String FOO = "foo";
B. public static final String FOO = "foo";
C. public final String FOO = "foo";
D. public final static String FOO = "foo";
参考答案
B 和 D 选项正确。
这是因为,如果需要声明静态常量,需要同时使用关键字 static 和 final 进行修饰,而二者的前后顺序不限。因此,本题的正确答案为选项 B 和D。
9 完成TetrominoGame(提高题,选做)
在课上案例“重写T类和J类的print方法并测试”的基础上,实现控制台版的对T型方块的下落,左移及右移,控制台输入效果如下所示:
- --------打印T型---------
- i am a T
- (0,4), (0,5), (0,6), (1,5)
- - - - - * * * - - -
- - - - - - * - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- 1 —— 下落,2——向左,3——向右,0 —— 退出
- 1
- - - - - - - - - - -
- - - - - * * * - - -
- - - - - - * - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- 1 —— 下落,2——向左,3——向右,0 —— 退出
- 2
- - - - - - - - - - -
- - - - * * * - - - -
- - - - - * - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- 1 —— 下落,2——向左,3——向右,0 —— 退出
- 3
- - - - - - - - - - -
- - - - - * * * - - -
- - - - - - * - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - -
- 1 —— 下落,2——向左,3——向右,0 —— 退出
- 0
当用户选择1时,表示选择了下落功能;当用户选择2时,表示选择向左移动功能;当用户选择了3时,表示用户选择了向右移动功能;当用户选择0表示用户选择了退出功能。
参考答案
分析课上案例所实现的程序,会发现,下落、左移及右移的方法都已在Tetromino类中实现完成。在此,我们只需要实现总体的流程控制即可。
首先,在TetrominoGame类的main方法中,使用while(true)循环来实现用户选择功能;
然后,在循环中,提示用户输入信息,并接收用户输入的信息;
第三,使用if-else if...-else结构来控制实现的功能。当用户选择1时,表示选择了下落功能,调用T类的drop方法即可;当用户选择2时,表示选择向左移动功能,调用T类的moveLeft方法即可;当用户选择了3时,表示用户选择了向右移动功能,调用T类的moveRight方法即可;当用户选择0表示用户选择了退出功能,在相应分支中使用break语句退出循环,在此,退出循环即意味着程序结束。
第四,在循环中,调用TetrominoGame类printTetromino方法,来查看T型方块的位置变化情况。
实现此案例需要按照如下步骤进行。
步骤一:循环接收用户所选功能
在TetrominoGame类的main方法中,首先,使用while(true)循环来实现用户选择功能;然后,在循环中,提示用户输入信息,并接收用户输入的信息。代码如下所示:
- import java.util.Scanner;
- public class TetrominoGame {
- public static void main(String[] args) {
- // 测试TetrominoT
- System.out.println("--------打印T型---------");
- Tetromino t = new TetrominoT(0, 4);
- t.print();
- printTetromino(t);
- // 测试TetrominoJ
- // System.out.println("--------打印J型---------");
- // Tetromino j = new TetrominoJ(0, 4);
- // j.print();
- // printTetromino(j);
- Scanner sc = new Scanner(System.in);
- while (true) {
- System.out.println("1 —— 下落,2——向左,3——向右,0 —— 退出");
- int cmd = sc.nextInt();
- }
- }
- /**
- * 打印出游戏所在的平面(宽10格,高20格)。用“-”号表示平面上的每个单元,用“*”号打印显示方块中的每个格子
- *
- * @param tetromino
- * 需要显示在游戏平面中的方块
- */
- public static void printTetromino(Tetromino tetromino) {
- int totalRow = 20;
- int totalCol = 10;
- // 获取方块中存储的四个格子的数组
- Cell[] cells = tetromino.cells;
- for (int row = 0; row < totalRow; row++) {
- for (int col = 0; col < totalCol; col++) {
- // 用于判断该位置是否包含在cells数组中
- boolean isInCells = false;
- for (int i = 0; i < cells.length; i++) {
- if (cells[i].row == row && cells[i].col == col) {
- System.out.print("* ");
- isInCells = true;
- break;
- }
- }
- if (!isInCells) {
- System.out.print("- ");
- }
- }
- System.out.println();
- }
- }
- }
步骤二:使用分支,实现用户所选功能
首先,使用if-else if...-else结构来控制实现的功能。当用户选择1时,表示选择了下落功能,调用T类的drop方法即可;当用户选择2时,表示选择向左移动功能,调用T类的moveLeft方法即可;当用户选择了3时,表示用户选择了向右移动功能,调用T类的moveRight方法即可;当用户选择0表示用户选择了退出功能,在相应分支中使用break语句退出循环,在此,退出循环即意味着程序结束。
然后,在循环中,调用TetrominoGame类printTetromino方法,来查看T型方块的位置变化情况。代码如下所示:
- import java.util.Scanner;
- public class TetrominoGame {
- public static void main(String[] args) {
- // 测试TetrominoT
- System.out.println("--------打印T型---------");
- Tetromino t = new TetrominoT(0, 4);
- t.print();
- printTetromino(t);
- // 测试TetrominoJ
- // System.out.println("--------打印J型---------");
- // Tetromino j = new TetrominoJ(0, 4);
- // j.print();
- // printTetromino(j);
- Scanner sc = new Scanner(System.in);
- while (true) {
- System.out.println("1 —— 下落,2——向左,3——向右,0 —— 退出");
- int cmd = sc.nextInt();
- if (0 == cmd) {
- break;
- } else if (1 == cmd) {
- t.drop();
- } else if (2 == cmd) {
- t.moveLeft();
- } else if (3 == cmd) {
- t.moveRight();
- }
- printTetromino(t);
- }
- }
- /**
- * 打印出游戏所在的平面(宽10格,高20格)。用“-”号表示平面上的每个单元,用“*”号打印显示方块中的每个格子
- *
- * @param tetromino
- * 需要显示在游戏平面中的方块
- */
- public static void printTetromino(Tetromino tetromino) {
- int totalRow = 20;
- int totalCol = 10;
- // 获取方块中存储的四个格子的数组
- Cell[] cells = tetromino.cells;
- for (int row = 0; row < totalRow; row++) {
- for (int col = 0; col < totalCol; col++) {
- // 用于判断该位置是否包含在cells数组中
- boolean isInCells = false;
- for (int i = 0; i < cells.length; i++) {
- if (cells[i].row == row && cells[i].col == col) {
- System.out.print("* ");
- isInCells = true;
- break;
- }
- }
- if (!isInCells) {
- System.out.print("- ");
- }
- }
- System.out.println();
- }
- }
- }
本案例中,TetrominoGame的完整代码如下所示:
- import java.util.Scanner;
- public class TetrominoGame {
- public static void main(String[] args) {
- // 测试TetrominoT
- System.out.println("--------打印T型---------");
- Tetromino t = new TetrominoT(0, 4);
- t.print();
- printTetromino(t);
- // 测试TetrominoJ
- // System.out.println("--------打印J型---------");
- // Tetromino j = new TetrominoJ(0, 4);
- // j.print();
- // printTetromino(j);
- Scanner sc = new Scanner(System.in);
- while (true) {
- System.out.println("1 —— 下落,2——向左,3——向右,0 —— 退出");
- int cmd = sc.nextInt();
- if (0 == cmd) {
- break;
- } else if (1 == cmd) {
- t.drop();
- } else if (2 == cmd) {
- t.moveLeft();
- } else if (3 == cmd) {
- t.moveRight();
- }
- printTetromino(t);
- }
- }
- /**
- * 打印出游戏所在的平面(宽10格,高20格)。用“-”号表示平面上的每个单元,用“*”号打印显示方块中的每个格子
- *
- * @param tetromino
- * 需要显示在游戏平面中的方块
- */
- public static void printTetromino(Tetromino tetromino) {
- int totalRow = 20;
- int totalCol = 10;
- // 获取方块中存储的四个格子的数组
- Cell[] cells = tetromino.cells;
- for (int row = 0; row < totalRow; row++) {
- for (int col = 0; col < totalCol; col++) {
- // 用于判断该位置是否包含在cells数组中
- boolean isInCells = false;
- for (int i = 0; i < cells.length; i++) {
- if (cells[i].row == row && cells[i].col == col) {
- System.out.print("* ");
- isInCells = true;
- break;
- }
- }
- if (!isInCells) {
- System.out.print("- ");
- }
- }
- System.out.println();
- }
- }
- }
Tetromino类的完整代码如下所示:
- public class Tetromino {
- Cell[] cells;// 属性,用来存储一个方块的四个格子的坐标
- /**
- * 构造方法,初始化cells数组
- */
- public Tetromino() {
- cells = new Cell[4];
- }
- /**
- * 按顺时针方向,打印方块中四个格子所在的坐标
- */
- public void print() {
- String str = "";
- for (int i = 0; i < cells.length - 1; i++) {
- str += "(" + cells[i].getCellInfo() + "), ";
- }
- str += "(" + cells[cells.length - 1].getCellInfo() + ")";
- System.out.println(str);
- }
- /**
- * 使方块下落一个格子
- */
- public void drop() {
- for (int i = 0; i < cells.length; i++) {
- cells[i].row++;
- }
- }
- /**
- * 使方块左移一个格子
- */
- public void moveLeft() {
- for (int i = 0; i < cells.length; i++) {
- cells[i].col--;
- }
- }
- /**
- * 使用方块右移一个格子
- */
- public void moveRight() {
- for (int i = 0; i < cells.length; i++) {
- cells[i].col++;
- }
- }
- }
TetrominoJ类的完整如下所示:
- public class TetrominoJ extends Tetromino {
- public TetrominoJ(int row, int col) {
- // 按顺时针方向初始化Cell
- cells[0] = new Cell(row, col);
- cells[1] = new Cell(row, col + 1);
- cells[2] = new Cell(row, col + 2);
- cells[3] = new Cell(row + 1, col + 2);
- }
- @Override
- public void print() {
- System.out.println("i am a J");
- super.print();
- }
- }
TetrominoT类的完整代码如下所示:
- public class TetrominoT extends Tetromino {
- public TetrominoT(int row, int col) {
- super();
- // 按顺时针方向初始化Cell
- cells[0] = new Cell(row, col);
- cells[1] = new Cell(row, col + 1);
- cells[2] = new Cell(row, col + 2);
- cells[3] = new Cell(row + 1, col + 1);
- }
- @Override
- public void print() {
- System.out.println("i am a T");
- super.print();
- }
- }