21.用偶判断,不用奇判断
在判断奇偶的情况下,用偶来判断,例:i%2==0?"偶":"奇"
取余基础知识(模拟代码如下):
public static int remainder(int dividend,int divisor){
return dividend-dividend/divisor*divisor;
}
22.用整数类型处理贷币
public
static
void
main
(
String
[]
args
) {
// TODO Auto-generated method stub
System
.out.
println
(10.00-9.60);
}
我们期望的结果是0.4,但打印出来的却是0.40000000000000036
这是因为在计算机中浮点数有可能(注意是可能)是不准确的,它只能无限接近准确值,而不能完全精确。
在程序中,虽然我们可以通过对结果取整来或格式化来处理,如:
NumberFormat
format=
new
DecimalFormat
(
"#.##"
);
System
.out.
println
(format.
format
(10.00-9.60));
但在大批量的加减乘除后结果会有很大的差距。
要解决此问题,有两种方法:
(1)使用BigDecimal:专门为弥补浮点数无法精确认算的缺憾而设计的类,特别是与数据库Decimal类型的字段映身时,BigDecimal是最优的解决方案
(2)使用整型:把参与运算的值扩大100倍,并转变为整型 ,然后在展现时再缩小100倍
23.不要让类型默认转换
此处讲的主要针对一些基本类型之间的转换,需要注意的一点是在java中是先运算然后再进行类型转换的。
例如:public static finat int LIGHT_SPEED=30*1000*1000;
在计算 long dis2=LIGHT_SPEED*60*8; 的时候,计算出来的dis2将会是一个负值。这是因为dis2的三个运算参数都是int类型,三者相乘的结果自然也是int类型,但是已经超过int的最大值(2147483647),越界了,变成了负值,再转换为long型时,结果还是负值。
问题知道了,解决起来也很简单,只要加个小小的“L”即可,long dis2=LIGHT_SPEED*60L*8;在实际开发中,更通用的做法是主动声明式类型转化:long dis2=1L*LIGHT_SPEED*60*8;
总之,基本类型转换时,使用主动声明方式减少不必要的bug
24.边界、边界,还是办界
主要是在一些数值的测试中要考虑到边界值可能出现的情况
25.不要让四舍五入亏了一方
主要说的是银行家舍入法则:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。
编码中可使用RoundingMode类提供的Round模式即可。如此处的银行家舍入法则对应的模式为RoundingMode.HALF_EVEN;
26.提防包装类型的null值
谨记一点:包装类型参与运算时,要做null校验
27.谨慎包装类型 的大小比较
在java中,“==”是用来判断两个操作数是否有相等关系的,如果是基本类型则判断值是否相等,如果是对象则判断是否是一个对象的两个引用,也就是地址是否相等。
引申:
java中的数据类型,可分为两类:
(1).基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean
他们之间的比较,应用双等号(==),比较的是他们的值。
(2).复合数据类型(类)
当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。 JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地 址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。
对于复合数据类型之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的,因为Object的equals方法也是用双等号(==)进行比较的,所以比较后的结果跟双等号(==)的结果相同。
(1).基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean
他们之间的比较,应用双等号(==),比较的是他们的值。
(2).复合数据类型(类)
当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。 JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地 址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。
对于复合数据类型之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的,因为Object的equals方法也是用双等号(==)进行比较的,所以比较后的结果跟双等号(==)的结果相同。
28.优先使用整型池
整型池,在整型的装箱动作valueOf方法中,IntegerCache.cache[i+offset]; 这里的cache是IntegerCache内部类的一个静态数组,容纳的是-128到127之间的Integer对象。能过valueOf产生包装对象时,如果int参数在-128至127之间,则直接从整型池中获得对象,不在该范围的int类型 则通过new生成包装对象。
总结:通过包装类型的valueOf生成包装实例可以显著提高空间和时间性能。
29.优先选择基本类型
自动装箱有一个重要的原则:基本类型可以先加宽,再转变为宽类型的包装类型,但不能直接转变成宽类型的包装类型。
以下程序编译是通不过的:
public
static
void
main
(
String
[]
args
) {
int
i =100;
f(i);
}
public
static
void
f
(
Long
l
){
}
30.不要随便设置随机种子
java.util.Random的有参构造,运行结果,计算机不同输出的随机数不同,但是有一点是相同的:在同一台机器上,甭管运行多少次,所打印的随机数都是相同的,也就是说第一次运行,会打印出三个随机数,第二次运行还是打印出这三个随机数,只要在同一台硬件机器上,就永远都会打印出相同的随机数,问题在哪?
//test1
Random
random=
new
Random
();
for
(
int
j=1;j<4;j++){
System
.out.
println
(
"第"
+j+
"次:"
+random.
nextInt
());
}
//test2 在同一台机器上,不管运行多少次,打印出相同的随机数
Random
r=
new
Random
(1000);
for
(
int
j=1;j<4;j++){
System
.out.
println
(
"第"
+j+
"次:"
+r.
nextInt
());
}
//test3 打印出不相同的随机数
Random
random2=
new
Random
();
for
(
int
j=1;j<4;j++){
System
.out.
println
(
"第"
+j+
"次:"
+random2.
nextInt
(1001));
}
那是因为产生随机数的种子被固定了,在java中,随机数的产生取决于种子,随机数和种子之间的关系遵从以下两个规则:1)种子不同,产生不同的随机数 2)种子相同,即使实例不同也产生相同的随机数。
参看Random的源码,Random类的默认种子(无参构造)是System.nanoTime()的返回值。这个值是距离某一个固定时间点的纳秒数,不同的操作系统和硬件有不同的固定时间点,也就是说不同的操作系统其纳称值是不同的,而同一个操作系统纳秒值也会不同,随机数自然也就不同了。(顺便说下,System.nanoTime不能用于计算日期,那是因为固定的时间点是不确定的,纳秒值可能是负值,这与System.currentTimeMills不同。
而new Random(1000),显示设置了随机种子为1000,运行多次,虽然实例不同,但都会获得相同的三个随机数。另:Math.Random()原理与其相同