北理工Java技术与应用考试试题参考答案及点评(下)

北理工Java技术与应用考试试题参考答案及点评(上)

三 编程题(共50分)


说明:

(1)编程题共分为两组:简单编程题与综合编程题。

(2)请在试卷后所附的答题纸上书写程序代码,注明题号。


1 简单编程题(每题5分,共35分)


(1) 编写一个程序,用户输入任何一个字串之后,反转输出其结果。其运行结果如下所示:


请输入一个字串:我在学习Java程序设计

您输入了:计设序程avaJ习学在我


参考答案:


这首题非常简单,提取出字符串中的每个字符,将其追加到StringBuilder中,然后调用StringBuilder.toString方法生成结果字串。


import java.util.Scanner;

 public class MyClass {

 public static void main(String[] args){
 System.out.print("请输入一个字串:");
 Scanner in=new Scanner(System.in);
 String input=in.nextLine();
 System.out.print("您输入了:");
 System.out.println(reverseString(input));
 }
 public static String reverseString(String str){
 char[] chars=str.toCharArray();
 StringBuilder sb=new StringBuilder();
 for(int i=chars.length-1;i>=0;i--){
 sb.append(chars[i]);
 }
 return sb.toString();
 }
 }

 我感到比较有意思的是,有几个同学“画蛇添足”地加上了以下判断:如果汉字,则占两个字节,如果英文,则占一个字节,这明显是受到C/C++的影响。在Java中,所有字符均占两个字节,英文汉字一视同仁。 

 (2) 编写一个方法,它接收一个int数组作为参数,外界只需调用一次这个方法,可以马上得到这个数组的最大值、最小值和平均值信息。

此题关键在于需要定义一个可用于保存多个结果的数据结构,并将其作为所编写方法的返回值。

 class ArrayInfo
 {
 public int max=0;
 public int min=0;
 public double average=0;
 }

 定义好了ArrayInfo类后,此方法就很容易编写了:

 public static ArrayInfo getArrayInfo(int[] arr) {
 ArrayInfo obj = new ArrayInfo();
 int sum = 0;
 obj.max = obj.min = arr[0];
 for (int value : arr) {
 if (value > obj.max) {
 obj.max = value;
 }
 if (value < obj.min) {
 obj.min = value;
 }
 sum += value;
 }
 obj.average = (double) sum / arr.length;
 return obj;
 }

 有的学生写出了这样的方法:

 Boolean ConfigArray(int[] Array, int Max, int Min, int Ave)
 {
 ……
 }
 这个方法能达到目的吗?
 另有几个学生则比较聪明,他们让方法向外界返回一个数组:

 public static int[] calculate(int[] arr){
 int[] result=new int[3];
 //查找arr中的最大值、最小值,判断平均值,放到result中。
 return result;
 }

 还有一种办法,就是将要获取的信息设计为一个类的字段,计算最大值等的方法作为此类的方法,这样,在方法体内就可以直接设置字段的值了,方法执行完后,外界通过访问这些字段得到结果。 

 (3) 请编写一个方法,其声明如下:

 public static int[] getRandomizeNumbers(int begin,int end,int count)

 此方法将生成一个数组,包容count个[begin,end)范围内的随机整数.

送分题。

 public static int[] getRandomizeNumbers(int begin,int end,int count)
 {
 int[] arr=new int[count];
 int scope=end-begin;
 for(int i=0;i<arr.length;i++)
 arr[i]=(int)(Math.random()*scope)+begin;
 return arr;

 }

 另外,Random类有一个nextInt(int n)方法,直接可以生成[0,n)之间的随机整数,使用更为方便。 

 (4) 数学中,一个复数包容着一个实部(Real)和一个虚部(Imaginary)。请设计一个Complex类,它的实例代表一个复数,并且用户可以这样使用它:

 //创建一个实部为3,虚部为4的复数
 Complex obj=new Complex(3,4); 
 //Complex对象具备按照数学中复数的习惯表示形式输出的能力。
 System.out.println(obj); //输出:3+4i

 送分题,覆盖一下toString()方法就行了。

 class Complex {
 public double real;
 public double imaginary;
 public Complex(double realValue, double imaginaryValue) {
 real = realValue;
 imaginary = imaginaryValue;
 }
 public String toString() {
 return real + "+" + imaginary + "i";
 }
 } 

 (5) A、B、C是三个类,它们之间有着以下的组合关系:



 请编写一个示例程序,这个程序可以完成对A对象的深复制(Deep Copy)。

