【java】java中常用的代码技巧 教你如何编写优雅代码

前言:博主之前写了一篇博客 :
【java】 java开发中 常遇到的各种难点 思路方案
上述博客主要描述的是该如何解决疑难场景,接下来博主带来姊妹篇,将针对用常规方式写出来的代码不太好看、效率不太高的场景 阐述该如何编写一些更加优雅的代码。

只有一个键值对如何存取更方便

只有一个键值对的时候 也使用hashmap来存取,就会导致虽然只有一个entry ,但也必须 get(key) 或遍历entrySet来取值,那有没有什么办法 我们不用传入任何参数 也可以直接getKey呢?

我们可以使用java.util下的AbstractMap.SimpleEntry来解决这个问题:


AbstractMap.SimpleEntry<String,Integer> simpleEntry = new AbstractMap.SimpleEntry("csdn:孟秋与你", 18);
String key = simpleEntry.getKey();
Integer value = simpleEntry.getValue();

注: AbstractMap.SimpleEntry 是 Map.Entry的一个实现类,
如果我们想通过fastjson来转型,需要指定类型为Map.Entry , 并不支持AbstractMap.SimpleEntry (且fastjson版本需要>= 1.2.24)
代码示例如下:

LinkedHashMap<String, AbstractMap.SimpleEntry<String, String>> map = new LinkedHashMap<>();
String str = "{\"甲方合同\":{\"contact\":123},\"客户\":{\"customerName\":222}}";
// 将json字符串转成map
map = JSON.parseObject(str , LinkedHashMap.class);
  for (Map.Entry<String, AbstractMap.SimpleEntry<String, String>> entry : map.entrySet()) {
      		// 将大map的value转成Map.Entry
            Map.Entry<String, String> value = JSON.parseObject(String.valueOf(entry.getValue()),Map.Entry.class);
        }

如何封装if的非空校验

(注:@valid不是阐述重点,主要讲述如何封装if )

什么叫封装if呢 比如我们要判断名字是否为空 常规写法:

