1. 日志打印使用占位符
1.1 占位符的作用
在日常开发中,为了方便问题定位,我们通常使用日志打印输出信息时。当程序出现了异常或者其他问题,需要通过日志来定位问题所在。
通常,我们在日志输出中会直接输出变量或者参数的值,那么在出现问题时,就更能准确地定位到关键点所在。
以下是常见的使用方式:
@Slf4j
public class Test2 {
public static void main(String[] args) {
cal(5);
// cal(0)
}
/**
* 计算100除以该数后等于多少
* @param x
*/
public static void cal(int x) {
int result = 0;
log.info("param: {}", x); //打印出相关参数
try {
result = 100/x;
log.info("100 div {} = {}", x, result); //输出结果
} catch (Exception e) {
log.info("exception occur, exception: {}", e);
}
}
}
1.2 错误的使用占位符
上述的代码乍看之下没有问题,但是当我们传入参数0,查看日志打印结果:
想要输出异常e时,e的信息确实打印了出来,但是占位符并没有被信息取代,这是为什么呢?
1.3 问题定位
在idea中,也准确的给出了该问题的相关说明,这个错误的意思是:代码中的日志语句使用了一个占位符,但是在日志语句中并没有提供足够的参数来填充这个占位符。
咦,可是我明明提供了参数e
啊,为什么没用呢?
点进源码一看,恍然大悟。
当传入的参数是异常类型时,就会直接走到日志打印逻辑,而不会进行占位符格式化处理。
1.4 正确用法
- 可以不设置占位符,这样错误信息依旧会输出(推荐)
catch (Exception e) {
log.info("exception occur", e);
}
- 使用
e.getMessage()
方法,仅把异常类型的信息输出
catch (Exception e) {
log.info("exception occur, excpetion: {}", e.getMessage());
}
1.5 总结
当需要将异常信息输出到日志时,无需使用占位符 {}
,也能将异常信息完整打印出来。
2. finally中不要使用return
2.1 问题说明
在阿里巴巴JAVA开发手册中有这么一条准则:
【强制】不要在finally块中使用return。
说明:finally块中的return返回后方法结束执行,不会再执行try块中的return语句。
以下是一个简单的代码示例:
@Slf4j
public class Test {
public static void main(String[] args) {
int result = testFinally();
log.info("result is {}", result); // 输出结果为2而不是1
}
public static int testFinally() {
int result = 0; // 初始化返回值变量
try {
result++;
return result; // 返回语句
} catch (Exception e) {
log.info("exception occur.", e);
} finally {
// 在finally块中执行一些操作
result++;
return result; // 在方法的最后返回结果
}
}
}
打印结果如下:
2.2 正确用法
@Slf4j
public class Test {
public static void main(String[] args) {
int result = testFinally();
log.info("result is {}", result);
}
public static int testFinally() {
int result = 0; // 初始化返回值变量
try {
result++;
} catch (Exception e) {
log.info("exception occur.", e);
} finally {
// 做一些清理资源或执行必要的清理操作
}
return result;
}
}
2.3 补充说明
如果在try
块和catch
块中都没有返回值,那么在finally
块中使用return
语句并不会
对程序产生任何影响。在这种情况下,finally
块中的代码只会执行清理资源或执行必要的清理操作的任务,而不会影响函数或方法的返回值。
但是,即使在try
块和catch
块中没有返回值,还是应该避免在finally
块中使用return
语句。因为在finally
块中使用return
语句可能会给其他开发人员或维护人员带来困惑,并且不符合通常的编码惯例。
因此,建议尽可能避免在finally
块中使用return
语句,以保持代码的清晰度和可读性。
3. matches后再find
3.1 问题说明
某一天,我在进行日志匹配的时候,发现一段诡异的问题。明明我都匹配成功了,但是当我想取出关键信息时,却给我报了异常。
代码复现结果如下:
public class Test2 {
public static void main(String[] args) {
String str = "username:aaa";
String regex = "username:(.*?)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
System.out.println("matches: " + matcher.matches()); // True
System.out.println("find: " + matcher.find()); //false
}
}
离了大谱,我明明都能匹配成功,却告诉我找不到匹配的信息?
3.2 问题定位
打上断点,可以很明显的看到,执行了matches方法后,match对象的内部参数值发生了变化。
点进find方法源码,我们可以看到,find方法查到的开始位置,就是last的值。
而matches()
方法会将last的值置为字符串的长度,当执行find()
方法时,模式匹配是从结尾处开始查找,而不是从输入序列的开头开始查找。因此,如果在Matcher
对象上先调用matches
方法,再调用find
方法,可能会导致find
方法无法找到任何匹配项。
3.3 解决办法
非常的简单,只需要使用find(0)
,重置查找其实位置即可。
public static void main(String[] args) {
String str = "username:aaa";
String regex = "username:(.*?)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
System.out.println("matches: " + matcher.matches()); //True
System.out.println("find: " + matcher.find(0)); //True
}