关闭

java难点之表达式

300人阅读 评论(0) 收藏 举报
分类:

1,奇数

问题描述:
如果想要判断一个数是否是奇数,
public static boolean isOdd(int i){
return i % 2 == 1;
}
在所有的 int 数值中,有一半都是负数,而 isOdd 方
法对于对所有负奇数的判断都会失败。在任何负整数上调用该方法都回返回
false ,不管该整数是偶数还是奇数。
这是 Java 对取余操作符(%)的定义所产生的后果。该操作符被定义为对于所
有的 int 数值 a 和所有的非零 int 数值 b,都满足下面的恒等式:
(a / b) * b + (a % b) == a
当 i 是一个负奇数时,i % 2 等于-1 而不是 1, 因此 isOdd 方法将错误地返
回 false。为了防止这种意外,请测试你的方法在为每一个数值型参数传递负数、
零和正数数值时,其行为是否正确。
解决方案:
方法一:
public static boolean isOdd(int i){
return i % 2 != 0;
}
方法二:
public static boolean isOdd(int i){
return (i & 1) != 0;
}

2,数字

问题:
Tom 在一家汽车配件商店购买了一个价值$1.10 的火花塞,但是他钱包中都是两
美元一张的钞票。如果他用一张两美元的钞票支付这个火花塞,那么应该找给他
多少零钱呢?
public class Change{
public static void main(String args[]){
System.out.println(2.00 - 1.10);
}
}
运行该程序,你就会发现它打
印的是 0.8999999999999999。问题在于 1.1 这个数字不能被精确表示成为一个 double,因此它被表示成为最
接近它的 double 值。该程序从 2 中减去的就是这个值。遗憾的是,这个计算的
结果并不是最接近 0.9 的 double 值。问题在于并不是所有的小数都可以用二进制浮点数来精确表示的。
解决方案:
1)System.out.printf("%.2f%n",2.00 - 1.10);不太支持这种方法
2)System.out.println((200 - 110) + "cents");
3)import java.math.BigDecimal;
public class Change1{
public static void main(String args[]){
System.out.println(new BigDecimal("2.00").
subtract(new BigDecimal("1.10")));
}
}
总结:在需要精确答案的地方,要避免使用 float 和 double;对于货币计算,
要使用 int、long 或 BigDecimal。

问题:长整除

涉及的程序是有关两个 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);
}
}
除数和被除数都是 long 类型的,long 类型大到了可以很容易地保存这两个乘积
而不产生溢出。因此,看起来程序打印的必定是 1000。
遗憾的是,它打印的是 5。
问题在于常数 MICROS_PER_DAY 的计算“确实”溢出了。尽管计算的结果适合放
入 long 中,并且其空间还有富余,但是这个结果并不适合放入 int 中。这个计
算完全是以 int 运算来执行的,并且只有在运算完成之后,其结果才被提升到
long,而此时已经太迟了:计算已经溢出了,它返回的是一个小了 200 倍的数值。
从 int 提升到 long 是一种拓宽原始类型转换(widening primitive conversion),
它保留了(不正确的)数值。这个值之后被 MILLIS_PER_DAY 整除,而
MILLIS_PER_DAY 的计算是正确的,因为它适合 int 运算。这样整除的结果就得
到了 5。
解决方案:
所有乘在一起的因子都是 int
数值。当你将两个 int 数值相乘时,你将得到另一个 int 数值。Java 不具有目
标确定类型的特性,这是一种语言特性,其含义是指存储结果的变量的类型会影
响到计算所使用的类型。
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);
}
总结:当你在操作很大的数字时,千万要提防溢出——它可是一个缄
默杀手。即使用来保存结果的变量已显得足够大,也并不意味着要产生结果的计
算具有正确的类型。当你拿不准时,就使用 long 运算来执行整个计算。

问题:

public class Elementary{
public static void main(String[] args){
System.out.println(12345+5432l);
}
}


