java解惑3-循环谜题

来源:互联网 发布:中性笔 知乎 编辑:程序博客网 时间:2024/04/28 08:34

谜题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!");        }    }}
                       题目: 如果你运行该程序,就会发现它没有打印任何东西

                       解决方案:

你可以将int转型为byte,之后你就可以拿一个byte与另一个byte进行比较了:

if (b == (byte)0x90)

   System.out.println("Joy!");

或者,你可以用一个屏蔽码来消除符号扩展的影响,从而将byte转型为int,之后你就可以拿一个int与另一个int进行比较了:

if ((b & 0xff) == 0x90)

   System.out.print("Joy!");

                        

谜题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);    }}
                        题目: 输出0

                        原因:当++操作符被置于一个变量值之后时,其作用就是一个后缀增量操作符
                                  (postfix increment operator)[JLS 15.14.2]: 表达式j++的值等于j在执
                                   行增量操作之前的初始值。因此,前面提到的赋值语句首先保存j的值,然后将j设置为其值加1,
                                   最后将j复位到它的初始值。换句话说,这个赋值操作等价于下面的语句序列:
                                     int tmp = j;
                                     j = j + 1;
                                     j = tmp?;

谜题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);    }}
                       题目:  无限循环

                        原因:问题在于这个循环会在循环索引(i)小于或等于Integer.MAX_VALUE时持续运行,
                                    但是所有的int变量都是小于或等于Integer.MAX_VALUE的。因为它被定义为所有int数值中的最大值。
                                     当i达到Integer.MAX_VALUE,并且再次被执行增量操作时,它就有绕回到了Integer.MIN_VALUE。
                                     如果你需要的循环会迭代到int数值的边界附近时,你最好是使用一个long变量作为循环索引。只需
                                    将循环索引的类型从int改变为long就可以解决该问题,从而使程序打印出我们所期望的101:

谜题27:变幻莫测的i值

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

                        原因:问题在于(-1 << 32)等于-1而不是0,因为移位操作符之使用其右操作数的低5位作为移位长度。
                                    或者是低6位,如果其左操作数是一个long类数值[JLS 15.19]。
                                    这条规则作用于全部的三个移位操作符:<<、>>和>>>。移位长度总是介于0到31之间,如果左操
                                    作数是long类型的,则介于0到63之间。这个长度是对32取余的,如果左操作数是long类型的,则对
                                    64取余。如果试图对一个int数值移位32位,或者是对一个long数值移位64位,都只能返回这个数值
                                    自身的值。没有任何移位长度可以让一个int数值丢弃其所有的32位,或者是让一个long数值丢弃其
                                     所有的64位。

                         解决方案:幸运的是,有一个非常容易的方式能够订正该问题。我们不是让-1重复地移位不同的移位长度,
                                      而是将前一次移位操作的结果保存起来,并且让它在每一次迭代时都向左再移1位。下面这个版本
                                      的程序就可以打印出我们所期望的32:
                                      public class Shifty {
                                             public static void main(String[] args) {
                                                           int distance = 0;
                                                            for (int val = -1; val != 0; val <<= 1)
                                                            distance++;
                                                            System.out.println(distance);
                                              }
                                       }

谜题28:循环者

什么样的声明能够让下面的循环变成一个无限循环? While (i == i + 1) {}
                        题目:Double i = Double.POSITIVE_INFINITY;
            System.out.println(i == i+1);

谜题29:循环者的新娘

请提供一个对i的声明,将下面的循环转变为一个无限循环: while (i != i) {}
                        题目: 
                        原因:IEEE 754浮点算术保留了一个特殊的值用来表示一个不是数字的数量[IEEE754]。
                                    这个值就是NaN(“不是一个数字(Not a Number)”的缩写),对于所有没有良
                                    好的数字定义的浮点计算,例如0.0/0.0,其值都是它。规范中描述道,NaN不等
                                    于任何浮点数值,包括它自身在内[JLS 15.21.1]。因此,如果i在循环开始之前被初
                                   始化为NaN,那么终止条件测试(i != i)的计算结果就是true,循环就永远不会终止。
                                   很奇怪但却是事实。 

谜题30:循环者的爱子

请提供一个对i的声明,将下面的循环转变为一个无限循环: while (i != i + 0) {}与前一个谜题不同,你必须在你的答案中不使用浮点数。换句话说,你不能把i声明为double或float类型的.

                                  题目: 使用Sring


谜题31:循环者的鬼魂

请提供一个对i的声明,将下面的循环转变为一个无限循环: while (i != 0) {    i >>>= 1;}
                                题目: short i = -1;
因为i的初始值((short)0xffff)是非0的,所以循环体会被执行。在执行移位操作时,第一步是将i提升为int类型。所有算数操作都会对short、byte和char类型的操作数执行这样的提升。这种提升是一个拓宽原始类型转换,因此没有任何信息会丢失。这种提升执行的是符号扩展,因此所产生的int数值是0xffffffff。然后,这个数值右移1位,但不使用符号扩展,因此产生了int数值0x7fffffff。最后,这个数值被存回到i中。为了将int数值存入short变量,Java执行的是可怕的窄化原始类型转换,它直接将高16位截掉。这样就只剩下(short)oxffff了,我们又回到了开始处。循环的第二次以及后续的迭代行为都是一样的,因此循环将永远不会终止。
如果你将i声明为一个short或byte变量,并且初始化为任何负数,那么这种行为也会发生。如果你声明i为一个char,那么你将无法得到无限循环,因为char是无符号的,所以发生在移位之前的拓宽原始类型转换不会执行符号扩展。
总之,不要在short、byte或char类型的变量之上使用复合赋值操作符。因为这样的表达式执行的是混合类型算术运算,它容易造成混乱。更糟的是,它们执行将隐式地执行会丢失信息的窄化转型,其结果是灾难性的。


谜题32:循环者的诅咒

请提供一个对i的声明,将下面的循环转变为一个无限循环: while (i <= j && j <= i && i != j) {}
                        题目:

Integer i = new Integer(0);

Integer j = new Integer(0);


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

请提供一个对i的声明,将下面的循环转变为一个无限循环。这个循环不需要使用任何5.0版的特性: while (i != 0 && i == -i) {}
                         题目:

int i = Integer.MIN_VALUE;

下面这个也可以:

long i = Long.MIN_VALUE;

谜题34:被计数击倒了

与谜题26和27中的程序一样,下面的程序有一个单重的循环,它记录迭代的次数,并在循环终止时打印这个数。那么,这个程序会打印出什么呢? 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类型的,而非int类型的。回想一下谜题28,很明显,增量操作(f++)不能正常工作。F的初始值接近于Integer.MAX_VALUE,因此它需要用31位来精确表示,而float类型只能提供24位的精度。对如此巨大的一个float数值进行增量操作将不会改变其值。因此,这个程序看起来应该无限地循环下去,因为f永远也不可能解决其终止值。但是,如果你运行该程序,就会发现它并没有无限循环下去,事实上,它立即就终止了,并打印出0。

谜题35:一分钟又一分钟

          
下面的程序在模仿一个简单的时钟。它的循环变量表示一个毫秒计数器,其计数值从0开始直至一小时中包含的毫秒数。循环体以定期的时间间隔对一个分钟计数器执行增量操作。最后,该程序将打印分钟计数器。那么它会打印出什么呢? 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);    }
                      修正: 

if (ms % (60 * 1000) == 0)

     minutes++;