我有个师弟在面试阿里的暑期实习,被问了一道题:2.0减去1.1等于多少?他直接回答0.9,结果面试官脸都黑了。
这个经历让我意识到在面试中,细节和对基本语法的了解非常重要。
这次面试让我意识到了一些常见的陷阱。尤其是在数值计算方面,计算机处理浮点数时可能会出现精度问题,导致结果不完全符合预期。
同时,在面试过程中,对于基本的语法和概念的掌握也是至关重要的。
所以,我们在准备面试时,一定要注重细节,牢固掌握基础知识,以避免掉入这些陷阱。
public class Main {
public static void main(String[] args) {
System.out.println(2.0 - 1.1);
}
}
这个2.0 - 1.1,很多人以为结果是0.9的,结果一运行才发现0.8999999999999999
这种情况是由于浮点数在计算机中的存储和表示方式导致的精度限制。
计算机在内部以二进制形式存储浮点数,而不是以十进制形式。
由于有限的存储空间和表示精度,某些十进制数可能无法精确地表示为二进制浮点数,从而导致小的舍入误差。
所以,虽然数学上2.0减去1.1等于0.9,但在计算机中,由于浮点数的存储和处理方式,得到的结果可能是一个非常接近0.9的近似值,比如0.8999999999999999。
这是浮点数运算中常见的现象,称为浮点数精度问题。
用BigDecimal
修正:
public class Main {
public static void main(String[] args) {
System.out.println(2.0 - 1.1);
System.out.println(new BigDecimal("2.0").subtract(new BigDecimal("1.1")));
System.out.printf("%.1f", 2.0 - 1.1);
}
}
想一下这个条件:i % 2 != 1;当把1、2、3输入进去时,结果好像也对哦,但是就是少了一方面。所以答案应该是:i % 2 != 0,正负数都是可以用的了
public class Main {
public static boolean isOdd(int i) {
return i % 2 != 1;
}
public static void main(String[] args) {
System.out.println(isOdd(1));
System.out.println(isOdd(2));
System.out.println(isOdd(3));
}
}
isOdd(1)
:1除以2的余数为1,不等于1,所以返回false
,打印结果为false
。isOdd(2)
:2除以2的余数为0,等于1,所以返回false
,打印结果为false
。isOdd(3)
:3除以2的余数为1,不等于1,所以返回false
,打印结果为false
。咋一看以为是1000,运行之后,没想到是5
public static void main(String[] args) {
final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
}
在Java中,整数乘法可能会导致溢出,因为表达式右侧的每个数值都是int
类型,而不是long
类型。因此,即使最终结果赋值给了long
类型的变量,但是计算过程中仍然会使用int
类型进行计算,这可能导致结果溢出。
让我们来具体分析:
final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
这里的计算结果是86,400,000,000,000
,它超出了int
类型的范围,因此会溢出,导致结果错误。
final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
这里的计算结果是86,400,000
,它仍然在int
类型的范围内,没有溢出。
因此,MICROS_PER_DAY / MILLIS_PER_DAY
将会产生错误的结果,因为MICROS_PER_DAY
已经溢出了。要修复这个问题,你可以确保至少有一个操作数是long
类型,这样计算过程中就会使用long
类型进行计算,而不会发生溢出。你可以通过在数值后面加上L
来将其转换为long
类型。
修正后的代码应该如下所示:
public static void main(String[] args) {
final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
}
这样做之后,MICROS_PER_DAY
将被计算为86,400,000,000,000
,而不会溢出,结果将打印为1000
。
public static void main(String[] args) {
int x = 1984;
int y = 2001;
x ^= y ^= x ^= y;
System.out.println("x= " + x + "; y= " + y);
}
这段代码展示了一个有趣的编程技巧,使用异或运算符(^)来实现变量值的交换,但是它可能会让人感到困惑。让我们来逐步分析这段代码:
int x = 1984;
int y = 2001;
x ^= y ^= x ^= y;
这里的代码是在一行中执行的,但它等价于以下步骤:
x ^= y
,即 x = x ^ y
,结果为 x = 1984 ^ 2001 = 193
.y ^= x
,即 y = y ^ x
,此时 x
的值已经更新为 193
,结果为 y = 2001 ^ 193 = 1976
.x ^= y
,即 x = x ^ y
,此时 y
的值已经更新为 1976
,结果为 x = 193 ^ 1976 = 2185
.因此,最终的结果是 x = 2185
,y = 1976
。
所以,打印的结果将是:
x= 2185; y= 1976
虽然这段代码展示了一种有趣的技巧,但它并不易读,而且可能会使代码的含义变得模糊。在实际的编码中,应该优先选择更易读和易理解的方式来实现变量值的交换。
public static void main(String[] args) {
System.out.println("H" + "a"); //Ha
System.out.println('H' + 'a'); //169
}
这段代码展示了字符串连接和字符相加之间的区别。
System.out.println("H" + "a");
:这里使用的是字符串连接操作符+
,它将两个字符串连接在一起。因此,结果是字符串 "Ha"。System.out.println('H' + 'a');
:这里使用的是字符相加操作符+
,它会将字符的Unicode值相加。字符'H'的Unicode值是 72,字符'a'的Unicode值是 97。所以,'H' + 'a'
实际上是 72 + 97
,结果是 169
。然后,这个结果被当做整数类型输出。所以,这段代码的输出是:
Ha
169
需要注意的是,字符相加操作并不会直接拼接字符,而是对字符的Unicode值进行数值相加。
public static void main(String[] args) {
String letters = "ABC";
char[] numbers = {'1', '2', '3'};
System.out.println(letters + " easy as " + numbers);
}
在这段代码中,letters
是一个字符串,numbers
是一个字符数组。当你使用 +
运算符将它们连接成一个字符串时,Java 会调用 toString()
方法将字符数组转换为字符串。由于数组没有覆盖 toString()
方法,因此会使用默认的 Object
类的实现,它返回的是对象的哈希码的十六进制表示。
所以,在执行 System.out.println(letters + " easy as " + numbers);
时,实际上发生了以下转换过程:
letters
是一个字符串 "ABC"。numbers
是一个字符数组,由于它没有覆盖 toString()
方法,所以会调用默认的 Object
类的 toString()
方法,返回的是对象的哈希码的十六进制表示。因此,结果是 "ABC easy as [C@7852e922"
,其中 [C@7852e922
是字符数组的哈希码的十六进制表示。