那天早上,我照例去公司樓下買咖啡。我掏出手機,對老闆説:“老闆,我錢包裏還有 0 塊錢,能不能先賒一杯?”

老闆瞄了一眼我的餘額頁面:

  • 頁面一:餘額:0
  • 頁面二:餘額:0.00

老闆一臉嚴肅:“小夥子,你這兩個不一樣。”,我當場愣住。“0 不就是 0 嗎?你這不是數學問題,是態度問題吧?”

老闆慢悠悠地説了一句讓我後來在代碼裏反覆咀嚼的話:“數值一樣,不代表記賬方式一樣。”

那一刻,我彷彿聽見了 BigDecimal 在我耳邊冷笑。

問題重現:一行代碼引發的血案

我們先來看那段“罪魁禍首”代碼。

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_數位

輸出結果是:

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_Java_02

是不是很反直覺?

“都是 0 啊兄弟,你倆怎麼就不能好好相處?”

別急,Java 沒瘋,是我們低估了 BigDecimal。

BigDecimal 到底在“較真”什麼?

BigDecimal 不只是“值”,還有“刻度”

BigDecimal 內部,其實由兩部分組成:

  • unscaledValue(未縮放的整數值)
  • scale(小數位數)

我們拆開來看:

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_數位_03

等價於:

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_數位_04

而:

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_Java_05

等價於:

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_數據結構_06

數值相同,但小數位精度不同。這就好比:

  • 0:口袋裏沒錢
  • 0.00:錢包裏有兩位小數精度的餘額,只是剛好是零

在財務系統眼裏,這倆可不是一回事。

equals 為啥這麼“死板”?

我們直接看源碼(簡化版)。

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_Java_07

劃重點:BigDecimal 的 equals,既比較值,也比較 scale。

所以:

  • 0(scale=0)
  • 0.00(scale=2)
  • scale 不同,equals = false

Java 在這裏的設計理念只有一句話:“你既然用 equals,那我就精確到每一位給你算。”

Objects.equals 並沒有“背鍋”

很多同學第一反應是:“是不是 Objects.equals 有問題?”

其實完全不是。

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_數位_08

Objects.equals 本質上只是:

  • 幫你處理 null
  • 最終還是調用 a.equals(b)

所以真正的“裁判”,從頭到尾都是 BigDecimal 的 equals。

一個更扎心的對照實驗

我們來做一組對比。

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_數位_09

但如果換一種方式:

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_數位_10

是不是瞬間清醒了?

equals vs compareTo,本質差異在哪?

我給你整理了一張“面試必背級別”的表。

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_數據結構_11

一句話總結:equals 是“格式敏感型選手”,compareTo 是“結果導向型選手”。

生活類比:記賬本 vs 心算

把 BigDecimal 想成兩種人:

  • equals:會計
  • “你給我的是 0 還是 0.00?憑證不一樣,我就不認。”
  • compareTo:老闆
  • “別跟我扯格式,你就説是不是零。”

業務判斷裏,你通常更像老闆;在數據結構(Set / Map)裏,Java 更像會計。

HashMap 裏的“隱形大坑”

如果你用 BigDecimal 作為 Map 的 key:

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_數位_12

結果是:

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_數位_13

這意味着什麼?在 HashMap 眼裏,0 和 0.00 是兩個完全不同的 key。

如果這是:

  • 金額緩存
  • 財務統計
  • 對賬系統

那基本就是線上事故預備役

正確姿勢一:統一 scale

在進入 Map / Set 之前,先“洗一遍數據”。

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_數據結構_14

示例:

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_數據結構_15

正確姿勢二:業務判斷永遠用 compareTo

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_數位_16

這是我在代碼評審裏見一次誇一次的寫法。

面試官最愛問的追問

Q:那為什麼 BigDecimal 不在 equals 裏忽略 scale?

這是個設計哲學問題。原因有三點:

  1. 精度本身就是信息
  2. 財務、統計、科學計算場景必須嚴謹
  3. equals 一旦改規則,會破壞 HashMap、HashSet 的一致性

Java 的選擇是:“我不替你做業務判斷,我只保證對象語義一致。”

終極對照表(建議收藏)

都是 0,為什麼 BigDecimal.ZERO 和 0.00 比較竟然是 false?_Java_17

一句話總結(可以直接背)

BigDecimal 的 equals 比的是“值 + 精度”,compareTo 比的是“數值大小”,業務判斷永遠優先用 compareTo。

END

寫到這裏,我又想起那家咖啡店老闆。在生活裏:0 就是沒錢。但在代碼裏:0 是 0,0.00 是 0.00,你要説清楚你想比什麼。

BigDecimal 從來不坑你,它只是比你想得更認真一點。

如果你覺得這篇文章幫你少踩了一個坑,歡迎點個“在看”、轉發給你的好朋友。

我是小米,一個喜歡分享技術的31歲程序員。如果你喜歡我的文章,歡迎關注我的微信公眾號“軟件求生”,獲取更多技術乾貨!