不積跬步,無以致千里;不積小流,無以成江海。 --荀子《勸學篇》java
建議21:用偶判斷,不用奇判斷算法
建議22:用證書類型處理貨幣數據庫
建議23:不要讓類型默默轉換安全
建議24:邊界仍是邊界dom
建議25:不要讓四捨五入虧了一方函數
建議26:提防包裝類型的null值性能
建議27:謹慎包裝類型額大小比較單元測試
建議28:優先使用整形池測試
建議29:優先選擇基本類型網站
建議30:不要隨便設置隨機種子
建議21:用偶判斷,不用奇判斷
判斷一個數是奇數仍是偶數是小學裏的基本知識,可以被2整除的整數是偶數,不能被2整除的數是奇數,這規則簡單明瞭,還有什麼可考慮的?好,咱們來看一個例子,代碼以下:
import java.util.Scanner; public class Client21 { public static void main(String[] args) { // 接收鍵盤輸入參數 Scanner input = new Scanner(System.in); System.out.println("輸入多個數字判斷奇偶:"); while (input.hasNextInt()) { int i = input.nextInt(); String str = i + "-->" + (i % 2 == 1 ? "奇數" : "偶數"); System.out.println(str); } } }
輸入多個數字,而後判斷每一個數字的奇偶性,不能被2整除的就是奇數,其它的都是偶數,徹底是根據奇偶數的定義編寫的程序,咱們開看看打印的結果:
輸入多個數字判斷奇偶:1 2 0 -1 -2 1-->奇數 2-->偶數 0-->偶數 -1-->偶數 -2-->偶數
前三個還很靠譜,第四個參數-1怎麼多是偶數呢,這Java也太差勁了吧。
看到這段程序,你們都會心的笑了,原來Java這麼處理取餘計算的呀,根據上面的模擬取餘可知,當輸入-1的時候,運算結果爲-1,固然不等於1了,因此它就被斷定爲偶數了,也就是咱們的判斷失誤了。問題明白了,修正也很簡單,改成判斷是不是偶數便可。代碼以下: i % 2 == 0 ? "偶數" : "奇數";
注意:對於基礎知識,咱們應該"知其然,並知其因此然"。
建議22:用證書類型處理貨幣
在平常生活中,最容易接觸的小數就是貨幣,好比,你付給售貨員10元錢購買一個9.6元的零食,售貨員應該找你0.4元,也就是4毛錢纔對,咱們來看下面的程序:
public class Client22 { public static void main(String[] args) { System.out.println(10.00-9.60); } }
咱們的指望結果是0.4,也應該是這個數字,可是打印出來的倒是:0.40000000000000036,這是爲何呢?
這是由於在計算機中浮點數有可能(注意是有可能)是不許確的,它只能無限接近準確值,而不能徹底精確。爲何會如此呢?這是由浮點數的存儲規則所決定的,咱們先來看看0.4這個十進制小數如何轉換成二進制小數,使用"乘2取整,順序排列"法,咱們發現0.4不能使用二進制準確的表示,在二進制數世界裏它是一個無限循環的小數,就像在十進制的世界裏沒有辦法惟一準確表示1/3。
public class Client22 { public static void main(String[] args) { NumberFormat f = new DecimalFormat("#.##"); System.out.println(f.format(10.00-9.60)); } }
打印出的結果是0.4,看似解決了。可是隱藏了一個很深的問題。咱們來思考一下金融行業的計算方法,會計系統通常記錄小數點後的4爲小數,可是在彙總、展示、報表中、則只記錄小數點後的2位小數,若是使用浮點數來計算貨幣,想一想看,在大批量加減乘除後結果會有很大的差距(其中還涉及到四捨五入的問題)!會計系統要求的就是準確,可是由於計算機的緣故不許確了,那真是罪過,要解決此問題有兩種方法:
一、使用BigDecimal
BigDecimal是專門爲彌補浮點數沒法精確計算的缺憾而設計的類,而且它自己也提供了加減乘除的經常使用數學算法。特別是與數據庫Decimal類型的字段映射時,BigDecimal是最優的解決方案。
代碼實例
聲明BigDecimal對象的時候必定要使用它構造參數爲String的類型的構造器。
二、使用整型
把參與運算的值擴大100倍,並轉爲整型,而後在展示時再縮小100倍,這樣處理的好處是計算簡單,準確,通常在非金融行業(如零售行業)應用較多。此方法還會用於某些零售POS機,他們輸入和輸出的所有是整數,那運算就更簡單了.
注:乘2取整,順序排列
十進制小數轉換成二進制小數採用"乘2取整,順序排列"法。具體作法是:用2乘十進制小數,能夠獲得積,將積的整數部分取出,再用2乘餘下的小數部分,又獲得一個積,再將積的整數部分取出,如此進行,直到積中的小數部分爲零,此時0或1爲二進制的最後一位。或者達到所要求的精度爲止。
0.96875 X 2 1.9375 取1後變成 0.9375 (如下省略不寫了) X 2 1.875 取1 X 2 1.75 取1 X 2 1.5 取1 X 2 1.0 取1後變成 0,結束轉換 0.96875=(0.11111)2
建議23:不要讓類型默默轉換
咱們作一個小學生的題目,光速每秒30萬千米,根據光線的旅行時間,計算月球和地球,太陽和地球之間的距離。代碼以下:
public class Client23 { // 光速是30萬千米/秒,常量 public static final int LIGHT_SPEED = 30 * 10000 * 1000; public static void main(String[] args) { System.out.println("題目1:月球照射到地球須要一秒,計算月亮和地球的距離。"); long dis1 = LIGHT_SPEED * 1; System.out.println("月球與地球的距離是:" + dis1 + " 米 "); System.out.println("-------------------------------"); System.out.println("題目2:太陽光照射到地球須要8分鐘,計算太陽到地球的距離."); // 可能要超出整數範圍,使用long型 long dis2 = LIGHT_SPEED * 60 * 8; System.out.println("太陽與地球之間的距離是:" + dis2 + " 米"); } }
Java是先運算而後再進行類型轉換的,具體的說dis2的三個運算參數都是int型,三者相乘結果也是int型,可是超出了int的最大值,就變成負值了,越界了從新開始。
而dis3中int60變爲了long60L,長整型,(Java基本轉換規則,向數據範圍大的方向轉換,也叫加寬類型),在尚未超出int型範圍的時候就已經轉化成long型了,解決了越界問題,實際開發中,更通用的作法是主動聲明類型轉化,而不是強制類型轉換,
long dis2 = 1L * LIGHT_SPEED * 60L * 8
既然指望的結果是long型,那就讓第一個參與的參數也是Long(1L)吧,也就說明"嗨"我已是長整型了,大家都跟着我一塊轉爲長整型吧。
注意:基本類型轉換時,使用主動聲明方式減小沒必要要的Bug。
建議24:邊界仍是邊界
某商家生產的電子產品很是暢銷,須要提早30天預訂才能搶到手,同時還規定了一個會員可擁有的最多產品數量,目的是爲了防止囤積壓貨肆意加價。會員的預訂過程是這樣的:先登陸官方網站,選擇產品型號,而後設置須要預訂的數量,提交,符合規則即提示下單成功,不符合規則提示下單失敗,後臺的處理模擬以下:
public class DynamicCompileDemo { // 一個會員擁有產品的最多數量 public final static 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) { cur = cur + order; System.out.println("你已經成功預約:" + cur + " 個產品"); } else { System.out.println("超過限額,預約失敗!"); } } } }
居然預約了-2147481829 個產品,真的是奇葩啊。
看着2147483647這個數字很眼熟?那就對了,這個數字就是int類型的最大值,沒錯,有人輸入了一個最大值,使校驗條件失敗了,Why?咱們來看程序,order的值是2147483647那再加上1000就超出int的範圍了,其結果是-2147482649,那固然是小於正數2000了!一句歸其緣由:數字越界使校驗條件失效。
在單元測試中,有一項測試叫作邊界測試(也叫臨界測試),若是一個方法接收的是int類型的參數,那麼如下三個值是必須測試的:0、正最大、負最小,其中正最大、負最小是邊界值,若是這三個值都沒有問題,方法纔是比較安全可靠的。咱們的例子就是由於缺乏邊界測試,導致生產系統產生了嚴重的誤差。
建議25:不要讓四捨五入虧了一方
System.out.println("10.5近似值: "+Math.round(10.5)); System.out.println("-10.5近似值: "+Math.round(-10.5));
輸出結果爲:10.5近似值: 11 -10.5近似值: -10
銀行家舍入(Banker's Round)的近似算法:
四捨六入五考慮,五後非零就進一,五後爲零看奇偶,五前爲偶應捨去,五前爲奇要進一。
咱們舉例說明,取2位精度;
round(10.5551) = 10.56 round(10.555) = 10.56 round(10.545) = 10.54
注意:根據不一樣的場景,慎重選擇不一樣的舍入模式,以提升項目的精準度,減小算法損失。
建議26:提防包裝類型的null值
包裝類型參與運算時,要作null值校驗。
建議27:謹慎包裝類型額大小比較
建議28:優先使用整形池
一、valueOf()返回的是原對象
二、valueOf()方法返回的是如今到1970年1月1日00:00:00的數值類型的毫秒數
三、包裝對象的valueOf()方法返回該包裝對象對應的原始值
四、裝箱動做是經過valueOf方法實現的
經過valueOf產生包裝對象時,若是int參數在-128到127之間,直接從整型池中獲取對象,不在範圍內的int類型則經過new生成包裝類型。
127的包裝對象直接從整型池中得到,而12八、555超出了整型池範圍,是經過new產生的一個新對象,地址不一樣,==對比就不一樣了。
整型池的存在提升了系統性能,節約了內存空間,也就是在生成包裝對象的時候使用valueOf生成,而不是經過構造函數來生成的緣由。
順便說一下,在判斷對象釋放相等的時候,最好使用equals方法,避免使用==產生非預期的效果。
這個的實際意思是否是就是:
使用Integer.valueOf(1)比使用Integer i = new Interger(1);
系統性能好,更節約內存空間。
建議29:優先選擇基本類型
建議30:不要隨便設置隨機種子
一、什麼是隨機種子?
隨機種子是基於隨機數的計算機專業術語,隨機數是以隨機種子做爲初始條件,而後經過必定的算法不停迭代產生的。
在Java中,隨機數的產生取決於種子,隨機數與種子之間的關係:
① 種子不一樣,產生不一樣的隨機數
② 種子相同,即便實例不一樣也會產生相同的隨機數。
二、設置隨機種子的兩種方法:
若設置隨機種子則相同的種子,產生的隨機數相同。若不設置則每次隨機的不一樣。 Random rnd = new Random(); rnd.setSeed()用於設置種子。 rnd.nextInt() 用於產生隨機數。 rnd.nextInt(10); // 0-10之間。
Random r = new Random(1000);//1000就是一個隨機種子 for(int i=1; i<=4; i++){ System.out.println("第"+i+"次:"+r.nextInt()); }
三、Java中獲取隨機數的兩種方式
java.util.Random類得到隨機數和Math.random()。