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

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

三、分组分隔符


,分组分隔符比较常用,它就是我们常看到的逗号,


@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


四、百分号%


在展示层面也比较常用,用于把一个数字用%形式表示出来。

@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));
    // 世界货币表达形式
    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


注意最后一条结果:如果连续出现两次,代表货币符号的国际代号。


说明:结果默认都做了Locale本地化处理的,若你在其它国家就不会再是¥人名币符号喽


DecimalFormat就先介绍到这了,其实掌握了它就基本等于掌握了NumberFormat。接下来再简要看看它另外一个“儿子”:ChoiceFormat。


ChoiceFormat


Choice:精选的,仔细推敲的。


这个格式化器非常有意思:相当于以数字为键,字符串为值的键值对。使用一组double类型的数组作为键,一组String类型的数组作为值,两数组相同(不一定必须是相同,见示例)索引值的元素作为一对。

@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));
}


运行程序,输出:


周一
周四
周五
周天
周天


结果解释:


  1. 4.3位于4和5之间,取值4;5.8位于5和6之间,取值5
  2. 9.1和11均超过了数组最大值(或者说找不到匹配的),则取值最后一对键值对。


可能你会想这有什么使用场景???是的,不得不承认它的使用场景较少,本文下面会介绍下它和MessageFormat的一个使用场景。


如果说DateFormat和NumberFormat都用没什么花样,主要记住它的pattern语法格式就成,那么就下来这个格式化器就是本文的主菜了,使用场景非常的广泛,它就是MessageFormat。


MessageFormat:字符串格式化


MessageFormat提供了一种与语言无关(不管你在中国还是其它国家,效果一样)的方式生成拼接消息/拼接字符串的方法。使用它来构造显示给最终用户的消息。MessageFormat接受一组对象,对它们进行格式化,然后在模式的适当位置插入格式化的字符串。



先来个最简单的使用示例体验一把:

