1、奇数性
下面的方法的目的是确定其唯一的参数是否为奇数,这个方法可行吗?
public class Oddity {
public static boolean isOdd(int i) {
return i % 2 == 1;
}
public static void main(String[] args) {
}
}
其实这个方法不可行的。因为他对于所有的负数的判断都是不正确的,,任何负数调用该方法的结果都为false,解决这个问题可以用下面这种方法:
public class Oddity {
public static boolean isOdd(int i) {
return i % 2 != 0;
}
public static void main(String[] args) {
}
}
这个代码可以进步一步优化为:
public class Oddity {
public static boolean isOdd(int i) {
return (i & 1) != 0;
}
public static void main(String[] args) {
}
}
2、找零时刻
Tom在一家汽车配件商店购买一个价值1.10美元的火花塞,但是他钱包中都是两美元一张的钞票、如果他用一张两美元的钞票购买这个火花塞,那么应该找给他多少零钱?
下面是一个试图解决上述问题的程序,它会打印什么呢?
public class Change {
public static void main(String args[]) {
System.out.println(2.00 - 1.10);
}
}
或许是觉得应该打印的是0.90或者0.9吧,但是很遗憾地告诉你,这是错的。问题在于并不是所有的小数都可以用二进制浮点数精确表示。下面这段代码虽然能够打印正确答案,但它并不是对底层问题的通用解决方法:
public class Change {
public static void main(String args[]) {
System.out.printf("%.2f\n",(2.00 - 1.10));
}
}
其实,二进制浮点对于货币计算是非常不适合的。对于这个问题我们有两种解决方案。第一,以分为单位表示货币值后重写prinfln语句,这个版本将打印正确答案90美分:
public class Change {
public static void main(String args[]) {
System.out.println((200 - 110) + "cents");
}
}
另一种解决方案是使用执行精确小数运算的 BigDecimal。这里注意一点:一定要用BigDecimal(String)构造器,而千万不要用BigDemal(double)。程序如下:
public class Change {
public static void main(String args[]) {
System.out.println(new BigDecimal("2.00").subtract(new BigDecimal("1.10")));
}
}
总之,在需要精确答案的地方,要避免是使用float和double;对于货币计算,要使用和int、long或BigDecimal。
3、长整数
这个谜题被称为长整数,因为它所涉及的程序是整除两个long型的整数值的。被除数表示一天里的微妙数,而除数表示一天里的毫秒数。这个程序会打印什么呢?
public class LongDivision {
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);
}
}
或许你也会和我一样地认为会打印1000吧,但是还是很遗憾地告诉你,我们都错了,这个程序打印的是5。因为这个计算完全是以int运算来执行的,并且只有在运算完成之后,其结果才被提升到long,但是此时已经太迟了,计算已经溢出,它返回的是一个1/200倍的数值。
Java不具有目标确定类型的特征,这是一种语言特征,其含义是指存储结果的变量类型会影响计算所使用的类型。我们可以将后续计算都用long运算来完成,程序如下:
public class LongDivision {
public static void main(String[] args) {
final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24L * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
}
}
这道题目给我们的教训是:当你在操作很大的数字时,千万要提放溢出---它可是一个缄默杀手。
4、初级问题
前面那个谜题有点棘手,但它是有关整除的,每个人都知道整除是很麻烦的。下面的程序只设计加法,它又会打印什么呢?
public class Elementary {
public static void main(String[] args) {
System.out.println(12345 + 5432l);
}
}
从表面上看这是一个简单的问题,简单到你只要口算就可以知道答案是666666了。但是当你运行这个程序的时候你发现结果并不是你想象中的那个数字。这一切到底是为什么呢?
这个时候你或许应该睁大眼睛看看源程序,你有没有注意到加号后面那个数的最后一位不是数字1而是小写字母l。所以正确答案是17777,。这时候你可能在大喊着“恶心”。但是至少你就应该明白这样一个教训:在long类型字面常量中,一定要用大写字母L,千万不要用小写的l。类似地,要避免使用单个小写字母l作为变量名。