一个Java类型的小问题记录

最近发现一个小问题,感觉挺有意思,我们来看下代码

1
2
3
4
public static void main(String[] args) {
Integer oneHundredYear = 36500;
System.out.println(oneHundredYear * 24 * 60 * 60 * 1000);
}

定义了个变量,用来表示100年的时间,然后计算出对应的时间戳
首先看下这个值

1
1094004736

如果对时间戳这些没概念的话,可能没发现有什么问题,但是好像也不对,乘以1000了,但是尾数是736
这个暂时我们先不管

1
2
3
4
5
public static void main(String[] args) {
Integer oneHundredYear = 36500;
System.out.println(oneHundredYear * 24 * 60 * 60 * 1000);
System.out.println(System.currentTimeMillis() + oneHundredYear * 24 * 60 * 60 * 1000);
}

然后来看下

1
2
1094004736
1726195843465

然后我们去看下这个时间戳,按正常理解应该是2124年的8月31日

但实际是不对的,只是12天后,跟100年这个差距天差地别了,这是为啥呢,可能大佬一眼就看出来了这是类型转换问题,但是比如说我这么改

1
2
3
4
5
public static void main(String[] args) {
Integer oneHundredYear = 36500;
System.out.println(oneHundredYear * 24 * 60 * 60 * 1000L);
System.out.println(System.currentTimeMillis() + oneHundredYear * 24 * 60 * 60 * 1000L);
}

或者

1
2
3
4
5
public static void main(String[] args) {
Integer oneHundredYear = 36500;
System.out.println(Long.valueOf(oneHundredYear * 24 * 60 * 60 * 1000));
System.out.println(System.currentTimeMillis() + Long.valueOf(oneHundredYear * 24 * 60 * 60 * 1000));
}

会发现还是一样,依旧是错误的
简单来说,就在于类型转换的时机,我们看下这个示例就知道了

1
2
3
4
5
6
7
public static void main(String[] args) {
Integer oneHundredYear = 36500;
System.out.println(oneHundredYear * 24);
System.out.println(oneHundredYear * 24 * 60);
System.out.println(oneHundredYear * 24 * 60 * 60);
System.out.println(oneHundredYear * 24 * 60 * 60 * 1000);
}

结果是这样

1
2
3
4
876000
52560000
-1141367296
1094004736

到乘第二个60的时候已经是负的了,因为已经超过了Integer的范围了
但是为啥用后面的示例转换成long类型还不行呢
这个就在于编译器是怎么做类型转换的
在第一个oneHundredYear跟24相乘的时候是认为两个Integer相乘,并且没有检查范围
只有在乘以显式申明的最后的1000的时候才会做转换
我们看下反编译

可以看到是先做int型的乘法,碰到有参数是long型时才会转类型
那么理论上其实我们只要在第二个60及之前申明long或者强转long就行了,这也是个很基础的问题,只是有时候写代码的时候直觉会以为加了个L就可以了,但实际是没那么简单