关键点:
 A完全包容B,但引用C。 因此,将B作为A的内部类比较合适。
 为了方便展示对象深复制的特性,特为A、B、C都添加一个int类型的字段。
 以下是代码:
 最简单的C:
 class C {
 public int cValue;
 }
 嵌在A内部的B,请注意B是内部类,在A创建时,自动创建B对象,为了能访问到B的字段值,为A添加了两个方法:
 class A {
 public int aValue;
 private B bObj = new B(); //重要!B对象由A对象负责创建
 //B对象定义为内部类,外界无法实例化它,为A所“专用”
 private class B {
 public int bValue;
 }
 //存取A对象所包容B对象的字段
 public void setBValue(int value) {
 bObj.bValue = value;
 }
 public int getBValue() {
 return bObj.bValue;
 }
 //A所引用的C对象
 public C cObj = null;
 }
 以下是深复制对象A的方法:

 private static A cloneA(A originalObject) {
 A cloned = new A(); //创建“副本”对象
 cloned.aValue = originalObject.aValue;
 //设置所包容的内部B对象的方法
 cloned.setBValue(originalObject.getBValue());
 //复制C对象
 C newCObject = new C();
 newCObject.cValue = originalObject.cObj.cValue;
 //将C对象关联上A对象
 cloned.cObj = newCObject;
 return cloned;
 }

 为方便起见,可以定义以下方法输出A对象的所有字段信息:

 private static void printEmbedObject(A obj) {
 System.out.println("A.aValue=" + obj.aValue);
 System.out.println("A.getBValue()返回:" + obj.getBValue());
 System.out.println("A.cObj.cValue=" + obj.cObj.cValue);
 }

 以下示例代码展示创建A对象并克隆它的方法:

 //创建A对象
 A a = new A();
 a.aValue = 100;
 //设置A对象所包容内部对象B的值
 a.setBValue(200);
 //创建C对象
 C c = new C();
 c.cValue = 300;
 //让A对象关联上C对象
 a.cObj = c;
 printEmbedObject(a);
 System.out.println("=============");
 printEmbedObject(cloneA(a));

 上述方法使用一个独立的静态方法实现对象克隆,另一种方法是;
 Object类提供了一个protected clone()方法,子类可将其定义为public的,从而向外界提供克隆自己的功能,另外,推荐让能支持深复制的对象实现Cloneable接口。

 (6) 我们可以调用Integer.parseInt()方法将一个字串转换为int类型的数值,当要转换的字串不是一个有效的数字时(比如“a123”),Integer.parseInt()方法会抛出一个NumberFormatException。
 你需要编写一个程序,当程序运行时让用户从键盘上输入一个字串,代表考试成绩,然后调用Integer.parseInt()方法将其转换为int类型,并给出是否通过的提示:
 请输入您的考试成绩:76(回车)
 恭喜您通过了考试!
 由于无法控制用户的输入,因此可能出现以下两种出错情况:
 1) 用户输入了一个无法转换为int数值的字符串。
 2) 用户输入的数字不在[0,100]区间内。
 请使用Java的异常捕获机制设计一个自定义的异常类InvalidScroreException,当出现上述出错情景时能抛出一个InvalidScroreException异常对象,通知用户他的输入有误。

 在实际开发中,经常需要将底层非常计算机化的异常转换为可供界面层直接捕获的异常,并且能提供用户易于理解的信息。、
 另外,出于业务逻辑的要求,需要将一些非法的输入数据、无效或错误的运行结果等转换为业务逻辑层的异常。
 这道题主要希望能了解一下学生是否掌握了这种异常类型转换的编程方法。。

 private static int getTestScore() throws InvalidScroreException {
 System.out.print("请输入您的考试成绩:");
 Scanner scanner = new Scanner(System.in);
 String userInput = scanner.nextLine();
 int score = 0;
 try {
 score = Integer.parseInt(userInput);
 } catch (NumberFormatException ex) {
 throw new InvalidScroreException("您输入的不是一个有效的整数!" );
 }
 if (score > 100 || score < 0) {
 throw new InvalidScroreException("有效的分数在0~100之内。");
 }
 return score;
 }

 以下为测试代码:

 public static void main(String[] args) {
 try {
 if (getTestScore() >= 60)
 System.out.println("恭喜您通过了考试!");
 else
 System.out.println("不要灰心,继续努力!");
 }
 catch(InvalidScroreException ex){
 System.out.println(ex.getMessage());
 }
 } 

 (7) 设计一个类,实现以下的状态机:




 这道题其实很简单,关键是将3个状态定义为枚举类型,并且需要检测出非法的状态转换。

 enum State {
 Created, Opened, Closed
 }

 class MyStateMachine {
 private State curState = State.Created;
 public void Open() throws Exception {
 if (curState != State.Created) {
 throw new Exception("已Closed的对象无法再Open");
 }
 curState = State.Opened;
 }
 public void Close() {
 curState = State.Closed;
 }
 }


也有的同学使用常量来定义状态,虽然不算很好,但也算不错。

还有的同学使用String变量,int变量来定义状态,就不太合适了,不过我都没扣分。

只有少数几个学生知道应该在状态转换时检测一下是否这种转换是有效的。


2 综合编程题(15分)


一个图书管理系统的面向对象设计方案如下图所示:……


这道题其实太简单了,除了代码量大些,没有任何难点。因此略过。在改卷时,只要学生思路清晰,代码意图明确,没有明显的语法错误,都给了较高的分数。


总评:


我设计这套试题的总体思路是:

(1)希望能检测出学生对Java面向对象编程基础知识与基本概念的理解程度。

(2)大大减少考核纯知识方面的题目,引导学生不要死记硬背地学习。

(3)设计的题目不少源自我本人的开发实践,我希望这些题目能给与学生一个明确的导向:学习编程必须重视动手。

(4)有意设计一些比较“活”题目,看看学生能否灵活应用已学到的知识,面对着一个“现实”的可能他并不知道答案的问题,具备一定的分析与推测能力。引导学生“透过代码看到背后的东西”,培养学生“探索式学习”的能力和意识。

我觉得这种能力和意识的培养远比具体的知识重要得多。

(5)在阅卷时,我比较注重学生在答题时是否思路清晰,回答是否有条理,具体答案反而是次要的。

(6)为了让大多学生可以及格,有意地设计一些送分题,但同时为了加大区分度,设计了一些有一定难度和灵活性的题目,整个试卷的题量是较大的,要及格其实容易,要得高分就不容易了。


最终的卷面成绩统计结果如下:


最高分:86

最低分:35

平均分:68.03

各分数段所占学生人数比例:

60以下: 12%

60~70: 39%

70~80: 21%

80~90: 27%

90以上: 0%