字符串占位替换,相信没有小伙伴是陌生的,这东西可以说是伴随着我们所有的项目工程,编码过程;别不相信,如
- String.format
- sql参数拼接的占位
- log日志输出
接下来我们看一下在我们的日常工作生涯中,经常涉及到的几种占位替换方式
1. String.format
这种可以说是最原始最基础的方式了,基本上在最开始学习java这门语言的时候就会涉及到,语法也比较简单
举例如下
1 | String.format("hello %s", "一灰灰blog”); |
使用%
来表示占位,后面跟上不同的标识符,用于限定这个占位处的参数类型
这种使用姿势,由jdk原生提供支持,下表为不同的转换符对应的说明
转换符 | 说明 | 参数实例 |
---|---|---|
%s |
字符串替换 | “一灰灰” |
%c |
字符类型 | ‘a’ |
%b |
布尔类型 | true/false |
%d |
整数,十进制 | 10 |
%x |
整数,十六进制 | 0x12 |
%o |
整数,八进制 | 012 |
%f |
浮点 | 0.12f |
%e |
指数 | 2e2 |
%g |
通用浮点型 | |
%h |
散列 | |
%% |
百分比 | |
%n |
换行 | |
%tx |
日期与时间类型(x代表不同的日期与时间转换符 |
虽然上面表中列出了很多,但实际使用时,%s
, %d
, %f
这三个就足以应付绝大部分的场景了;使用姿势和上面的实例参不多,第一个参数为字符串模板,后面的可变参数为待替换的值
下面是在实际使用过程中的注意事项
1.1 类型不匹配
上面的表中介绍了不同的转换符,要求的参数类型,如果没有对应上,会怎样
%s
,传入非字符串类型
1 | @Test |
输出如下
1 | hello 120 |
也就是说,%s
的占位标记,传参如果不是String类型,那么实际替换的是 arg.toString()
(所以数组输出的是地址,而list输出了内容)
%d
,传入非整数
与字符串的不一样的是,如果我们定义要求替换的参数类型为整数,那么传参不是整数,就会抛异常
1 | System.out.println(String.format("hello %d", 1.0F)); |
上面这两个,一个传入的参数为浮点,一个传入的是字符串,在实际替换的时候,可不会调用Integer.valufOf(String.valueOf(xxx))
来强转,而是采用更直接的方式,抛异常
关键的提示信息如下
1 | java.util.IllegalFormatConversionException: d != java.lang.Float |
因此在实际使用这种方式进行替换时,推荐选择 %s
,毕竟兼容性更好
1.2 参数个数不匹配
我们会注意到,String.format
接收的参数是不定长的,那么就可能存在字符串模板中预留的占位与实际传入的参数个数不匹配的场景,那么出现这种场景时,会怎样
参数缺少
1 | System.out.println(String.format("hello %s %s", "yihui")); |
上面的例子中,模板要求两个,实际只传入一个参数,会直接抛异常MissingFormatArgumentException
1 | java.util.MissingFormatArgumentException: Format specifier '%s' |
参数过多
1 | System.out.println(String.format("hello %s", "yihuihui", "blog")); |
执行正常,多余的参数不会被替换
因此,我们在使用String.format
进行字符串替换时,请确保传参不要少于实际定义的参数个数;多了还好,少了就会抛异常
2. MessageFormat
上面介绍的String.format虽说简单好用,但我们用多之后,自然会遇到,一个参数,需要替换模板中多个占位的场景,针对这种场景,更友好的方式是MessageFormat
,这个也是jdk原生提供的
我们来简单看一下它的使用姿势
1 | String ans = MessageFormat.format("hello {0}, wechart site {0}{1}", "一灰灰", "blog"); |
使用{数字}
来表示占位,其中数字对应的是传参的下标,因此当一个参数需要复用时,使用MessageFormat就可以比较简单的实现了,上面就是一个实例,替换之后的字符串为
1 | hello 一灰灰, wechart site 一灰灰blog |
接下来说一下它使用时的注意事项
2.1 {}成对出现
如果字符串中,只出现一个{
,而没有配套的}
,会抛异常
1 | System.out.println(MessageFormat.format("hello }", 123)); |
注意上面两种case,上面一个是有}
而缺少{
,这样是没有问题的;而下面那个则会抛异常
1 | java.lang.IllegalArgumentException: Unmatched braces in the pattern. |
如果字符串中却是希望输出{
,可以使用单引号来处理
1 | System.out.println(MessageFormat.format("hello '{' world", 456)); |
2.2 单引号
上面提到需要转移时,可以用单引号进行处理,在字符串模板的定义中,如果有单引号,需要各位注意
只有一个单引号,会导致后面所有占位都不生效
1 | System.out.println(MessageFormat.format("hello {0}, I'm {1}", "一灰灰", "blog")); |
上面这个输出结果可能和我们实际希望的不一致
1 | hello 一灰灰, Im {1} |
要解决上面这个,就是使用两个单引号
1 | System.out.println(MessageFormat.format("hello {0}, I''m {1}", "一灰灰", "blog")); |
这样输出的就是我们预期的
1 | hello 一灰灰, I'm blog |
2.3 序号省略
上面的定义中,已经明确要求我们在{}
中指定参数的序号,如果模板中没有指定会怎样?
1 | System.out.println(messageFormat.format("hello {}, world", "yihuihui")); |
直接抛异常
1 | java.lang.IllegalArgumentException: can't parse argument number: |
3. 小结
本文介绍的实战小技巧属于是jdk原生提供的两种实现字符串占位替换的方式,除了这两个之外,我们日常开发中还会遇到其他的占位替换方式
比如sql的?
替换,mybatis中sql参数组装使用${paramName}
,或者logback日志输出中的{}
来表示占位,spring的@Value注解声明的配置注入方式${name:defaultValue}
,这些也都属于占位替换的范畴,那么它们又是怎么实现的呢?
且看下文
II. 其他
1. 一灰灰Blog: https://liuyueyi.github.io/hexblog
一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
2. 声明
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
- 微博地址: 小灰灰Blog
- QQ: 一灰灰/3302797840
3. 扫描关注
一灰灰blog