建议1:用偶判断,不用奇判断
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
//接收键盘输入参数
Scanner scanner = new Scanner(System.in);
System.out.println("请输入多个数字判断奇偶性:");
while (scanner.hasNextInt()) {
int i = scanner.nextInt();
String str = i + "->" + (i % 2 == 1 ? "奇数" : "偶数");
System.out.println(str);
String str2 = i + "->" + (remainder(i, 2) == 1 ? "奇数" : "偶数");
System.out.println(str2);
System.out.println(remainder(i, 2));
}
}
//Java的取余(%)算法模拟代码
public static int remainder(int dividend, int divisor) {
return dividend - dividend / divisor * divisor;
}
}
当输入的数值为负奇数时(如:-1),该方法得到的结果却是偶数。
正确判断应该是:i % 2 == 0? "偶数" : "奇数"
建议2:用整数类型处理货币
public class Client {
public static void main(String[] args) {
System.out.println(10.00 - 9.60);
}
}
得出结果:0.40000000000000036。利用乘2取整,顺序排列
法,不能将结果0.4从十进制小数转换为二进制小数。
解决办法:
- 使用
Bigdecimal
:常用于金融行业 - 使用整型:一般在非金融行业应用较多
建议3:不要让类型默默转换
问题:光速每秒30万公里,根据光线旅行的时间,计算月亮与地球、太阳与地球之间的距离
public class Client {
//光速:30万公里每秒,相当于30*10000*1000米每秒
public static final int LIGHT_SPEED = 30 * 10000 * 1000;
public static void main(String[] args) {
System.out.println("题目1:月亮光照射到地球需要1秒,计算月亮和地球的距离:");
long dis1 = LIGHT_SPEED * 1;
System.out.println("月亮与地球的距离为:" + dis1);
System.out.println("题目2:月亮光照射到地球需要8分钟,计算月亮和地球的距离:");
long dis2 = LIGHT_SPEED * 60 * 8;
System.out.println("月亮与地球的距离为:" + dis2);
}
}
输出结果如下:
题目1:月亮光照射到地球需要1秒,计算月亮和地球的距离:
月亮与地球的距离为:300000000
题目2:太阳光照射到地球需要8分钟,计算月亮和地球的距离:
太阳与地球的距离为:-2028888064
太阳与地球的距离竟然是负值,这是因为Java是先运算然后再进行类型转换的,即先计算LIGHT_SPEED * 60 * 8
的值,这时计算出的值已经超过int的最大值,所以就成了负值,最后再转换成long型,结果还是负值。
解决这类问题的办法是:主动声明式类型转换,如long dis2 = 1L * LIGHT_SPEED * 60 * 8;
。
结论:基本类型转换时,使用主动声明方式减少不必要的Bug。
建议4:边界,边界,还是边界
import java.util.Scanner;
public class Client {
//一个会员拥有产品的最多数量
public static final int LIMIT = 2000;
public static void main(String[] args) {
//会员当前拥有的产品数量
int cur = 1000;
Scanner input = new Scanner(System.in);
System.out.println("请输入预订的数量:");
while (input.hasNextInt()) {
int order = input.nextInt();
//当前拥有的与准备订购的产品数量之和
if (order > 0 && (order + cur) <= LIMIT) {
System.out.println("你已经成功预订" + order + "个产品");
} else {
System.out.println("超过限额,预订失败!");
}
}
}
}
可以得到如下结果:
请输入预订的数量:
800
你已经成功预订800个产品
2147483647
你已经成功预订2147483647个产品
2147483647
是int类型
的最大值,再加上1000就超出了int
的范围了,其结果是-2147482649
,当然是小于2000了!原因就在于:数字越界使校验条件失效。
在单元测试中,有一项测试叫做边缘测试(或临界测试),如果一个方法接收的是int类型
的参数,那以下三个值是必测的:0
、正最大
、负最小
,其中正最大和负最小是边界值,如果这三个值都没有问题,方法才是比较安全可靠的。
建议5:不要让四舍五入亏了一方
场景:银行结息方式
场景:银行结息方式
- 四舍。舍弃的数值:0.000、0.001、0.002、0.003、0.004
- 五入:进位的数值:0.005、0.006、0.007、0.008、0.009
因为舍弃和进位的数字是在0到9之间均匀分布的,所以对于银行家来说,每10笔存款的利息因采用四舍五入而获得的盈利是:0.000+0.001+0.002+0.003+0.004-0.005-0.00-60.007-0.008-0.009=-0.005
,也就是说,每10笔的利息计算中就损失0.005元,即每笔利息计算损失0.005元。
这个算法误差是由美国银行家发现的,并对此提出了一个修正算法:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一
。
Java 5以上提供的舍入法则:直接使用RoundingMode类提供的Round模式即可
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Client {
public static void main(String[] args) {
//存款
BigDecimal d = new BigDecimal(888888);
//月利率,乘3计算季利率
BigDecimal r = new BigDecimal(0.0018 * 75 * 3);
//计算利息,setScale方法设置精度和舍入法则
BigDecimal i = d.multiply(r).setScale(2, RoundingMode.HALF_EVEN);
System.out.println("季利息是:" + i);
}
}
目前Java支持的舍入方式
- ROUND_UP:远离零方向舍入,只要舍弃位非零即进位
- ROUND_DOWN:趋向零方向舍入,所有的位都舍弃,不存在进位情况
- ROUND_CEILING:向正无穷方向舍入,向正最大方向靠拢
- ROUND_FLOOR:向负无穷方向舍入,向负无穷方向靠拢
- ROUND_HALF_UP:最近数字舍入(5进),经典四舍五入法则
- ROUND_HALF_DOWN:最近数字舍入(5舍)
- ROUND_HALF_EVEN:银行家算法
结论:根据不同的场景,慎重选择不同的舍入模式,以提高项目的精准度,减少算法损失。
建议6:提防包装类型的null值
import java.util.ArrayList;
import java.util.List;
public class Client {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
//Exception in thread "main" java.lang.NullPointerException
list.add(null);
System.out.println(f(list));
}
public static int f(List<Integer> list) {
int count = 0;
for (int i : list) {
count += i;
}
return count;
}
}
结论
- 包装类型参与运算时,要做null值校验
- 包装类型作为函数实参传递给基本类型的形参,如果传入的参数是null,同样会报空指针异常
建议7:谨慎包装类型的大小比较
public class Client {
public static void main(String[] args) {
Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.println(i == j);
System.out.println(i > j);
System.out.println(i < j);
}
}
Java中的判断
==
:用来判断两个操作数是否相等,基本数据类型判断值是否相等,对象则判断引用的地址是否相等>
、<
:用来判断两个数字类型的大小关系,注意只能是数字类型,对于Integer等包装类型,是根据其intValue()
方法的返回值(即相对应的基本类型)进行比较的,很显然,两者不可能有大小关系的。
解决办法:直接使用Integer
实例的compareTo()
方法即可
建议8:优先使用整型池
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入一个整数数字:");
while (input.hasNextInt()) {
int ii = input.nextInt();
System.out.println("\n======" + ii + "的相等判断======");
//通过new产生的Integer对象
Integer i = new Integer(ii);
Integer j = new Integer(ii);
System.out.println("new产生的对象:" + (i == j));
//基本类型转换位包装类型后比较
i = ii;
j = ii;
System.out.println("基本类型转换的对象:" + (i == j));
//通过静态方法生成一个实例
i = Integer.valueOf(ii);
j = Integer.valueOf(ii);
System.out.println("valueOf产生的对象:" + (i == j));
}
}
}
输出结果:
127
====== 127 的相等判断 ======
new产生的对象:false
基本类型转换的对象:true
valueOf产生的对象:true
128
====== 128 的相等判断 ======
new产生的对象:false
基本类型转换的对象:false
valueOf产生的对象:false
555
====== 555 的相等判断 ======
new产生的对象:false
基本类型转换的对象:false
valueOf产生的对象:false
源码
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
通过valueOf()
产生包装对象时,如果int
参数在-128
和127
之间,则直接从整型池中获得对象,不在该范围的int
类型则通过new生成包装对象。
结论:通过包装类的valueOf生成包装实例可以显著提高空间和时间性能
建议9:优先选择基本类型
public class Client {
public static void main(String[] args) {
Client client = new Client();
int i = 140;
//分别传递int类型和Integer类型
client.f(i);
client.f(Integer.valueOf(i));
}
public void f(long a) {
System.out.println("基本类型的方法被调用");
}
public void f(Long a) {
System.out.println("引用类型的方法被调用");
}
}
输出结果:
基本类型的方法被调用
基本类型的方法被调用
分析
自动装箱重要原则:基本类型可以先加宽,再转变成宽类型的包装类型,但不能直接转变成宽类型的包装类型。
结论:基本类型优先考虑
基本类型优先考虑。
建议10:不要随便设置随机种子
import java.util.Random;
public class Client {
public static void main(String[] args) {
Random random = new Random(1000);
for (int i = 0; i < 4; i++) {
System.out.println("第" + i + "次:" + random.nextInt());
}
}
}
输出结果
第0次:-1244746321
第1次:1060493871
第2次:-1826063944
第3次:1976922248
分析:每次执行时产生的结果都是相同的
在Java中,随机数的产生取决于种子,随机数和种子之间的关系遵从以下两个规则:
- 种子不同,产生不同的随机数
- 种子相同,即使实例不同也产生相同的随机数
new Random(1000)
显式地设置了随机种子为1000,运行多次,显然实例不同,但都会获得相同的三个随机数。