java疑难Word下载.docx
《java疑难Word下载.docx》由会员分享,可在线阅读,更多相关《java疑难Word下载.docx(60页珍藏版)》请在冰豆网上搜索。
5:
十六进制的趣事
下面的程序是对两个十六进制(hex)字面常量进行相加,然后打印出十六进制的结果。
publicclassJoyOfHex{
System.out.println(
Long.toHexString(0x100000000L+0xcafebabe));
6:
多重转型
转型被用来将一个数值从一种类型转换到另一种类型。
下面的程序连续使用了三个转型。
那么它到底会打印出什么呢?
publicclassMulticast{
publicstaticvoidmain(String[]args){
System.out.println((int)(char)(byte)-1);
7:
互换内容
下面的程序使用了复合的异或赋值操作符,它所展示的技术是一种编程习俗。
那么它会打印出什么呢?
publicclassCleverSwap{
intx=1984;
//(0x7c0)
inty=2001;
//(0x7d1)
x^=y^=x^=y;
System.out.println("
x="
+x+"
;
y="
+y);
8:
DosEquis
这个将测试你对条件操作符的掌握程度,这个操作符有一个更广为人知的名字:
问号冒号操作符。
下面的程序将会打印出什么呢?
publicclassDosEquis{
charx='
X'
inti=0;
System.out.println(true?
x:
0);
System.out.println(false?
i:
x);
9:
半斤
现在该轮到你来写些代码了,好消息是,你只需为这个编写两行代码,并为下一个也编写两行代码。
这有什么难的呢?
我们给出一个对变量x和i的声明即可,它肯定是一个合法的语句:
x+=i;
但是,它并不是:
x=x+i;
许多程序员都会认为该迷题中的第一个表达式(x+=i)只是第二个表达式(x=x+i)的简写方式。
但是这并不十分准确。
这两个表达式都被称为赋值表达式。
第二条语句使用的是简单赋值操作符(=),而第一条语句使用的是复合赋值操作符。
(复合赋值操作符包括+=、-=、*=、/=、%=、<
<
=、>
>
=、&
=、^=和|=)Java语言规范中讲到,复合赋值E1op=E2等价于简单赋值E1=(T)((E1)op(E2)),其中T是E1的类型,除非E1只被计算一次。
换句话说,复合赋值表达式自动地将它们所执行的计算的结果转型为其左侧变量的类型。
如果结果的类型与该变量的类型相同,那么这个转型不会造成任何影响。
然而,如果结果的类型比该变量的类型要宽,那么复合赋值操作符将悄悄地执行一个窄化原始类型转换。
因此,我们有很好的理由去解释为什么在尝试着执行等价的简单赋值可能会产生一个编译错误。
为了说得具体一些,并提供一个解决方案给这个,假设我们在该的两个赋值表达式之前有下面这些声明:
shortx=0;
inti=123456;
复合赋值编译将不会产生任何错误:
//包含了一个隐藏的转型!
你可能期望x的值在这条语句执行之后是123,456,但是并非如此l,它的值是-7,616。
int类型的数值123456对于short来说太大了。
自动产生的转型悄悄地把int数值的高两位给截掉了。
这也许就不是你想要的了。
相对应的简单赋值是非法的,因为它试图将int数值赋值给short变量,它需要一个显式的转型:
//不要编译——“可能会丢掉精度”
这应该是明显的,复合赋值表达式可能是很危险的。
为了避免这种令人不快的突袭,请不要将复合赋值操作符作用于byte、short或char类型的变量上。
在将复合赋值操作符作用于int类型的变量上时,要确保表达式右侧不是long、float或double类型。
在将复合赋值操作符作用于float类型的变量上时,要确保表达式右侧不是double类型。
这些规则足以防止编译器产生危险的窄化转型。
总之,复合赋值操作符会悄悄地产生一个转型。
如果计算结果的类型宽于变量的类型,那么所产生的转型就是一个危险的窄化转型。
这样的转型可能会悄悄地丢弃掉精度或数量值。
对语言设计者来说,也许让复合赋值操作符产生一个不可见的转型本身就是一个错误;
对于在复合赋值中的变量类型比计算结果窄的情况,也许应该让其非法才对。
10:
八两
与上面的例子相反,如果我们给出的关于变量x和i的声明是如下的合法语句:
乍一看,这个可能看起来与前面一个相同。
但是请放心,它们并不一样。
这两个在哪一条语句必是合法的,以及哪一条语句必是不合法的方面,正好相反。
就像前面的一样,这个也依赖于有关复合赋值操作符的规范中的细节。
二者的相似之处就此打住。
基于前面的,你可能会想:
符合赋值操作符比简单赋值操作符的限制要少一些。
在一般情况下,这是对的,但是有这么一个领域,在其中简单赋值操作符会显得更宽松一些。
复合赋值操作符要求两个操作数都是原始类型的,例如int,或包装了的原始类型,例如Integer,但是有一个例外:
如果在+=操作符左侧的操作数是String类型的,那么它允许右侧的操作数是任意类型,在这种情况下,该操作符执行的是字符串连接操作。
简单赋值操作符(=)允许其左侧的是对象引用类型,这就显得要宽松许多了:
你可以使用它们来表示任何你想要表示的内容,只要表达式的右侧与左侧的变量是赋值兼容的即可。
你可以利用这一差异来解决该。
要想用+=操作符来执行字符串连接操作,你就必须将左侧的变量声明为String类型。
通过使用直接赋值操作符,字符串连接的结果可以存放到一个Object类型的变量中。
Objectx="
Buy"
Stringi="
EffectiveJava!
"
简单赋值是合法的,因为x+i是String类型的,而String类型又是与Object赋值兼容的:
复合赋值是非法的,因为左侧是一个Object引用类型,而右侧是一个String类型:
这个对程序员来说几乎算不上什么教训。
对语言设计者来说,加法的复合赋值操作符应该在右侧是String类型的情况下,允许左侧是Object类型。
这项修改将根除这个所展示的违背直觉的行为。
Java2——字符
11:
最后的笑声
下面的程序将打印出什么呢?
publicclassLastLaugh{
System.out.print("
H"
+"
a"
);
System.out.print('
H'
+'
a'
12:
ABC
这个要问的是一个悦耳的问题,下面的程序将打印什么呢?
publicclassABC{
Stringletters="
ABC"
char[]numbers={'
1'
'
2'
3'
};
System.out.println(letters+"
easyas"
+numbers);
13:
畜牧场
GeorgeOrwell的《畜牧场(AnimalFarm)》一书的读者可能还记得老上校的宣言:
“所有的动物都是平等的。
”下面的Java程序试图要测试这项宣言。
那么,它将打印出什么呢?
publicclassAnimalFarm{
finalStringpig="
length:
10"
finalStringdog="
"
+pig.length();
System.out.println("
Animalsareequal:
+pig==dog);
14:
转义字符的溃败
下面的程序使用了两个Unicode的转义字符,它们是用其十六进制代码来表示Unicode字符。
那么,这个程序会打印什么呢?
publicclassEscapeRout{
//\u0022是双引号的Unicode转义字符
System.out.println("
a\u0022.length()
+\u0022b"
.length());
15:
令人晕头转向的Hello
下面的程序是对一个老生常谈的例子做出了稍许的变化之后的版本。
那么,它会打印出什么呢?
/**
*GeneratedbytheIBMIDL-to-Javacompiler,version1.0
*fromF:
\TestRoot\apps\a1\units\include\PolicyHome.idl
*Wednesday,June17,19986:
44:
40o’clockAMGMT+00:
00
*/
publicclassTest{
Hell"
oworld"
16:
行打印程序
行分隔符(lineseparator)是为用来分隔文本行的字符或字符组合而起的名字,并且它在不同的平台上是存在差异的。
在Windows平台上,它是CR字符(回车)和紧随其后的LF字符(换行)组成的,而在UNIX平台上,通常单独的LF字符被当作换行字符来引用。
下面的程序将这个字符传递给了println方法,那么,它将打印出什么呢?
它的行为是否是依赖于平台的呢?
publicclassLinePrinter{
//Note:
\u000AisUnicoderepresentationoflinefeed(LF)
charc=0x000A;
System.out.println(c);
17:
嗯?
下面的是一个合法的Java程序吗?
如果是,它会打印出什么呢?
\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020
\u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079
\u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020
\u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063
\u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028
\u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020
\u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b
\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074
\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020
\u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b
\u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d
18:
字符串奶酪
下面的程序从一个字节序列创建了一个字符串,然后迭代遍历字符串中的字符,并将它们作为数字打印。
请描述一下程序打印出来的数字序列:
publicclassStringCheese{
publicstaticvoidmain(String[]args){
bytebytes[]=newbyte[256];
for(inti=0;
i<
256;
i++)
bytes[i]=(byte)i;
Stringstr=newString(bytes);
for(inti=0,n=str.length();
n;
System.out.println((int)str.charAt(i)+"
19:
漂亮的火花
下面的程序用一个方法对字符进行了分类。
publicclassClassifier{
System.out.println(
classify('
n'
)+classify('
));
staticStringclassify(charch){
if("
0123456789"
.indexOf(ch)>
=0)
return"
NUMERAL"
abcdefghijklmnopqrstuvwxyz"
LETTER"
/*(Operatorsnotsupportedyet)
+-*/&
|!
="
>
OPERATOR"
UNKNOWN"
20:
我的类是什么?
下面的程序被设计用来打印它的类文件的名称。
如果你不熟悉类字面常量,那么我告诉你Me.class.getName()将返回Me类完整的名称,即“com.javapuzzlers.Me”。
那么,这个程序会打印出什么呢?
packagecom.javapuzzlers;
publicclassMe{
Me.class.getName().
replaceAll("
."
"
/"
)+"
.class"
21:
II
下面的程序所要做的事情正是前一个所做的事情,但是它没有假设斜杠符号就是分隔文件名组成部分的符号。
相反,该程序使用的是java.io.File.separator,它被指定为一个公共的String域,包含了平台相关的文件名分隔符。
那么,这个程序会打印出其正确的、平台相关的类文件名吗?
importjava.io.File;
publicclassMeToo{
System.out.println(MeToo.class.getName().
replaceAll("
\\."
File.separator)+"
22:
URL的愚弄
本利用了Java编程语言中一个很少被人了解的特性。
请考虑下面的程序将会做些什么?
publicclassBrowserTest{
iexplore:
;
:
maximize"
23:
不劳无获
下面的程序将打印一个单词,其第一个字母是由一个随机数生成器来选择的。
请描述该程序的行为:
importjava.util.Random;
publicclassRhymes{
privatestaticRandomrnd=newRandom();
StringBufferword=null;
switch(rnd.nextInt
(2)){
case1:
word=newStringBuffer('
P'
case2:
G'
default:
M'
word.append('
i'
System.out.println(word);
Java3——循环
24:
尽情享受每一个字节
下面的程序循环遍历byte数值,以查找某个特定值。
publicclassBigDelight{
for(byteb=Byte.MIN_VALUE;
b<
Byte.MAX_VALUE;
b++){
if(b==0x90)
Joy!
25:
无情的增量操作
下面的程序对一个变量重复地进行增量操作,然后打印它的值。
那么这个值是什么呢?
publicclassIncrement{
intj=0;
100;
j=j++;
System.out.println(j);
26:
在循环中
下面的程序计算了一个循环的迭代次数,并且在该循环终止时将这个计数值打印了出来。
那么,它打印的是什么呢?
publicclassInTheLoop{
publicstaticfinalintEND=Integer.MAX_VALUE;
publicstaticfinalintSTART=END-100;
intcount=0;
for(inti=START;
=END;
count++;
System.out.println(count);
27:
变幻莫测的i值
与26中的程序一样,下面的程序也包含了一个记录在终止前有多少次迭代的循环。
与那个程序不同的是,这个程序使用的是左移操作符(<
)。
你的任务照旧是要指出这个程序将打印什么。
当你阅读这个程序时,请记住Java使用的是基于2的补码的二进制算术运算,因此-1在任何有符号的整数类型中(byte、short、int或long)的表示都是所有的位被置位:
publicclassShifty{
inti=0;
while(-1<
i!
i++;
System.out.println(i);
28:
循环者
下面的以及随后的五个对你来说是扭转了局面,它们不是向你展示某些代码,然后询问你这些代码将做些什么,它们要让你去写代码,但是数量会很少。
这些被称为“循环者(looper)”。
你眼前会展示出一个循环,它看起来应该很快就终止的,而你的任务就是写一个变量声明,在将它作用于该循环之上时,使得该循环无限循环下去。
例如,考虑下面的for循环:
for(inti=start;
=start+1;
i++){}
看起来它好像应该只迭代两次,但是通过利用在26中所展示的溢出行为,可以使它无限循环下去。
下面的的声明就采用了这项技巧:
intstart=Integer.MAX_VALUE-1;
现在该轮到你了。
什么样的声明能够让下面的循环变成一个无限循环?
While(i==