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++;
- java解惑3-循环谜题
- 《java解惑》——循环谜题
- 《java解惑》读书笔记4——循环谜题
- Java解惑3——循环谜题(易混淆12处)
- Java解惑3——循环谜题(易混淆12处)
- Java解惑三:循环之谜
- Java解惑3-26在循环中
- java解惑之循环中
- java解惑之死循环
- Java for-each循环解惑
- Java for-each循环解惑
- java for-each循环解惑
- Java解惑学习有感(三)---循环之谜
- Java解惑3-29循环者的新娘
- Java解惑3-30循环者的爱子
- Java解惑3-31循环者的鬼魂
- Java解惑3-32循环者的诅咒
- Java解惑3-33循环者遇到了狼人
- oracle job
- Python常用辅助安全测试6个代码例子
- 管道/消息队列/共享内存
- 监控系统服务器设计文档_详细设计
- linux dibian 服务器设置 utf-8环境变量为utf-8编码
- java解惑3-循环谜题
- jQuery 在Iframe父窗口中查找对象
- MyEclipse 8.5 开发环境配置,汉化,SVN 插件,Flex Builder 3/4 插件安装
- LISTVIEW动态加载网络数据2
- WinInet提交数据
- winform: 设置下拉框数据源的公共函数
- HTML5 Canvas 初步:字符串,路径,背景,图片
- 清除访问共享时保存的密码和浏览记录
- 各种名称内存卡的说明及购买注意事项