关于溢出的问题,可以先翻书、百度,了解一下预备知识;
关于程序是否能通过编译、运行时时候会异常退出的问题,可以自行用eclipse验证。
Q19. What is the value of i printed?
public class Test {
public static void main(String[] args) {
int j = 0;
int i = j++ + j * 5;
System.out.println("What is i? " + i);
}
}
要先明确一点,在写代码的过程中,我们一般不会写int i = j++ + j * 5; 这样比较让人有歧义、迷惑的语句,这样会使得程序的可读性下降;
另一方面,这种题型的存在,是为了考察大家对优先级的理解。
大家可以找到书里关于操作符的优先级与结合性的表格。会发现++, *和+的优先级是依次降低的。所以程序的执行顺序是:
int i = j++ + j * 5;
// j++先执行,j变成1, 返回0;
// 然后j * 5 == 1 * 5, 返回5;
// 最后int i = 0 + 5 = 5.
Q21. What is y displayed in the following code?
public class Test {
public static void main(String[] args) {
int x = 1;
int y = x + x++;
System.out.println("y is " + y);
}
}
OS: 我在StackOverflow上问了这个问题,结果因为"duplicate questions"我被扣了4分的reputation...
在解答这题前,我想先介绍三个概念:优先级(precedence), 结合性(associativity), 和表达式求值顺序(expression evaluation order).
优先级(precendence)
优先级解决的是,操作符优先级不同时的执行顺序:
A() + B() + C() * D().
由于乘法的优先级高于加法,我们可以将表达式写成 A() + B() + (C() * D()).
结合性(associativity)
结合性解决的是,操作符优先级相同时的执行顺序:
由于加法的结合性是左集合,那么我们有
((A() + B()) + (C() * D()))
通过优先级,我们保证了左边的(第一个)加法,在第二个加法之前被执行;
通过优先级,我们保证了乘法在第二次加法之前被执行。
但是上述两种约束,并没有规定乘法必须发生在第一个加法之前。
同时,考虑下述代码:
a = A() // 1
b = B() // 2
c = C() // 3
d = D() // 4
sum = a + b //5
product = c * d
result = sum + product
事实上,语句1, 2, 3, 4的执行顺序是可以任意打乱的,至少只要它们在语句5之前执行完即可。
Evaluation Order正是规定了表达式的求值顺序。
求值顺序(evaluation order)
Evaluation Order规定了在Java中,表达式从左往右求值。
所以按照我的理解,对于A() + B() + C() * D()的计算顺序应该是
a = A()
b = B()
c = C()
d = D()
sum = a + b // 1
product = c * d // 2
result = sum + product
其中第一次加法(1)和乘法(2)的顺序是可以相互替换的。
讲完了优先级、结合性和求值顺序,我们就可以有理有据地,毫无歧义地,获得一个表达式的值。
一时间没能理解这三个概念是正常的,记住表达式是从左往右求值的,然后慢慢从题目中验证这三个概念即可。
回到原题Q21.
int x = 1;
int y = x + x++;
// ++优先级高于+, 表达式y = x + (x++)
// 表达式从左往右求值:
// 1. 左边的x先被求值,表达式变成 y = 1 + (x++);
// 2. 右边的x++再被求值,x自增成2, 表达式变成y = 1 + 1 = 2.
Q25. What is the value of (double)(5/2)?
整数除整数,仍为整数。
10 / 9 = 1,
5 / 9 = 0,
5 / 2 = 2.
Q31. Suppose i is an int type variable. Which of the following statements display the character whose Unicode is stored in variable i?
a. System.out.println(i);
b. System.out.println((char)i);
c. System.out.println((int)i);
d. System.out.println(i + " ");
char在内存中的表示
在Java中,char是用16个比特位(bit)保存的,bit非1即0. 所以char的表示范围是 0000_0000_0000_0000 ~ 1111_1111_1111_1111, 十进制表示是0 ~ 65535.
一方面,在我们使用println输出一个char类型时,我们可以在屏幕上看到它的字符表示;
另一方面,在计算机在内部,对char和int一视同仁,都用二进制的比特位对它们进行表示。不同的是,计算机用16位bit表示char, 但可能用32位或者64位表示int(或者long).
所以,char和int是可以相互转换的。
char和int间的转换
上面说到了,char和int在内存中都是用二进制表示的。那么它们之间是可以相互转换的。
大家可以去百度一下ASCII码表,码表中给出了字符类型(char)与整型间的对应关系。比如说,'a'的十进制整型表示是97, 'b'的十进制整型表示是98. 大家可以在eclipse下运行如下程序:
public class CharAndInt {
public static void main(String[] args) {
char c1 = 'a';
System.out.println((int) c1); // 输出 97
System.out.println(c1 + 1); // 输出 98
int i1 = 97;
System.out.println((char) i1); // 输出 a
System.out.println((char) (i1+1)); // 输出 b
}
}
注意到,(int) 是强制转换符,(int) c1的含义是将c1强制转换成int类型,println则将接收到一个int类型,将这个int类型以十进制输出;
(char) i1则是将i1强制转成成char类型,println将接收到一个char类型,输出这个char的字符表示(Unicode表示)。
回到原题Q31
我们已知println会根据接收到的类型,打印出相应的内容。
如果println接受到的是一个int类型(如a, c选项),那么println就输出这个int类型的十进制表示;
如果println接受到的是一个char类型(如b选项),那么println就输出这个char类型对应的字符;
如果println接受到的是一个String类型(如d选项),那么println久输出这个字符串。
Q35. Which of the following statement is correct?
把选项复制到eclipse下运行就可以知道答案,原因在Q31中解释了。
Q39. Which of the following statement is false?
答案D错误。
x > 0 || x < 10 && y < 0
// &&优先级比||高,这个表达式等价于x > 0 || (x < 10 && y < 0)
// 亦即x<0和y<0的逻辑与关系(&&)先被计算
// 再计算逻辑或关系(||)
(x > 0 || x < 10) && y < 0
// 这个先计算了||, 之后才计算&&
Q42. 下面哪个是错误的字符常量?
在Q31中讲到,char在内存中用16个bit保存。
所以我们可以用'\uxxxx'来表示,这个char在Unicode字符集下的十六进制:
- 每个x都是0-9, a-f或A-F的十六进制表示,代表了内存中的4个bit
- 有且只有4个x, 总共代表16个bit.