原文发表于网易博客 2008-08-22 21:09:22
1.toString()
一个char型的非空的数组array和字符串b被用连接符"+"连接起来时,形成的新字符串实际上是array.toString()+b. 一个非空的char型数组是从Object那里集成的toString方法,返回一个字符串,它包含该对象所属类的名字,'@'符号,以及表示对象散列码的一个无符号十六进制整数.而Class.getName的规范描述到:char[]类型的类对象调用该方法返回的结果字符串是"[c".
2. ==
在Java语言中,"=="用来判断两个对象引用是否相同,即这两个对象引用是否正好引用到同一个对象上.
字符连接符号"+"的优先级比等号"=="要高.
3.位移与数值类型
在java中,一个int型的值左移32位结果并不是0,而是它本身.long型的也一样.
在移位过程中,java总会保证左移不会被执行成右移,或者右移执行成左移.例如对以一个int型的数i,对i执行左移n(n也是int类型)位操作时,只使用到n转换成二进制数字的低5位,并且是当作正数来使用.因此,如果要讲i左移-1位,那么实际上是左移了31位.
用一个double或float数值来表示无穷大是可以的.对于float而言,加1不会产生任何效果的最小级数是2的25次方,即3355432;对于double而言,,最小级数是2的54次方,大约是1.8E16.
一般来说,一个基本类型变量i是等于它本身的,不过,有特殊情况存在,即i是一个NaN(Not a Number)时,i是不等于自身的.所以i-i==0产生的结果是false;
一旦一个计算产生了NaN,它就被损坏了,没有任何更进一步的计算可以修复这样的损坏.
要让i!=i+0,除了i是NaN以外,还可以是String类型.
>>>=是无符号右移.对于short,byte,char类型的数据i来说,i>>=1操作会先将i提升位int类型.执行的是有符号扩展.右移1位侯,如果原先是short i=-1;则提升侯的int类型值是0xffffffff,移位后变成0x7fffffff,最后这个值被存回了i,高16位被截去,就剩下oxffff,这样,又回到了起点,i的值没有发生任何变化.因此,我们得出的结论是:不要在short,byte,char类型的变量上使用符合赋值运算符.
在5.0版本前,Java的数字比较操作符(<,<=,>,>=)要求他们的两个操作数都是原始数字类型(byte,char,short,int,long,float,double).在5.0版本中又做出了修改:每一个操作数的类型必须可以转换成原始数字类型.这样,被包装了的数字类型(Byte,Character,Short,Integer,Long,Float,Double)都可以作为数字比较运算符的操作数了.因此要使(i<=j&&j<=i&&i!=j)这个式子的值位false,则可以这样声明i和j:Integer i= new Integer(0);Integer j=new Integer(0);注意,当用"=="比较两个非原始数据类型的变量时,实际上是看这两个变量是否引用了相同的对象.
由于整数是以反码的形式在内存中存储的,int占32个bit,共有2的32次方个int数,是偶数个,但是+0和-0只有一种表示方法,因此正整数和负整数的个数是不相等的,负整数比正整数多了一个,即Integer.MIN_VALUE,即0x80000000.对这个值求反码,结果是0x7fffffff,再加1,得到的还是它本身,因此,Integer.MIN_VALUE的反码是它本身.所以如果int i=Integer.MAX_VALUE;则i=-i;成立.
4.变量声明
Java语言规范不允许一个本地变量声明语句作为一条语句在for,while或者dowhile循环中重复执行.因此下面的语句:
for(int i=0;i<=100;i++)
SomeClass class=new SomeClass();
这样的语句根本就不能编译通过.一个本地变量声明作为一条语句只能直接出现在一个语句块中.(一个语句块是由一对花括号以及包含这对花括号中的语句和声明构成的.)因此,要解决上面所产生的问题,可以把声明放进语句块中,如下:
for(int i=0;i<=100;i++){
SomeClass class=new SomeClass();
}
5.封装类
String,BigInteger,BigDecimal,Integer,Long,Short,Byte,Character,Boolean,Float,Double这些包装了的类的实例的值是不可改变的,尽管你可以让这些实例重新指向另外的类对象.例如String型的s,再其调用了toUpperCase()方法之后,s的值依然不会发生改变.调用函数只会返回一个临时的大写化了的字符串.
6.equals
java语言规范约定,无论何时,只要你复写了类的equals方法,你就必须同时复写hashCode方法.hashCode约定要求相等的对象要具有相同的散列码.从Object继承而来的equals方法的原型为:public boolean equals(Object o),复写equals方法时,一定要遵照Object中方法的原型.如何你自己定义的是public boolean equals(SomeClass class)之类的原型,这就属于方法重载,而不是方法复写.就不会达到想要的效果.再jdk5.0版本之后,可以再要复写的方法声明上添加@override 注释,例如:@override public Boolean equals(Object o){...} 这样写了之后,如果你写的这个函数不属于重写,编译就不会通过.
7.数值不同的进制表示
以0开头的整形字面常量将被解释成为八进制数值.这个隐晦的结构时从C语言遗留下来的.例如int i=012;实际上是把10赋值给了i.
8.日期
Date类将一月表示为0,而Calendar类延续了这个错误.因此,想将一个日历类Calendar的对象cal设置为99年12月31日,如果写成cal.set(1999,12,31),实际上它会产生一个IllegalArgumentException异常.但是不会报错,而是自动将日期转换为2000年1月31日.而Date的getDay方法返回的是一个表示星期几的日期,而不是月份日期.相当于Calendar的get(Calendar.DAY_OF_WEEK)方法.如果想得到月份日期,可以直接调用Canlendar的get(Calendar.DAY_OF_MONTH)方法.
9.数学函数
Math.abs(int i)一般都能返回正确的参数的绝对值,不过,有个以外,如果i的值为Integer.MIN_VALUE,那么这个函数将会返回它本身.
10.包的约束
一个包内的私有方法不能被位于另外一个包中的某个方法直接复写.
11.运算符
条件运算符(? : )在java5.0版本以前是非常受限制的,当第二个和第三个操作数是引用类型时,条件运算符要求它们其中的一个必须是另一个的子类型.而到了5.0版本,条件运算符在第二个和第三个操作数是引用类型时总是合法的.
名字重用的术语表
1. 复写(override) : 一个实例方法可以复写在其超类中可访问到的具有相同签名的所有实例方法,从而使得动态分配成为了可能.VM将基于实例的运行期类型来选择要调用的复写方法.复写是面向对象编程技术的基础.
2. 隐藏(hide) : 一个域,静态方法或成员类型可以分别隐藏(hide)在其超类中可以访问到的具有相同名字(对方法而言就是相同的方法签名)的所有域,静态方法或成员类型.隐藏一个成员将阻止其被继承.
3. 重载(overload) : 在一个类中重载(overload)一个方法,只要它们具有相同的名字和不同的签名.
4. 遮蔽(shadow) : 一个变量,方法或类型可以分别遮蔽在一个闭合的文本范围内的具有相同名字的所有变量,方法或类型.对于变量来说,类型于全局变量和局部变量的问题.在函数中,局部变量会遮蔽全局变量.
5. 遮掩(obscure) : 一个变量可以遮掩(obscure)具有相同名字的一个类型,只要它们都在同一个范围内:如果这个名字被用于变量与类型都被许可的范围,那么它将被引用到变量上.相似地,一个变量或者一个类可以遮掩一个包.遮掩是唯一一种两个名字位于不同名字空间的名字重用形式,这些名字空间包括:变量,包,方法和类型.如果一个类型或者一个包被遮掩了,那么不能通过其简单名引用到它,除非是语法只允许在其名字空间中出现一种名字.遵守命名规范就能极大地消除遮掩的可能性.