总结java研发过程中一些实用的工具/类/方法/特性

在这里总结java研发过程中一些实用的工具/类/方法/特性,有时合理运用能够提升效率并且节省工作量,使得代码更丝滑更优雅。

MapStruct 是一个代码生成器(可以对比其他的一共12种如BeanUtils和Jmapper;地址zhuanlan.zhihu.com/p/460067093...[1] Java Bean 类型之间映射的实现。生成的映射代码使用纯方法调用,因此快速、类型安全且易于理解。 MapStruct 是一个 Java 注释处理器,适用于Java Bean 类型之间映射,多层应用程序通常需要在不同的对象模型(例如实体和 DTO)之间进行映射。与其他映射框架相比,MapStruct在编译时生成bean映射,这确保了高性能,允许快速的开发人员反馈和彻底的错误检查。遵循约定而不是配置方法,MapStruct使用合理的默认值,但在配置或实现特殊行为时会避开您的方式。当不同层级的类对象之间有公共的属性,需要进行两者间相互转换的时候,可以用mapstruct来构建两者的convert方法进行直接转换,不需要手动设置属性方法,减少工作量。

import org.mapstruct.factory.Mappers;

@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)

@DecoratedWith(IssueRecordConverterDecorator.class)
public interface IssueRecordConverter {
 
    IssueRecordConverter INSTANCE = Mappers.getMapper(IssueRecordConverter.class);
 
    @Mapping(target = "id", ignore = true)
    IssueRecordPO convertToInsert(IssueRecord record);
}

至于最后生成的映射代码在哪里,其实是在target的生成文件包里面,为了方便查看,可以参考下面这个设置

操作过后,idea就能识别实现类,就可以点绿色快捷键进入实现类,方便检查mapstruct的结果 blog.csdn.net/Yzy9595/art...[2]

