分享、成长,拒绝浅藏辄止。关注公号【BAT的乌托邦】,回复
专栏
获取原创专栏:重学Spring、重学MyBatis、中间件、云计算…本文已被 https://www.yourbatman.cn 收录。
✍前言
你好,我是A哥(YourBatman)。本文所属专栏:Spring类型转换,公号后台回复专栏名即可获取全部内容。
在日常开发中,我们经常会有格式化的需求,如日期格式化、数字格式化、钱币格式化等等。
格式化器的作用似乎跟转换器的作用类似,但是它们的关注点却不一样:
- 转换器:将类型S转换为类型T,关注的是类型而非格式
- 格式化器: String
<->
Java类型。这么一看它似乎和PropertyEditor
类似,但是它的关注点是字符串的格式
Spring有自己的格式化器抽象org.springframework.format.Formatter
,但是谈到格式化器,必然就会联想起来JDK自己的java.text.Format
体系。为后文做好铺垫,本文就先介绍下JDK为我们提供了哪些格式化能力。
版本约定
- JDK:8
✍正文
Java里从来都缺少不了字符串拼接的活,JDK也提供了多种“工具”供我们使用,如:StringBuffer、StringBuilder以及最直接的+
号,相信这些大家都有用过。但这都不是本文的内容,本文将讲解格式化器,给你提供一个新的思路来拼接字符串,并且是推荐方案。
JDK内置有格式化器,便是java.text.Format
体系。它是个抽象类,提供了两个抽象方法:
1 | public abstract class Format implements Serializable, Cloneable { |
- format:将Object格式化为String,并将此String放到toAppendTo里面
- parseObject:讲String转换为Object,是format方法的逆向操作
Java SE针对于Format抽象类对于常见的应用场景分别提供了三个子类实现:
DateFormat:日期时间格式化
抽象类。用于用于格式化日期/时间类型java.util.Date
。虽然是抽象类,但它提供了几个静态方法用于获取它的实例:
1 | // 格式化日期 + 时间 |
有了这些静态方法,你可在不必关心具体实现的情况下直接使用:
1 | /** |
运行程序,输出:
1 | class java.text.SimpleDateFormat-->20-12-25 上午7:19 |
嗯,可以看到底层实现其实是咱们熟悉的SimpleDateFormat
。实话说,这种做法不常用,狠一点:基本不会用(框架开发者可能会用做兜底实现)。
SimpleDateFormat
一般来说,我们会直接使用SimpleDateFormat
来对Date进行格式化,它可以自己指定Pattern,个性化十足。如:
1 |
|
运行程序,输出:
1 | 2020-12-25 |
关于SimpleDateFormat
的使用方式不再啰嗦,不会的就可走自行劝退手续了。此处只提醒一点:SimpleDateFormat线程不安全。
说明:JDK 8以后不再建议使用Date类型,也就不会再使用到DateFormat。同时我个人建议:在项目中可强制严令禁用
NumberFormat:数字格式化
抽象类。用于格式化数字,它可以对数字进行任意格式化,如小数、百分数、十进制数等等。它有两个实现类:
类结构和DateFormat类似,也提供了getXXXInstance
静态方法给你直接使用,无需关心底层实现:
1 |
|
运行程序,输出:
1 | class java.text.DecimalFormat-->1,220.045 |
这一看就知道DecimalFormat是NumberFormat的主力了。
DecimalFormat
Decimal:小数,小数的,十进位的。
用于格式化十进制数字。它具有各种特性,可以解析和格式化数字,包括:西方数字、阿拉伯数字和印度数字。它还支持不同种类的数字,包括:整数(123)、小数(123.4)、科学记数法(1.23E4)、百分数(12%)和货币金额($123)。所有这些都可以进行本地化。
下面是它的构造器:
其中最为重要的就是这个pattern(不带参数的构造器一般不会用),它表示格式化的模式/模版。一般来说我们对DateFormat的pattern比较熟悉,但对数字格式化的模版符号了解甚少。这里我就帮你整理出这个表格(信息源自JDK官网),记得搜藏哦:
符号 | Localtion | 是否本地化 | 释义 |
---|---|---|---|
0 |
Number | 是 | Digit |
# |
Number | 是 | Digit。若是0就显示为空 |
. |
Number | 是 | 小数/货币分隔符 |
- |
Number | 是 | 就代表减号 |
, |
Number | 是 | 分组分隔符 |
E |
Number | 是 | 科学计数法分隔符(位数和指数) |
% |
前/后缀 | 是 | 乘以100并显示为百分数 |
¤ |
前/后缀 | 否 | 货币记号。若连续出现两次就用国际货币符号代替 |
' |
前后缀 | 否 | 用于引用特殊字符。作用类似于转义字符 |
说明:Number和Digit的区别:
- Number是个抽象概念,其表达形式可以是数字、手势、声音等等。如1024就是个number
- Digit是用来表达的单独符号。如0-9这是个digit就可以用来表示number,如1024就是由1、0、2、4这四个digit组成的
看了这个表格的符号规则,估计很多同学还是一脸懵逼。不啰嗦了,上干货
一、0和#的使用(最常见使用场景)
这是最经典、最常见的使用场景,甚至来说你有可能职业生涯只会用到此场景。
1 | /** |
运行程序,输出:
1 | ===============0的使用=============== |
通过此案例,大致可得出如下结论:
- 整数部分:
- 0和#都可用于取出全部整数部分
- 0的个数决定整数部分长度,不够高位补0;#则无此约束,N多个#是一样的效果
- 小数部分:
- 可保留小数点后N位(0和#效果一样)
- 若小数点后位数不够,若使用的0那就低位补0,若使用#就不补(该是几位就是几位)
- 数字(1-9):并不建议模版里直接写1-9这样的数字,了解下即可
二、科学计数法E
如果你不是在证券/银行行业,这个大概率是用不着的(即使在,你估计也不会用它)。来几个例子感受一把就成:
1 |
|
运行程序,输出:
1 | 1E3 |
三、分组分隔符,
分组分隔符比较常用,它就是我们常看到的逗号,
1 |
|
运行程序,输出:
1 | 1,220 |
四、百分号%
在展示层面也比较常用,用于把一个数字用%形式表示出来。
1 |
|
运行程序,输出:
1 | 百分位表示:122004.55% |
五、本地货币符号¤
嗯,这个符号¤
,键盘竟无法直接输出,得使用软键盘(建议使用copy大法)。
1 |
|
运行程序,输出:
1 | 1,220.05¥ |
注意最后一条结果:如果连续出现两次,代表货币符号的国际代号。
说明:结果默认都做了Locale本地化处理的,若你在其它国家就不会再是¥人名币符号喽
DecimalFormat就先介绍到这了,其实掌握了它就基本等于掌握了NumberFormat。接下来再简要看看它另外一个“儿子”:ChoiceFormat。
ChoiceFormat
Choice:精选的,仔细推敲的。
这个格式化器非常有意思:相当于以数字为键,字符串为值的键值对。使用一组double类型的数组作为键,一组String类型的数组作为值,两数组相同(不一定必须是相同,见示例)索引值的元素作为一对。
1 |
|
运行程序,输出:
1 | 周一 |
结果解释:
- 4.3位于4和5之间,取值4;5.8位于5和6之间,取值5
- 9.1和11均超过了数组最大值(或者说找不到匹配的),则取值最后一对键值对。
可能你会想这有什么使用场景???是的,不得不承认它的使用场景较少,本文下面会介绍下它和MessageFormat
的一个使用场景。
如果说DateFormat
和NumberFormat
都用没什么花样,主要记住它的pattern语法格式就成,那么就下来这个格式化器就是本文的主菜了,使用场景非常的广泛,它就是MessageFormat
。
MessageFormat:字符串格式化
MessageFormat提供了一种与语言无关(不管你在中国还是其它国家,效果一样)的方式生成拼接消息/拼接字符串的方法。使用它来构造显示给最终用户的消息。MessageFormat接受一组对象,对它们进行格式化,然后在模式的适当位置插入格式化的字符串。
先来个最简单的使用示例体验一把:
1 | /** |
运行程序,输出:
1 | Hello girl,my name is YourBatman |
有没有中似曾相似的感觉,是不是和String.format()
的作用特别像?是的,它俩的用法区别,到底使用税文下也会讨论。
要熟悉MessageFormat的使用,主要是要熟悉它的参数模式(你也可以理解为pattern)。
参数模式
MessageFormat采用{}
来标记需要被替换/插入的部分,其中{}
里面的参数结构具有一定模式:
1 | ArgumentIndex[,FormatType[,FormatStyle]] |
ArgumentIndex
:非必须。从0
开始的索引值FormatType
:非必须。使用不同的java.text.Format
实现类对入参进行格式化处理。它能有如下值:- number:调用NumberFormat进行格式化
- date:调用DateFormat进行格式化
- time:调用DateFormat进行格式化
- choice:调用ChoiceFormat进行格式化
FormatStyle
:非必须。设置FormatType使用的样式。它能有如下值:- short、medium、long、full、integer、currency、percent、SubformPattern(如日期格式、数字格式#.##等)
说明:FormatType和FormatStyle只有在传入值为日期时间、数字、百分比等类型时才有可能需要设置,使用得并不多。毕竟:我在外部格式化好后再放进去不香吗?
1 |
|
运行程序,输出:
1 | Hello, my name is YourBatman. I’am 24.12 years old. Today is 2020-12-26 15:24:28 |
它既可以直接在模版里指定格式化模式类型,也可以通过API方法set指定格式化器,当然你也可以再外部格式化好后再放进去,三种方式均可,任君选择。
注意事项
下面基于此示例,对MessageFormat的使用注意事项作出几点强调。
1 |
|
- 参数模式的索引值必须从0开始,否则所有索引值无效
- 实际传入的参数个数可以和索引个数不匹配,不报错(能匹配上几个算几个)
- 两个单引号
''
才算作一个'
,若只写一个将被忽略甚至影响整个表达式- 谨慎使用单引号
'
- 关注
'
的匹配关系
- 谨慎使用单引号
{}
只写左边报错,只写右边正常输出(注意参数的对应关系)
static方法的性能问题
我们知道MessageFormat
提供有一个static静态方法,非常方便的的使用:
1 | public static String format(String pattern, Object ... arguments) { |
可以清晰看到,该静态方法本质上还是构造了一个MessageFormat
实例去做格式化的。因此:若你要多次(如高并发场景)格式化同一个模版(参数可不一样)的话,那么提前创建好一个全局的(非static) MessageFormat实例再执行格式化是最好的,而非一直调用其静态方法。
说明:若你的系统非高并发场景,此性能损耗基本无需考虑哈,怎么方便怎么来。毕竟朝生夕死的对象对JVM来说没啥压力
和String.format选谁?
二者都能用于字符串拼接(格式化)上,撇开MessageFormat支持各种模式不说,我们只需要考虑它俩的性能上差异。
- MeesageFormat:先分析(模版可提前分析,且可以只分析一次),再在指定位置上插入相应的值
- 分析:遍历字符串,维护一个
{}
数组并记录位置 - 填值
- 分析:遍历字符串,维护一个
- String.format:该静态方法是采用运行时用正则表达式 匹配到占位符,然后执行替换的
- 正则表达式为
"%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])"
- 根据正则匹配到占位符列表和位置,然后填值
- 正则表达式为
一说到正则表达式,我心里就发触,因为它对性能是不友好的,所以孰优孰劣,高下立判。
说明:还是那句话,没有绝对的谁好谁坏,如果你的系统对性能不敏感,那就是方便第一
经典使用场景
这个就很多啦,最常见的有:HTML拼接、SQL拼接、异常信息拼接等等。
比如下面这个SQL拼接:
1 | StringBuilder sb =new StringBuilder(); |
你看,多工整。
说明:如果值是字符串需要
'
包起来,那么请使用两边各两个包起来
✍总结
本文内容介绍了JDK原生的格式化器知识点,主要作用在这三个方面:
- DateFormat:日期时间格式化
- NumberFormat:数字格式化
- MessageFormat:字符串格式化
Spring是直接面向使用者的框架产品,很显然这些是不够用的,并且JDK的格式化器在设计上存在一些弊端。比如经常被吐槽的:日期/时间类型格式化器SimpleDateFormat
为毛在java.text包里,而它格式化的类型Date却在java.util包内,这实为不合适。
有了JDK格式化器作为基础,下篇我们就可以浩浩荡荡的走进Spring格式化器的大门了,看看它是如何优于JDK进行设计和抽象的。
✔✔✔推荐阅读✔✔✔
【Spring类型转换】系列:
- 1. 揭秘Spring类型转换 - 框架设计的基石
- 2. Spring早期类型转换,基于PropertyEditor实现
- 3. 搞定收工,PropertyEditor就到这
- 4. 上新了Spring,全新一代类型转换机制
- 5. 穿过拥挤的人潮,Spring已为你制作好高级赛道
- 6. 抹平差异,统一类型转换服务ConversionService
【Jackson】系列:
- 1. 初识Jackson – 世界上最好的JSON库
- 2. 妈呀,Jackson原来是这样写JSON的
- 3. 懂了这些,方敢在简历上说会用Jackson写JSON
- 4. JSON字符串是如何被解析的?JsonParser了解一下
- 5. JsonFactory工厂而已,还蛮有料,这是我没想到的
- 6. 二十不惑,ObjectMapper使用也不再迷惑
- 7. Jackson用树模型处理JSON是必备技能,不信你看
【数据校验Bean Validation】系列:
- 1. 不吹不擂,第一篇就能提升你对Bean Validation数据校验的认知
- 2. Bean Validation声明式校验方法的参数、返回值
- 3. 站在使用层面,Bean Validation这些标准接口你需要烂熟于胸
- 4. Validator校验器的五大核心组件,一个都不能少
- 5. Bean Validation声明式验证四大级别:字段、属性、容器元素、类
- 6. 自定义容器类型元素验证,类级别验证(多字段联合验证)
【新特性】系列:
- Spring Cloud 2020.0.0正式发布,再见了Netflix
- IntelliJ IDEA 2020.3正式发布,年度最后一个版本很讲武德
- IntelliJ IDEA 2020.2正式发布,诸多亮点总有几款能助你提效
- IntelliJ IDEA 2020.1正式发布,你要的Almost都在这!
- Spring Framework 5.3.0正式发布,在云原生路上继续发力
- Spring Boot 2.4.0正式发布,全新的配置文件加载机制(不向下兼容)
- Spring改变版本号命名规则:此举对非英语国家很友好
- JDK15正式发布,划时代的ZGC同时宣布转正
【程序人生】系列:
还有诸如【Spring配置类】【Spring-static关键字】【Spring数据绑定】【Spring Cloud Netflix】【Feign】【Ribbon】【Hystrix】…更多原创专栏,关注BAT的乌托邦
回复专栏
二字即可全部获取,也可加我fsx1056342982
,交个朋友。
有些已完结,有些连载中。我是A哥(YourBatman),咱们下期见
关注YourBatman
Author | A哥(YourBatman) |
---|---|
个人站点 | www.yourbatman.cn |
yourbatman@qq.com | |
微 信 | fsx1056342982 |
活跃平台 |
|
公众号 | BAT的乌托邦(ID:BAT-utopia) |
知识星球 | BAT的乌托邦 |
每日文章推荐 | 每日文章推荐 |