/**
 * {@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()的作用特别像?是的,它俩的用法区别,到底使用税文下也会讨论。


要熟悉MessageFormat的使用,主要是要熟悉它的参数模式(你也可以理解为pattern)。


参数模式


MessageFormat采用{}来标记需要被替换/插入的部分,其中{}里面的参数结构具有一定模式:


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只有在传入值为日期时间、数字、百分比等类型时才有可能需要设置,使用得并不多。毕竟:我在外部格式化好后再放进去不香吗?


@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指定格式化器,当然你也可以再外部格式化好后再放进去,三种方式均可,任君选择。


注意事项


下面基于此示例,对MessageFormat的使用注意事项作出几点强调。

@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
    // 若你数据库值两边都需要''包起来,请你这么写
    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.
}


1.参数模式的索引值必须从0开始,否则所有索引值无效


2.实际传入的参数个数可以和索引个数不匹配,不报错(能匹配上几个算几个)


3.两个单引号''才算作一个',若只写一个将被忽略甚至影响整个表达式

   1.谨慎使用单引号'

   2.关注'的匹配关系


4.{}只写左边报错,只写右边正常输出(注意参数的对应关系)


static方法的性能问题


我们知道MessageFormat提供有一个static静态方法,非常方便的的使用:

public static String format(String pattern, Object ... arguments) {
    MessageFormat temp = new MessageFormat(pattern);
    return temp.format(arguments);
}


可以清晰看到,该静态方法本质上还是构造了一个MessageFormat实例去做格式化的。因此:若你要多次(如高并发场景)格式化同一个模版(参数可不一样)的话,那么提前创建好一个全局的(非static) MessageFormat实例再执行格式化是最好的,而非一直调用其静态方法。


说明:若你的系统非高并发场景,此性能损耗基本无需考虑哈,怎么方便怎么来。毕竟朝生夕死的对象对JVM来说没啥压力


和String.format选谁?

二者都能用于字符串拼接(格式化)上,撇开MessageFormat支持各种模式不说,我们只需要考虑它俩的性能上差异。


  • MeesageFormat:先分析(模版可提前分析,且可以只分析一次),再在指定位置上插入相应的值
  • 分析:遍历字符串,维护一个{}数组并记录位置
  • 填值
  • String.format:该静态方法是采用运行时用正则表达式 匹配到占位符,然后执行替换的
  • 正则表达式为"%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])"
  • 根据正则匹配到占位符列表和位置,然后填值
  • 一说到正则表达式,我心里就发触,因为它对性能是不友好的,所以孰优孰劣,高下立判。


说明:还是那句话,没有绝对的谁好谁坏,如果你的系统对性能不敏感,那就是方便第一


经典使用场景


这个就很多啦,最常见的有: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);


你看,多工整。


说明:如果值是字符串需要'包起来,那么请使用两边各两个包起来


✍总结


本文内容介绍了JDK原生的格式化器知识点,主要作用在这三个方面:


  • DateFormat:日期时间格式化
  • NumberFormat:数字格式化
  • MessageFormat:字符串格式化


Spring是直接面向使用者的框架产品,很显然这些是不够用的,并且JDK的格式化器在设计上存在一些弊端。比如经常被吐槽的:日期/时间类型格式化器SimpleDateFormat为毛在java.text包里,而它格式化的类型Date却在java.util包内,这实为不合适。


有了JDK格式化器作为基础,下篇我们就可以浩浩荡荡的走进Spring格式化器的大门了,看看它是如何优于JDK进行设计和抽象的。

相关文章
|
安全 Java 编译器
JDK21更新内容:字符串模板
JDK21更新内容:字符串模板
|
3月前
|
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问题的解决
|
3月前
|
IDE Java 数据处理
【字符串构建的全新时代】JDK 22字符串模板:让字符串操作如行云流水,代码更流畅!
【9月更文挑战第8天】虽然目前JDK 22的确切内容尚未公布,但我们可以根据Java语言的演进趋势和社区的需求,构想出一种可能在未来版本中引入的字符串模板机制。这种机制有望为Java的字符串操作带来革命性的变化,让代码编写如行云流水般流畅。我们期待Java语言能够不断进化,为开发者们提供更加高效、便捷和强大的编程工具。
|
4月前
|
XML JSON Java
JDK8到JDK26版本升级的新特性问题之在JDK 13中,字符串文本块改进字符串嵌入是如何实现的
JDK8到JDK26版本升级的新特性问题之在JDK 13中,字符串文本块改进字符串嵌入是如何实现的
|
Java
java202303java学习笔记第三十五天IO流中不同JDK版本字符串方式4
java202303java学习笔记第三十五天IO流中不同JDK版本字符串方式4
41 0
|
7月前
|
XML SQL 自然语言处理
JDK 21中的字符串模板:提升代码可读性与维护性的新利器
本文将介绍JDK 21中引入的字符串模板特性,它是一种创新的文本生成技术,旨在提高代码的可读性和维护性。字符串模板允许开发者使用简洁的语法来构建复杂的字符串,减少了硬编码和字符串拼接的工作量。本文将详细阐述字符串模板的语法、使用场景以及与传统字符串处理方法的比较,并通过示例代码展示其在实际开发中的应用。
|
7月前
|
前端开发 Java API
Java【代码分享 05】实现字符串转数据库的inStr使用JDK8 stream.collect(Collectors.joining(delimiter, prefix, suffix)) 实现
Java【代码分享 05】实现字符串转数据库的inStr使用JDK8 stream.collect(Collectors.joining(delimiter, prefix, suffix)) 实现
55 0
|
Java
java202303java学习笔记第三十五天IO流中不同JDK版本字符串方式2
java202303java学习笔记第三十五天IO流中不同JDK版本字符串方式2
49 0
|
Java 编译器 Android开发
IDEA-设置-Java编译器对常量字符串过长的处理之适用于JDK17版本eclipse编译解决方案
IDEA-设置-Java编译器对常量字符串过长的处理之适用于JDK17版本eclipse编译解决方案
1303 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​报错
601 0