关于变量的初始化
声明一个变量之后,系统会不会自动为这个变量初始化?答案为,函数的局部变量不会被自动初始化,而其他的变量比如类成员、使用new操作符分配的变量则会被自动初始化。
关于垃圾回收
Java程序在运行时,垃圾回收线程会以较低的优先度运行,该线程检查系统中的所有对象,如果发现某个对象不能被任何线程访问,则将该对象释放。这里存在如下两个容易误解的问题。
一是如何使得某个对象不能被任何线程访问。当我们分配一个对象并使用之后,只要使用这个对象的线程还存在,系统就无法得知这个对象是否有效,从而垃圾回收时该对象不会被释放。一个显式的做法就是赋 null 值,即 anObject = null; ,这样 anObject 所指向的对象就会在下次垃圾回收时被释放。
另一个问题是垃圾回收的执行时机。由于垃圾回收线程的优先度较低,所以并不能保证垃圾回收总能在恰当的时候进行,整个执行过程中完全不进行垃圾回收也有可能。系统虽然提供了一个 System.gc() 函数,但是该函数并不是强制执行垃圾回收,而是“建议”系统进行垃圾回收,实际上系统是否进行垃圾回收并无保证。
关于赋值运算符的运算顺序
连续的几个赋值运算符写在一起时,它们的执行顺序如何?让我们来看这段代码。
int a = 1;
int b[] = { 1, 2, 3, 4, 5 };
b[a] = a = 0;
在Java下运行结果为 b = { 1, 0, 3, 4, 5 }。如果当作C语言来执行的话,gcc v3.2下的执行结果为 b = { 1, 0, 3, 4, 5 },而 Microsoft Visual C++ 6.0 下的执行结果为 b = { 0, 2, 3, 4, 5 }。
可见,在Java(或gcc)中该表达式的执行顺序为:1) b[a] = x;2) x = (a =0)。执行第一步时,变量 a 的值还没有被改变,因此最后的结果是给 b[1] 赋值而不是给 b[0] 赋值。
而在 Microsoft Visual C++ 6.0 中,该表达式的执行顺序为:1) x = (a = 0); 2) b[a] = x。最后的结果是给 b[0] 赋值。
关于取余运算的问题
与C语言相同,取余运算的结果的符号取决于前面的运算数,与后面的运算数无关。例如:
8 % 5 = 3
8 % -5 = 3
-8 % 5 = -3
-8 % -5 = -3
另外与C语言不同的是,Java的取余运算可以对float和double类型使用(C语言会报告编译错误)。运算法则相当于从前面的运算数的绝对值中减去若干个后面的运算数。例如 8.3 % 5.1 = 3.2。
右移位运算符和除法
一般来说可以认为右移运算符与除2等价。但是-1 / 2 = 0,-1 >> 1 = -1。
移位运算数的还原
如果移位的位数超过了运算数本身的位数,那么结果如何?例如
int a = 0x1234;
int b = a >> 36;
int a为32位,将其右移36位,结果会是多少?C语言的结果是0(gcc会出警告,说移位位数超过了运算数位数)。但实际上结果为 0×123,与右移4位的结果相同。左移运算符也有同样的结果。
运算数扩展造成的无符号右移结果错误
下面的程序中,b 的值是多少?
byte a = -64;
byte b = (byte)(a >>> 4);
这里 a = 11000000b,那么无符号右移4位的结果应为 00001100b,即 b = 12。但是实际的执行结果为 b = -4,即 11111100b。
得到这个错误结果的原因是,Java中任何双目运算的运算数都会首先被扩展为 int 或者 long,然后再进行运算。在这里运算过程如下:byte 型 11000000扩展为 int 型 11111111 11111111 11111111 11000000无符号右移4位 00001111 11111111 11111111 11111100强制转换为 byte 型 11111100
从上面的过程中我们可以得出结论,对于比int还要小的数值类型(short、byte)不应当进行无符号右移运算。
隐式类型转换的规则
当我们用一个变量给另外一个类型不同的变量进行赋值时,隐式类型转换会自动发生。通常,隐式类型转换的规则遵循“小转大”,即长度较小的类型会自动转换为长度较大的类型,而反之则被认为是编译错误。一般规则如下图所示:
但是需要注意两点,一是char不能转换成short类型,虽然两者都是两个字节;二是boolean不能转换成任何类型,任何类型也不能转换成boolean型。
switch语句所使用的表达式
大家都知道switch语句后面的表达式不能是float或者double类型。但是实际上,switch语句后能够使用的类型只有byte、short、char、int四种。出乎意料的是long型不能作为switch语句的表达式。
instance of运算符
该运算符的使用方法为:对象 instance of 类,返回值为boolean类型。运算规则是:如果左运算数为右运算数(类)的实例,或者是其子类的示例,或者实装了右运算数(接口),运算结果为true,否则为false。
静态方法的覆盖
声明为静态(static)的方法不能被非静态覆盖(override),也不能覆盖其他非静态方法。覆盖只能发生在覆盖方法和被覆盖方法都不是静态方法的时候。而静态方法“覆盖”静态方法,严格来说不属于覆盖,而是被“覆盖”的静态方法被隐藏了。
标识符中能使用的文字
与C语言相似,定义标识符的规则为首字符必须为英文字母或下划线,其后的字符可以为英文字母、下划线或数字。但是,在Java中,$符号也可以出现在首字符或者其他的文字。例如,$1为合法标识符。