Fork me on GitHub

Java基础刷题指南

刷题来自牛客网。lee 的错题记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192

1. 说法正误
在java中,无论在何处调用,使用静态属性必须以类名做前缀。//false。
//1如果是本类使用,可以直接就用静态变量名。2如果是其他类使用,可以使用类名来调用,也可以创建一个实例对象来调用。3如果静态变量所在的类是静态类,那么不管在本类里或者在其他外部类,都可以直接使用静态变量名。

2. 说法正误
在方法中定义的局部变量在该方法被执行时创建。//false。
//不是局部变量在该方法被执行/调用时创建,而是应该为在该变量被声明并赋值时创建,可以理解为“当代码执行到该变量被赋值的代码时才被创建”。

3. 语法正误
public abstract final class Test {
abstract void method(); // false。final修饰的类为终态类,而抽象类是必须被继承的才有其意义的。所以 final 不能用来修饰抽象类的。
}
public abstract class Test {
abstract final void method(); //false。final修饰的方法为终态方法,不能被重写,而继承抽象类,必须重写其方法。
}
public abstract class Test {
abstract void method() { // false。抽象方法是仅声明,并不做实现的方法。
}
}
public class Test {
final void method() {
// true
}
}

4. 结果正误
String str1="hello";
String str2="he"+ new String("llo");
System.out.println(str1==str2); // false。str1 指向字面量“hello”,这里的str2必须在运行时才知道str2是什么,所以它是指向的是堆里定义的字符串“hello”

// new String("llo")在编译期时创建一个“llo”的字面量(在 String pool 中),在运行期执行new命令创建另一个“llo”(在堆中)
//String s = "a"+"b"+"c"; 语句中,“a”,"b", "c"都是常量,编译时就直接存储他们的字面值,而不是他们的引用,在编译时就直接将它们连接的结果提取出来变成"abc"了。

5. 语法正误
public class Test{
private float f = 1.0f;
int m = 12;
static int n = 1;
public static void main (String args[]){
Test t = new Test();
t.f;//true。因为main函数在该类中,所以即使private也仍可使用
this.n;//false。this不能在static的方法中使用
Test.m;//false。m 和 f 都是普通成员属性
Test.f;//false。m 和 f 都是普通成员属性
}
}

6. 输出结果:
public class CharToString {
public static void main(String[] args)
{
char myChar = 'g';
String myStr = Character.toString(myChar);
System.out.println("String is: "+myStr);
myStr = String.valueOf(myChar);
System.out.println("String is: "+myStr);
}
}
output:
String is: g // toString 和 valueOf 方法返回值都是 String
String is: g // 只有char变成 int 的时候才会变为对应的assic码

7. 输出结果
public static void main(String args[])throws InterruptedException{
Thread t=new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.print("2");
}
});
t.start();
t.join();
System.out.print("1");
}
output:21 // thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
//t.join(); //使调用线程 t 在此之前执行完毕。
//t.join(1000); //等待 t 线程,等待时间是1000毫秒

8. 说法正误
Hashtable 和 HashMap 的区别是:
Hashtable 是一个哈希表,该类继承了 AbstractMap,实现了Map接口 // false。Hashtable 继承了Dictionary 类。
HashMap 是内部基于哈希表实现,该类继承AbstractMap,实现Map接口 // true。
Hashtable 线程安全的,而 HashMap 是线程不安全的 // true。
Properties 类 继承了 Hashtable 类,而 Hashtable 类则继承Dictionary 类 // true。
HashMap允许将 null 作为一个 entry 的 key 或者 value,而 Hashtable 不允许。// true。Hashtable 的key、value都不可以为null。
// 另外,Hashtable 使用对象的hashCode,而 HashMap 重新计算 hash

