总结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研发过程中一些实用的工具/类/方法/特性

相关推荐
C雨后彩虹几秒前
计算疫情扩散时间
java·数据结构·算法·华为·面试
2401_832131951 分钟前
Python单元测试(unittest)实战指南
jvm·数据库·python
2601_949809594 分钟前
flutter_for_openharmony家庭相册app实战+我的Tab实现
java·javascript·flutter
猷咪26 分钟前
C++基础
开发语言·c++
IT·小灰灰27 分钟前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧29 分钟前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q30 分钟前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳030 分钟前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾30 分钟前
php 对接deepseek
android·开发语言·php
vx_BS8133034 分钟前
【直接可用源码免费送】计算机毕业设计精选项目03574基于Python的网上商城管理系统设计与实现:Java/PHP/Python/C#小程序、单片机、成品+文档源码支持定制
java·python·课程设计