解决方案:在代码中由于1和L的小写很难分得清楚,这在代码中很容易产生错误,
在 long 型字面常量中,一定要用大写的 L,千万不要用小写的 l。
小写字母 l 和数字 1 在大多数打字机字体中都是几乎一样的。为避免你的
程序的读者对二者产生混淆,千万不要使用小写的 l 来作为 long 型字面常量的
结尾或是作为变量名。


关于十六进制

问题:
下面的程序是对两个十六进制(hex)字面常量进行相加,然后打印出十六进制
的结果。这个程序会打印出什么呢?
public class JoyOfHex{
public static void main(String[] args){
System.out.println(
Long.toHexString(0x100000000L + 0xcafebabe));
}
}
该程序应该打印出 1cafebabe,实际它打印出来的是 cafebabe。
解决方案:
public class JoyOfHex{
public static void main(String[] args){
System.out.println(
Long.toHexString(0x100000000L + 0xcafebabeL));
}
}
总结:
:混合类型的计算可能会产生混淆,尤其是十六进制和
八进制字面常量无需显式的减号符号就可以表示负的数值。为了避免这种窘境,
通常最好是避免混合类型的计算。对于语言的设计者们来说,应该考虑支持无符
号的整数类型,从而根除符号扩展的可能性。

问题:多重转型

public class Multicast{
public static void main (String[] args){
System.out.println((int)(char)(byte) -1);
}
}
它以 int 数值-1 开始,然后从 int
转型为 byte,之后转型为 char,最后转型回 int。第一个转型将数值从 32 位窄
化到了 8 位,第二个转型将数值从 8 位拓宽到了 16 位,最后一个转型又将数值
从 16 位拓宽回了 32 位。这个数值最终是回到了起点吗?如果你运行该程序,它打印出来的是 65535。
解决方案:
有一条很简单的规则能够描述从较窄的整
型转换成较宽的整型时的符号扩展行为:如果最初的数值类型是有符号的,那么
就执行符号扩展;如果它是 char,那么不管它将要被转换成什么类型,都执行
零扩展。因为 byte 是一个有符号的类型,所以在将 byte 数值-1 转换成 char 时,会发生
符号扩展。作为结果的 char 数值的 16 个位就都被置位了,因此它等于 216-1,
即 65535。从 char 到 int 的转型也是一个拓宽原始类型转换,所以这条规则告
诉我们,它将执行零扩展而不是符号扩展。作为结果的 int 数值也就成了 65535,
这正是程序打印出的结果

问题:

public class CleverSwap{
public static void main(String[] args){
int x = 1984; // (0x7c0)
int y = 2001; // (0x7d1)
x^= y^= x^= y;
System.out.println("x= " + x + "; y= " + y);
}
}
这个程序应该交换变量 x 和 y 的值。如果你运行它,就会
发现它打印的是
x = 0; y = 1984。
在单个的表达式中不要对相同的变量赋值两次。表达式如果包
含对相同变量的多次赋值,就会引起混乱,并且很少能够执行你希望的操作。即
使对多个变量进行赋值也很容易出错。

问题:

public class DosEquis{
public static void main(String[] args){
char x = 'X';
int i = 0;
System.out.println(true ? x : 0);
System.out.println(false ? i : x);
}
}
这个程序应该打印 XX。然而,如果你运行该程序,你就会
发现它打印出来的是 X88。


复合赋值操作符要求两个操作数都是原始类型的,例如 int,或包装了的原始类
型,例如 Integer,但是有一个例外:如果在+=操作符左侧的操作数是 String
类型的,那么它允许右侧的操作数是任意类型,在这种情况下,该操作符执行的
是字符串连接操作。简单赋值操作符(=)允许其左侧的是对象引用类型,这就
显得要宽松许多了:你可以使用它们来表示任何你想要表示的内容,只要表达式
的右侧与左侧的变量是赋值兼容的即可。
1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:331166次
    • 积分:7200
    • 等级:
    • 排名:第3216名
    • 原创:391篇
    • 转载:20篇
    • 译文:0篇
    • 评论:62条
    博客专栏