建議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,運行多次,顯然實例不同,但都會獲得相同的三個隨機數。