9. 输出结果
class Test {
public static void main(String[] args) {
System.out.println(new B().getValue());
}
static class A {
protected int value;
public A (int v) {
setValue(v);
}
public void setValue(int value) {
this.value= value;
}
public int getValue() {
try {
value ++;
return value;
} finally {
this.setValue(value);
System.out.println(value);
}
}
}
static class B extends A {
public B () {
super(5);
setValue(getValue()- 3);
}
public void setValue(int value) {
super.setValue(2 * value);
}
}
}
output:22 34 17
Step 1: new B()构造一个B类的实例
此时super(5)语句调用显示调用父类A带参的构造函数,该构造函数调用setValue(v),这里有两个注意点一是虽然构造函数是A类的构造函数,但此刻正在初始化的对象是B的一个实例,因此这里调用的实际是B类的setValue方法,于是调用B类中的setValue方法 ==> 而B类中setValue方法显示调用父类的setValue方法,将B实例的value值设置为2 x 5 = 10
紧接着,B类的构造函数还没执行完成,继续执行setValue(getValue()- 3) // 备注1语句。
先执行getValue方法,B类中没有重写getValue方法,因此调用父类A的getValue方法。这个方法比较复杂,需要分步说清楚:
调用getValue方法之前,B的成员变量value值为10
value++ 执行后, B的成员变量value值为11,此时开始执行到return语句,将11这个值作为getValue方法的返回值返回出去
但是由于getValue块被try finally块包围,因此finally中的语句无论如何都将被执行,所以步骤211这个返回值会先暂存起来,到finally语句块执行完毕后再真正返回出去。
这里有很重要的一点:finally语句块中 this.setValue(value)方法调用的是B类的setValue方法。为什么?因为此刻正在初始化的是B类的一个对象(运行时多态),就像最开始第一步提到的一样(而且这里用了使用了this关键词显式指明了调用当前对象的方法)。因此,此处会再次调用B类的setValue方法,同上,super.关键词显式调用A的setValue方法,将B的value值设置成为了2 * 11 = 22
因此第一个打印项为22
finally语句执行完毕 会把刚刚暂存起来的11 返回出去,也就是说这么经历了这么一长串的处理,getValue方法最终的返回值是11
回到前面标注了 //备注1 的代码语句,其最终结果为setValue(11-3)=>setValue(8)
而大家肯定也知道,这里执行的setValue方法,将会是B的setValue方法。 之后B的value值再次变成了2*8 = 16;
Step2: new B().getValue()
B类中没有独有的getValue方法,此处调用A的getValue方法。同Step 1
调用getValue方法之前,B的成员变量value值为16
value++ 执行后, B的成员变量value值为17,此时执行到return语句,会将17这个值作为getValue方法的返回值返回出去
但是由于getValue块被try finally块包围而finally中的语句无论如何都一定会被执行,所以步骤217这个返回值会先暂存起来,到finally语句块执行完毕后再真正返回出去。
finally语句块中继续和上面说的一样: this.setValue(value)方法调用的是B类的setValue()方法将B的value值设置成为了2 * 17 = 34
因此第二个打印项为34
finally语句执行完毕 会把刚刚暂存起来的17返回出去。
因此new B().getValue()最终的返回值是17.
Step3: main函数中的System.out.println
将刚刚返回的值打印出来,也就是第三个打印项:17

10. 输出结果
package Test;
public class Test {
private static void test(int[] arr) {
for (int i = 0; i < arr.length; i++) {
try {
if (arr[i] % 2 == 0) {
throw new NullPointerException();
} else {
System.out.print(i);
}
} finally {
System.out.print("e");
}
}
}
public static void main(String[]args) {
try {
test(new int[] {0, 1, 2, 3, 4, 5});
} catch (Exception e) {
System.out.print("E");
}
}
} // 由于arr[0] =0,所以在进入 test()方法里面会在第一个if 上抛出一个 NullPointerException,接着会执行 finally 的语句, (finally语句先于 return 和 throw语句执行),输出一个'e,然后回到 main方法中,由于捕捉到异常,所以进入到catch语句中,然后打印一个'E',所以最终结果为"eE"

11. 输出结果
String str =
"";
System.out.print(str.split(",").length);
output:1 // String split 这个方法默认返回一个数组,如果没有找到分隔符,会把整个字符串当成一个长度为1的字符串数组

12. 判断正误:关于Java中的ClassLoader 的描述:
默认情况下,Java应用启动过程涉及三个ClassLoader: Boostrap, Extension, System // true, System加载器也称应用加载器
一般的情况不同ClassLoader装载的类是不相同的,但接口类例外,对于同一接口所有类装载器装载所获得的类是相同的 // false
类装载器需要保证类装载过程的线程安全 // true
ClassLoader的loadClass在装载一个类时,如果该类不存在它将返回null //false,会直接报错
ClassLoader的父子结构中,默认装载采用了父优先 // true
所有ClassLoader装载的类都来自CLASSPATH环境指定的路径 // false,自定义类加载器实现继承ClassLoader后重写了findClass方法,所以会加载指定路径(程序员自定义的)上的class

13. 输出结果
20092009次方,将结果各位数相加,得出结果如果不是一位数就继续各位相加,直到一位数,最后的结果是?
output:5 // 数字各位相加相当于原数字对 9 取模,2009 % 9可以表示成(9x+2) % 9 = 2,而2009*2009可以表示成(9x+2)(9x+2) % 9 = (9y + 4) % 9 = 4,同理接下去可以表示成(9z+4)(9x+2) % 9 = 8,可以得到规律是2,4,8,7,5,1,那第2009次就是5了
-------------The End-------------