JAVA NumberFormat和DecimalFormat小结
中文互聯(lián)網(wǎng)上很多介紹這兩個(gè)類的博客質(zhì)量真是一眼難盡,遇到什么問題想百度的時(shí)候發(fā)現(xiàn)就是屎里淘金,非常浪費(fèi)時(shí)間。格式化數(shù)字這種不常用但是一定有機(jī)會(huì)遇到的場(chǎng)景,還是提前做好功課為好。
本篇文章簡(jiǎn)單說明一下NumberFormat和DecimalFormat這兩個(gè)類,主要是我對(duì)這兩個(gè)類用法的一些理解。
首先是類的繼承關(guān)系:
可以看到在JAVA的Format家族中,主要分為3個(gè)分支,分別是
- 格式化日期時(shí)間的DateFormat分支,主要用的其實(shí)現(xiàn)類SimpleDateFormat
- 格式化文本消息的MessageFormat分支,自己就是實(shí)現(xiàn)類,常和它的親戚ChoiceFormat配合使用
- 格式化數(shù)字的NumberFormat分支,主要用的是NumberFormat和DecimalFormat
在上面的界面右鍵-Show Categories-選擇Methods,再右鍵-Change Visiable Level-選擇Public,可以看到類中所有的public方法
可以看到里面的方法相當(dāng)?shù)亩?#xff0c;但是由于我們使用Format類家族的時(shí)候,目的都是為了將對(duì)象轉(zhuǎn)化為對(duì)應(yīng)的字符串表示形式,或者反過來將字符串轉(zhuǎn)化為對(duì)應(yīng)的對(duì)象。聽起來有點(diǎn)像序列化和反序列化,不過實(shí)際還是差別很大的(序列化是將對(duì)象轉(zhuǎn)變成二進(jìn)制的字節(jié)數(shù)組,以保存成文件或者通過網(wǎng)絡(luò)傳輸,序列化后的文件人類是看不懂的。而format則是將對(duì)象變成它的字符串表現(xiàn)形式,這個(gè)字符串就是專門設(shè)計(jì)為讓人類可以看懂的)
所以對(duì)于Format類家族中的方法,我們主要只需要關(guān)心在抽象父類Format中定義的兩組重載方法:
- format()
- parseObject()
顧名思義,format中文意思是格式化,就是是將某個(gè)對(duì)象格式化為字符串;parse中文意思是解析,就是將字符串轉(zhuǎn)化為對(duì)象。理論上來說,將一個(gè)對(duì)象a先用format格式化為字符串,再將字符串解析為對(duì)象b,對(duì)象a和b應(yīng)該是相等的。讀者可以實(shí)驗(yàn)一下,下面的代碼運(yùn)行結(jié)果是true。
public void test() throws ParseException {Format format = new DecimalFormat();Object obj = 12.3;Object newObj = format.parseObject(format.format(obj));System.out.println(newObj.equals(obj)); }當(dāng)然,由于parseObject()方法的返回值類型是Object,為了使用上的方便,各個(gè)子類都有其對(duì)應(yīng)的parse()方法以返回更具體的對(duì)象,比如NumberFormat:
再比如DateFormat:
在format()和parse()兩組方法之中,其實(shí)以format()方法的使用場(chǎng)景占多數(shù)。因?yàn)楸疚挠懻摰氖荖umberFormat和DecimalFormat,所以下文的結(jié)論只針對(duì)數(shù)字的格式化。
首先,我們要理解在JAVA中,NumberFormat類和DecimalFormat類究竟是用來干什么的。這一點(diǎn)在源碼的注釋里已經(jīng)有答案了。
NumberFormat helps you to format and parse numbers for any locale. Your code can be completely independent of the locale conventions for decimal points, thousands-separators, or even the particular decimal digits used, or whether the number format is even decimal.
翻譯:NumberFormat幫助您格式化和解析任何地區(qū)的數(shù)字。您的代碼可以完全獨(dú)立于小數(shù)點(diǎn)、千位分隔符、甚至所使用的特定小數(shù)位數(shù)的語言環(huán)境約定,或者數(shù)字格式是否為十進(jìn)制。
DecimalFormat is a concrete subclass of NumberFormat that formats decimal numbers. It has a variety of features designed to make it possible to parse and format numbers in any locale, including support for Western, Arabic, and Indic digits. It also supports different kinds of numbers, including integers (123), fixed-point numbers (123.4), scientific notation (1.23E4), percentages (12%), and currency amounts ($123). All of these can be localized.
翻譯:DecimalFormat是NumberFormat的一個(gè)具體子類,用于格式化十進(jìn)制數(shù)字。它具有各種設(shè)計(jì)用來解析和格式化任何語言環(huán)境中的數(shù)字的特性,包括對(duì)西方數(shù)字、阿拉伯?dāng)?shù)字和印度數(shù)字的支持。它還支持不同種類的數(shù)字,包括整數(shù)(123)、定點(diǎn)數(shù)字(123.4)、科學(xué)記數(shù)法(1.23E4)、百分比(12%)和貨幣金額($123)。所有這些都可以本地化。
也就是說,NumberFormat和DecimalFormat是被設(shè)計(jì)用來格式化所有地區(qū)對(duì)應(yīng)數(shù)字格式的全能類。簡(jiǎn)單解釋一下:在JAVA中,數(shù)字只有那幾種表示數(shù)字的基本類型和它們對(duì)應(yīng)的包裝類(int,Integer,long,Long等),但是這些對(duì)象只能存在于JAVA虛擬機(jī)中,要讓人類看見這些數(shù)字,就需要將它們轉(zhuǎn)化成字符串。在不同的地區(qū),表示數(shù)字的習(xí)慣千差萬別,如果我們的代碼要對(duì)不同地區(qū)的人們輸出以不同方式表示的數(shù)字,就輪到NumberFormat和DecimalFormat出場(chǎng)了,它們之間的區(qū)別就在于前者是格式化數(shù)字,而后者是格式化十進(jìn)制數(shù)字。
為了讓使用者理解不同地區(qū)的數(shù)字表示方式究竟“有何不同”,在DecimalFormat的類注釋中甚至給出了下面一段示例代碼:
輸出的結(jié)果很長(zhǎng),我就不全部貼上來了,讀者可以自行測(cè)試。我只拿其中幾條比較有特點(diǎn)的數(shù)據(jù)來展示。以下是case 0:分支的輸出結(jié)果,在該分支中座的操作是將-1234.56格式化為小數(shù)形式的字符串,再將字符串解析回?cái)?shù)字:
阿拉伯文 (阿拉伯聯(lián)合酋長(zhǎng)國(guó)): #,##0.###;#,##0.###- -> 1,234.56- -> -1234.56
中文 (中國(guó)): #,##0.### -> -1,234.56 -> -1234.56
芬蘭文 (芬蘭): #,##0.### -> -1 234,56 -> -1234.56
波蘭文 (波蘭): #,##0.### -> -1 234,56 -> -1234.56
法文 (瑞士): #,##0.### -> -1’234.56 -> -1234.56
法文 (盧森堡): #,##0.### -> -1 234,56 -> -1234.56
法文 (比利時(shí)): #,##0.### -> -1.234,56 -> -1234.56
西班牙文 (委內(nèi)瑞拉): #,##0.### -> -1.234,56 -> -1234.56
泰文 (泰國(guó),TH): #,##0.### -> -?,???.?? -> -1234.56
印地文 (印度): #,##0.### -> -?,???.?? -> -1234.56
可以看到,在所有的輸出中代碼都成功地將-1234.56"format"成了對(duì)應(yīng)的字符串表示,然后又"pase"回了-1234.56,但是在不同的國(guó)家和地區(qū),數(shù)字的字符串表示方式卻差異極大:
中國(guó)是我們熟悉的用英文逗號(hào)做千分位符,英文句號(hào)做小數(shù)點(diǎn),但是芬蘭和波蘭就是用空格作千分位符,瑞內(nèi)瑞拉則是用英文句號(hào)做千分位符,英文逗號(hào)做小數(shù)點(diǎn),跟中國(guó)正好相反;
同樣使用法文的三個(gè)國(guó)家——瑞士、盧森堡、比利時(shí)——它們對(duì)同一個(gè)數(shù)字的表示方式還全都不一樣;
更奇葩的是泰國(guó)和印度,在泰文和印地文中,甚至不用阿拉伯?dāng)?shù)字來表示數(shù)字;
而在阿拉伯?dāng)?shù)字的起源地,使用阿拉伯文的阿聯(lián)酋,他們寫負(fù)數(shù)時(shí),是把負(fù)號(hào)放在數(shù)字后邊的……
看了我摘選出來的幾個(gè)例子,我想讀者應(yīng)該能理解在表現(xiàn)數(shù)字的方式上,“世界的參差”了吧。而這,只是case 0:分支,將-1234.56格式化為小數(shù)后的字符串表示。還有三個(gè)分支分別是轉(zhuǎn)成整數(shù)、貨幣、百分?jǐn)?shù),我就不貼上來了,有興趣的自己復(fù)制代碼執(zhí)行看一下吧。
可以想象,如果JAVA不給我們提供NumberFormat類和DecimalFormat類,要我們自己實(shí)現(xiàn)“針對(duì)不同地區(qū)的用戶,提供對(duì)應(yīng)的個(gè)性化的表示數(shù)字的字符串輸出”這一功能會(huì)有多復(fù)雜了。
很可惜的一件事是,雖然我前面寫了這么多,說明了Format類“地區(qū)化輸出”以及將“地區(qū)化輸出”的字符串無損解析成JAVA數(shù)字對(duì)象的功能有多么強(qiáng)大,但是對(duì)于絕大多數(shù)開發(fā)者來說,其實(shí)是很少用到“地區(qū)化輸出”這一功能的。相反,在我們的日常工作中,實(shí)際的需求可能是“保留xx位小數(shù)”、“百分?jǐn)?shù)和小數(shù)的相互轉(zhuǎn)化”、“將數(shù)字轉(zhuǎn)化成包含萬分位符的格式”等等。所以盡管NumberFormat可以在無其它配置的情況下將-1234.56正確地格式化為" -1,234.56"字符串,由于默認(rèn)格式無法滿足需求,我們往往需要自定義格式化模板,就是在DecimalFormat的構(gòu)造方法中傳入一個(gè)符合預(yù)設(shè)語法標(biāo)準(zhǔn)的字符串,告訴DecimalFormat要如何格式化數(shù)字。
以下是我總結(jié)的NumberFormat和DecimalFormat的format()方法自定義模板時(shí)的注意要點(diǎn):
1、Format對(duì)象的聲明類型什么時(shí)候用NumberFormat,什么時(shí)候用DecimalFormat
2、如何實(shí)例化NumberFormat和DecimalFormat對(duì)象
目前Format對(duì)象的獲取有2種方式:
實(shí)際上,由于所有NumberFormat.getInstance()方法拿到的NumberFormat實(shí)例的實(shí)際類型都是DecimalFormat,所以兩種獲取實(shí)例方式的區(qū)別僅在于可自定義程度的大小。使用方式1獲取實(shí)例,由于聲明類型是NumberFormat,所以只能通過幾個(gè)set方法自定義幾個(gè)有限的參數(shù),比如整數(shù)與小數(shù)部分的最大和最小位數(shù)、舍入方式、貨幣類型、是否展示千分位符等;使用方式2獲取實(shí)例,除了可以自定義上述參數(shù)外,還可以實(shí)現(xiàn)很多其它的自定義模板,比如科學(xué)計(jì)數(shù)法、千分?jǐn)?shù)、使用萬分位符等等。
所以,對(duì)于上面兩個(gè)問題,我的建議是:如果格式化的是貨幣、百分?jǐn)?shù)等沒有太大自定義需求的字符串,可以使用方式1獲取聲明類型為NumberFormat的實(shí)例,否則,就用方式2,獲取DecimalFormat實(shí)例。
3、模板字符串中幾個(gè)常用符號(hào)的說明
下表在DecimalFormat類的注釋里有,我只是對(duì)每個(gè)符號(hào)的含義添加了中文說明
| 0 | Number | Yes | Digit | 數(shù)字 |
| # | Number | Yes | Digit, zero shows as absent | 數(shù)字,如果是0則不展示 |
| . | Number | Yes | Decimal separator or monetary decimal separator | 數(shù)字或者貨幣的小數(shù)位分隔符 |
| - | Number | Yes | Minus sign | 負(fù)號(hào) |
| , | Number | Yes | Grouping separator | 英文逗號(hào)。分組分隔符(千分位符、萬分位符) |
| E | Number | Yes | Separates mantissa and exponent in scientific notation. Need not be quoted in prefix or suffix. | 科學(xué)計(jì)數(shù)法中用來分離尾數(shù)和指數(shù)的符號(hào)。不能用在前綴或者后綴中。(科學(xué)計(jì)數(shù)法中a·10n10^n10n記作aEn,其中a叫做底數(shù),n叫做指數(shù)) |
| ; | Subpattern boundary | Yes | Separates positive and negative subpatterns | 分隔正數(shù)和負(fù)數(shù)子模式 |
| % | Prefix or suffix | Yes | Multiply by 100 and show as percentage | 乘以100并以百分?jǐn)?shù)顯示 |
| \u2030 | Prefix or suffix | Yes | Multiply by 1000 and show as per mille value | 乘以1000并以千分?jǐn)?shù)顯示 |
| ¤ (\u00A4) | Prefix or suffix | No | Currency sign, replaced by currency symbol. If doubled, replaced by international currency symbol. If present in a pattern, the monetary decimal separator is used instead of the decimal separator. | 貨幣記號(hào),由貨幣符號(hào)替換。如果兩個(gè)同時(shí)出現(xiàn),則用國(guó)際貨幣符號(hào)替換。如果出現(xiàn)在某個(gè)模式中,則使用貨幣小數(shù)分隔符,而不使用小數(shù)分隔符 |
| ’ | Prefix or suffix | No | Used to quote special characters in a prefix or suffix, for example, “’#’#” formats 123 to “#123”. To create a single quote itself, use two in a row: “# o’'clock”. | 英文單引號(hào)。在特殊字符左右用單引號(hào)圍起來可以用于標(biāo)識(shí)特殊字符,比如使用模式"’#’#“可以將123格式化為”#123"。如果想要表示單引號(hào)本身,則使用兩個(gè)單引號(hào),如"# o’'clock" |
3.1、符號(hào)0和#的區(qū)別
兩者是自定義模板時(shí)最常用到的符號(hào),區(qū)別在于,在數(shù)字前后遇到0時(shí),如果用"0"會(huì)強(qiáng)制顯示,如果用"#"則會(huì)省略。
// 需求:將精度為6位小數(shù)的數(shù)字截取為保留4位小數(shù) DecimalFormat df1 = new DecimalFormat("#.0000"); DecimalFormat df2 = new DecimalFormat("#.####"); double d1 = 12.345678; double d2 = 12.340000; System.out.println("使用\"#.0000\"模板得到的結(jié)果:"); System.out.println(df1.format(d1)); System.out.println(df1.format(d2)); System.out.println("使用\"#.####\"模板得到的結(jié)果:"); System.out.println(df2.format(d1)); System.out.println(df2.format(d2));輸出結(jié)果如下:
使用"#.0000"模板得到的結(jié)果:
12.3457
12.3400
使用"#.####"模板得到的結(jié)果:
12.3457
12.34
3.2、負(fù)號(hào)"-“與子模式分隔符”;"的使用
一般來說,這兩個(gè)符號(hào)是組合使用的。在默認(rèn)情況下,DecimalFormat在格式化負(fù)數(shù)時(shí),會(huì)自動(dòng)在前面加上一個(gè)符號(hào)"-",但是如果你想自定義負(fù)號(hào)的位置(就如前面官方例子中的阿聯(lián)酋一樣),就需要再寫一個(gè)負(fù)數(shù)子模式,放在正數(shù)子模式后面,中間用";"分隔。
double d1 = 123.4567; double d2 = -123.4567; DecimalFormat df1 = new DecimalFormat("#.00"); DecimalFormat df2 = new DecimalFormat("#.00;#.00-"); System.out.println("使用\"#.00\"模板得到的結(jié)果:"); System.out.println(df1.format(d1)); System.out.println(df1.format(d2)); System.out.println("使用\"#.00;#.00-\"模板得到的結(jié)果:"); System.out.println(df2.format(d1)); System.out.println(df2.format(d2));輸出結(jié)果如下:
使用"#.00"模板得到的結(jié)果:
123.46
-123.46
使用"#.00;#.00-"模板得到的結(jié)果:
123.46
123.46-
3.3、百分?jǐn)?shù)符號(hào)"%" 與千分?jǐn)?shù)符號(hào)"\u2030"
由于千分符號(hào)"‰"不方便在普通鍵盤上打出,所以DecimalFormat的設(shè)計(jì)者使用它的Unicode編碼來代替。在使用上,百分?jǐn)?shù)符號(hào)和千分?jǐn)?shù)符號(hào)沒有什么不同
double d1 = 12.34567; double d2 = -12.34567; DecimalFormat df1 = new DecimalFormat("0.00%"); System.out.println("使用\"0.00%\"模板得到的結(jié)果:"); System.out.println(df1.format(d1)); System.out.println(df1.format(d2)); DecimalFormat df2 = new DecimalFormat("0.00\u2030"); System.out.println("使用\"0.00\u2030\"模板得到的結(jié)果:"); System.out.println(df2.format(d1)); System.out.println(df2.format(d2));輸出結(jié)果如下:
使用"0.00%"模板得到的結(jié)果:
1234.57%
-1234.57%
使用"0.00‰"模板得到的結(jié)果:
12345.67‰
-12345.67‰
3.4、自定義千分位符、萬分位符
由于中國(guó)在讀數(shù)字時(shí)習(xí)慣以萬為單位分隔大數(shù),所以將數(shù)字以萬分隔是很常見的需求
double d = 123456789.87654; DecimalFormat df1 = new DecimalFormat("#,####.#"); DecimalFormat df2 = new DecimalFormat("#,###.#"); System.out.println("使用\"#,####.#\"模板得到的結(jié)果:"); System.out.println(df1.format(d)); System.out.println("使用\"#,###.#\"模板得到的結(jié)果:"); System.out.println(df2.format(d));輸出結(jié)果如下:
使用"#,####.#“模板得到的結(jié)果:
1,2345,6789.9
使用”#,###.#"模板得到的結(jié)果:
123,456,789.9
總結(jié)
以上是生活随笔為你收集整理的JAVA NumberFormat和DecimalFormat小结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手机:导致手机发烫的原因有哪些?
- 下一篇: NumberFormat格式化数字