现在的位置: 首页 > 综合 > 正文

Java谜题畅读版之循环谜题

2013年10月23日 ⁄ 综合 ⁄ 共 2920字 ⁄ 字号 评论关闭
文章目录

谜题24:尽情享受每一个字节

public class BigDelight {
    public static void main(String[] args) {
        for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) {
             if (b == 0x90)
                 System.out.print("Joy!");
        }
    }
}

这个程序有两个问题:
问题1 0x90超出了byte的范围;
问题2 0x90是int型,byte与int进行比较的时候,将强行把byte转int再做比较. byte转int执行有符号拓展;
所以上面的程序什么也不会打印出来。
可以思考一下这个比较运算:((byte)0x90 == 0x90)

谜题25:无情的增量操作

public class Increment {
    public static void main(String[] args) {
        int j = 0;
        for (int i = 0; i < 100; i++)
              j = j++;
        System.out.println(j);
    }
}

这个程序的问题就比较明显了,应该看一眼就发现它的问题所在:j = j++;。

谜题26:在循环中

public class InTheLoop {
    public static final int END = Integer.MAX_VALUE;
    public static final int START = END - 100;
    public static void main(String[] args) {
        int count = 0;
        for (int i = START; i <= END; i++)
            count++;
        System.out.println(count);
    }
}

乍一看,程序会打印出100,细想,(int i = START; i <= END; i++)会执行101次,所以结果应该是101,但是它是个死循环。
当i==Integer.MAX_VALUE这一次循环结束时,i自加1,这个时候i溢出,为-2147483648.

谜题27:变幻莫测的i值

public class Shifty {
    public static void main(String[] args) {
        int i = 0;
        //-1左移i位
        while (-1 << i != 0)
            i++;
        System.out.println(i);
    }
}

如果你知道-1的二进制所有的位都是1,你又知道左移位的结果是在移位后,右边空出的位用0补齐, 那么你可能会说这个结果是32. 实际上结果是它进入了死循环.
问题的关键在于移位操作的移位数,即移位操作的右操作数只能是对32取模(如果左操作数是int),或64取模(如果左操作数是long). JLS上面说移位操作只会拿右操作数的低5位(,如果最大为31),或低6位(最大为63)来计算实际要移动的位数.

谜题28:循环者

定义一个start和i,是下面的两个循环成为死循环

for (int i = start; i <= start + 1; i++) {}
While (i == i + 1) {}

如果你看过了谜题26,则第一个你应该很容易想出来, int start = Integer.MAX_VALUE - 1;
第二个恐怕没那么好想. 要记住的是, 一个很大的double或float,加上一个很小的数,是没有变化的. 浮点类型的数字越大,它和它相邻数字之间的距离越大, 以至于+1之后不足以弥补这个距离. 这个距离叫做ULP, java里面Math.ulp可以计算这个距离.

这个ulp怎么产生的呢?这就要问浮点在内存中怎么存放的了.比如float类型,在java里面是4byte的32位, 实际在内存中存放的时候,它用一个位存放符号, 用8个位表示阶码, 用23个位表示尾数, 组合起来就像科学记数法那样, 这种表示法下, 产生缝隙就显而易见了.

谜题29:循环者的新娘

请提供一个对i的声明,将下面的循环转变为一个无限循环:
while (i != i) {
}
如果你记得NaN, 就是它了. NaN是个很奇怪的常量, 它都不等于它自己.

谜题30:循环者的爱子

请提供一个对i的声明,将下面的循环转变为一个无限循环:
while (i != i + 0) {
}
这个应该好想把? String i = "i";

谜题31:循环者的鬼魂

请提供一个对i的声明,将下面的循环转变为一个无限循环:
while (i != 0) {
    i >>>= 1;
}
如果你定义long i = -1; 然后想像一下, 所有的位都是1, 然后每右移一位,就朝左面加一个0; 最后一共移动了64次就结束了. 怎么才能死循环呢?
如果我定义short i = -1; 会怎么样?  >>>=是复合赋值运算(它有隐含的转型), 每朝右移动之前, 要把i转成int, 则执行了有符号拓展, 即前面 高位1补齐; 然后右移一位, 最高位补0, 然后把结果再转成short, 即高16位砍掉. 于是永远也得不到0.

谜题32:循环者的诅咒

请提供一个对i和j的声明,将下面的循环转变为一个无限循环:
while (i <= j && j <= i && i != j) {
}
其实这个题还是值得思考的, 至少应该能超前多都一步, 即i<=j&&j<=i这个条件, 它有一个暗示, 就是i和j的内在值相等, 而后面的i!=j可以理解为非同一性.
Integer i = new Integer(0); Integer j = new Integer(0); 即可. 但是Integer i = 0; Integer j = 0;是不行的. 因为Integer在构造的时候, 对于字面量的形式, 会使用缓冲池(它缓冲了-128~127的Integer)里面的Integer实例, 即
Integer i = 1;Integer j = 1;它们具有同一性. 当然如果你知道了这点, 则你也能理解Integer i = 128;Integer j = 128; 也是能让我们这个命题成立的.

谜题33:循环者遇到了狼人

再看看这个:
while (i != 0 && i == -i) {
}
这个题很难想的到. Integer.MIN_VALUE可以满足,它的值为-2^32, 取反之后, 本应该等于2^32, 但是2^32已经大于Integer.MAX_VALUE, 即溢出了, 然后又变成-2^32.

谜题34:被计数击倒了

下面程序的结果是什么?

public class Count {
    public static void main(String[] args) {
        final int START = 2000000000;
        int count = 0;
        for (float f = START; f < START + 50; f++)
            count++;
        System.out.println(count);
    }
}

这个题还是考察可大的浮点数和一个较小的数相加的问题. 如果float f1 = 2000000000;float f2 = 2000000050; 则f1==f2.

本题输出0

谜题35:一分钟又一分钟

下面程序会打印出什么呢?

public class Clock {
    public static void main(String[] args) {
        int minutes = 0;
        for (int ms = 0; ms < 60*60*1000; ms++)
            if (ms % 60*1000 == 0)
                minutes++;
        System.out.println(minutes);
    }
}

这个题考察了求值顺序问题, ms % 60*1000 == 0 等同于ms % 60 == 0. 所以结果不是60, 而是60000.

抱歉!评论已关闭.