if(StringUtils.isBlank(name){
  throw new RuntimeException("名字不能为空");
}

还可能需要校验住址:

if(StringUtils.isBlank(address){
  throw new RuntimeException("地址不能为空");
}

在很多业务场景中,可能需要大量的判断,那就会导致大量的if 以及大括号 ,非常占用代码行数,那我们该如何简化呢?可以如下的写一个方法:


public static void checkBlank(String param,String message){
	if(StringUtils.isBlank(param){
	  throw new RuntimeException(message);
	}
}

调用方使用:

// 假设类名为 Check
Check.checkBlank(name,"姓名不能为空");
Check.checkBlank(address,"地址不能为空");

这样在业务流程代码 就不会看到大量的if以及大括号了,当然 针对不同类型的参数进行非空校验、是否相等校验 按照checkBlank方法自己去拓展就好了。

Optional orElseThrow如何记录错误日志

我们传统的orElseThrow可能就是抛一个异常:

// 可以将() -> new RuntimeException("error msg") 封装,参考封装if的方式
Object o = Optional.ofNullable(null).orElseThrow(() -> new RuntimeException("error msg"));

有时候为null就是严重的业务异常 需要事后通过日志排查,我们可能需要记录详细的日志信息 我们只需要在箭头后面加上大括号,并在抛异常前记录日志即可 对lambda熟悉的同学应该比较容易想到,如果lambda不熟练可能会不知道如何写。

  Optional.ofNullable(null).orElseThrow(() -> {
            log.info("other error");
            return new RuntimeException("xxx");
        });
// 异常封装:
public static Supplier<RuntimeException> supplier(String bizMessage,String logMsg) {
        log.info(logMsg);
        return () -> new RuntimeException(bizMessage);
    }

如何避免字段名被修改无法感知问题 如何避免魔法值

某个类的字段名修改了 其它地方可能不知道 解决方法 : 使用Function转换 ,这样不仅可以避免下游无法感知字段名变更的问题 也可以避免魔法值。

让我们用个例子来说明是什么意思: 比如我们的Oper实体类里面 有一个ukKey字段,我们在某个地方 将ukKey作为key 存放到map中,某一天 团队中其他人修改了这个字段名,比如改成了uniqueKey,这个时候map的key值 极大可能就会被疏忽。

转换代码:

   Map<String,Integer> map = new HashMap<>();
   map.put(FunctionUtils.function2Str(Oper::getUkKey), 123456);

FunctionUtils 工具类
(这是博主多年前写的一段代码 也是博主非常为之骄傲的一段代码):


public class FunctionUtils <T>{
    /**
     * Description: 函数式参数转回String属性名
     * date: 2021/06/09 12:05
     * @param sFunctionField
     * @return
     * @author qkj
     */
     public static <T> String function2Str (SFunction<T, ?> sFunctionField) {
        Method writeReplace = null;
        try {
            // 函数式方法可以直接调用writeReplace
            writeReplace = sFunctionField.getClass().getDeclaredMethod("writeReplace");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        writeReplace.setAccessible(true);
        String fieldName = "";
        try {
            // 序列化
            Object object = writeReplace.invoke(sFunctionField);
            // 反序列化
            SerializedLambda serializedLambda = (SerializedLambda) object;
            String methodName = serializedLambda.getImplMethodName();
            String temp = methodName.substring(3);
            fieldName = temp.replaceFirst(temp.substring(0,1),temp.substring(0,1).toLowerCase());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return fieldName;

    }

}

如何简洁的往list里面添加有限个数元素

按照常规写法,我们可能是先new ArrayList() ; 接着往list里面逐个add,这样也会导致add的行数较多;我们可以通过更简洁的方式:

 List<Integer> list = Arrays.asList(1, 2, 3);
// >= jdk9 还可以通过List.of的方式
List<Integer> list1 = List.of(1, 2, 3);

注意:上述方式的list都不能remove 是不可变的,我们一般都过这种方式添加元素 基本上都是作为返回结果的 业务上已经确保不会remove了。

Arrays.asList原因简述: Arrays.asList返回的是Arrays类的内部类ArrayList ,这个ArrayList和我们常用的java.util.ArrayList是同名,但它是实现AbstractList的,且没有重写remove方法。
我们可以看看AbstractList的源码:

在这里插入图片描述

List.of 也是类似的,就不再分析了。

如何简洁的往map里面添加有限个数元素

//与List.of()类似,jdk >= 9的时候 也有Map.of()方法
Map<String,Integer> map = Map.of("length",666,"age",18);

// jdk < 9  可以使用 谷歌guava包 (注意k v都不能为null,且键值对需要 <= 5)
Map immutableMap =  ImmutableMap.of();

如何批量删除mysql中某前缀开头的表

SELECT CONCAT('DROP TABLE IF EXISTS `', table_name, '`;')
FROM information_schema.tables

WHERE table_schema = 'your db name' AND table_name LIKE 'table_prefix%';

用上述语句 就可以得到所有以 table_prefix开头表的drop删除语句,我们可以在navicat中全选复制并手动执行
在这里插入图片描述

如何封装if return

我们以mybatis plus为例,in查询时,如果in参数为空数组 查询会报错

// 该方法会报错
List<Long> idList = new ArrayList();
orderService.list(new QueryWrapper<Order>().lambda()
				.in(Order::getId, idList))

传统校验写法:

if (CollectionUtils.isEmpty(idList)) {
			return  Collections.emptyList();
	}
orderService.list(new QueryWrapper<Order>().lambda()
				.in(Order::getId, idList))

封装if return写法:

public List<Order> listOrder(List<Long> idList) {
		// 减少了每次查询时 手动写if判断参数是否为空数组
		return MybatisPlusUtils.checkThenExecuteInQuery(
			idList,
			// mybatis plus 的 in 查询 : 不做任何处理时  当idList为空,会报错
			nonEmptyIdList -> orderService.list(new QueryWrapper<Order>().lambda()
				.in(Order::getId, idList))
		);
	}

MybatisPlusUtils:

public class MybatisPlusUtils {


	@SuppressWarnings("unchecked")
	public static <T, R> R checkThenExecuteInQuery(Collection<T> param, Function<Collection<T>, R> action) {
		if (CollectionUtils.isEmpty(param)) {
			return (R) Collections.emptyList();
		}
		return action.apply(param);
	}


}

或者

	public List<Order> listOrder(List<Long> idList) {
		// 减少了每次查询时 手动写if判断参数是否为空数组
		return MybatisPlusUtils.checkThenExecuteInQuery(
			idList, Collections::emptyList,
			// mybatis plus 的 in 查询 : 不做任何处理时  当idList为空,会报错
			nonEmptyIdList -> orderService.list(new QueryWrapper<Order>().lambda()
				.in(Order::getId, idList))
		);
	}

utils则对应为:

public class MybatisPlusUtils {


	public static <T, R> R checkThenExecuteInQuery(Collection<T> param, Supplier<R> emptyResult, Function<Collection<T>, R> action) {
		if (CollectionUtils.isEmpty(param)) {
			return emptyResult.get();
		}
		return action.apply(param);
	}


}

待续… 关注博主的同学可能比较清楚 博主的待续是真的有后续的 会动态更新
(本文原创:csdn 孟秋与你)

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孟秋与你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值