在日常開發(fā)中,我們經(jīng)常會有格式化的需求,如日期格式化、數(shù)字格式化、錢幣格式化等等。
格式化器的作用似乎跟轉(zhuǎn)換器的作用類似,但是它們的關(guān)注點卻不一樣:
Spring有自己的格式化器抽象org.springframework.format.Formatter,但是談到格式化器,必然就會聯(lián)想起來JDK自己的java.text.Format體系。為后文做好鋪墊,本文就先介紹下JDK為我們提供了哪些格式化能力。
Java里從來都缺少不了字符串拼接的活,JDK也提供了多種“工具”供我們使用,如:StringBuffer、StringBuilder以及最直接的+號,相信這些大家都有用過。但這都不是本文的內(nèi)容,本文將講解格式化器,給你提供一個新的思路來拼接字符串,并且是推薦方案。
JDK內(nèi)置有格式化器,便是java.text.Format體系。它是個抽象類,提供了兩個抽象方法:
public abstract class Format implements Serializable, Cloneable {
public abstract StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos);
public abstract Object parseObject (String source, ParsePosition pos);
}
Java SE針對于Format抽象類對于常見的應(yīng)用場景分別提供了三個子類實現(xiàn):
抽象類。用于用于格式化日期/時間類型java.util.Date。雖然是抽象類,但它提供了幾個靜態(tài)方法用于獲取它的實例:
// 格式化日期 + 時間
public final static DateFormat getInstance() {
return getDateTimeInstance(SHORT, SHORT);
}
public final static DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale){
return get(timeStyle, dateStyle, 3, aLocale);
}
// 格式化日期
public final static DateFormat getDateInstance(int style, Locale aLocale) {
return get(0, style, 2, aLocale);
}
// 格式化時間
public final static DateFormat getTimeInstance(int style, Locale aLocale){
return get(style, 0, 1, aLocale);
}
有了這些靜態(tài)方法,你可在不必關(guān)心具體實現(xiàn)的情況下直接使用:
/**
* {@link DateFormat}
*/
@Test
public void test1() {
Date curr = new Date();
// 格式化日期 + 時間
System.out.println(DateFormat.getInstance().getClass() + "-->" + DateFormat.getInstance().format(curr));
System.out.println(DateFormat.getDateTimeInstance().getClass() + "-->" + DateFormat.getDateTimeInstance().format(curr));
// 格式化日期
System.out.println(DateFormat.getDateInstance().getClass() + "-->" + DateFormat.getDateInstance().format(curr));
// 格式化時間
System.out.println(DateFormat.getTimeInstance().getClass() + "-->" + DateFormat.getTimeInstance().format(curr));
}
運行程序,輸出:
class java.text.SimpleDateFormat-->20-12-25 上午7:19
class java.text.SimpleDateFormat-->2020-12-25 7:19:30
class java.text.SimpleDateFormat-->2020-12-25
class java.text.SimpleDateFormat-->7:19:30
嗯,可以看到底層實現(xiàn)其實是咱們熟悉的SimpleDateFormat。實話說,這種做法不常用,狠一點:基本不會用(框架開發(fā)者可能會用做兜底實現(xiàn))。
一般來說,我們會直接使用SimpleDateFormat來對Date進(jìn)行格式化,它可以自己指定Pattern,個性化十足。如:
@Test
public void test2() {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); // yyyy-MM-dd HH:mm:ss
System.out.println(dateFormat.format(new Date()));
}
運行程序,輸出:
2020-12-25
關(guān)于SimpleDateFormat的使用方式不再啰嗦,不會的就可走自行勸退手續(xù)了。此處只提醒一點:SimpleDateFormat線程不安全。
說明:JDK 8以后不再建議使用Date類型,也就不會再使用到DateFormat。同時我個人建議:在項目中可強(qiáng)制嚴(yán)令禁用
抽象類。用于格式化數(shù)字,它可以對數(shù)字進(jìn)行任意格式化,如小數(shù)、百分?jǐn)?shù)、十進(jìn)制數(shù)等等。它有兩個實現(xiàn)類:
類結(jié)構(gòu)和DateFormat類似,也提供了getXXXInstance靜態(tài)方法給你直接使用,無需關(guān)心底層實現(xiàn):
@Test
public void test41() {
double myNum = 1220.0455;
System.out.println(NumberFormat.getInstance().getClass() + "-->" + NumberFormat.getInstance().format(myNum));
System.out.println(NumberFormat.getCurrencyInstance().getClass() + "-->" + NumberFormat.getCurrencyInstance().format(myNum));
System.out.println(NumberFormat.getIntegerInstance().getClass() + "-->" + NumberFormat.getIntegerInstance().format(myNum));
System.out.println(NumberFormat.getNumberInstance().getClass() + "-->" + NumberFormat.getNumberInstance().format(myNum));
System.out.println(NumberFormat.getPercentInstance().getClass() + "-->" + NumberFormat.getPercentInstance().format(myNum));
}
運行程序,輸出:
class java.text.DecimalFormat-->1,220.045
class java.text.DecimalFormat-->¥1,220.05
class java.text.DecimalFormat-->1,220
class java.text.DecimalFormat-->1,220.045
class java.text.DecimalFormat-->122,005%
這一看就知道DecimalFormat是NumberFormat的主力了。
Decimal:小數(shù),小數(shù)的,十進(jìn)位的。
用于格式化十進(jìn)制數(shù)字。它具有各種特性,可以解析和格式化數(shù)字,包括:西方數(shù)字、阿拉伯?dāng)?shù)字和印度數(shù)字。它還支持不同種類的數(shù)字,包括:整數(shù)(123)、小數(shù)(123.4)、科學(xué)記數(shù)法(1.23E4)、百分?jǐn)?shù)(12%)和貨幣金額(3)。所有這些都可以進(jìn)行本地化。
下面是它的構(gòu)造器:
其中最為重要的就是這個pattern(不帶參數(shù)的構(gòu)造器一般不會用),它表示格式化的模式/模版。一般來說我們對DateFormat的pattern比較熟悉,但對數(shù)字格式化的模版符號了解甚少。這里我就幫你整理出這個表格(信息源自JDK官網(wǎng)),記得收藏哦:
說明:Number和Digit的區(qū)別:
Number是個抽象概念,其表達(dá)形式可以是數(shù)字、手勢、聲音等等。如1024就是個numberDigit是用來表達(dá)的單獨符號。如0-9這是個digit就可以用來表示number,如1024就是由1、0、2、4這四個digit組成的
看了這個表格的符號規(guī)則,估計很多同學(xué)還是一臉懵逼。不啰嗦了,上干貨
這是最經(jīng)典、最常見的使用場景,甚至來說你有可能職業(yè)生涯只會用到此場景。
/**
* {@link DecimalFormat}
*/
@Test
public void test4() {
double myNum = 1220.0455;
System.out.println("===============0的使用===============");
System.out.println("只保留整數(shù)部分:" + new DecimalFormat("0").format(myNum));
System.out.println("保留3位小數(shù):" + new DecimalFormat("0.000").format(myNum));
System.out.println("整數(shù)部分、小數(shù)部分都5位。不夠的都用0補(bǔ)位(整數(shù)高位部,小數(shù)低位補(bǔ)):" + new DecimalFormat("00000.00000").format(myNum));
System.out.println("===============#的使用===============");
System.out.println("只保留整數(shù)部分:" + new DecimalFormat("#").format(myNum));
System.out.println("保留2為小數(shù)并以百分比輸出:" + new DecimalFormat("#.##%").format(myNum));
// 非標(biāo)準(zhǔn)數(shù)字(不建議這么用)
System.out.println("===============非標(biāo)準(zhǔn)數(shù)字的使用===============");
System.out.println(new DecimalFormat("666").format(myNum));
System.out.println(new DecimalFormat(".6666").format(myNum));
}
運行程序,輸出:
===============0的使用===============
只保留整數(shù)部分:1220
保留3位小數(shù):1220.045
整數(shù)部分、小數(shù)部分都5位。不夠的都用0補(bǔ)位(整數(shù)高位部,小數(shù)低位補(bǔ)):01220.04550
===============#的使用===============
只保留整數(shù)部分:1220
保留2為小數(shù)并以百分比輸出:122004.55%
===============非標(biāo)準(zhǔn)數(shù)字的使用===============
661220
1220.666
通過此案例,大致可得出如下結(jié)論:
如果你不是在證券/銀行行業(yè),這個大概率是用不著的(即使在,你估計也不會用它)。來幾個例子感受一把就成:
@Test
public void test5() {
double myNum = 1220.0455;
System.out.println(new DecimalFormat("0E0").format(myNum));
System.out.println(new DecimalFormat("0E00").format(myNum));
System.out.println(new DecimalFormat("00000E00000").format(myNum));
System.out.println(new DecimalFormat("#E0").format(myNum));
System.out.println(new DecimalFormat("#E00").format(myNum));
System.out.println(new DecimalFormat("#####E00000").format(myNum));
}
運行程序,輸出:
1E3
1E03
12200E-00001
.1E4
.1E04
1220E00000
分組分隔符比較常用,它就是我們??吹降亩禾?/span>,
@Test
public void test6() {
double myNum = 1220.0455;
System.out.println(new DecimalFormat(",###").format(myNum));
System.out.println(new DecimalFormat(",##").format(myNum));
System.out.println(new DecimalFormat(",##").format(123456789));
// 分隔符,左邊是無效的
System.out.println(new DecimalFormat("###,##").format(myNum));
}
運行程序,輸出:
1,220
12,20
1,23,45,67,89
12,20
在展示層面也比較常用,用于把一個數(shù)字用%形式表示出來。
@Test
public void test42() {
double myNum = 1220.0455;
System.out.println("百分位表示:" + new DecimalFormat("#.##%").format(myNum));
System.out.println("千分位表示:" + new DecimalFormat("#.##\u2030").format(myNum));
}
運行程序,輸出:
百分位表示:122004.55%
千分位表示:1220045.5‰
嗯,這個符號¤,鍵盤竟無法直接輸出,得使用軟鍵盤(建議使用copy大法)。
@Test
public void test7() {
double myNum = 1220.0455;
System.out.println(new DecimalFormat(",000.00¤").format(myNum));
System.out.println(new DecimalFormat(",000.¤00").format(myNum));
System.out.println(new DecimalFormat("¤,000.00").format(myNum));
System.out.println(new DecimalFormat("¤,000.¤00").format(myNum));
// 世界貨幣表達(dá)形式
System.out.println(new DecimalFormat(",000.00¤¤").format(myNum));
}
運行程序,輸出:
1,220.05¥
1,220.05¥
¥1,220.05
1,220.05¥¥
¥1,220.05¥
1,220.05CNY
注意最后一條結(jié)果:如果連續(xù)出現(xiàn)兩次,代表貨幣符號的國際代號。
說明:結(jié)果默認(rèn)都做了Locale本地化處理的,若你在其它國家就不會再是¥人民幣符號嘍
DecimalFormat就先介紹到這了,其實掌握了它就基本等于掌握了NumberFormat。接下來再簡要看看它另外一個“兒子”:ChoiceFormat。
Choice:精選的,仔細(xì)推敲的。
這個格式化器非常有意思:相當(dāng)于以數(shù)字為鍵,字符串為值的鍵值對。使用一組double類型的數(shù)組作為鍵,一組String類型的數(shù)組作為值,兩數(shù)組相同(不一定必須是相同,見示例)索引值的元素作為一對。
@Test
public void test8() {
double[] limits = {1, 2, 3, 4, 5, 6, 7};
String[] formats = {"周一", "周二", "周三", "周四", "周五", "周六", "周天"};
NumberFormat numberFormat = new ChoiceFormat(limits, formats);
System.out.println(numberFormat.format(1));
System.out.println(numberFormat.format(4.3));
System.out.println(numberFormat.format(5.8));
System.out.println(numberFormat.format(9.1));
System.out.println(numberFormat.format(11));
}
運行程序,輸出:
周一
周四
周五
周天
周天
結(jié)果解釋:
可能你會想這有什么使用場景???是的,不得不承認(rèn)它的使用場景較少,本文下面會介紹下它和MessageFormat的一個使用場景。
如果說DateFormat和NumberFormat都用沒什么花樣,主要記住它的pattern語法格式就成,那么就下來這個格式化器就是本文的主菜了,使用場景非常的廣泛,它就是MessageFormat。
MessageFormat提供了一種與語言無關(guān)(不管你在中國還是其它國家,效果一樣)的方式生成拼接消息/拼接字符串的方法。使用它來構(gòu)造顯示給最終用戶的消息。MessageFormat接受一組對象,對它們進(jìn)行格式化,然后在模式的適當(dāng)位置插入格式化的字符串。
先來個最簡單的使用示例體驗一把:
/**
* {@link MessageFormat}
*/
@Test
public void test9() {
String sourceStrPattern = "Hello {0},my name is {1}";
Object[] args = new Object[]{"girl", "YourBatman"};
String formatedStr = MessageFormat.format(sourceStrPattern, args);
System.out.println(formatedStr);
}
運行程序,輸出:
Hello girl,my name is YourBatman
有沒有中似曾相似的感覺,是不是和String.format()的作用特別像?是的,它倆的用法區(qū)別,到底使用稅文下也會討論。
要熟悉MessageFormat的使用,主要是要熟悉它的參數(shù)模式(你也可以理解為pattern)。
MessageFormat采用{}來標(biāo)記需要被替換/插入的部分,其中{}里面的參數(shù)結(jié)構(gòu)具有一定模式:
ArgumentIndex[,FormatType[,FormatStyle]]
說明:FormatType和FormatStyle只有在傳入值為日期時間、數(shù)字、百分比等類型時才有可能需要設(shè)置,使用得并不多。畢竟:我在外部格式化好后再放進(jìn)去不香嗎?
@Test
public void test10() {
MessageFormat messageFormat = new MessageFormat("Hello, my name is {0}. I’am {1,number,#.##} years old. Today is {2,date,yyyy-MM-dd HH:mm:ss}");
// 亦可通過編程式 顯示指定某個位置要使用的格式化器
// messageFormat.setFormatByArgumentIndex(1, new DecimalFormat("#.###"));
System.out.println(messageFormat.format(new Object[]{"YourBatman", 24.123456, new Date()}));
}
運行程序,輸出:
Hello, my name is YourBatman. I’am 24.12 years old. Today is 2020-12-26 15:24:28
它既可以直接在模版里指定格式化模式類型,也可以通過API方法set指定格式化器,當(dāng)然你也可以在外部格式化好后再放進(jìn)去,三種方式均可,任君選擇。
下面基于此示例,對MessageFormat的使用注意事項作出幾點強(qiáng)調(diào)。
@Test
public void test11() {
System.out.println(MessageFormat.format("{1} - {1}", new Object[]{1})); // {1} - {1}
System.out.println(MessageFormat.format("{0} - {1}", new Object[]{1})); // 輸出:1 - {1}
System.out.println(MessageFormat.format("{0} - {1}", new Object[]{1, 2, 3})); // 輸出:1 - 2
System.out.println("---------------------------------");
System.out.println(MessageFormat.format("'{0} - {1}", new Object[]{1, 2})); // 輸出:{0} - {1}
System.out.println(MessageFormat.format("''{0} - {1}", new Object[]{1, 2})); // 輸出:'1 - 2
System.out.println(MessageFormat.format("'{0}' - {1}", new Object[]{1, 2})); // {0} - 2
// 若你數(shù)據(jù)庫值兩邊都需要''包起來,請你這么寫
System.out.println(MessageFormat.format("''{0}'' - {1}", new Object[]{1, 2})); // '1' - 2
System.out.println("---------------------------------");
System.out.println(MessageFormat.format("0} - {1}", new Object[]{1, 2})); // 0} - 2
System.out.println(MessageFormat.format("{0 - {1}", new Object[]{1, 2})); // java.lang.IllegalArgumentException: Unmatched braces in the pattern.
}
我們知道MessageFormat提供有一個static靜態(tài)方法,非常方便地的使用:
public static String format(String pattern, Object ... arguments) {
MessageFormat temp = new MessageFormat(pattern);
return temp.format(arguments);
}
可以清晰看到,該靜態(tài)方法本質(zhì)上還是構(gòu)造了一個MessageFormat實例去做格式化的。因此:若你要多次(如高并發(fā)場景)格式化同一個模版(參數(shù)可不一樣)的話,那么提前創(chuàng)建好一個全局的(非static) MessageFormat實例再執(zhí)行格式化是最好的,而非一直調(diào)用其靜態(tài)方法。
說明:若你的系統(tǒng)非高并發(fā)場景,此性能損耗基本無需考慮哈,怎么方便怎么來。畢竟朝生夕死的對象對JVM來說沒啥壓力
二者都能用于字符串拼接(格式化)上,撇開MessageFormat支持各種模式不說,我們只需要考慮它倆的性能上差異。
一說到正則表達(dá)式,我心里就發(fā)怵,因為它對性能是不友好的,所以孰優(yōu)孰劣,高下立判。
說明:還是那句話,沒有絕對的誰好誰壞,如果你的系統(tǒng)對性能不敏感,那就是方便第一
這個就很多啦,最常見的有:HTML拼接、SQL拼接、異常信息拼接等等。
比如下面這個SQL拼接:
StringBuilder sb =new StringBuilder();
sb.append("insert into user (");
sb.append(" name,");
sb.append(" accountId,");
sb.append(" zhName,");
sb.append(" enname,");
sb.append(" status");
sb.append(") values (");
sb.append(" ''{0}'',");
sb.append(" {1},");
sb.append(" ''{2}'',");
sb.append(" ''{3}'',");
sb.append(" {4},");
sb.append(")");
Object[] args = {name, accountId, zhName, enname, status};
// 最終SQL
String sql = MessageFormat.format(sb.toString(), arr);
你看,多工整。
說明:如果值是字符串需要'包起來,那么請使用兩邊各兩個包起來
本文內(nèi)容介紹了JDK原生的格式化器知識點,主要作用在這三個方面:
Spring是直接面向使用者的框架產(chǎn)品,很顯然這些是不夠用的,并且JDK的格式化器在設(shè)計上存在一些弊端。比如經(jīng)常被吐槽的:日期/時間類型格式化器SimpleDateFormat為毛在java.text包里,而它格式化的類型Date卻在java.util包內(nèi),這實為不合適。
有了JDK格式化器作為基礎(chǔ),下篇我們就可以浩浩蕩蕩地走進(jìn)Spring格式化器的大門了,看看它是如何優(yōu)于JDK進(jìn)行設(shè)計和抽象的。
作者:YourBatman
原文鏈接:https://fangshixiang.blog.csdn.net/article/details/111752597
種方式是es6的語法.使用鍵盤1左邊的那個字符 `` 拼接,
再加上js自帶的模板引擎拼接字符串非常快速.這東西也沒什么高深的,看幾個例子就懂了.
console.log(`<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>`);
var str1 = 'hello';
console.log(`${str1} world`);
var a = 10;
var b = 20;
console.log(`a+1=${a+1};b*2=${b*2}`);
運行結(jié)果:
----瀏覽器中
在nodejs中目前的版本對 ``是完全支持的,瀏覽器支持就各不相同了,手機(jī)端安卓支持,但蘋果手機(jī)貌似不支持,至少蘋果微信瀏覽器對這個是不支持的.
大概就以下幾點功能:
換行不用加號拼接
可以用${}傳入變量
可以進(jìn)行數(shù)字的計算
不過呢有利有弊,據(jù)說性能上沒有正常的拼接高.
意外金喜的博客:http://blog.csdn.net/zzwwjjdj1
鄭重申明,發(fā)表的文章都是我自己博客原創(chuàng)的,這是技術(shù)分享,再被封就不在這里玩了. ╭(╯^╰)╮
題: 很多時間,當(dāng)我們前后端分離的時候。有這種場景:
前端:兄弟你把數(shù)據(jù)分頁返回給我就行。我自己處理頁面。后端:兄弟。好的。哈哈。
這種(這種肯定是最好的,前后都笑哈哈的。各管各的);
但是:突然有一天,我跳槽了。進(jìn)入了一家外包公司。老板:趕緊幾下給我做出來,我要進(jìn)度,我需要功能。趕快,抓緊點。假如后端突然是新手,前端js可能剛開始有點不熟悉。那且不是很尷尬。(開玩笑的,但是這種情況一般都很少,一般后端都會前端的js和html);
很多外包公司都是為了追求速度:
當(dāng)時我就遇到這種情況。因為當(dāng)時用的是thinkPHP,前端頁面就手機(jī)。就是用戶看列表的時候,數(shù)據(jù)可以往下拉無限加載所有數(shù)據(jù),也就是所謂的瀑布流。當(dāng)時我就想用個字符串拼接就行了。老板是搞技術(shù)的,他說不行,這里必須要用插件(好強(qiáng)勢的感覺)。用撒了?infinite-scroll插件。各位可以去百度下。
首先開始如何使用了:
首先網(wǎng)頁頭引入jQuery 和infinite-scroll插件。不解釋,jq肯定要放在前面。
因為用的thinkphp,所以前端我肯定就是volist或者foreach循環(huán)數(shù)據(jù)了。(什么volist和foreach都不懂,該打屁股了。)。
服務(wù)端,肯定用了分頁類。到時候復(fù)制分頁代碼,然后輸出到前端。但是有個。一定要把數(shù)據(jù)的總頁數(shù)發(fā)送給前端。$this->maxPage = 5;前端 $maxpage 就是需要的總頁碼數(shù)。
注:在實際中 #navigation 和 #navigation a 是隱藏的喲。maxpage 就是剛才穿的那個總頁碼。不然的話會導(dǎo)致數(shù)據(jù)加載異常。
也可以添加其他屬性。比如,手機(jī)滾動下離最下面距離多少像素值就可以加載下個頁面內(nèi)容了
extraScrollPx: 50, 等等,還有其他很多屬性,比如加載的時候添加動畫,大家可以自行百度下 這個插件的使用。
最后總結(jié): 其實我覺得這個分頁不好,只是大家學(xué)習(xí)下,我覺得異步請求分頁比較好的。特別是現(xiàn)在用了很多的前端的js其他類庫,處理數(shù)據(jù)起來更6了。
最好還是前后分離開。分頁發(fā)送數(shù)據(jù),前端處理最完美了。
(每日一更,偶爾周末休息,多謝大家支持。)
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。