java上机测试错题回顾(1)
平时不能摸鱼太多,这样导致到最后不能摸鱼......
看了看日历原来是6.12就结课了,啊哈哈,真没几天准备了,期末月你要来了吗?
1 参数传递,值传递~!
题目
以下代码的输出结果为( )。
public class Pass{static int j = 20;public void amethod(int x){x = x*2;j = j*2;}public static void main(String args[]){int i = 10;Pass p = new Pass();p.amethod(i);p.amethod(i);System.out.println(i+" and "+j);}
}
A. 错误:方法参数与变量不匹配
B. 20 and 40
C. 10 and 40
D. 10 and 20
答案:C 我的错误答案:B
在 Java 中,方法参数传递是值传递。
在main
方法里定义的int i = 10
,调用p.amethod(i)
时,只是把i
的值(也就是 10 )传递给了方法amethod
的参数x
。
在amethod
方法内部对x
进行x = x*2
操作,只是改变了方法内部x
的值,并不会影响到main
方法里的i
,所以i
的值还是 10 。
而j
是类的静态变量,在amethod
方法里执行j = j*2
,会改变j
的值(从 20 变为 40 ) 。所以最终输出是10 and 40
,不是20 and 40
。
2 编译出错
题目
编译和运行下列程序会出现什么样的结果( )。
public class Ref {public static void main(String[] args){Ref r = new Ref();r.amethod(r);}public void amethod(Ref r){int i = 99;multi(r);System.out.println(i);}public void multi(Ref r){r.i = r.i * 2;}
}
A. 编译出错
B. 输出: 99
C. 输出: 198
D. 运行出错
解析
- 变量作用域分析:在
amethod
方法中,定义了局部变量int i = 99
,这个i
只在amethod
方法内部有效。 multi
方法分析:multi
方法中尝试访问r.i
,但类Ref
中并没有定义成员变量i
,这里会导致编译错误,因为无法识别r.i
。
所以答案是 A,编译出错。
3 生成随机整数
题目
要产生 [20,999] 之间的随机整数使用哪个表达式?( )
A. (int)(20+Math.random ()*97)
B. 20+(int)(Math.random ()*980)
C. (int) Math.random ()*999
D. 20+(int) Math.random ()*980
解析
- 选项 A:
Math.random()
会生成一个大于等于 0.0 且小于 1.0 的随机小数。Math.random()*97
得到的随机数范围是大于等于 0.0 且小于 97.0 ,再加上 20 后,范围是大于等于 20.0 且小于 117.0 ,强制类型转换为int
后,得到的随机整数范围是 [20, 116] ,不符合 [20, 999] 的要求,所以 A 选项错误。 - 选项 B:
Math.random()*980
得到的随机数范围是大于等于 0.0 且小于 980.0 ,将其强制转换为int
后,范围是 [0, 979] ,再加上 20 ,得到的随机整数范围是 [20, 999] ,符合题目要求,所以 B 选项正确。 - 选项 C:
Math.random()
强制转换为int
后结果为 0 (因为Math.random()
生成的数小于 1.0 ),再乘以 999 结果还是 0 ,无法得到 [20, 999] 之间的随机整数,所以 C 选项错误。 - 选项 D:在 Java 中,
(int)Math.random()*980
这种写法,先对Math.random()
进行强制类型转换为int
结果为 0 ,再乘以 980 还是 0 ,加上 20 结果为 20 ,只能得到 20 这一个数,不能得到 [20, 999] 之间的随机整数,所以 D 选项错误。
知识点
Math.random()
函数:在 Java 中,Math.random()
是java.lang.Math
类中的静态方法,用于生成一个伪随机的 double 类型数,其取值范围是大于等于 0.0 且小于 1.0 ,即 [0.0, 1.0) 。- 生成指定范围随机整数的公式:若要生成范围在
[min, max]
(min
、max
为整数)之间的随机整数,公式为min + (int)(Math.random() * (max - min + 1))
。在本题中,min = 20
,max = 999
,代入公式可得20 + (int)(Math.random() * (999 - 20 + 1)) = 20 + (int)(Math.random() * 980)
。
4 操作符的权限
题目
下面的( )操作符可以使其修饰的变量只能对同包中的类或子类可见
A. private
B. public
C. protected
D. default
答案:C
知识点
- private:是访问修饰符,被
private
修饰的变量、方法等,只能在本类内部被访问 ,在类外部以及其他类中都无法访问。 - public:也是访问修饰符,被
public
修饰的变量、方法等,具有最大的访问权限,在任何包中的任何类都可以访问。 - protected:此访问修饰符修饰的变量、方法等,在同包中的类可以访问,不同包中的子类也可以通过继承关系访问 。满足题目中 “同包中的类或子类可见” 的要求。
- default(默认,无修饰符):当成员变量或方法没有使用任何访问修饰符时,具有包访问权限,即只能在同一个包中的类访问,不同包子类不能访问。
5 继承
题目提取
给定如下代码,哪个表达是错误的?
class C1 {}
class C2 extends C1 {}
class C3 extends C2 {}
class C4 extends C1 {}C1 c1 = new C1();
C2 c2 = new C2();
C3 c3 = new C3();
C4 c4 = new C4();
A. c1 是 C1 的实例
B. c2 是 C1 的实例
C. c3 是 C1 的实例
D. c4 是 C2 的实例
解析
在 Java 中,涉及到类的继承关系时,存在 “多态” 特性,子类对象可以被视为父类对象 。
- 选项 A:
c1 = new C1()
,c1
明确是通过C1
类实例化得到的对象,所以c1
是C1
的实例,该选项正确。 - 选项 B:
C2
类extends C1
,即C2
是C1
的子类。根据继承关系,子类对象也是父类的一种特殊形式,所以c2
这个C2
的实例也可以被看作是C1
的实例,该选项正确。 - 选项 C:
C3
类继承自C2
,而C2
又继承自C1
,C3
是C1
的间接子类,c3
这个C3
的实例同样也可被视为C1
的实例,该选项正确。 - 选项 D:
C4
类继承自C1
,和C2
没有继承关系,所以c4
是C4
和C1
的实例,但不是C2
的实例 ,该选项错误。
综上,答案是 D。
6 多态
Java 中的多态性是面向对象编程的重要特性之一,它允许不同类的对象对同一消息作出不同的响应,主要通过以下几种方式体现:
方法重载(静态多态)
- 定义:在同一个类中,方法名相同,但参数列表(参数个数、参数类型、参数顺序)不同的多个方法。编译器根据方法调用时实际传入的参数来确定调用哪个方法,这是在编译期就确定的,因此也称为静态多态。
- 示例
java
public class OverloadExample {public int add(int a, int b) {return a + b;}public double add(double a, double b) {return a + b;}
}
这里 add
方法有两个不同的版本,一个处理 int
类型参数,一个处理 double
类型参数,调用时根据传入参数类型决定调用哪个方法。
方法重写(动态多态)
- 定义:子类对父类中已有的方法进行重新实现。要求方法名、参数列表、返回值类型必须与父类中被重写的方法一致(在 Java 5 之后,返回值类型可以是被重写方法返回值类型的子类,即协变返回类型)。重写体现了运行时多态,程序运行时根据对象实际类型决定调用哪个类的重写方法。
- 示例
class Animal {public void speak() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {@Overridepublic void speak() {System.out.println("Dog barks");}
}
这里 Dog
类重写了 Animal
类的 speak
方法。当使用父类引用指向子类对象时,调用 speak
方法实际执行的是子类重写后的方法。
多态的实现机制
- 动态绑定:Java 虚拟机(JVM)在运行时根据对象的实际类型来决定调用哪个类的方法。当通过父类引用调用一个被子类重写的方法时,JVM 会在子类中查找对应的重写方法并调用。
- 向上转型和向下转型
- 向上转型:子类对象赋值给父类引用,如
Animal animal = new Dog();
。向上转型是安全的,因为子类是父类的一种特殊形式,这样可以方便地将子类对象当作父类对象进行统一处理,实现多态调用。 - 向下转型:将父类引用转换为子类类型,如
Dog dog = (Dog)animal;
。向下转型需要谨慎,因为只有当父类引用实际指向的是子类对象时才是合法的,否则会抛出ClassCastException
异常。通常需要结合instanceof
关键字进行类型判断后再转型,如if (animal instanceof Dog) { Dog dog = (Dog)animal; }
。
- 向上转型:子类对象赋值给父类引用,如
多态的优点
- 提高代码的可维护性和扩展性:当增加新的子类时,只要遵循父类的方法定义,不需要修改调用这些方法的代码,只需要在子类中实现相应方法即可。
- 增强代码的灵活性:可以用统一的方式处理不同类型的对象,使程序设计更加灵活和通用。
7 对象声明与多态
题目
有如下程序代码,执行的结果是( )。
java
class Father {int a = 100;int b = 200;public void print() {System.out.println(a + " " + b);}
}
class Child extends Father {int b = 300;int c = 400;public void print() {System.out.println(a + " " + b + " " + c);}public void printExtend() {System.out.println(c);}
}
public class Main {public static void main(String[] a) {Father obj=new Child();System.out.println(obj.a+" "+obj.b);obj.print();}
}
解析
- 对象声明与多态
Father obj=new Child();
这行代码创建了一个Child
类的对象,但是使用Father
类型的引用变量obj
来指向它,这是多态的体现。【向上转型~】
- 访问成员变量
- 对于
System.out.println(obj.a+" "+obj.b);
,在 Java 中,当通过父类引用访问成员变量时,访问的是父类中定义的成员变量。所以这里访问的a
是Father
类中的a
,值为100
;访问的b
也是Father
类中的b
,值为200
。因此这行代码输出100 200
。
- 对于
- 方法重写与动态绑定
obj.print();
这里调用print
方法,由于Child
类重写了Father
类的print
方法,根据动态绑定机制,在运行时会调用Child
类中重写后的print
方法。在Child
类的print
方法中,a
仍然使用父类的a
(值为100
),b
使用Child
类中定义的b
(值为300
),c
是Child
类中定义的(值为400
),所以这行代码输出100 300 400
。
综上,程序的输出结果是先输出 100 200
,然后输出 100 300 400
。
8 构造方法调用顺序
题目
分析如下代码,正确的选项是( )。
java
public class Test {public static void main(String[] args) {new B();}
}
class A {int i = 7;public A() {setI(20);System.out.println("i from A is " + i);}public void setI(int i) {this.i = 2 * i;}
}
class B extends A {public B() {// System.out.println("i from B is " + i);}@Overridepublic void setI(int i) {this.i = 3 * i;}
}
A. A 的构造方法未被调用。
B. A 的构造方法被调用,并且输出 "i from A is 7"。
C. A 的构造方法被调用,并且输出 "i from A is 40"。
D. A 的构造方法被调用,并且输出 "i from A is 60"。
解析
- 构造方法调用顺序:在 Java 中,当创建子类对象时,会先调用父类的构造方法 。这里
new B()
创建B
类对象,B
继承自A
,所以会先调用A
的构造方法,所以选项 A 错误。 - 方法重写影响:在
A
的构造方法中调用了setI(20)
方法,由于B
类重写了setI
方法,在创建B
类对象调用父类A
构造方法时,此时调用的setI
方法是B
类重写后的setI
方法。 - 计算过程:
B
类的setI
方法中this.i = 3 * i
,传入的参数i
为 20,执行后this.i
即A
类中的成员变量i
的值变为3 * 20 = 60
,之后在A
的构造方法中输出i from A is 60
,所以选项 B 和 C 错误,选项 D 正确。