7. JDK拍了拍你:字符串拼接一定记得用MessageFormat#format(上)

简介: 7. JDK拍了拍你:字符串拼接一定记得用MessageFormat#format(上)

✍前言



你好,我是A哥(YourBatman)。本文所属专栏:Spring类型转换,公号后台回复专栏名即可获取全部内容。


在日常开发中,我们经常会有格式化的需求,如日期格式化、数字格式化、钱币格式化等等。


image.png


格式化器的作用似乎跟转换器的作用类似,但是它们的关注点却不一样:


  • 转换器:将类型S转换为类型T,关注的是类型而非格式
  • 格式化器: String <-> Java类型。这么一看它似乎和PropertyEditor类似,但是它的关注点是字符串的格式


Spring有自己的格式化器抽象org.springframework.format.Formatter,但是谈到格式化器,必然就会联想起来JDK自己的java.text.Format体系。为后文做好铺垫,本文就先介绍下JDK为我们提供了哪些格式化能力。


版本约定


JDK:8


image.png


✍正文



Java里从来都缺少不了字符串拼接的活,JDK也提供了多种“工具”供我们使用,如:StringBuffer、StringBuilder以及最直接的+号,相信这些大家都有用过。但这都不是本文的内容,本文将讲解格式化器,给你提供一个新的思路来拼接字符串,并且是推荐方案。


JDK内置有格式化器,便是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);
}


  • format:将Object格式化为String,并将此String放到toAppendTo里面
  • parseObject:讲String转换为Object,是format方法的逆向操作


Java SE针对于Format抽象类对于常见的应用场景分别提供了三个子类实现:



image.png


DateFormat:日期时间格式化


抽象类。用于用于格式化日期/时间类型java.util.Date。虽然是抽象类,但它提供了几个静态方法用于获取它的实例:


// 格式化日期 + 时间
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);
}


image.png


有了这些静态方法,你可在不必关心具体实现的情况下直接使用:


/**
 * {@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



嗯,可以看到底层实现其实是咱们熟悉的SimpleDateFormat。实话说,这种做法不常用,狠一点:基本不会用(框架开发者可能会用做兜底实现)。


SimpleDateFormat


一般来说,我们会直接使用SimpleDateFormat来对Date进行格式化,它可以自己指定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


关于SimpleDateFormat的使用方式不再啰嗦,不会的就可走自行劝退手续了。此处只提醒一点:SimpleDateFormat线程不安全。


说明:JDK 8以后不再建议使用Date类型,也就不会再使用到DateFormat。同时我个人建议:在项目中可强制严令禁用


NumberFormat:数字格式化


抽象类。用于格式化数字,它可以对数字进行任意格式化,如小数、百分数、十进制数等等。它有两个实现类


image.png


类结构和DateFormat类似,也提供了getXXXInstance静态方法给你直接使用,无需关心底层实现:


image.png


@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的主力了。


DecimalFormat


Decimal:小数,小数的,十进位的。


用于格式化十进制数字。它具有各种特性,可以解析和格式化数字,包括:西方数字、阿拉伯数字和印度数字。它还支持不同种类的数字,包括:整数(123)、小数(123.4)、科学记数法(1.23E4)、百分数(12%)和货币金额($123)。所有这些都可以进行本地化。


下面是它的构造器:


image.png


其中最为重要的就是这个pattern(不带参数的构造器一般不会用),它表示格式化的模式/模版。一般来说我们对DateFormat的pattern比较熟悉,但对数字格式化的模版符号了解甚少。这里我就帮你整理出这个表格(信息源自JDK官网),记得搜藏哦:


image.png


说明:Number和Digit的区别:


Number是个抽象概念,其表达形式可以是数字、手势、声音等等。如1024就是个number

Digit是用来表达的单独符号。如0-9这是个digit就可以用来表示number,如1024就是由1、0、2、4这四个digit组成的

看了这个表格的符号规则,估计很多同学还是一脸懵逼。不啰嗦了,上干货


一、0和#的使用(最常见使用场景)


这是最经典、最常见的使用场景,甚至来说你有可能职业生涯只会用到此场景。


/**
 * {@link DecimalFormat}
 */
@Test
public void test4() {
    double myNum = 1220.0455;
    System.out.println("===============0的使用===============");
    System.out.println("只保留整数部分:" + new DecimalFormat("0").format(myNum));
    System.out.println("保留3位小数:" + new DecimalFormat("0.000").format(myNum));
    System.out.println("整数部分、小数部分都5位。不够的都用0补位(整数高位部,小数低位补):" + new DecimalFormat("00000.00000").format(myNum));
    System.out.println("===============#的使用===============");
    System.out.println("只保留整数部分:" + new DecimalFormat("#").format(myNum));
    System.out.println("保留2为小数并以百分比输出:" + new DecimalFormat("#.##%").format(myNum));
    // 非标准数字(不建议这么用)
    System.out.println("===============非标准数字的使用===============");
    System.out.println(new DecimalFormat("666").format(myNum));
    System.out.println(new DecimalFormat(".6666").format(myNum));
}


