Java開發之基本數據類型使用相關建議

建議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個產品

    2147483647int類型的最大值,再加上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參數在-128127之間,則直接從整型池中獲得對象,不在該範圍的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,運行多次,顯然實例不同,但都會獲得相同的三個隨機數。

結論:若非必要,不要設置隨機數種子

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章