大家好,欢迎来到IT知识分享网。
记录一些了解的高逼格、好用的java代码
欢迎大家补充,共同学习
1. 函数式接口—@FunctionalInterface
好处:高逼格、代码收拢、解藕、统一处理
适用范围:具有共性的接口调用代码
举个栗子:
在我们平时的微服务开发中,调用其他服务的接口,通常要把接口调用部分做异常处理(try catch),或者打印异常日志或者结果日志,并且也有可能做一些统一的调用处理,比如微服务接口熔断等处理,这个时候可以适用函数式接口收拢所有的微服务调用集中处理
这是一个微服务接口
public interface TestServer {
T test(); void test2(); }
普通的方式调用上面的微服务方法
public class RpcTestServerImpl{
//引用微服务接口 private TestServer testServer; public T test(){
try{
return testServer.test(); }catch (Exception e){
log.error("RPC error: ", e); } return null; } public void test2(){
try{
testServer.test2(); }catch (Exception e){
log.error("RPC error: ", e); } } }
使用函数式接口后的写法:先定义统一调用类
//首先定义一个ServerExecutor public class ServerExecutor {
//不需要返回值的 public static void execute(Runnable runnable) {
try {
//调用前和调用后可以做一些处理,比如熔断、日志等等 runnable.run(); //调用前和调用后可以做一些处理,比如熔断、日志等等 } catch (Exception e) {
log.error("RPC error: ", e); } } //需要返回值的 public static <T> T executeAndReturn(Callable<T> callable) {
try {
//调用前和调用后可以做一些处理,比如熔断、日志等等 return callable.call(); } catch (Exception e) {
log.error("RPC invoke error", e); } return null; } }
使用统一定义的接口调用之后:
public class RpcTestServerImpl{
//引用微服务接口 private TestServer testServer; public T test(){
return ServerExecutor.executeAndReturn(() -> testServer.test()); } public T test2(){
ServerExecutor.execute(() -> testServer.test2()); } }
扩展知识:
@FunctionalInterface
官方文档:https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html
通过文档可以知道:
1、该注解只能标记在有且仅有一个抽象方法的接口上。
2、JDK8接口中的静态方法和默认方法,都不算是抽象方法。
3、接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么也不算抽象方法。
4、该注解不是必须的,如果一个接口符合”函数式接口”定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。
2. List分页工具类
平时开发中可能会遇到一批数据的批量查询,但是接口限制每次只能查10个,多了查询失败,这个时候就很烦,还的手动给这一批数据分页再去掉接口查询,这个时候就可以用到com.google.common.collect.Lists
工具的com.google.common.collect.Lists#partition
方法,这个方法可以帮我们自动拆分List,根据我们传入的分批大小,把大集合拆成多个小集合
好处:方便实用,不用手写
举个栗子:
List<Long> userIdList = new ArrayList<>(); userIdList.add(1L); userIdList.add(2L); userIdList.add(3L); userIdList.add(4L); userIdList.add(5L); userIdList.add(6L); userIdList.add(7L); userIdList.add(8L); List<List<Long>> partition = Lists.partition(userIdList, 3);
上面这个代码,就可以把8个元素的userIdList通过Lists.partition拆分成3个元素一个的小集合,而且顺序不变,以前不知道这个方法,被同事diss一波,然后记录一下
3. 分割符的字符串和集合互转
日常开发经常遇到这种:1,2,3,4,5,6
这种逗号分割的,要把他转成List,或者把一个List转成根据逗号分割的字符串,自己写的话就只能split加循环,这个时候又可以用到谷歌工具包
好处:方便实用,不用手写
举个栗子:
import com.google.common.base.Joiner; import com.google.common.base.Splitter; import java.util.List; public class CollectionUtil {
/ * 把list的数据根据split拼接起来,自动过滤data中的null * 栗子:data:[1,3,4,6] split:"." return: "1.3.4.6" */ public static String jointList(List data, String split){
return Joiner.on(split).skipNulls().join(data); } / * 把根据split分隔的字符串str变成List * 栗子:str:"1.3.5.7.9" split:"." return:["1","3","5","7","9"] */ public static List<String> splitIntoList(String str, String split){
return Splitter.on(split).omitEmptyStrings().splitToList(str); } }
4. 使用策略模式消除大篇幅if else
日常开发经常会遇到需要大量的if else判断,然后执行对应的代码,如果业务复杂,可能会十几个或者几十个if else,对于代码的扩展性和可读性有很大影响,而且看起来就很low,所以我们可以用到策略模式来消除大量的if else,并且让代码更具有健壮性
好处:高逼格,代码健壮,扩展性强
举个栗子:
//举例老的if else if ("xxx".equals(type)) {
System.out.println("do xxx"); } else if ("yyy".equals(type)) {
System.out.println("do yyy"); } else if ("zzz".equals(type)) {
System.out.println("do zzz"); } else if ("xyz".equals(type)) {
System.out.println("do xyz"); } // 后面可能会有更多
//定义策略执行接口 public interface DataProcessor {
Result handleData(Data data); } //定义每种数据的处理实现类 @Service("XxxProcessor") public class XxxProcessor implements DataProcessor {
@Override public Result handleData(Data data){
//do xxx .... return null; } } @Service("YyyProcessor") public class YyyProcessor implements DataProcessor {
@Override public Result handleData(Data data){
//do yyy .... return null; } } @Service("ZzzProcessor") public class ZzzProcessor implements DataProcessor {
@Override public Result handleData(Data data){
//do zzz .... return null; } } @Service("XyzProcessor") public class XyzProcessor implements DataProcessor {
@Override public Result handleData(Data data){
//do Xyz .... return null; } }
public enum DataProcessorEnum {
XXX(1, "xxx业务描述", "XxxProcessor"), YYY(2, "yyy业务描述", "YyyProcessor"), ZZZ(3, "zzz业务描述", "ZzzProcessor"), XYZ(4, "xyz业务描述", "XyzProcessor"); / * 业务type */ private Integer type; / * 业务描述 */ private String name; / * 对应处理器 * {@link DataProcessor} */ private String processor; public static DataProcessorEnum valueOf(Integer type) {
if (type == null) {
return null; } for (DataProcessorEnum dataProcessorEnum : DataProcessorEnum.values()) {
if (type.equals(dataProcessorEnum.getType())) {
return dataProcessorEnum; } } return null; } //省略get等方法 }
当实际使用的时候,我们需要拿到对应处理器的实例,这里举例是在spring容器中为例,因为DataProcessor实现上都有@Service
注解,如果不在spring容器中,可以自己实现反射获取实例的方法,这里就不再写反射获取了,大多数情况我们都是在spring环境中适用的,所以需要工具从spring 中获取对应实例,提供一个spring实例获取工具,这个应该很常见:
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext; } } //获取applicationContext public static ApplicationContext getApplicationContext() {
return applicationContext; } //通过name获取 Bean. public static Object getBean(String name){
return getApplicationContext().getBean(name); } //通过class获取Bean. public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz); } //通过name,以及Clazz返回指定的Bean public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz); } }
准备工作到这里应该算是完成了,实际使用中,可以这样操作:
public void dealData(Data bizData){
if(bizData == null){
return; } DataProcessorEnum dataProcessorEnum = DataProcessorEnum.valueOf(bizData.getType()); if(dataProcessorEnum == null){
//没有在枚举中匹配到处理器,说明业务参数不合法或者没有添加对应的业务枚举 return; } DataProcessor processor = SpringUtil.getBean(dataProcessorEnum.getProcessor(), DataProcessor.class); if(processor == null){
//没有从spring容器中获取到对应处理器到实例,属于异常情况,检查枚举配置和处理器是否正确注入spring容器 return; } //交给对应到处理器去处理 Result result = processor.handleData(bizData); //处理完成 }
到这里,使用策略模式消除if else就基本写完了,这里只是写出了基本到策略模式使用,还有其他更多的用法可以自己学习一下,而且其实我们使用策略模式前和使用后,感觉代码篇幅不减反增,肯定会想,这改造越改越复杂了呀
所以得出结论:if-else或switch case 这种分支判断的方式对于分支逻辑不多的简单业务,还是直观高效的。对于业务复杂,分支逻辑多,采用适当的模式技巧,会让代码更加清晰,容易维护,但同时类或方法数量也是倍增的。我们需要对业务做好充分分析,避免一上来就设计模式,避免过度设计!
另外再说一下,你就天天光写if else 和实习生有啥区别?怎么提升逼格?对吧
5. 当策略模式使用不值得的时候,如果优雅的处理大量if else
上面刚说完使用策略模式消除if else,立马先打脸自己,打脸自己才能成长,这次说一个使用java.util.function.Function
jdk1.8 函数接口方式处理大篇幅if else的方式
好处:高逼格、比策略模式看起来简单明了(但是需要了解函数式编程,很简单)
import java.util.HashMap; import java.util.Map; import java.util.function.Function; public class Test {
/ * 传统的 if else 解决方法 * 当每个业务逻辑有 3 4 行时,用传统的策略模式不值得,直接的if else又显得不易读 */ public String doBizService(String order) {
if ("type1".equals(order)) {
return "执行业务逻辑1"; } else if ("type2".equals(order)) {
return "执行业务逻辑2"; }else if ("type3".equals(order)) {
return "执行业务逻辑3"; }else if ("type4".equals(order)) {
return "执行业务逻辑4"; }else if ("type5".equals(order)) {
return "执行业务逻辑5"; }else if ("type6".equals(order)) {
return "执行业务逻辑6"; }else if ("type7".equals(order)) {
return "执行业务逻辑7"; }else if ("type8".equals(order)) {
return "执行业务逻辑8"; }else if ("type9".equals(order)) {
return "执行业务逻辑9"; } return "不在处理的逻辑中返回业务错误"; } //---------------------------高逼格分割线------------------------------------ / * 业务逻辑分派Map * Function为函数式接口, * 下面代码中 Function<String, Object> 的含义是接收一个String类型的变量用来获取你要执行哪个Function,实际使用中可自行定义 * Function执行完成返回一个Object类型的结果,这个结果就是统一的业务处理返回结果,实际使用中可自行定义 */ private static Map<String, Function<String, Object>> checkResultDispatcher = new HashMap<>(); / * 初始化 业务逻辑分派Map 其中value 存放的是 lambda表达式 * 也可以依赖于spring的@PostConstruct 初始化checkResultDispatcher 根据各个技术栈调整 */ static {
checkResultDispatcher.put("type1", order -> testService.handleTyep1(order)); checkResultDispatcher.put("type2", order -> testService.handleTyep2(order)); checkResultDispatcher.put("type3", order -> testService.handleTyep3(order)); checkResultDispatcher.put("type4", order -> testService.handleTyep4(order)); checkResultDispatcher.put("type5", order -> testService.handleTyep5(order)); checkResultDispatcher.put("type6", order -> testService.handleTyep6(order)); checkResultDispatcher.put("type7", order -> testService.handleTyep7(order)); checkResultDispatcher.put("type8", order -> testService.handleTyep8(order)); checkResultDispatcher.put("type9", order -> testService.handleTyep9(order)); } public Object handleBizService(String type) {
//从逻辑分派Dispatcher中获得业务逻辑代码,result变量是一段lambda表达式 Function<String, Object> result = checkResultDispatcher.get(type); if (result != null) {
//执行这段表达式获得String类型的结果 return result.apply(type); } return "不在处理的逻辑中返回业务错误"; } }
补一下关于java.util.function
包下面各个类的简单了解
name | type | description |
---|---|---|
Consumer | Consumer< T > | 接收T对象,不返回值 |
Predicate | Predicate< T > | 接收T对象并返回boolean |
Function | Function< T, R > | 接收T对象,返回R对象 |
Supplier | Supplier< T > | 提供T对象(例如工厂),不接收值 |
UnaryOperator | UnaryOperator< T > | 接收T对象,返回T对象 |
BiConsumer | BiConsumer<T, U> | 接收T对象和U对象,不返回值 |
BiPredicate | BiPredicate<T, U> | 接收T对象和U对象,返回boolean |
BiFunction | BiFunction<T, U, R> | 接收T对象和U对象,返回R对象 |
BinaryOperator | BinaryOperator< T > | 接收两个T对象,返回T对象 |
使用Function
处理策略需求确实很方便很好用,但是有一个问题,就是在Function
中无法抛出非RuntimeException
的异常,很多时候我们自定义的异常并不是继承自RuntimeException
的,这个时候使用Function
就会被异常困扰,这个时候我们可以模仿Function
来自定义一个我们自己的FunctionWithException
:
/ * 一个可以抛出Exception的Function * 是java.util.function.Function的变体 * 主要解决java.util.function.Function中不能抛出非RuntimeException的问题 */ @FunctionalInterface public interface FunctionWithException<T, R> {
R apply(T t) throws Exception; }
然后再做一个FunctionWithException
的包装类:
import java.util.function.Function; / * FunctionWithException的包装器 */ public class ExceptionWrapper {
public static <T, R> Function<T, R> wrap(FunctionWithException<T, R> function) {
return t -> {
try {
return function.apply(t); } catch (Exception e) {
throw new RuntimeException(e); } }; } }
这个时候我们就可以不使用java.util.function.Function
,而使用我们自己的FunctionWithException
,使用例子如下:
private static Map<String, Function<String, Object>> checkResultDispatcher = new HashMap<>(); @PostConstruct public void init() {
checkResultDispatcher.put("type1", ExceptionWrapper.wrap(this::handleTyep1)); checkResultDispatcher.put("type2", ExceptionWrapper.wrap(this::handleTyep2)); } public Object handleBizService(String type) throws Exception {
//从逻辑分派Dispatcher中获得业务逻辑代码,result变量是一段lambda表达式 Function<String, Object> result = checkResultDispatcher.get(type); if (result == null) {
// 这个BizException是自定义的异常 它继承自Exception.class throw new BizException("参数异常"); } try {
return result.apply(type); } catch (Exception e) {
// 捕获内部的异常,转成我们自定义的异常处理 throw new BizException(e.getMessage()); } } private Object handleTyep1(String type){
//... } private Object handleTyep2(String type){
//... }
这样就可以完美的处理java.util.function.Function
的异常抛出问题了.
6. 通用的redis获取值并处理空缓存方法
日常开发会遇到一种缓存无效的情况:普通用获取redis值的时候,发现获取到的是空字符串,这个时候就会去查库,然后mysql库里面查询结果也是空的,这个时候就会有问题,如果一直调用这个key获取数据,缓存查不到,数据库也查不到,如果高频调用就会对数据库造成压力。对于这种情况,可以在库里查询为空的时候,给redis里面存一个空缓存{}
,当下次查询redis的时候,得到的不再是空字符串""
,对于这种逻辑,本来是需要自己写逻辑的,然后每个这种都要写一遍判断逻辑,后来在开发中遇到同事(大佬)写的一个通用方法,很好用,记录下来:
public class CacheConstant {
/ * 各类空缓存对象value */ public static final String EMPTY_CACHE = "{}"; }
public static <T> T get(String key, Long timeout, Class<T> clazz, Callable<T> getFunction, Runnable exceptionRunnable) {
RedisService redisService = SpringUtil.getBean(RedisService.class); if (redisService == null) {
log.error("Redis查询接口 初始化异常"); return null; } if (StringUtils.isBlank(key)) {
return null; } String cacheValue = redisService.get(key); T object = null; if (StringUtils.isNotBlank(cacheValue)) {
if (CacheConstant.EMPTY_CACHE.equals(cacheValue)) {
return null; } try {
object = JSONObject.parseObject(cacheValue, clazz); } catch (Exception e) {
log.error("RedisUtil get error, key:[" + key + "], cacheValue:[" + cacheValue + "]", e); } if (object != null) {
return object; } else {
//说明解析发生了异常,调用解析异常处理,比如可以做一些缓存过期处理之类的操作 if(exceptionRunnable != null){
exceptionRunnable.run(); } } } try {
if(getFunction != null){
object = getFunction.call(); } } catch (Exception e) {
log.error("getFunction.call error", e); return null; } cacheValue = object != null ? JsonUtil.object2json(object) : CacheConstant.EMPTY_CACHE; redisService.set(key, cacheValue, timeout); return object; }
7. CompletableFuture基本使用
//创建第一个任务CompletableFuture 使用线程池taskExecutor CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> countVO.setTotal(baseDao.selectCount(queryWrapper)), taskExecutor); //创建第二个任务CompletableFuture 使用线程池taskExecutor CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> countVO.setTotal(baseDao.selectCount(queryWrapper)), taskExecutor); //创建第三个任务CompletableFuture 使用线程池taskExecutor CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> countVO.setTotal(baseDao.selectCount(queryWrapper)), taskExecutor); //添加三个CompletableFuture到allFuture CompletableFuture<Void> allFuture = CompletableFuture.allOf(future1, future2, future3); // allFuture 等待三个任务执行完成并结束,超时时间200毫秒 allFuture.get(200, TimeUnit.MILLISECONDS);
创建类
- runAsync 异步执行,无返回值
- supplyAsync 异步执行,有返回值
- anyOf 任意一个执行完成,就可以进行下一步动作
- allOf 全部完成所有任务,才可以进行下一步任务
接续类
- 以Async结尾的方法,都是异步方法,对应的没有Async则是同步方法,一般都是一个异步方法对应一个同步方法。
- 以Async后缀结尾的方法,都有两个重载的方法,一个是使用内容的forkjoin线程池,一种是使用自定义线程池
- 以run开头的方法,其入口参数一定是无参的,并且没有返回值,类似于执行Runnable方法。
- 以supply开头的方法,入口也是没有参数的,但是有返回值
- 以Accept开头或者结尾的方法,入口参数是有参数,但是没有返回值
- 以Apply开头或者结尾的方法,入口有参数,有返回值
- 带有either后缀的方法,表示谁先完成就消费谁
- whenCompleteAsync:处理完成或异常,无返回值
- handleAsync:处理完成或异常,有返回值
状态取值类
- join 合并结果,等待
- get 合并等待结果,可以增加超时时间;get和join区别,join只会抛出unchecked异常,get会返回具体的异常
8. @ConditionalOnMissingBean使用
@ConditionalOnMissingBea该注解表示,如果存在它修饰的类的bean,则不需要再创建这个bean。日常代码中写公共jar包代码,经常会有接口需要默认实现和自定义实现,jar包中写公共实现,如果需要对接口实现自定义实现,就可以使用@ConditionalOnMissingBean快速实现
/ * 默认实现配置 */ @Configuration public class DefaultConfiguration {
@Bean @ConditionalOnMissingBean(name = "TestDefaultService")//指定Bean的名称 public TestDefaultService testService() {
return (param) -> {
//...默认公共实现 }; } }
@Qualifier("TestDefaultService")//指定Bean名称 @Resource private TestDefaultService testDefaultService;
这样使用之后,当手动实现了TestDefaultService
并指定BeanName为“TestDefaultService”
,比如在接口实现类上这样写之后
@Service("TestDefaultService")
手动实现的接口就会被注入,而默认实现的就不会被注入,如果不手动实现接口,那么默认实现就会被注入。
9. 使用函数式编程实现代理模式
先上代码:
import lombok.Data; import lombok.EqualsAndHashCode; / * 返回值 */ @EqualsAndHashCode(callSuper = true) @Data public class Test1DTO extends TestBaseDTO {
private String name; }
import lombok.Data; / * 返回值基类 */ @Data public class TestBaseDTO {
private Integer value; }
public interface Test1Service {
TestBaseDTO handle() throws Exception; }
public class Test1Util {
public static <R extends TestBaseDTO> R execute(Test1Service test1Service) throws Exception {
System.out.println("在执行之前做一些事情"); R handle = (R) test1Service.handle(); System.out.println("在执行之后做一些事情"); return handle; } }
/ * 测试类 */ public class Test1 {
public static void main(String[] args) throws Exception {
TestBaseDTO execute = Test1Util.execute(() -> {
// 这里可以写想要执行的逻辑代码 // 返回值可以自定义 只要继承TestBaseDTO就行 Test1DTO testBaseDTO = new Test1DTO(); testBaseDTO.setValue(1); testBaseDTO.setName("test"); return testBaseDTO; }); System.out.println(execute); } }
如上代码,就是通过函数式编程搞了一个Test1Util
来处理逻辑代码执行时被代理的逻辑。这个方式可改造的方式很多,可以轻易改造成适合自己的业务的写法。
10. 根据数学公式计算结果的工具类
依赖:
<dependency> <groupId>com.alibaba</groupId> <artifactId>QLExpress</artifactId> <version>3.2.0</version> </dependency>
工具类:
/ * 公式计算工具 * * @author jingchuan */ public class FormulaCalculateUtil {
public static final ExpressRunner runner = new ExpressRunner(true, false); public static final DefaultContext<String, Object> context = new DefaultContext<>(); / * 根据计算公式 和 公式中每个元素的值 计算出结果 * * @param formula 计算公式 * @param map 公式中每个元素的值 * @param <T> * @return * @throws Exception */ public static <T> T calculate(String formula, Map<String, Object> map) throws Exception {
if (map != null && !map.isEmpty()) {
context.putAll(map); } try {
return (T) runner.execute(formula, context, null, true, false); } finally {
context.clear(); } } public static void main(String[] args) throws Exception {
String expression = "A+(B-C)*D/E"; Map<String, Object> map = new HashMap<>(); map.put("A", 5); map.put("B", 4); map.put("C", 3); map.put("D", 2); map.put("E", 2); Object calculate = FormulaCalculateUtil.calculate(expression, map); System.out.println("计算结果:" + calculate.toString()); } }
这个工具类是阿里大佬写的,依赖中有很多其他功能,这里只是使用了其中的公式计算
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/143060.html