运行程序,输出:

===============0的使用===============
只保留整数部分:1220
保留3位小数:1220.045
整数部分、小数部分都5位。不够的都用0补位(整数高位部,小数低位补):01220.04550
===============#的使用===============
只保留整数部分:1220
保留2为小数并以百分比输出:122004.55%
===============非标准数字的使用===============
661220
1220.666



通过此案例,大致可得出如下结论:


  • 整数部分:
  • 0和#都可用于取出全部整数部分
  • 0的个数决定整数部分长度,不够高位补0;#则无此约束,N多个#是一样的效果
  • 小数部分:
  • 可保留小数点后N位(0和#效果一样)
  • 若小数点后位数不够,若使用的0那就低位补0,若使用#就不补(该是几位就是几位)
  • 数字(1-9):并不建议模版里直接写1-9这样的数字,了解下即可

二、科学计数法E


如果你不是在证券/银行行业,这个大概率是用不着的(即使在,你估计也不会用它)。来几个例子感受一把就成:


@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




相关文章
|
安全 Java 编译器
JDK21更新内容:字符串模板
JDK21更新内容:字符串模板
|
2月前
|
Oracle Java 关系型数据库
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
如果遇到"exec format error"问题,文章建议先检查Linux操作系统是32位还是64位,并确保安装了与系统匹配的JDK版本。如果系统是64位的,但出现了错误,可能是因为下载了错误的JDK版本。文章提供了一个链接,指向Oracle官网上的JDK 17 Linux版本下载页面,并附有截图说明。
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
|
2月前
|
IDE Java 数据处理
【字符串构建的全新时代】JDK 22字符串模板:让字符串操作如行云流水,代码更流畅!
【9月更文挑战第8天】虽然目前JDK 22的确切内容尚未公布,但我们可以根据Java语言的演进趋势和社区的需求,构想出一种可能在未来版本中引入的字符串模板机制。这种机制有望为Java的字符串操作带来革命性的变化,让代码编写如行云流水般流畅。我们期待Java语言能够不断进化,为开发者们提供更加高效、便捷和强大的编程工具。
|
3月前
|
XML JSON Java
JDK8到JDK26版本升级的新特性问题之在JDK 13中,字符串文本块改进字符串嵌入是如何实现的
JDK8到JDK26版本升级的新特性问题之在JDK 13中,字符串文本块改进字符串嵌入是如何实现的
|
Java
java202303java学习笔记第三十五天IO流中不同JDK版本字符串方式4
java202303java学习笔记第三十五天IO流中不同JDK版本字符串方式4
41 0
|
6月前
|
XML SQL 自然语言处理
JDK 21中的字符串模板:提升代码可读性与维护性的新利器
本文将介绍JDK 21中引入的字符串模板特性,它是一种创新的文本生成技术,旨在提高代码的可读性和维护性。字符串模板允许开发者使用简洁的语法来构建复杂的字符串,减少了硬编码和字符串拼接的工作量。本文将详细阐述字符串模板的语法、使用场景以及与传统字符串处理方法的比较,并通过示例代码展示其在实际开发中的应用。
|
6月前
|
前端开发 Java API
Java【代码分享 05】实现字符串转数据库的inStr使用JDK8 stream.collect(Collectors.joining(delimiter, prefix, suffix)) 实现
Java【代码分享 05】实现字符串转数据库的inStr使用JDK8 stream.collect(Collectors.joining(delimiter, prefix, suffix)) 实现
53 0
|
Java
java202303java学习笔记第三十五天IO流中不同JDK版本字符串方式2
java202303java学习笔记第三十五天IO流中不同JDK版本字符串方式2
46 0
|
Java 编译器 Android开发
IDEA-设置-Java编译器对常量字符串过长的处理之适用于JDK17版本eclipse编译解决方案
IDEA-设置-Java编译器对常量字符串过长的处理之适用于JDK17版本eclipse编译解决方案
1293 0
IDEA-设置-Java编译器对常量字符串过长的处理之适用于JDK17版本eclipse编译解决方案
|
Oracle Java 关系型数据库
华为云aarch64架构下载jdk;Linux上jdk无法执行二进制文件及​gzip: stdin: invalid compressed data–format violated​报错
华为云aarch64架构下载jdk;Linux上jdk无法执行二进制文件及​gzip: stdin: invalid compressed data–format violated​报错
572 0