java8的新特性,Java中的Optional是一个容器对象,它可以包含一个非空值,也可以为空。它的主要作用是在编写代码时避免空指针异常。用了之后其实不需要后面加多余的 if(obj!=null) 的判断了

    public List<ActivityConfig> queryActivityConfigList(Long areaId, ActivityTypeEnum activityType) {
        List<ActivityConfigPO> configPOList = xxxConfig.getAreaAcvitityMap().get(areaId);
        
        return Optional.ofNullable(configPOList)
            .map(list -> list.stream()
                .map(mapToConfig(areaId, activityType)
                .collect(Collectors.toList()))
            .orElse(new ArrayList<>());
    }

java8的新特性,函数式接口,即只包含一个抽象方法的接口,也称为SAM接口,即Single Abstract Method interfaces,用@FunctionalInterface注解进行标注,常见的函数式接口如Comparator,Runnable,Callable。可以轻量级的将代码封装为数据,使代码简洁,易于理解。

通常在流式计算返回时候内部调用一下。

    @NotNull
    private static Function<ActivityConfigPO, ActivityConfig> mapToConfig(Long areaId, ActivityTypeEnum tag) {
        return configPO -> ActivityConfig.builder()
            .id(configPO.getId())
            .areaId(areaId)
            .desc(configPO.getDesc())
            .activityType(tag)
            .activityId(configPO.getActivityId())
            .build();
    }

java8新特性,简单返回对象映射转换的时候可以使用。上面示例返回map(list -> list.stream().map(mapToConfig(areaId, activityType).collect(Collectors.toList())));用到了

    public static ActivityConfigPO findItem(List<ActivityConfigPO> list, Long Id) {
        return list.stream().filter(item -> item.getId().equals(Id)).findFirst().orElse(null);
    }

又或者java的基本数据和引用数据转换,将int[]数组转换成Integer[]数组: ArrayList.asList List.toArray Stream()->函数 boxed()拆装箱

import java.util.Arrays;
import java.util.stream.IntStream;

public class ArrayConvertExample {
    
    public static void main(String[] args) {
        Integer[] obj = new Integer[] {null, 1, 2, 3};
        int[] newObj = Arrays.stream(obj).mapToInt(i -> i == null ? 0 : i.intValue()).toArray();
        
        
        
        int[] obj2 = new int[] {4, 5, 6};
        Integer[] newObj2 = Arrays.stream(obj2).boxed().toArray(Integer[]::new);
        Integer[] newObj3 = IntStream.of(obj2).boxed().toArray(Integer[]::new);
    }

}

注意下Stream.map()、Stream.filter()功能区别 map--映射,filter--匹配过滤 filter()方法用于根据条件过滤流中的元素; map()方法用于对流中的每个元素执行转换操作。 根据具体的需求,选择使用filter()方法还是map()方法。如果你想筛选满足特定条件的元素,使用filter();如果你想对元素进行转换,使用map()。 1.forEach----主要用于改变当前数组里面的值,forEach方法是Stream中的一个方法,它可以让我们对Stream中的元素进行遍历。forEach方法接收一个Consumer类型的参数,这个Consumer类型的参数其实就是一个函数式接口,它的参数为Stream中的元素,没有返回值。 2.filter----主要用于过滤当前数组,找出符合条件的元素,返回一个新的数组,不会改变原数组。filter()方法用于根据指定的条件筛选流中的元素,只保留符合条件的元素。它接受一个Predicate函数式接口作为参数,该接口定义了用于判断元素是否满足条件的方法。 3.map----则可以改变当前循环的值,返回一个新的被改变过值之后的数组,一般用来处理需要修改某一个数组的值。map()方法用于对流中的每个元素执行一个映射操作,将每个元素根据指定的映射规则转换为另一种类型。它接受一个Function函数式接口作为参数,该接口定义了将元素转换为另一种类型的方法。 参考博客blog.csdn.net/Whx123A/art...[3]

以往创建对象和属性Set赋值是分离的,或者通过构造器方式需要有对应的构造函数,整个过程不够直观和连贯。 因此我们可以通过@lombok.Builder注解,进行建造者模式的构建,能够清晰理解对象创建和属性注入需要的内容和具体构建过程。

@Data
@lombok.Builder
public class ActivityEntity {
}

private xxx builderActivity(ActivityRequest request) {
  
        return ActivityEntity.builder()
                .id(request.getActivityId()) 
                .name(request.getName()) 
                .status(request.getStatus()) 
                .startTime(request.getStartTime()) 
                .endTime(request.getEndTime()) 
                .description(request.getDescription()) 
                .build();
        }

以前我们写胶水代码时候喜欢通过类似str1+str2的方式拼串,但是这种拼起来的结构看起来比较混乱,阅读起来不直观,容易出错。现在我们可以用String.format划分固定和可变部分进行格式化替换输出:

示例:客户端展示输出"满x元减y元优惠券"

String str = "满"+x+"元减"+y+"元优惠券";

private static final String COUPON_DESC = "满%s元减%s元优惠券";
String realCoupon = String.format(COUPON_DESC, x, y);

或者当我们想进行指定类型输出的时候,如汇率与金额的浮点运算结果需要保留特定有效位数:

log.fine((String.format("ExRate %.2f, 金额$%.2f)", x, y));

以前我们打印日志喜欢用的输出可能是这些:

System.out.println("xxxxxx"); 
System.out.printf("xxxxxx"); 
try{

}catch(exception e){
 e.printStackTrace("xxxxxx");
}finally{

}

但是System.out和printStackTrace都是存在线程死锁风险的: 参考www.kdun.cn/ask/9230.ht...[4] System.out属于输出流,多线程争夺共享资源时候可能会发生阻塞导致死锁; printStackTrace占用系统资源,多线程争夺共享资源时候可能会死锁。并且可能会不断向上请求栈,直到异常被JVM捕获,如果一直没处理则也可能会导致死锁。 早期的java自带的log4j也可能死锁,还是推荐使用@slf4j,可以避免死锁问题。 业务常用的方式是@Slf4j带的日志,不会有上面的风险,先定义一个Logger,然后支持不同级别的输出如fine()/info()/error()等。

private static final Logger LOGGER = LoggerFactory.getLogger(xxx.class);

 try {
        
    } catch (Throwable e) {
        LOGGER.error("创建订单异常:buildOrderEntity={}, buildOrderEntity, e);
        throw new BizException(OrderErrorCode.ORDER_CREATE_ERROR);
    }

据说是速度最快的JSON序列化工具(可以对比一些其他的比如Gson、java自带的Jackson等;地址blog.csdn.net/qinxun20080...[5]

Object obj = new Obj();
String jsonString = JSON.toJSONString(obj);
Object obj = JSON.parseObject(jsonString);
JSONobject jsonObj = JSON.toJSON(obj);

List<Object> objList = new ArrayList<>();
String jsonListString = JSON.toJSONString(objList);
List<Object> objList = JSON.parseArray(jsonListString);




if(content instanceof String) return content;
else return JSON.toJSONString(content);

但是要注意有一些坑,比如JSON.toJSONString里面传入String参数会报错,输入的时候先校验一下,if(content instanceof String) 就直接传输String类型的content就行。并且要区分是转成对象用parseObject,转成列表要用parseArray。

通常在Facade层或者Service入参调用真正的内部方法之前先加上一层参数校验,以防止对真正的业务逻辑造成注入干扰。Validator可以手动定义,也可以基于javax.validation包的默认方法校验,或者公司内部的FastValidator工具。

使用示例,调用Service注册方法之前对用户上传信息进行前置校验:

    @Autowired
    private UserInfoValidator userInfoValidator ;
    
    @Override
    public Boolean validUserInfo(Request request) {
        return userInfoValidator.validUserInfo(request);
    }
    
    Boolean validUserInfo(){return xxxx;}
   
   xxx validate = validator.validate(request);

对返回的结果进行包装,这个Result泛型类是可以自己定义的,把核心返回值作为泛型类Result的一个子属性,并且可以返回一些其他扩展属性比如自定义的model数据、响应码code和message等,方便定位分析排查执行信息和返回状态。

    @Override
    public Result<xxxxResponse> xxxQuery (Request request) {
        return ResultWrapper.wrapper(() -> {
          XxxxResponse xxxxResponse = new xxxxResponse();
    response.setXXXX(xxxx);
    response.setXXXX(xxxx);
    response.setXXXX(xxxx);
    return xxxxResponse;
        });
    }

可以理解成一种装饰器模式,通过wrapper方法对返回结果进行包装,支持传入一个函数式接口作为参数,并且返回结果可以用Result< xxxxResponse>去接收。

@Slf4j
public class ResultWrapper {
    public static <T> Result<T> wrapper(Supplier<T> supplier) {
    
    }
@Override
    public Result<xxxxResponse> xxxQuery (Request request) {
        return ResultWrapper.wrapper(() -> {
          XxxxResponse xxxxResponse = new xxxxResponse();
    response.setXXXX(xxxx);
    response.setXXXX(xxxx);
    response.setXXXX(xxxx);
    return xxxxResponse;
        });
    }

很多场景Spring提供的BeanUtils性能不如MapStruct,但是还是介绍一下用法。

@ApiModelProperty(value = "Function_call")
private Function_call function_call;

BeanUtils.copyProperties(functionDTO.getProperties(), properties);

虽然Assembler/Converter是非常好用的对象,但是当业务复杂时,手写Assembler/Converter是一件耗时且容易出bug的事情,所以业界会有多种Bean Mapping的解决方案,从本质上分为动态和静态映射。 动态映射方案包括比较原始的 BeanUtils.copyProperties、能通过xml配置的Dozer等,其核心是在运行时根据反射动态赋值。动态方案的缺陷在于大量的反射调用,性能比较差,内存占用多,不适合特别高并发的应用场景。 所以在这里我给用Java的同学推荐一个库叫MapStruct(MapStruct官网)。MapStruct通过注解,在编译时静态生成映射代码,其最终编译出来的代码和手写的代码在性能上完全一致,且有强大的注解等能力。如果你的IDE支持,甚至可以在编译后看到编译出来的映射代码,用来做check。在这里我就不细讲MapStruct的用法了,具体细节请见官网。 用了MapStruct之后,会节省大量的成本,让代码变得简洁。 参考博客 blog.csdn.net/Taobaojishu...[6]

JDK1.5以后java提供了自动拆装箱的机制,但是有时候比如Stream计算时候,还是可能需要手动代码实现拆装箱的。比如Java中的IntStream boxed()方法,可以将int基本类型装箱成Integer。

import java.util.*;
import java.util.stream.Stream;
import java.util.stream.IntStream;
public class Demo {
   public static void main(String[] args) {
      IntStream intStream = IntStream.range(20, 30);
      Stream<Integer> s = intStream.boxed();
      s.forEach(System.out::println);
   }
}

常用的场景是 Java Stream中List、Integer[]、int[] 的相互转换。参考博客 blog.csdn.net/studyday1/a...[7]

Integer[] vinteger = Arrays.stream(vint).boxed().toArray(Integer[]::new);

int[] vint = Arrays.stream(vinteger).mapToInt(Integer::valueOf).toArray();

还有一件事,List.toArray() Arrays.asList()方法底层可能也有坑的。 参考一下 blog.csdn.net/h4241778/ar...[8] 比如ArrayList.toArray() 传入String会报出类转换异常, 并且Arrays.asList()转换以后得list的size是不能更改的。

通常会用到可以选择的类型的时候,如果直接用String类型进行基于map映射后转到对应执行,有时候会有误操作增删导致字符串更改造成不匹配的风险,排查起来困难,并且把String定义成常量的实际意义不如定义成枚举更合理。

public enum AppEnum {
    IOS("iOS"),
    ANDROID("android");
    @Getter
    final private String platform;
    AppEnum(String platform) {
        this.platform = platform;
    }
    public static AppEnum of(String platform) {
        return Arrays.stream(values())
                .filter(item -> item.getPlatform().equalsIgnoreCase(platform))
                .findFirst()
                .orElse(null);
    }
}

 if (AppEnum.ANDROID.getPlatform().equals(type)) {
            
} else if (AppEnum.IOS.getPlatform().equals(type) {
            
}

但是注意枚举类在序列化时候可能会出现一些问题,Go语言就不支持枚举,转化之前可以先变成String,枚举类在业务类内部用一下还是比较方便的。

可以将Lambda表达式理解为一个匿名函数; Lambda表达式允许将一个函数作为另外一个函数的参数; 我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码作为实参),也可以理解为函数式编程,将一个函数作为参数进行传递。Lambda表达式能够让程序员的编程更加高效。 lambda表达式和方法引用使用前提:函数式接口 1.@FunctionalInterface 语法格式严格要求当前接口有且只能有一个尚未完成的缺省属性为 public abstract 修饰方法。 2.函数式接口一般用于方法的增强,直接作为方法的参数,实现函数式编程。 只有函数式接口的变量或者是函数式接口,才能够赋值为Lambda表达式。这个接口中,可以有默认方法,或者是静态方法。

    @Override
    public ActivityConfig queryActivityConfig(Long areaId, Long configId) {
        return Optional.of(xxxConfig.getActivityConfig().getAreaAcvitityMap().get(areaId))
            .map(list -> findItem(list, configId))
            .map(mapToConfig(areaId, ActivityTypeEnum.XXX))
            .orElse(null);
    }
    @NotNull
    private static Function<ActivityConfigPO, ActivityConfig> mapToConfig(Long areaId, ActivityTypeEnum tag) {
    
        return configPO -> ActivityConfig.builder()
            .id(configPO.getId())
            .areaId(areaId)
            .desc(configPO.getDesc())
            .activityType(tag)
            .activityId(configPO.getActivityId())
            .build();
    }

其他一些场景,比如实现手动定制排序的时候,可以不同new Comparator()接口了,我们可以直接用lambda表达式,代码量会降低很多。

import java.util.*;
class Solution {
    public String sort(Integer[] nums) {
        Arrays.sort(nums, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
             
                return o2 - o1;
            }
        });
         
        return Arrays.toString(nums);
    }
}

public class Main {
    public static void main(String[] args) {
        Solution solution = new Solution();
        Integer[] arr = {1, 2, 3, 4, 5};
        System.out.println(solution.sort(arr));
    }
}

其他内容参考博客 blog.csdn.net/qq_45944185...[9]

git发布相关,研发血泪经验总结 1.每次注意修改的是不是Feature分支 2.合并之前先git pull一下最新代码 3.合并master之前,feature分支先本地拉取master合并一下,预先更新包引用并解决冲突 4.按照紧急程度选择新功能放在一个分支还是两个分支,前后端发的时候商量好,先发后端 5.测试和预发公共环境合并commit以后先测一下,跑通了再push和发布,别污染公共环境。

最近项目也有一个执行脚本读取接口数据生成Excel的任务,于是首先想到了阿里的EasyExcel,对它的了解始于尚硅谷宋**的那个医院在线挂号项目。 用的比较多的功能,主要是文件的读取解析和数据写入文件。 参考blog.csdn.net/weixin_6850...[10] blog.csdn.net/wyr1235/art...[11] blog.csdn.net/weixin_7307...[12]

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.support.ExcelTypeEnum;
import org.apache.poi.ss.formula.functions.T;
import java.util.LinkedList;
import java.util.List;
 

 * @author wzx
 */
public class ExcelUtil {
 
 
    
     * 封装Excel中的数据到指定的实体类中
     * @param typeClass 指定的实体类的字节码类别
     * @param readPath Excel的文件路径
     * @return 指定的实体类对象的集合(每个对象代表每一条数据)
     */
    public static List<T> getDataFromExcel(Class<T> typeClass , String readPath){
        List<T> list = new LinkedList<>();
        EasyExcel.read(readPath)
                .head(typeClass)
                .sheet()
                .registerReadListener(new AnalysisEventListener<T>() {
 
                    @Override
                    public void invoke(T excelData, AnalysisContext analysisContext) {
                        list.add(excelData);
                    }
 
                    @Override
                    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                        System.out.println("数据读取完毕");
                    }
                }).doRead();
        return list;
    }
 
    
     * 将封装好的数据写入Excel中
     * @param list 写入的数据集合
     * @param writePath 写入的Excel文件的路径
     * @param sheet excel表中生成的sheet表名
     * @param excelType 插入的excel的类别,有xls、xlsx两种
     */
    public static <T> void saveDataToExcel(List<T> list, String writePath, String sheet, ExcelTypeEnum excelType, Class<T> clazz) {
        
        EasyExcel.write(writePath)
                .head(clazz)
                .excelType(excelType)
                .sheet(sheet)
                .doWrite(list);
    }
}

有已经帮忙封装好工具类的,写文件比较简单,配置好对应的属性名称映射就行,文件会在指定的路径生成,读可能分成从磁盘路径读和从网络接收读取并解析。 读的方式可以分成同步读和监听器读取。 blog.csdn.net/m0_59084856...[13] 另外补充一些东西,ivoke返回LinkedHashMap类型的数据,如果我们想要获取表格头的属性,可以通过invokeHeadMap方法来获取。还有就是Controller层的文件的传输,参数类型是@RequestParam("file") MultipartFile file,然后通过InputStream inputStream = file.getInputStream()---调用 readExcel(inputStream,"xxxdatatype");进行输入流的读取。

    private List<HashMap<String, String>> readExcel(InputStream inputStream) throws IOException {

        HashMap<String,String> header = new HashMap<>();
        List<HashMap<String, String>> data = new ArrayList<>();
        EasyExcel.read(inputStream, new AnalysisEventListener<Object>() {
            @Override
            public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context){
                Integer currentRowNum = context.getCurrentRowNum(); 

                for (Map.Entry<Integer, String> entry : headMap.entrySet()) {
                    header.put(String.valueOf(entry.getKey()),entry.getValue());
                }
                LOGGER.info("Parsed header: {}", header);
            }

            @Override
            public void invoke(Object object, AnalysisContext context) {
                
                Integer currentRowNum = context.getCurrentRowNum(); 
                
                    HashMap<String, String> row = new HashMap<>();
                    LinkedHashMap<Integer, String> dataMap = (LinkedHashMap<Integer, String>) object;

                    int index = 0;
                    for (Map.Entry<Integer, String> entry : dataMap.entrySet()) {
                        row.put(header.get(String.valueOf(index++)),entry.getValue());
                        if(index==header.keySet().size()) index = 0;
                    }
                    data.add(row);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                
            }
        }).sheet().doRead();

        
        data.add(0, header); 
        return data;
    }

另外注意文件上传的一些问题,网络请求的type类型是multipart/form-data,经过F12查看网络传输的内容可以看到文件传输的类型是二进制文件binary类型。

帮助你做分页数据处理的,比如chat对话记录的批量写入和批量读取,参考文章介绍: 官网github.com/pagehelper/...[14] blog.csdn.net/qq_25498677...[15] 可以顺便下一个maybatis-x的插件,红色和蓝色小鸟点击跳转切换着看挺方便的。

    @Resource
    private xxxMsgMapper xxxMsgMapper;
    
    
    
        PageInfo<xxxMsg> pageInfo = PageHelper.startPage(1, limit, false).doSelectPageInfo(() -> xxxMsgMapper.selectByExample(example));
        List<xxxMsg> xxxMsgs = pageInfo.getList();

这个上手起来感觉挺高大上的,线上排查很方便。线上机器部署多个集群的时候,流量大的时候一定能打到某个机器,访问量不多的时候,比如预发就两台机器pods,从webshell进入,这个时候请求负载均衡不确定路由到哪一台,可以两边都开着,然后监听访问,区分读还是写的请求,如果是读接口,可以多点几下,肯定有一些会路由到的。

操作步骤--- (1)机器的webShell进入终端,可能会要求VPN的6位动态口令单点登录(阿里郎或者飞连) (2)使用下面命令安装并启动进入Arthas 下载arthas-boot.jar,然后用java -jar的方式启动

curl -O https:
java -jar arthas-boot.jar

(3)选择进程 启动后首先选择attach的进程,Arthas会attach到目标进程上

(4)然后就可以用命令的形式,使用Arthas的功能 命令列表 arthas.aliyun.com/doc/command...[16] 常用的命令 dashboard-----当前系统的实时数据面板 sc----查看 JVM 已加载的类信息 trace----链路排查 watch---查看出入参 heapdump - dump java heap, 类似 jmap 命令的 heap dump 功能 jvm - 查看当前 JVM 的信息 stack - 输出当前方法被调用的调用路径 monitor - 方法执行监控,比如性能耗时等 thread----- 查看当前 JVM 的线程堆栈信息 还支持一些基础linux命令 推出就用quit/exit关闭当前会话 或者 直接stop完全退出

注意格式 有的命令需要类的全路径名称

sc com.xxx.generate*
trace com.xxx.GenerateImpl generatexxxData
watch com.xxx.GenerateImplxxxData -x 4

官方的手册 arthas.aliyun.com/doc/quick-s...[17] 一些额外的链接 blog.csdn.net/beiduofen20...[18] blog.51cto.com/u_12218/848...[19]

功能调试相关,MockMVC、PostMan、ApiPost、HTTP请求Client、okhttp-utils等 据说Apipost = Postman + Swagger + Mock + JMeter JSON查看工具 www.json.cn/[20] 可以看到内部有多少个对象

plantUML语雀里面操作起来很方便,语雀画个时序图 blog.csdn.net/asd13583550...[21]

内存泄漏排查 MAT分析 JProfile等 zhuanlan.zhihu.com/p/535069800...[22] 项目线上的GC频次和CPU、内存等的占用监控情况 可以看深堆区和浅堆区的比例 大对象的占用排序 线程调用stack 找到具体代码行数

forEach 增强for循环 blog.csdn.net/weixin_4599...[23] out 对比break/continue 终止多轮循环 clone方法 深/浅拷贝 @Value("${xxx.xxxxx}") private String xxxxx; 读取prop配置文件里面的内容进行动态赋值 分页查询 子查询 深分页问题 ORM框架 MybatisPlus 或者 基于JPA的Script标签形式

String字符串长度超过javac编译器运行的类型,可以更改类型选择Eclipse编译器。

在Java中,String类型的长度取决于系统内存的限制,因此在理论上没有固定的长度限制。 在Java中,String类型是使用Unicode字符集来存储字符串的,每个字符使用16位表示,因此单个字符串对象的最大长度是65535个字符(即2的16次方减1)。然而,这只是一个理论上的限制,因为实际上在Java中创建String类型的对象时,受限于内存大小和可用的虚拟机堆空间大小。 因此,String类型的长度在Java中受系统内存的限制,而不是固定的字符数限制。 参考 blog.csdn.net/weixin_4257...[24] www.jb51.net/article/273...[25]


在这里总结java研发过程中一些实用的工具/类/方法/特性,有时能提升效率并且节省工作量,使得代码更丝滑更优雅,用的时候回来查一下,结合业务实践真正理解它们的精髓和方便之处,更好地为实际工作服务。

原文地址:总结java研发过程中一些实用的工具/类/方法/特性

相关推荐
希忘auto12 分钟前
详解MySQL安装
java·mysql
娅娅梨14 分钟前
C++ 错题本--not found for architecture x86_64 问题
开发语言·c++
汤米粥20 分钟前
小皮PHP连接数据库提示could not find driver
开发语言·php
冰淇淋烤布蕾23 分钟前
EasyExcel使用
java·开发语言·excel
拾荒的小海螺29 分钟前
JAVA:探索 EasyExcel 的技术指南
java·开发语言
秀儿还能再秀44 分钟前
机器学习——简单线性回归、逻辑回归
笔记·python·学习·机器学习
Jakarta EE1 小时前
正确使用primefaces的process和update
java·primefaces·jakarta ee
马剑威(威哥爱编程)1 小时前
哇喔!20种单例模式的实现与变异总结
java·开发语言·单例模式
白-胖-子1 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
好睡凯1 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法