今天來聊聊Java中跟數(shù)值處理相關(guān)的兩個(gè)類型Integer和BigDecimal。 說起這兩個(gè)類型,我們肯定都不陌生,但是其中有些容易踩到的坑需要注意避讓。
Integer
整型我們應(yīng)該每天都會用到,但是每種語言還是有自己的特性。從敬姐剛從.NET轉(zhuǎn)過來的時(shí)候踩過的一個(gè)坑說起:話說在.NET世界中,數(shù)值的基本類型和包裝類型是會自動轉(zhuǎn)換的,所以數(shù)值比較很自然地就會使用 a==b,但是到j(luò)ava這卻行不通了,頓時(shí)一臉懵。
數(shù)值比較及自動裝箱
@Test
public void Interger(){
Integer x = 127;
Integer y = 127;
Integer m = 99999;
Integer n = 99999;
System.out.println("x == y: " + (x==y));
System.out.println("m == n: " + (m==n));
System.out.println("x.equals(y): " + x.equals(y));
System.out.println("m.equals(n): " + m.equals(n));
//執(zhí)行結(jié)果
// x == y: true
// m == n: false
// x.equals(y): true
// m.equals(n): true
}
仔細(xì)觀察可以發(fā)現(xiàn),==比較,較小的兩個(gè)相同的Integer返回true,較大的兩個(gè)相同的Integer返回false,這是為何呢?
一起看一下Integer類的源碼,發(fā)現(xiàn)其中的IntegerCache類。這是Java為了節(jié)省空間、提升性能采取的優(yōu)化機(jī)制,常量池的大小為一個(gè)字節(jié)(-128~127)。
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer[] cache;
static Integer[] archivedCache;
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
h = Math.max(parseInt(integerCacheHighPropValue), 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
// Load IntegerCache.archivedCache from archive, if possible
CDS.initializeFromArchive(IntegerCache.class);
int size = (high - low) + 1;
// Use the archived cache if it exists and is large enough
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = low;
for(int i = 0; i < c.length; i++) {
c[i] = new Integer(j++);
}
archivedCache = c;
}
cache = archivedCache;
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
而對于valueOf(int i)方法,直接使用了常量池IntegerCache
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
所以當(dāng)遇到 Integer x = 127; 時(shí),會進(jìn)行自動裝箱,調(diào)用的是:
Integer x = Integer.valueOf(127);
為了節(jié)省內(nèi)存,Integer.valueOf()對于較小的數(shù),始終返回相同的實(shí)例,因此,比較“恰好”為true。但我們絕不能因?yàn)镴ava標(biāo)準(zhǔn)庫的Integer內(nèi)部有緩存優(yōu)化就用比較,必須用equals()方法比較兩個(gè)Integer。
創(chuàng)建實(shí)例
因?yàn)镮nteger.valueOf()可能始終返回同一個(gè)Integer實(shí)例,因此,在我們自己創(chuàng)建Integer的時(shí)候,以下兩種方法:
方法1:Integer n = new Integer(100);
方法2:Integer n = Integer.valueOf(100);
方法2更好,因?yàn)榉椒?總是創(chuàng)建新的Integer實(shí)例,方法2把內(nèi)部優(yōu)化留給Integer的實(shí)現(xiàn)者去做,即使在當(dāng)前版本沒有優(yōu)化,也有可能在下一個(gè)版本進(jìn)行優(yōu)化。
我們把能創(chuàng)建“新”對象的靜態(tài)方法稱為靜態(tài)工廠方法。Integer.valueOf()就是靜態(tài)工廠方法,它盡可能地返回緩存的實(shí)例以節(jié)省內(nèi)存。簡而言之:創(chuàng)建新對象時(shí),優(yōu)先選用靜態(tài)工廠方法而不是new操作符。
上個(gè)看個(gè)有點(diǎn)特別的例子:
@Test
public void instance() {
//每次創(chuàng)建一個(gè)新實(shí)例
Integer a1 = new Integer(100);
Integer a2 = new Integer(100);
Assert.assertFalse(a1 == a2);
//add
Integer a3 = new Integer(200);
//注意這里嘍?。? Assert.assertTrue(a1 + a2 == 200);
Assert.assertTrue(a1 + a2 == a3);
}
因?yàn)?這個(gè)操作符不適用于 Integer 對象,首先 a1 和 a2 進(jìn)行自動拆箱操作,進(jìn)行數(shù)值相加,即a3 == 40。
BigDecimal
BigDecimal適合商業(yè)計(jì)算場景,用來對超過16位有效位的數(shù)進(jìn)行精確的運(yùn)算。
Double轉(zhuǎn)換為BigDecimal
我們在使用BigDecimal時(shí),為了防止精度丟失,推薦使用它的 BigDecimal(String) 構(gòu)造方法來創(chuàng)建對象。
@Test
public void double2decimal() {
Double d = 0.1d;
System.out.println(new BigDecimal(d));//0.1000000000000000055511151231257827021181583404541015625
System.out.println(new BigDecimal(d.toString()));//0.1
System.out.println(BigDecimal.valueOf(d));//0.1
}
保留幾位小數(shù)
通過 setScale方法設(shè)置保留幾位小數(shù)以及保留規(guī)則。
@Test
public void decimalTest() {
BigDecimal a = new BigDecimal("1.2345");
System.out.println(a.toString());
//BigDecimal保留幾位小數(shù)
BigDecimal b = a.setScale(3, RoundingMode.HALF_DOWN);
System.out.println(b.toString());
}
BigDecimal 值比較
BigDecimal的等值比較應(yīng)該使用compareTo()方法,而不是equals()方法。
/**
* BigDecimal等值比較
* equals:既比較數(shù)值,又比較精度;
* compareTo:僅比較數(shù)值
*/
@Test
public void compare() {
BigDecimal a = BigDecimal.valueOf(1);
BigDecimal b = BigDecimal.valueOf(1.00);
Assert.assertFalse(a.equals(b));
Assert.assertEquals(0, a.compareTo(b));
}
調(diào)試一下BigDecimal的equals和compareTo方法,發(fā)現(xiàn)equals()方法會比較精度,但是compare()方法不會。


BigDecimal 除法
BigDecimal.divide(),除法運(yùn)算注意要設(shè)置精度,否則在除不盡的情況下會拋異常。
@Test
public void divide(){
BigDecimal a=BigDecimal.valueOf(1);
BigDecimal b=BigDecimal.valueOf(3);
//直接拋異常
// System.out.println(a.divide(b));
//正常返回 0.3333
System.out.println(a.divide(b,4,RoundingMode.HALF_EVEN));
}
代碼示例
文示例代碼參考:jing-yes-java (https://github.com/cathychen00/jing-yes-java/tree/master/jing-yes-j2se/src/test/java/com/jingyes/j2se/tests)
本人公眾號[ 敬YES ]同步更新,歡迎大家關(guān)注~

作者:陳敬(公眾號:敬YES)
出處:http://www.rzrgm.cn/janes/
博客文章僅供交流學(xué)習(xí),請勿用于商業(yè)用途。如需轉(zhuǎn)載,請務(wù)必注明出處。
浙公網(wǎng)安備 33010602011771號