1-0.9=0.1是天經地義的,但在計算機的世界裏,0.1恰恰是無法精確表示的一個小數,只有2的冪次倍小數才能夠精確表示,如:0.5、0.25、0.125等。由於0.1是近似表達,在各種情形中的計算存在數位的取捨精度不一樣,所以1-0.9未必等於0.9-0.8,所以浮點數之間的等值判斷,基本數據類型不能用==來比較,包裝數據類型不能用equals來判斷。包裝類型運算也是調用的浮點類型的計算
public class FloatPrimitiveTest {
public static void main(String[] args) {
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
if (a == b) {
System.out.println("true");
} else {
System.out.println("false");
}
}
}
輸出------------false
public class FloatWrapperTest {
public static void main(String[] args) {
Float a = Float.valueOf(1.0f - 0.9f);
Float b = Float.valueOf(0.9f - 0.8f);
if (a.equals(b)) {
System.out.println("true");
} else {
System.out.println("false");
}
}
}
輸出--------false
正例--------------------------------
(1) 指定一個誤差範圍,兩個浮點數的差值在此範圍之內,則認爲是相等的。
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
float diff = 1e-6f;
if (Math.abs(a - b) < diff) {
System.out.println("true");
}
(2) 使用 BigDecimal 來定義值,再進行浮點數的運算操作。
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);
if (x.equals(y)) {
System.out.println("true");
}
當 switch 括號內的變量類型爲 String 並且此變量爲外部參數時,必須先進行 null判斷。
public class SwitchTest {
public static void main(String[] args) {
String param = null;
switch (param) {
case "null":
System.out.println("null");
break;
default:
System.out.println("default");
}
}
}
拋出NPE異常
爲了防止精度損失,禁止使用構造方法 BigDecimal(double)的方式把 double 值轉化爲 BigDecimal 對象。
BigDecimal(double)存在精度損失風險,在精確計算或值比較的場景中可能會導致業務邏輯異常。
如:BigDecimal g = new BigDecimal(0.1f); 實際的存儲值爲:0.10000000149
優先推薦入參爲 String 的構造方法,或使用 BigDecimal 的 valueOf 方法,此方法內部其實執行了Double 的 toString,而 Double 的 toString 按 double 的實際能表達的精度對尾數進行了截斷
推薦使用的方式
BigDecimal recommend1 = new BigDecimal("0.1");
BigDecimal recommend2 = BigDecimal.valueOf(0.1);
在使用阻塞等待獲取鎖的方式中,必須在 try 代碼塊之外,並且在加鎖方法與 try 代碼塊之間沒有任何可能拋出異常的方法調用,避免加鎖成功後,在 finally 中無法解鎖。
如果 lock 方法在 try 代碼塊之內,可能由於其它方法拋出異常,導致在 finally代碼塊中,unlock 對未加鎖的對象解鎖,它會調用 AQS 的 tryRelease 方法(取決於具體實現類),拋出 IllegalMonitorStateException 異常。在 Lock 對象的 lock方法實現中可能拋出 unchecked 異常。而在使用嘗試機制來獲取鎖的方式中,比如 tryLock(),在進入業務代碼塊之前,必須先判斷當前線程是否持有鎖。
public class LockTest {
private final static Lock lock = new ReentrantLock();
public static void main(String[] args) {
try {
lock.tryLock();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
所以這個unlock不會拋出異常
正確加鎖寫法
Lock lock = new XxxLock();
// ...
lock.lock();
try {
doSomething();
doOthers();
} finally {
lock.unlock();
}