本实验平台主要是基于本人的MacbookPro,之后会考虑测试其他操作系统版本
基于jdk1.8,HotSpot
macOS Catalina 10.15
内存 8 GB 2133 MHz LPDDR3
$ uname -a
Darwin MacBook-Pro.local 19.0.0 Darwin Kernel Version 19.0.0: Wed Sep 25 20:18:50 PDT 2019; root:xnu-6153.11.26~2/RELEASE_X86_64 x86_64
$ java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
实验下来的正确答案已用粗斜体表示
1
float a = 0.125f; double b = 0.125d;
System.out.println((a - b) == 0.0);
代码的输出结果是什么?
A. true
B. false
这道题解释起来会牵涉到怎么用二进制表示小数,小数部分不停的乘以2,取整数部分作为二进制数位
0 | 0.125 * 2 = 0.25
0 | 0.25 * 2 = 0.5
1 | 0.5 * 2 = 1
结合整数部分就是 0.001
是因为不停乘以2之后,小数部分就消失了,无论是单精度还是双精度都是一样,答案才会是true
如果题目中的0.125换成0.124或者其他,就会是false
以0.124举例
0 | 0.124 * 2 = 0.248
0 | 0.248 * 2 = 0.496
0 | 0.496 * 2 = 0.992
0 | 0.992 * 2 = 1.984
1 | 0.984 * 2 = 1.968
1 | 0.968 * 2 = 1.936
1 | 0.936 * 2 = 1.872
1 | 0.872 * 2 = 1.744
1 | 0.744 * 2 = 1.488
1 | 0.488 * 2 = 0.976
0 | 0.976 * 2 = 1.952
.
.
.
可以看到0.124不管算多久都算不到小数部分消失(就算能算到小数部分消失,也早就超过了double所能存储的64位长度)
所以转换成二进制的0.124其实已经不等于数学中的0.124了,另外float和double的精度不同,4位和8位存储的区别导致截取之后结果变得不同,当然相减后不会等于0
2
double c = 0.8;
double d = 0.7;
double e = 0.6;
// false
System.out.println((c - d) == (d - e));
那么 c-d 与 d-e 是否相等?
A. true
B. false
这道题和上一道其实差不多一个意思,因为0.8、0.7、0.6用二进制都无法做到精确表示,自然在两两相减的结果是不一样的
3
System.out.println(1.0 / 0);
1.0 / 0 首先会把除数向上转型成double,就变成了1.0 / 0.0,又由于浮点数表示问题,0.0在二进制中并不等于0,而是一个非常非常小的数,那任意数(不包括0或0.0)除以一个这样的数字都会得到(正负)无穷大
在Double中也有两个成员定义成了这样
public final class Double extends Number implements Comparable<Double> {
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
...
}
A. 抛出异常
B. Infinity
C. NaN
4
System.out.println(0.0 / 0.0);
A. 抛出异常
B. Infinity
C. NaN
D. 1.0
5
>> 和 >>> 的区别是?
A. 任何整数没有区别
B. 负整数一定没有区别
C. 浮点数可以 >> 运算,但是不可以 >>> 运算
D. 正整数一定没有区别
以int为例,32位的int由于要表示正负,只能用最高位来表示,1为负数,0为正数,所以正整数只能表示最大为2^31 - 1
以右移一位举例
正整数
16
00000000 00000000 00000000 00010000
>>1(整体右移,高位补0,符号位是不动的)
8
00000000 00000000 00000000 00001000
>>>1(整体右移,高位补0,符号位是跟着一起动的)
8
00000000 00000000 00000000 00001000
负整数
-16
11111111 11111111 11111111 11110000
>>1(整体右移,高位补1,符号位是不动的)
-8
11111111 11111111 11111111 11111000
>>>1(整体右移,高位补1,符号位是跟着一起动的,符号位是补0,所以对负数做无符号右移会变成正数)
2147483640
01111111 11111111 11111111 11111000
6
void f(String s) {
}
void f(Integer s) {
}
那么 f(null) 的会调用哪个方法?
A. 前者
B. 后者
C. 随机调用
D. 编译出错
常量null,无法确定类型,所以这里编译会报错
7
void g(double d) {
}
void g(Integer i) {
}
那么 g(1) 的会调用哪个方法?
A. 前者
B. 后者
C. 随机调用
D. 编译出错
Java方法重载的优先级是基本类型>引用类型的
而基本类型中又以byte>short>int>long>float>double
但是由于Java中字面数字常量默认就是int,所以byte和short基本不用讨论
8
String a = null; switch(a) 匹配 case 中的哪一项?
A. null
B. "null"
C. 不与任何东西匹配,但不抛出异常
D. 直接抛出异常
9
<String, T, Alibaba> String get(String string, T t) {
return string;
}
此方法:
A. 编译错误,从左往右第一个 String 处
B. 编译错误,T 处
C. 编译错误,Alibaba 处
D. 编译正确
10
HashMap 初始容量 10000 即 new HashMap(10000),当往里 put 10000 个元素时,需要 resize 几次?
A. 1 次
B. 2 次
C. 3 次
D. 0 次
jdk1.8中的HashMap在new完之后是不会创建数组的,只有在第一次put元素的时候,才会进行数组的初始化,原因在源码中如下:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
...
}
而又由于在创建HashMap的时候传入了构造器参数10000,在构建数组的时候会找到离10000最近的2的次方的数(16384),而HashMap的loadFactor没有被修改,默认为0.75f,所以下一次数组扩容的
时机在16384*0.75=12288
个元素的时候,而往Map中放10000个元素是达不到扩容的条件的。
所以总结:
只会在第一个元素被put的时候调用resize方法,之后就不会再调用
网友评论