• [技术干货] java线程池怎么用最安全?
     Java线程池是一种管理线程的工具,它可以在需要时创建新的线程,也可以在不需要时回收和重用已经存在的线程。使用线程池可以有效地减少线程的创建和销毁开销,提高系统性能。然而,如果不正确使用线程池,可能会导致资源浪费、系统崩溃等问题。因此,如何安全地使用Java线程池是非常重要的。  以下是一些关于如何最安全地使用Java线程池的建议:  1. 合理设置线程池大小:线程池的大小应该根据系统的硬件资源、并发任务的数量和任务的处理时间来设置。如果线程池太小,可能会导致任务排队等待处理,影响系统性能;如果线程池太大,可能会消耗过多的系统资源,导致系统崩溃。  2. 使用有界队列:有界队列可以防止任务过多而无法处理的情况。当队列满时,线程池会根据设定的策略拒绝新任务或者丢弃旧任务。这样可以保证系统的稳定性。  3. 避免长时间执行的任务:线程池中的线程是复用的,如果某个任务长时间执行,可能会导致其他任务无法得到及时处理。因此,对于长时间执行的任务,应该单独启动一个线程来处理。  4. 使用合适的拒绝策略:当线程池和队列都无法处理新任务时,线程池会调用设定的拒绝策略。常见的拒绝策略有抛出异常、丢弃任务、在队列中等待等。应该根据实际需求选择合适的拒绝策略。  5. 定期检查和调整线程池:应该定期检查线程池的状态,如线程数量、队列大小等,并根据实际需求进行调整。例如,如果发现队列经常满,可能需要增加线程池的大小或者增大队列的容量。  6. 使用线程池监控工具:有许多第三方库提供了线程池的监控工具,如VisualVM、JConsole等。这些工具可以帮助我们更好地了解线程池的状态,及时发现和解决问题。  7. 注意线程安全:在多线程环境下,需要注意线程安全问题。例如,多个线程同时访问共享资源时,可能会导致数据不一致的问题。可以使用synchronized关键字、Lock接口等机制来保证线程安全。  8. 及时关闭线程池:当不再需要线程池时,应该及时关闭它,以释放资源。可以通过调用ThreadPoolExecutor的shutdown()方法来关闭线程池。 
  • [技术干货] Java 反射机制
    什么是反射Java 中的反射(Reflection)是一种强大的编程特性,它允许程序在运行时动态地获取类的信息,包括类名、属性、方法等,并且可以对这些信息进行操作,如创建对象、调用方法和访问属性。反射机制使得程序在运行时能够更加灵活地处理不同类型的对象和情况,提高了程序的扩展性和灵活性。反射的作用反射主要提供以下功能:在运行时判断任意一个对象所属的类。在运行时构造任意一个类的对象。在运行时判断任意一个类所具有的成员变量和方法。在运行时调用任意一个对象的方法。Java为什么要支持反射Java 支持反射机制的原因主要有以下几点:灵活性:反射机制使得 Java 程序在运行时能够动态地获取类的信息,包括类名、属性、方法等。这使得程序在运行时能够更加灵活地处理不同类型的对象和情况。解耦:反射机制有助于实现程序组件之间的解耦。通过反射,可以在运行时动态地加载类、创建对象、调用方法等,这使得程序之间的依赖关系变得更加松散。扩展性:反射机制为 Java 程序提供了强大的扩展能力。通过反射,可以在运行时动态地加载和调用自定义的类和方法,这为程序的扩展和定制提供了便利。高效性:反射机制可以实现程序的优化。在某些场景下,通过反射创建对象、调用方法等操作可能会比使用静态语句更加高效。反射可以在运行时直接调用指定的方法,避免了额外的类型检查和绑定过程。安全性:反射机制可以提高 Java 程序的安全性。通过反射,可以对程序的运行状态进行动态监控和调试,有助于及时发现和修复潜在的安全隐患。框架基础:反射机制是许多 Java 框架(如 Hibernate、Struts 等)的基础。这些框架利用反射实现对对象、方法和属性的操作,从而简化了开发者的任务,提高了开发效率。反射机制为 Java 程序带来了更高的灵活性、扩展性、高效性和安全性,同时还是许多重要框架和技术的基础。反射的应用场景反射机制在 Java 中的应用场景包括:动态地加载类和对象。实现多态性,即在运行时动态地确定对象类型。序列化库(如 Jackson)和 Web 框架(如 Spring MVC、Jersey)中,利用反射和注解实现通用的序列化/反序列化机制以及请求参数和内容的转换。 通过灵活掌握反射机制,开发者可以更好地定制和优化程序,提高代码的灵活性和扩展性。同时,反射机制也是许多 Java 框架和高级技术的基础。代码示例以下是一个简单的 Java 反射应用示例,通过代码演示了如何使用反射创建对象、访问属性和方法:首先,定义一个简单的类 Person:public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }然后,使用反射创建 Person 对象、访问属性和方法:import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectionExample { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { // 1. 获取 Class 对象 Class<?> personClass = Class.forName("Person"); // 2. 创建对象 Constructor<?> constructor = personClass.getConstructor(String.class, int.class); Person person = (Person) constructor.newInstance("张三", 25); // 3. 访问属性 Field nameField = personClass.getDeclaredField("name"); nameField.setAccessible(true); nameField.set(person, "李四"); Field ageField = personClass.getDeclaredField("age"); ageField.setAccessible(true); ageField.set(person, 26); // 4. 访问方法 Method getNameMethod = personClass.getMethod("getName"); String name = (String) getNameMethod.invoke(person); System.out.println("Name: " + name); Method setNameMethod = personClass.getMethod("setName", String.class); setNameMethod.invoke(person, "王五"); Method getAgeMethod = personClass.getMethod("getAge"); int age = (int) getAgeMethod.invoke(person); System.out.println("Age: " + age); Method setAgeMethod = personClass.getMethod("setAge", int.class); setAgeMethod.invoke(person, 27); } }在这个示例中,我们首先使用 Class.forName() 方法获取 Person 类的 Class 对象。然后,通过 getConstructor() 方法获取构造方法,创建新的 Person 对象。接下来,使用 getDeclaredField() 方法获取类的属性,并通过 setAccessible() 方法访问私有属性。最后,使用 getMethod() 方法获取类的方法,并调用这些方法。
  • [技术干货] JAVA简单制作excel文件
    JAVA简单制作excel文件 需求:查询的业务数据根据指定的样式下载 比如这个模板包含了行合并和表头字体颜色和内容字体和下面的小计的颜色的行合并 怎么去做呢? 其实使用excel有很多种做法,要么你对自己的手法很自信那就直接手撸代码写样式,如果你想偷懒那就按照我说这种方式也许能给你提供一些想法。 1.创建一个excel文件制作两个sheet页 第一个sheet页制作: 我们可以根据模板将一些固定不变的模板画在这个sheet页,就像现在这个,我就可以直接去填充数据 第二个sheet页制作: 第二个sheet页你就放只有一条数据的一个简单的完整样式,用于下面的取样式  2.核心代码 //将你创建好的excel文件放到静态文件夹下面然后引用 ClassPathResource classPathResource = new ClassPathResource("static/zlfxb.xlsx"); InputStream inputStream = classPathResource.getStream(); XSSFWorkbook workBook = new XSSFWorkbook(inputStream); //引用完成 //接下来一个一个来获取业务数据的样式 //解读:正文样式:第2个sheet页第三行第一列的样式 XSSFCellStyle ptzt = workBook.getSheetAt(1).getRow(2).getCell(0).getCellStyle(); //这里可能会有多个样式,这就按照你的需求来,像上面一样创建多个样式.... XSSFSheet sheet1 = workBook.getSheetAt(0);//获取第一个sheet页,也就是正常填充业务数据的sheet页 XSSFRow row = null; Cell cell = null; //现在是循环填充数据 //这个mapList就是你的数据有多少行就是for多少个 for (int i = 0; i < mapList.size(); i++) {     //我们是从第三行开始填充     row = sheet1.createRow(i + 2);     //现在是要看你有多少列也是需要循环的     for(int x = 0; x < 11;x++){         //你有11列那就赋值下去咯,这里不是统一的,看你的数据格式         //这里是第i行创建第x列         cell = row.createCell(x);         //使用的是上面我们创建的正文格式         cell.setCellStyle(ptzt);         //给这个列表赋值,但是一定要是string不然会报错         cell.setCellValue(String.valueOf(map.get("orgNo")));     } } //赋值完数据后然后就把第二个样式sheet页删除掉就ok了 workBook.removeSheetAt(1);   FileOutputStream fos = null;   File file = File.createTempFile("分析表", ".xlsx");  fos = new FileOutputStream(file, false);  workBook.write(fos);  //弄成文件输出就行  核心代码就是这样的,上面的代码是提供的一些思路  3.一些常用java制作excel操作: 1.合并列:  sheet.addMergedRegion(new CellRangeAddress(firstRow,lastRow    ,firstCol, lastCol)); //例子解读:从第三行开始到第三行结束,第一列到第五列合并 sheet.addMergedRegion(new CellRangeAddress(2,2, 0, 4)); 2.删除行:  //其实java是没有删除excel这一说法的,只是说从第几行开始,下面的行要往上移几行,这一行代码最好写在需要删除行的下面,及时删除是最好的 sheet.shiftRows(5,sheet1.getLastRowNum(),-2); ———————————————— 原文链接:https://blog.csdn.net/weixin_42235875/article/details/132497421 
  • [技术干货] Java创建Excel文件
     public synchronized static int writeExcel(JSONArray jsonArray,int k) {         //创建Excel文件薄         HSSFWorkbook workbook=new HSSFWorkbook();         //创建工作表sheeet         HSSFSheet sheet=workbook.createSheet();         //创建第一行         HSSFRow row=sheet.createRow(0);         String[] title={"货号","中文名","英文名","CAS","分子式","分子量","图片网址","svg网址"};         HSSFCell cell_title = null;         for (int i=0;i<title.length;i++){             cell_title=row.createCell(i);             cell_title.setCellValue(title[i]);         }         //追加数据         for (int i=0;i<jsonArray.size();i++){             JSONObject jsonObject =(JSONObject) jsonArray.get(i);             HSSFRow nextrow=sheet.createRow(i+1);             HSSFCell cell_value=nextrow.createCell(0);             cell_value.setCellValue("");             cell_value=nextrow.createCell(1);             cell_value.setCellValue(TransApi.EN_to_ZH(jsonObject.get("name").toString()));             cell_value=nextrow.createCell(2);             cell_value.setCellValue(jsonObject.get("name").toString());             cell_value=nextrow.createCell(3);             cell_value.setCellValue(jsonObject.get("CAS").toString());             cell_value=nextrow.createCell(4);             cell_value.setCellValue(jsonObject.get("FCS").toString());             cell_value=nextrow.createCell(5);             cell_value.setCellValue(jsonObject.get("MW").toString());             cell_value=nextrow.createCell(6);             cell_value.setCellValue(jsonObject.get("img").toString());             cell_value=nextrow.createCell(7);             cell_value.setCellValue(jsonObject.get("svg").toString());         }         //创建一个文件         try {             File file=new File("D:/pacong/poi_test_"+k+".xls");             file.createNewFile();             FileOutputStream stream= FileUtils.openOutputStream(file);             workbook.write(stream);             stream.close();         }catch (Exception e){             e.printStackTrace();         }finally {             return 1;         }     }原文链接:https://blog.csdn.net/qq_31122833/article/details/123449030
  • [技术干货] java代码生成excel表格
    在很多项目中,都需要我们把数据库查询出来的数据导出成excel表格,导出就算了,还给我弄个模板,大标题要写在合并表格里还要居中,标题要有底色,还要加粗,等等,下面我就来讲一下我用到的表格操作:  首先要进行表格操作,要添加相关依赖  这里我重点说明一下,我用4.3及以上版本就不行,可能是方法有升级  <dependency>             <groupId>org.apache.poi</groupId>             <artifactId>poi-ooxml</artifactId>             <version>4.0.1</version>         </dependency>         <dependency>             <groupId>org.apache.poi</groupId>             <artifactId>poi</artifactId>             <version>4.0.1</version>         </dependency> 创建excel文件  如果想生成.xls文件:  Workbook workbook = new HSSFWorkbook(); 如果想生成.xlsx文件  Workbook workbook = new XSSFWorkbook(); 创建工作簿  也就是我们说的Sheet页,sheet页是根据Workbook对象生成的,也就是sheet页依赖excel文件而存在。  Sheet sheet = workbook.createSheet("Sheet1");  // Sheet1为要生成的sheet页的名称 写入操作  完成以上操作,在不考虑样式的前提下,我们就可以写数据了。写数据的话是按行进行写入的。 首先声明行变量 行是根据sheet声明的,参数为该行的位置(从0开始算)  Row titleRow = sheet.createRow(0); 写入数据 有了行对象,那么我们就可以对该行的某列进行操作了 首先要创建该行某列对象,然后用getCell方法获取单元格,参数为单元格索引位置(从0开始算),然后用setCellValue方法进行内容写入,参数为内容  titleRow.createCell(0); titleRow.getCell(0).setCellValue("序号"); 写完之后,返回workbook对象就可以了。 按这样操作,一份没有样式的表格文件就生成好了。 样式操作  首先要声明样式对象 因为创建的workbook对象方式不同,样式对象的类型也不一样,不过方法是类似的  XSSFCellStyle cellStyle = workbook.createCellStyle();  背景颜色  这里修改只需要改new java.awt.Color里的参数值即可,也支持输入0xff000000(默认值)16进制数 需要与设置填充格式一起使用才生效,使用无先后顺序  cellStyle.setFillForegroundColor(new XSSFColor(new java.awt.Color(89, 214, 255),new DefaultIndexedColorMap())); 字体居中  cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);  // 垂直居中 cellStyle.setAlignment(HorizontalAlignment.CENTER); // 水平居中 设置边框  BorderStyle.THIN为宽度属性  cellStyle.setBorderBottom(BorderStyle.THIN); // 下边框 cellStyle.setBorderRight(BorderStyle.THIN);  // 右边框 cellStyle.setBorderLeft(BorderStyle.THIN);   // 左边框 cellStyle.setBorderTop(BorderStyle.THIN);    // 上边框 设置填充格式  cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); 自动换行  cellStyle.setWrapText(true); 字体设置  Font titleFont = workbook.createFont();  // 声明字体对象 titleFont.setBold(true);        // 字体加粗 titleFont.setFontName("华文彩云");  // 字体类型 titleFont.setFontHeightInPoints((short) 20);  // 字体大小 cellStyle.setFont(titleFont);   // 赋值样式对象 设置列宽  sheet.setColumnWidth(0, 4200);  // 第0列列宽设置为4200 合并单元格  sheet.addMergedRegion(new CellRangeAddress(1, 3, 1, 3));  // 参数分别为起始行,结束行,起始列,结束列 1 返回前端的话,写法是这样的,方法是没有返回的,也就是void  public void index(HttpServletResponse response) {         XSSFWorkbook workbook = service.test();         if (workbook!=null){             String fileName = "1.xlsx";             try {                 fileName = URLEncoder.encode(fileName, "UTF-8");                 //设置ContentType请求信息格式                 response.setContentType("application/vnd.ms-excel;charset=utf-8");                 response.setHeader("Content-disposition", "attachment;filename=" + fileName);                 OutputStream outputStream = response.getOutputStream();                 workbook.write(outputStream);                 outputStream.flush();                 outputStream.close();             } catch (Exception e) {                 System.out.println(e);             }         }     }  我这边用到的基本是这些,如果有其他的操作可以评论我或者私信我,我这边在做补充 ———————————————— 原文链接:https://blog.csdn.net/weixin_45494557/article/details/132088533 
  • [技术干货] Java8让Excel的读写变得更加简单高效
    Apache POI 在业务开发中我们经常会遇到Excel的导入导出,而 Apache POI 是Java开发者常用的API。 【https://poi.apache.org/components/spreadsheet/index.html】  GridExcel Universal solution for reading and writing simply Excel based on functional programming and POI EventModel  GridExcel是基于Java8函数式编程和POI EventModel实现的用于Excel简单读写的通用解决方案。  基于POI EventModel,在读写数据量非常大的Excel时,降低内存占用避免OOM与频繁FullGC 基于函数编程,支持关联对象等多种复杂情况的处理,学习成本低 支持流式API,使代码编写和理解更简单,更直观 EventModel 什么是EventModel?在POI FAQ(常见问题解答)【https://poi.apache.org/help/faq.html#faq-N100C2】官方给出解释:  The SS eventmodel package is an API for reading Excel files without loading the whole spreadsheet into memory. It does require more knowledge on the part of the user, but reduces memory consumption by more than tenfold. It is based on the AWT event model in combination with SAX. If you need read-only access, this is the best way to do it.  SS eventmodel包是一个用于读取Excel文件而不将整个电子表格加载到内存中的API。 它确实需要用户掌握更多知识,但是将内存消耗减少了十倍以上。 它基于AWT(Abstract Window Toolkit)event model与SAX的结合。 如果您需要只读访问权限,这是最好的方式。  函数编程 说到函数编程,就不得不提Lambda表达式,如果对Java8中的Lambda不了解或理解不深刻,可以看下甲骨文官网给出的这篇文章,【https://www.oracle.com/technetwork/articles/java/architect-lambdas-part1-2080972.html】,个人认为这是Java8 Lambda从入门到进阶最好的文章之一。  其中函数编程的目的就是实现代码块传递,即,将方法作为参数在方法间传递。为此,随着Java语言的发展,不断出现一些解决方案:  Java 1.0, 使用Abstract Window Toolkit (AWT) EventModel来实现,但笨拙且不可行 Java 1.1,提出一系列“Listeners” 后来使用内部类和匿名内部类来实现,但是大多数情况下,它们只是被用作事件处理。 再后来发现更多地方将代码块作为对象(实际上是数据)不仅有用而且是必要的,但是Java中函数编程还是很笨拙,它需要成长。 直到Java 1.7,Java引入了java.lang.invoke包,提供一 种新的动态确定目标方法的机制(可以不用再单纯依靠固化在虚拟机中的字节码调用指令),称为MethodHandle,模拟字节码的方法指针调用,类似于C/C++的Function Pointer(函数指针)并引入第5条方法调用的字节码指令invokedynamic。 直到Java 1.8,基于Java 1.7提出的字节码指令invokedynamic,实现了Lamda技术,将函数作为参数在方法间传递,Java开始更好的支持函数式编程。 用反射不是早就可以实现了吗?Reflection API 重量级,性能低。 注意: 5、6、7参考《深入理解Java虚拟机》第2版,8.3.3 动态类型语言支持。  在POI的使用过程中,对大多数API User来说经常面临两个问题,这也是GridExcel致力解决的问题。  问题1. 仅使用简单的导入导出功能,但每次业务的数据对象结构不同,需要重新编写处理方法,很麻烦! 解决方法 将Excel处理逻辑抽取出来,封装成工具类。  封装条件 与大多数Java API一样,POI把更多的精力放在高级功能的处理上,比如Formula(公式)、Conditional Formatting(条件格式)、Zoom(缩放)等。对于仅仅做数据导入导出功能的API User,很少使用这些高级特性,这允许API用户对POI的使用进行简单的封装。  封装方式 无论是读是写,我们都需要解决Excel中的Columns(列)与Java数据对象Fields(字段)的映射关系,将这种映射关系作为参数(Map对象HashMap或LinkedHashMap),传递给工具类。  对于Columns不难理解,它可以是有序的数字或字母,也可以是其它字符串用来作为首行,表示该列数据的含义。  对于Fields,它的处理需要兼容复杂情况,如下:  查询字段时出现异常 字段或单元格的值为null 该列的值可能对应关联对象、甚至是关联集合中的某个字段值 字段或单元格的值需要做特殊处理,例如value == true?完成:失败; 反射 首先想到,也是大多数封装者都在使用的方式是就是Reflection API,从上文 函数编程 章节我们了解到,反射重量级,会降低代码的性能,同时对复杂情况的处理支持性不够好。  反射+注解 这种方式可以更好的支持复杂情况,但是反射依然会降低性能,同时注解对数据对象会造成代码侵入,而且对该工具类封装者的其他使用者无疑会增加学习成本。  匿名内部类 这种方式也可以很好的支持复杂情况,但是使用匿名内部类的语法显然患有“垂直问题”(这意味着代码需要太多的线条来表达基本概念),太过冗杂。至于性能,应该也不如直接传递函数来的快吧。  函数接口(Lambda) 这种方式是基于第5条方法调用的字节码指令invokeDynamic实现的,直接传递函数代码块,很好的支持复杂情况,性能较高,代码编写更简单结构更加简洁,而且对数据对象代码零侵入。  问题2. Excel导入或导出数据量比较大,造成内存溢出或频繁的Full GC,该如何解决? 解决方法 读Excel —— eventmodel 写Excel —— streaming.SXSSFWorkbook 原理 POI的使用对我们来说很常见,对下面两个概念应该并不陌生:  HSSFWorkbook(处理97(-2007) 的.xls) XSSFWorkbook(处理2007 OOXML (.xlsx) ) 但是对于eventmodel和streaming.SXSSFWorkbook就很少接触了,它们是POI提供的专门用来解决内存占用问题的low level API(低级API),使用它们可以读写数据量非常大的Excel,同时可以避免内存溢出或频繁的Full GC。【https://poi.apache.org/components/spreadsheet/how-to.html】  eventmodel,用来读Excel,并没有将Excel整个加载到内存中,而是允许用户从InputStream每读取一些信息,就交给回调函数或监听器,至于丢弃,存储还是怎么处理这些内容,都交由用户。 streaming.SXSSFWorkbook,用来写Excel(是对XSSFWorkbook的封装,仅支持.xlsx),通过滑动窗口来实现,只在内存中保留滑动窗口允许存在的行数,超出的行Rows被写出到临时文件,当调用write(OutputStream stream)方法写出内容时,再直接从临时内存写出到目标OutputStream。SXSSFWorkbook的使用会产生一些局限性。 Only a limited number of rows are accessible at a point in time. Sheet.clone() is not supported. Formula evaluation is not supported 解决途径 https://github.com/liuhuagui/gridexcel 基于Java函数编程(Lambda),支持流式API,使用环境Java1.8或更高,学习成本:Lambda 实际上POI官网已经给了用户使用示例,而上述工具只是做了自己的封装实现,让使用更方便。  快速使用 <dependency>     <groupId>com.github.liuhuagui</groupId>     <artifactId>gridexcel</artifactId>     <version>2.2</version> </dependency> GridExcel.java GridExcel.java提供了多种静态方法,可以直接使用,具体式例可参考测试代码(提供了测试数据和测试文件):  https://github.com/liuhuagui/gridexcel/blob/master/src/test/java/ReadTest.java https://github.com/liuhuagui/gridexcel/blob/master/src/test/java/WriteTest.java 流式API /**   * 业务逻辑处理方式三选一:   * 1.启用windowListener,并将业务逻辑放在该函数中。   * 2.不启用windowListener,使用get()方法取回全部数据集合,做后续处理。   * 3.readFunction函数,直接放在函数中处理 或 使用final or effective final的局部变量存放这写数据,做后续处理。   * 注意:使用EventModel时readFunction函数的输入为每行的cell值集合List<String>。   * @throws Exception   */  @Test  public void readXlsxByEventModel() throws Exception {      InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("2007.xlsx");      GridExcel.readByEventModel(resourceAsStream,TradeOrder.class,ExcelType.XLSX)              .window(2,ts -> System.out.println(JSON.toJSONString(ts)))//推荐在这里执行自己的业务逻辑              .process(cs ->{                  TradeOrder tradeOrder = new TradeOrder();                  tradeOrder.setTradeOrderId(Long.valueOf(cs.get(0)));                  Consultant consultant = new  Consultant();                  consultant.setConsultantName(cs.get(3));                  tradeOrder.setConsultant(consultant);                  tradeOrder.setPaymentRatio(cs.get(16));                  return tradeOrder;              },1);  }  /**   * 使用Streaming UserModel写出数据到Excel   * @throws Exception   */  @Test  public void writeExcelByStreaming() throws Exception {      GridExcel.writeByStreaming(TradeOrder.class)              .head(writeFunctionMap())//对象字段到Excel列的映射              .createSheet()              .process(MockData.data())//模拟数据。在这里设置业务数据集合。              .write(FileUtils.openOutputStream(new File("/excel/test.xlsx")));  }  ReadExcel ReadExcelByUserModel Use user model to read excel file. userModel ——  缺点:内存消耗大,会将excel信息全部加载到内存再进行处理。 优点:现成的API,使用和理解更简单。 使用场景:可以处理数据量较小的Excel。 ReadExcelByEventModel Use event model to read excel file. eventModel ——  缺点:没有现成的API,使用和理解较为复杂,适合中高级程序员(GridExcel的目标之一就是让EventModel的使用变得简单) 优点:非常小的内存占用,并没有在一开始就将所有内容加载到内存中,而是把主体内容的处理(存储,使用,丢弃)都交给了用户,用户可以自定义监听函数来处理这些内容。 使用场景:可以处理较大数据量的Excel,避免OOM和频繁FullGC WriteExcel WriteExcelByUserModel Use user model to write excel file. userModel ——  缺点:会将产生的spreadsheets对象整个保存在内存中,所以write Excel的大小受到堆内存(Heap space)大小限制。 优点:使用和理解更简单。 使用场景:可以写出数据量较小的Excel。 WriteExcelByStreaming Use API-compatible streaming extension of XSSF to write very large excel file. streaming userModel——  缺点: 仅支持XSSF; Sheet.clone() is not supported; Formula evaluation is not supported; Only a limited number of rows are accessible at a point in time. 优点:通过滑动窗口来实现,内存中只保留指定size of rows的内容,超出部分被写出到临时文件,write Excel的大小不再受到堆内存(Heap space)大小限制。 使用场景:可以写出非常大的Excel。 Issues 在使用工具过程中出现问题,有功能添加或改动需求的可以向作者提Issue:https://github.com/liuhuagui/gridexcel/issues 比如说,想要增加对首行以外的行列做样式扩展 ————————————————  原文链接:https://blog.csdn.net/qq_32331073/article/details/97650960 
  • [技术干货] 小型java easy Excel导出工具通类(可拓展) jdk8函数式接口
    小型java easy Excel导出工具类(可拓展) jdk8函数式接口 首先需求是前端传入对象list,指定后端配置的Excel导出实体类package地址(根据需求可脱离前端根据业务需求定义接口直接调工具类方法即可,传参数方式由指向地址改为Excel导出实体类Class对象)并且以application/vnd.ms-excel类型的回执返回给前端download(实现逻辑简单不喜勿喷,希望得到各路神仙🐮的指点OwO)。讲一下实现思路首先后端拿到list和实体类映射地址或者类Class定义文件输出流定义两个方法一个作为创建列头和数据,另一个作为导出操作(这一步操作是由Lambda表达式写入提交)直接写入到流中返回给前端。  1.引入maven poi依赖以及其他应用到的依赖         <dependency>            <groupId>org.apache.poi</groupId>            <artifactId>poi</artifactId>        </dependency>        <dependency>            <groupId>org.apache.poi</groupId>            <artifactId>poi-ooxml</artifactId>        </dependency>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web</artifactId>         </dependency> 2.结构目录  annotations:字段型自定义注解(列头/日期格式/字典式数字替换文字/列头宽度……后期拓展字段交给你们OwO🐮) as:函数接口/自定义接口/定义接口实现 entity:Excel导出行数据转换实体 model:Excel导出实体类 uitls:Excel导出工具类  3.定义字段型注解(解释都在注释里了) package 这个你们放你们自己的包吧;  import java.lang.annotation.*;  @Documented @Target({ElementType.FIELD}) //注解应用类型(应用到方法的注解,还有类的可以自己试试) @Retention(RetentionPolicy.RUNTIME) // 注解的类型 public @interface Excel {      /**      * Excel列头      **/     String name() default "";      /**      * 日期格式转换      **/     String format() default "yyyy/MM/dd";      /** 文字替换根据 ":" 提供两种方式如下      * 1.普通数组替换: {"文本1:文本2",……}      * 2.反射方法替换: "${无参数方法返回值类型为List<Object>}"      * 且方法必须在同一实体类      **/     String[] filterText() default {};      /**      * 列头宽度      **/     int width() default 30;      /**      * 函数配置      **/     String func() default ""; }  4.自定义一个消费型函数式接口 这里着重讲一下含义所谓消费,其实就是将数据提交到接口里由方法重写由Lambda表达式的方式处理数据优化代码避免性能浪费  package 这个你们放你们自己的包吧;  /**  有且只有一个的函数式接口,称之为函数接口,接口可以包含其他方法(静态和默认以及私有)  * @Author du_zy  * @Description //TODO  * @Date 3:18 PM 2022/3/8  **/ @FunctionalInterface public interface ExportConsumer<ByteArrayOutputStream, Workbook, List>{      /** 自定义导出数据拼装      * @Author du_zy      * @Description //TODO      * @Date 2:46 PM 2022/3/8      * @Param [fileOut, wk, list]      * fileOut文件输出流最后是将文件写入到这个里传给前端的,wk是poi处理Excel样式以及数据的APi里面有很多强大的功能可以参考官方有兴趣可以看看,list是前端传入的数据      * @return void      **/     public abstract void accept(ByteArrayOutputStream fileOut, Workbook wk, List list) throws Exception; } 5.定义数据转换类实体 这里是对list的属性的一些定义因为要对list里的数据标记那些要日期格式化那些要文字替换下文在注释里有详解  package 这个你们放你们自己的包吧;  /**  * @ClassName FieldExcel  * @Description TODO  * @Author du_zy  * @Date 2022/3/11 10:09 AM  * @Version 1.0  **/ public class FieldExcel {      private String[] fieldArr;      private String fieldName;      private String fieldType;      private String fieldFormat;      public String[] getFieldArr() {         return fieldArr;     }      public void setFieldArr(String[] fieldArr) {         this.fieldArr = fieldArr;     }      public String getFieldName() {         return fieldName;     }      public void setFieldName(String fieldName) {         this.fieldName = fieldName;     }      public String getFieldType() {         return fieldType;     }      public void setFieldType(String fieldType) {         this.fieldType = fieldType;     }      public String getFieldFormat() {         return fieldFormat;     }      public void setFieldFormat(String fieldFormat) {         this.fieldFormat = fieldFormat;     }      public FieldExcel() {         this.fieldArr = new String[]{};         this.fieldName = "null_flag";     } } 6.导出列头实体类(这个类里的属性根据业务需求制作) 主要需要看Excel注解  package 这个你们放你们自己的包吧;  import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import 这个你们放你们自己的包吧.CfmRepairCodeEnum; import 这个你们放你们自己的包吧.annotations.Excel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component;  import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map;  /**  *  实体类  * @author du  * @since 2021-12-07 15:23:53  */  @Data @EqualsAndHashCode(callSuper = false) @NoArgsConstructor @AllArgsConstructor @Component public class FitMentExcelModel implements Serializable {      private static final long serialVersionUID = 1L;     /**      * 装店装修业务单主键id      */     private Long cfmRepairId;     /**      * 装店装修单R单单号      */     @Excel(name = "装店装修单R单单号")     private String cfmRepairCode;     /**      * 申请日期      */     @Excel(name = "申请日期")     private LocalDateTime applyTime;     /**      * 申请人      */     @Excel(name = "申请人")     private String applyName;     /**      * 申请人电话      */     @Excel(name = "申请人电话")     private String applyMobile;     /**      * 城市经理名称      */     @Excel(name = "城市经理名称")     private String cityManager;     /**      * 城市经理电话      */     @Excel(name = "城市经理电话")     private String cityManagerPhone;     /**      * 装修负责人      */     @Excel(name = "装修负责人")     private String repairLeader;     /**      * 负责人电话      */     @Excel(name = "负责人电话")     private String leaderPhone;     /**      * 省份      */     @Excel(name = "省份")     private String province;     /**      * 城市      */     @Excel(name = "城市")     private String city;     /**      * 区      */     @Excel(name = "区")     private String area;     /**      * 店面名称      */     @Excel(name = "店面名称")     private String storeName;     /**      * 店面类别      */     @Excel(name = "店面类别")     private String storeType;     /**      * 店面类型      */     @Excel(name = "店面类型")     private String storeCategory;     /**      * 店面性质      */     @Excel(name = "店面性质")     private String storeProperty;     /**      * 店面面积      */     @Excel(name = "店面面积")     private BigDecimal storeArea;     /**      * 店面所属商圈      */     @Excel(name = "店面所属商圈")     private String storeBusinessArea;     /**      * 地级/县级      */     @Excel(name = "地级/县级", filterText = {"1:地级", "2:县级"})     private String cityLevel;     /**      * 老店历史装修时间      */     @Excel(name = "老店历史装修时间")     private LocalDate decorateDate;     /**      * 装修性质      */     @Excel(name = "装修性质")     private String repairLevelName;     /**      * 装修模式      */     @Excel(name = "装修模式")     private String repairModelName;     /**      * 预计开业时间      */     @Excel(name = "预计开业时间")     private LocalDate openDate;     /**      * 店面专区备注      */     @Excel(name = "店面专区备注")     private String storeTeamRemark;     /**      * 店面专区面积      */     @Excel(name = "店面专区面积")     private BigDecimal storeTeamArea;     /**      * 开始设计时间      */     @Excel(name = "开始设计时间")     private LocalDate designingDate;     /**      * 设计完成时间      */     @Excel(name = "设计完成时间")     private LocalDate designFinishDate;     /**      * 设计周期      */     @Excel(name = "设计周期")     private Integer designCycle;     /**      * 铺地砖日期      */     @Excel(name = "铺地砖日期")     private LocalDate brickDate;     /**      * 木工日期      */     @Excel(name = "木工日期")     private LocalDate woodDate;     /**      * 油工日期      */     @Excel(name = "油工日期")     private LocalDate oilDate;     /**      * 安装样品日期      */     @Excel(name = "安装样品日期")     private LocalDate installDate;     /**      * 软装摆场日期      */     @Excel(name = "软装摆场日期")     private LocalDate softDate;     /**      * 暂不装修时间      */     @Excel(name = "暂不装修时间")     private LocalDate notRepairDate;     /**      * 暂不装修原因      */     @Excel(name = "暂不装修原因")     private String notRepairReason;     /**      * 不装修时间      */     @Excel(name = "不装修时间")     private LocalDate noRepairDate;     /**      * 不装修原因      */     @Excel(name = "不装修原因")     private String noRepairReason;     /**      * 装修完成时间      */     @Excel(name = "装修完成时间")     private LocalDate repairFinishDate;     /**      * 装修周期      */     @Excel(name = "装修周期")     private Integer repairCycle;     /**      * 验收完成时间      */     @Excel(name = "验收完成时间")     private LocalDate checkDate;     /**      * 验收周期      */     @Excel(name = "验收周期")     private Integer checkCycle;     /**      * 验收状态 0:不合格 1:合格 2:无验收      */     @Excel(name = "验收状态", filterText = {"0:不合格", "1:合格", "2:无验收"})     private Integer checkState;     /**      * 装修单状态      */     @Excel(name = "装修单状态", filterText = "${getEnumRepairSate}")     private Integer repairState;     @Excel(name = "状态")     private String state;     /**      * 支付时间      */     @Excel(name = "支付时间")     private LocalDateTime payTime;     /**      * 支付单号      */     @Excel(name = "支付单号")     private String cfmPayCode;     /**      * 设计费金额(分)      */     @Excel(name = "设计费金额(分)")     private Long sumDesignCost;     /**      * 支付方式      */     @Excel(name = "支付方式")     private String payMethod;     /**      * 评价状态 0:未评价 1:已评价      */     @Excel(name = "评价状态", filterText = {"0:未评价", "1:已评价"})     private Integer evaluateState;     /**      * 评价信息  图纸设计      */     @Excel(name = "评价信息-图纸设计")     private BigDecimal evaluateDesign;     /**      * 评价信息  装修效果      */     @Excel(name = "评价信息-装修效果")     private BigDecimal evaluateRepair;     /**      * 评价信息  服务沟通      */     @Excel(name = "评价信息-服务沟通")     private BigDecimal evaluateCommunication;     /**      * 评价内容      */     @Excel(name = "评价内容")     private String evaluateContent;     /**      * 设计师负责人名称      */     @Excel(name = "设计师负责人名称")     private String responseName;     /**      * 设计师名称      */     @Excel(name = "设计师名称")     private String designName;     /**      * 表单备注      */     @Excel(name = "表单备注")     private String formRemark;     /**      * 备注      */     @Excel(name = "备注")     private String remark;     /**      * 关联店面id      */     private String storeId;     /**      * 店面编码      */     private String storeCode;     /**      * 申请人id      */     private Long applyId;       /**将一串key-value数据转换成key:value数据并返回成list      * @Author du_zy      * @Description //TODO       * @Date 4:12 PM 2022/3/23      * @Param []      * @return java.util.List<java.lang.Object>      **/     @Bean     public List<Object> getEnumRepairSate() {         List<Object> resultList = new ArrayList<>();         for (Map.Entry<String, Object> stringObjectEntry : CfmRepairCodeEnum.getEnum().entrySet()) {             JSONObject j = JSONObject.parseObject(JSON.toJSONString(stringObjectEntry.getValue()));             resultList.add(j.get("code") + ":" + j.get("msg"));         }       return resultList;     }          /**      * 装修性质code      */     private String repairLevelCode;     /**      * 联系电话      */     private String mobile;     /**      * 省份编码      */     private String provinceCode;     /**      * 城市编码      */     private String cityCode;     /**      * 区编码      */     private String areaCode;     /**      * 装修模式code      */     private String repairModelCode;     /**      * 设计费单价(分)      */     private Long designCost;     /**      * 设计部数据员id      */     private Long dataId;     /**      * 设计部数据员名称      */     private String dataName;     /**      * 设计师负责人id      */     private Long responseId;     /**      * 设计师id      */     private Long designId;     /**      * 验收图片 0:未上传 1:已上传      */     private Integer checkPhotoState;     /**      * 关闭时间      */     private LocalDateTime closeTime;     /**      * 是否删除(0:未删除、1:删除)      */     private String delFg;     /**      * 创建人      */     private String createBy;     /**      * 创建人编码      */     private String createCode;     /**      * 创建时间      */     private LocalDateTime createDt;     /**      * 更新人      */     private String updateBy;     /**      * 更新人编码      */     private String updateCode;     /**      * 更新时间      */     private LocalDateTime updateDt;     /**      * 时间戳      */     private Long timeStamp;  }  7.导出工具类(创建列头和数据以及提交写入流方法)都在注释里了 package 这个你们放你们自己的包吧;  import com.alibaba.fastjson.JSONObject; import 这个你们放你们自己的包吧; import 这个你们放你们自己的包吧; import 这个你们放你们自己的包吧; import 这个你们放你们自己的包吧; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.WorkbookUtil; import org.springframework.beans.TypeMismatchException; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils;  import java.io.ByteArrayOutputStream; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import java.util.regex.Pattern;  /**  * @ClassName ExportUtils  * @Description TODO  * @Author du_zy  * @Date 2022/3/8 11:03 AM  * @Version 1.0  **/ public class ExportUtils {      /** 导出Excel      * @Author du_zy      * @Description //TODO      * @Date 3:04 PM 2022/3/8      * @Param [fileOut, wb, listData, fileName, consumer]      * @return org.springframework.http.ResponseEntity<byte[]>      **/     public static <T> ResponseEntity<byte[]> exportExcel(ByteArrayOutputStream fileOut,                                                          Workbook wb,                                                          List<T> listData,                                                          String fileName,                                                          ExportConsumer<ByteArrayOutputStream, Workbook, List<T>> consumer) {         HttpHeaders headers = new HttpHeaders();         try {             consumer.accept(fileOut, wb, listData); // 函数接口数据配置             headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); // 配置请求头类型             headers.setContentDispositionFormData("attachment", new String((fileName + ".xlsx").getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1)); //配置表单信息,字符转换             return new ResponseEntity<>(fileOut.toByteArray(), headers, HttpStatus.OK);         } catch (Exception e) {             return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);          }      }      /** 创建标头和数据      * @Author du_zy      * @Description //TODO      * @Date 2:59 PM 2022/3/8      * @Param [wb, data, className]      * @return org.apache.poi.ss.usermodel.Workbook      **/     public static <T> Workbook createCell(Workbook wb, List<T> data, Class<?> className) {         try {             String safeName = WorkbookUtil.createSafeSheetName("sheet1");             Sheet sheet = wb.createSheet(safeName);  // 创建工作空间             //第一行标题             Row title = sheet.createRow(0);             List<Map<String, Object>> cols = new LinkedList<>();             for (int i = 0; i < className.getDeclaredFields().length; i++) { // 循环如果反射里的类里有注解name配置就存入到标头list里                 boolean is = className.getDeclaredFields()[i].isAnnotationPresent(Excel.class);                 if (is) {                     Map<String, Object> coverMap = new HashMap<>();                     Excel annotation = className.getDeclaredFields()[i].getAnnotation(Excel.class);                     coverMap.put("name", annotation.name());                     coverMap.put("width", annotation.width());                     cols.add(coverMap);                 }             }              for (int i = 0; i < cols.size(); i++) { //第一行放标题                 title.createCell(i).setCellValue(cols.get(i).get("name").toString()); //                sheet.autoSizeColumn(i, true);                 sheet.setColumnWidth(i,  (int)((Integer.parseInt(cols.get(i).get("width").toString()) + 0.72) * 256));              } //            //如果list里有数组数据,加入到list里 //            String dataJson = JSONArray.toJSONString(data);// 先把list转换成json str数组格式 //            JSONArray objects = JSONArray.parseArray(dataJson); // 在转成json 数组 //            for (Object o : objects) { //                String s = JSONObject.toJSONString(o); //Object 对象转成jsonString //                JSONObject jsonObject = JSONObject.parseObject(s); //                jsonObject.forEach( (key, value) -> { //                    if (jsonObject.get(key) instanceof List) { // 如果这一行数据里有list数据,那么循环他并给给他放初始list里一并导出 //                        JSONArray jsonArray = jsonObject.getJSONArray(key); //                        for (Object o1 : jsonArray) { //                            data.add((T) o1); //                        } //                        System.out.println("是list类型"); //                    } else { //                        System.out.println("不是list类型"); //                    } //                }); //            } //            if (1==1) { //                throw new NullPointerException(); //            }             Field[] fields = className.getDeclaredFields();// 获取到反射类里所有的属性             Map<Integer, FieldExcel> fieldMap = new HashMap<>(); // integer: 存放的Excel列下标, map:存放的属性名称、类型、日期格式转换             data.forEach( o -> { // 数据拼接                 Row row = sheet.createRow(data.indexOf(o) + 1);// 创建一行数据                 String s = JSONObject.toJSONString(o); //Object 对象转成jsonString                 JSONObject jsonObject = JSONObject.parseObject(s); //jsonString转成JSONObject                 int mapIndex = 0; // 定义Excel列下标                 for (int i = 0; i < fields.length; i++) {// 循环找出如果有注解配置的字段添加到数组里                     boolean is = fields[i].isAnnotationPresent(Excel.class);                     if (is) {                         Excel annotation = fields[i].getAnnotation(Excel.class); // 获取注解里的属性                         fields[i].setAccessible(true); // 设置私有属性可见                         FieldExcel fieldExcel = new FieldExcel();                         if (!StringUtils.isEmpty(jsonObject.get(fields[i].getName()))) { // 如果反射里的字段可以在传入list里json对象找到就存找的数据,没有存空                             if (annotation.filterText().length > 0) { // 如果注解里配置了数组类型属性,将会被替换自负分割0 替换 1                                 String s1 = annotation.filterText()[0];                                 if (s1.startsWith("${") && s1.endsWith("}")) {// 用第一个元素做校验                                     try {                                         int startIndex = s1.indexOf("${") + 2;                                         int endIndex = s1.indexOf("}");                                         String methodName = s1.substring(startIndex, endIndex); // 获取到反射类里的方法名称这个是在注解上定义的名称不包含El表达式的名称                                         List<String> beanList = SpringUtils.getBean(methodName, List.class); // 获取实体里的list方法方式1:通过spring bean装载方式实现映射方法返回(实体类规范: 1.类必须交给spring管理 2.方法必须加注解Bean(name = "方法名")) //                                        Method getEnumRepairSate = className.getMethod(methodName); // 获取实体里的list方法方式2: 获取到方法实例通过java反射机制的方式加载射射方法返回 //                                        Object obj = className.newInstance(); // 生成反射加载类实例(调用实体类里带有无参数的构造方法) 从jvm角度看此类必须保证是加载了才能够使用此方法,而new不同new可以不用加载 //                                        List<Object> fieldCoverList = (List<Object>) getEnumRepairSate.invoke(obj); // 指定类实例转List<Object>类型                                         String[] fieldArr = beanList.toArray(new String[beanList.size()]); // List 转 数组                                         fieldExcel.setFieldArr(fieldArr);                                     } catch (Exception e) {                                         e.printStackTrace();                                     }                                 } else {                                     fieldExcel.setFieldArr(annotation.filterText());                                 }                             }                             fieldExcel.setFieldName(fields[i].getName());                             if (fields[i].getGenericType().toString().contains("Date")) { //如果属性类型包含Date说明是日期类型放入注解的format的属性                                 fieldExcel.setFieldFormat(annotation.format());                             }                         }                         fieldExcel.setFieldType(fields[i].getGenericType().toString());                         fieldMap.put(mapIndex, fieldExcel);                         mapIndex++;                     }                 }                 for (Map.Entry<Integer, FieldExcel> i : fieldMap.entrySet()) { //循环带有注解的字段依次排放数据(在加个循环的原因是:第一次循环有注解的for拼接数据会导致数据顺序错乱,因为注解的顺序不一定每个属性都有)                     FieldExcel value = i.getValue();                     if (value.getFieldArr().length > 0) {                         for (String s1 : value.getFieldArr()) {                             String[] split = s1.split(":"); // ":"号分割                             if (split[0].equals(jsonObject.get(value.getFieldName()).toString())) { // 0 替换 1                                 row.createCell(i.getKey()).setCellValue(split[1]);                             }                         }                     } else if (!StringUtils.isEmpty(value.getFieldFormat()) && value.getFieldType().contains("Date")) { // 如果日期格式转换属性不为空并且字段类型包含Date进行格式转换,如果没有就正常赋值                         SimpleDateFormat dateFormat = getDateFormat(jsonObject.get(value.getFieldName()).toString()); // 验证日期格式这里做了一个正则日期校验 斜杠和- 任意转换加时间或者不加时间                         Date fieldNameDate = null;                         try {                              fieldNameDate = dateFormat.parse(jsonObject.get(value.getFieldName()).toString());                         } catch (ParseException e) {                             e.printStackTrace();                         }                         SimpleDateFormat format = new SimpleDateFormat(value.getFieldFormat()); // 转换成注解定义的格式 yyyy/MM/dd HH:mm:ss yyyy-MM-dd HH:mm:ss ……                         String strFieldName = format.format(fieldNameDate);                         row.createCell(i.getKey()).setCellValue(strFieldName);                     } else {                         row.createCell(i.getKey()).setCellValue(value.getFieldName().equals("null_flag") ? "" : jsonObject.get(value.getFieldName()).toString());                     }                  }                 mapIndex = 0;                 fieldMap.clear();// 下标清0 map清空 进行下一次循环             });             return wb;         }catch (Exception e) {             e.printStackTrace();             return null;         }     }      private static SimpleDateFormat getDateFormat(String source) {         SimpleDateFormat sdf = new SimpleDateFormat();         sdf.setLenient(false);         if(Pattern.matches("^\\d{4}-\\d{2}-\\d{2}$",source)){             sdf = new SimpleDateFormat("yyyy-MM-dd");         }else if(Pattern.matches("^\\d{4}/\\d{2}/\\d{2}$",source)){             sdf = new SimpleDateFormat("yyyy/MM/dd");         }else if(Pattern.matches("^\\d{4}\\d{2}\\d{2}$",source)){             sdf = new SimpleDateFormat("yyyyMMdd");         }else if(Pattern.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}$",source)) {             sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");         }else if(Pattern.matches("^\\d{4}/\\d{2}/\\d{2}\\s\\d{2}:\\d{2}:\\d{2}$",source)) {             sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");         }else if(Pattern.matches("^\\d{4}\\d{2}\\d{2}\\s\\d{2}:\\d{2}:\\d{2}$",source)) {             sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss");         }else {             //这里抛出一个异常,             throw new TypeMismatchException("",Date.class);         }         return sdf;     } } 8.自定义接口调用 到了这一步这个方法已经注入到spring的管辖范围内了,就可以根据业务需求处理list然后导出啦!  package  这个你们放你们自己的包吧;  import  这个你们放你们自己的包吧; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service;  import javax.annotation.Resource; import java.io.ByteArrayOutputStream; import java.util.List;  @Service public class OrdCfmExport implements IOrdCfmExport {      @Override     public ResponseEntity<byte[]> export(List<Object> list, String className){         try {             ByteArrayOutputStream fileout = new ByteArrayOutputStream();             Workbook wb = new HSSFWorkbook();             Class<?> aClass = Class.forName(className); // 反射加载类实体             return ExportUtils.exportExcel(                     fileout,                     wb,                     list,                     "",                     (out, workbook, data) -> ExportUtils.createCell(                             wb,                             data,                             aClass).write(fileout));         } catch (Exception e) {             e.printStackTrace();             return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);         }     }          @Override     public ResponseEntity<byte[]> export(List<Object> list, Class<?> className)                    {         try {             ByteArrayOutputStream fileout = new ByteArrayOutputStream();             Workbook wb = new HSSFWorkbook();             Class<?> aClass = className; // 反射加载类实体             return ExportUtils.exportExcel(                     fileout,                     wb,                     list,                     "",                     (out, workbook, data) -> ExportUtils.createCell(                             wb,                             data,                             aClass).write(fileout));         } catch (Exception e) {             e.printStackTrace();             return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);         }     } }  9.最后贴一下vue代码         export() {             let that = this             let params = {}             params.data = that.exportBatchWrapper             params.baseUrl = that.url.baseUrlExport             framework.save(that.url.data.saveExport, params, 'EXPORT_POST').then((res) => {                 let blob = new Blob([res.data], { type: 'application/vnd.ms-excel' })                 // 其他浏览器                 let link = document.createElement('a') // 创建a标签                 link.style.display = 'none'                 let objectUrl = URL.createObjectURL(blob)                 link.href = objectUrl                 link.click()                 URL.revokeObjectURL(objectUrl)             }).catch((e) => {                 that.$message.error('导出失败,内部服务器错误!')             })         }      save: function (url, params, method) {         if (method === 'GET') {             return http.get(url, params.data, null, params.baseUrl)         } else if (method === 'POST') {             return http.post(url, params.data, null, params.baseUrl)         } else if (method === 'EXPORT_POST') {             return http.post(url, params.data, { responseType: 'blob' }, params.baseUrl)         }     } /**  * http post请求  * @param url请求的url地址  * @param params 基于post请求参数,格式必须是json,例如{id: '1111'}  * @param config 其他配置,一般是用于配置其他属性  */ export function post(url, data, config, baseUrl) {     let options = { url: url, data: data, method: 'post' }     let opt = config ? Object.assign(config, options) : options     if (baseUrl === '' || baseUrl === null || baseUrl === undefined) {         axios = new HttpRequest(getBaseUrl())         return axios.request(opt)     } else {         if (nodeEnv === 'production') {             axios = new HttpRequest(getBaseUrl() + baseUrl)         } else {             axios = new HttpRequest(baseUrl)         }         return axios.request(opt)     } } axios配置这里不做展示了,相信到这里就已经能明白了。后期如果有代码技术上的问题请后台联系我发起技术讨论大家相互学习。最后希望大家学无止境,欲穷千里目,更上一层楼。———————————————— 原文链接:https://blog.csdn.net/weixin_44556981/article/details/123677796 
  • [技术干货] Java中操作Excel的3种方法,太好用了!
    一、介绍在平时的业务系统开发中,少不了需要用到导出、导入excel功能,今天我们就一起来总结一下,如果你正为此需求感到困惑,那么阅读完本文,你一定会有所收获!二、poi大概在很久很久以前,微软的电子表格软件 Excel 以操作简单、存储数据直观方便,还支持打印报表,在诞生之初,可谓深得办公室里的白领青睐,极大的提升了工作的效率,不久之后,便成了办公室里的必备工具。随着更多的新语言的崛起,例如我们所熟悉的 java,后来便有一些团队开始开发一套能与 Excel 软件无缝切换的操作工具!这其中就有我们所熟悉的 apache 的 poi,其前身是 Jakarta 的 POI Project项目,之后将其开源给 apache 基金会!当然,在java生态体系里面,能与Excel无缝衔接的第三方工具还有很多,因为 apache poi 在业界使用的最广泛,因此其他的工具不做过多介绍!话不多说,直接开撸!2.1、首先引入apache poi的依赖<dependencies>    <!--xls(03)-->    <dependency>        <groupId>org.apache.poi</groupId>        <artifactId>poi</artifactId>        <version>4.1.2</version>    </dependency>    <!--xlsx(07)-->    <dependency>        <groupId>org.apache.poi</groupId>        <artifactId>poi-ooxml</artifactId>        <version>4.1.2</version>    </dependency>    <!--时间格式化工具-->    <dependency>        <groupId>joda-time</groupId>        <artifactId>joda-time</artifactId>        <version>2.10.6</version>    </dependency></dependencies>2.2、导出excel导出操作,即使用 Java 写出数据到 Excel 中,常见场景是将页面上的数据导出,这些数据可能是财务数据,也可能是商品数据,生成 Excel 后返回给用户下载文件。在 poi 工具库中,导出 api 可以分三种方式HSSF方式:这种方式导出的文件格式为office 2003专用格式,即.xls,优点是导出数据速度快,但是最多65536行数据XSSF方式:这种方式导出的文件格式为office 2007专用格式,即.xlsx,优点是导出的数据不受行数限制,缺点导出速度慢SXSSF方式:SXSSF 是 XSSF API的兼容流式扩展,主要解决当使用 XSSF 方式导出大数据量时,内存溢出的问题,支持导出大批量的excel数据2.2.1、HSSF方式导出HSSF方式,最多只支持65536条数据导出,超过这个条数会报错!public class ExcelWrite2003Test {    public static String PATH = "/Users/hello/Desktop/";    public static void main(String[] args) throws Exception {        //时间        long begin = System.currentTimeMillis();        //创建一个工作簿        Workbook workbook = new HSSFWorkbook();        //创建表        Sheet sheet = workbook.createSheet();        //写入数据        for (int rowNumber = 0; rowNumber < 65536; rowNumber++) {            //创建行            Row row = sheet.createRow(rowNumber);            for (int cellNumber = 0; cellNumber < 10; cellNumber++) {                //创建列                Cell cell = row.createCell(cellNumber);                cell.setCellValue(cellNumber);            }        }        System.out.println("over");        FileOutputStream fileOutputStream = new FileOutputStream(PATH + "用户信息表2003BigData.xls");        workbook.write(fileOutputStream);        fileOutputStream.close();        long end = System.currentTimeMillis();        System.out.println((double) (end - begin) / 1000);//4.29s    }}2.2.2、XSSF方式导出XSSF方式支持大批量数据导出,所有的数据先写入内存再导出,容易出现内存溢出!public class ExcelWrite2007Test {    public static String PATH = "/Users/hello/Desktop/";    public static void main(String[] args) throws Exception {        //时间        long begin = System.currentTimeMillis();        //创建一个工作簿        Workbook workbook = new XSSFWorkbook();        //创建表        Sheet sheet = workbook.createSheet();        //写入数据        for (int rowNumber = 0; rowNumber < 65537; rowNumber++) {            Row row = sheet.createRow(rowNumber);            for (int cellNumber = 0; cellNumber < 10; cellNumber++) {                Cell cell = row.createCell(cellNumber);                cell.setCellValue(cellNumber);            }        }        System.out.println("over");        FileOutputStream fileOutputStream = new FileOutputStream(PATH + "用户信息表2007BigData.xlsx");        workbook.write(fileOutputStream);        fileOutputStream.close();        long end = System.currentTimeMillis();        System.out.println((double) (end - begin) / 1000);//15.87s    }}2.2.3、SXSSF方式导出SXSSF方式是XSSF方式的一种延伸,主要特性是低内存,导出的时候,先将数据写入磁盘再导出,避免报内存不足,导致程序运行异常,缺点是运行很慢!public class ExcelWriteSXSSFTest {    public static String PATH = "/Users/hello/Desktop/";    public static void main(String[] args) throws Exception {        //时间        long begin = System.currentTimeMillis();        //创建一个工作簿        Workbook workbook = new SXSSFWorkbook();        //创建表        Sheet sheet = workbook.createSheet();        //写入数据        for (int rowNumber = 0; rowNumber < 100000; rowNumber++) {            Row row = sheet.createRow(rowNumber);            for (int cellNumber = 0; cellNumber < 10; cellNumber++) {                Cell cell = row.createCell(cellNumber);                cell.setCellValue(cellNumber);            }        }        System.out.println("over");        FileOutputStream fileOutputStream = new FileOutputStream(PATH + "用户信息表2007BigDataS.xlsx");        workbook.write(fileOutputStream);        fileOutputStream.close();        long end = System.currentTimeMillis();        System.out.println((double) (end - begin) / 1000);//6.39s    }}2.3、导入excel导入操作,即将 excel 中的数据采用java工具库将其解析出来,进而将 excel 数据写入数据库!同样,在 poi 工具库中,导入 api 也分三种方式,与上面的导出一一对应!五、小结总体来说,easypoi和easyexcel都是基于apache poi进行二次开发的。不同点在于:1、easypoi 在读写数据的时候,优先是先将数据写入内存,优点是读写性能非常高,但是当数据量很大的时候,会出现oom,当然它也提供了 sax 模式的读写方式,需要调用特定的方法实现。2、easyexcel 基于sax模式进行读写数据,不会出现oom情况,程序有过高并发场景的验证,因此程序运行比较稳定,相对于 easypoi 来说,读写性能稍慢!easypoi 与 easyexcel 还有一点区别在于,easypoi 对定制化的导出支持非常的丰富,如果当前的项目需求,并发量不大、数据量也不大,但是需要导出 excel 的文件样式千差万别,那么我推荐你用 easypoi;反之,使用 easyexcel !六、参考1、apache poi - 接口文档2、easypoi - 接口文档3、easyexcel - 接口文档原文链接:https://blog.csdn.net/sufu1065/article/details/115301974
  • [技术干货] Java操作Excel三种方式POI、Hutool、EasyExcel
    Java操作Excel三种方式POI、Hutool、EasyExcel 1. Java操作Excel概述 1.1 Excel需求概述 1.2 Excel操作三种方式对比 2. ApachePOIExcel 2.1 ApachePOI简介 2.2 ApachePOI功能结构 2.3 ApachePOI官网说明 2.4 ApachePOI实现验证 3. HutoolExcel 3.1 Hutool简介 3.2 Hutool组件 3.1 HutoolExcel实现验证 4. EasyExcel 4.1 EasyExcel简介 4.2 HutoolExcel实现验证 5. 源码地址 1. Java操作Excel概述 1.1 Excel需求概述 Excel作为常用的报表文件,项目开发中经常用到Excel操作功能,对于Excel操作目前常见的有三种方式,Apache的POI,Hutool的ExcelUtil,Ali的EasyExcel。  1.2 Excel操作三种方式对比 Apache的POI相对比较原生,功能强大,使用复杂繁琐,内存占用比较大。 Hutool的ExcelUtil在POI基础上进行封装,简单易用,功能不太全,对于一些特殊操作,如标注等不具备。 Ali的EasyExcel也是在POI基础上进行封装,简单易用,功能相对比较全面,且性能十分强悍,最为推荐。  2. ApachePOIExcel 2.1 ApachePOI简介 Apache POI [1] 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”。  2.2 ApachePOI功能结构 HSSF [1]  - 提供读写Microsoft Excel XLS格式档案的功能。 XSSF [1]  - 提供读写Microsoft Excel OOXML XLSX格式档案的功能。 HWPF [1]  - 提供读写Microsoft Word DOC格式档案的功能。 HSLF [1]  - 提供读写Microsoft PowerPoint格式档案的功能。 HDGF [1]  - 提供读Microsoft Visio格式档案的功能。 HPBF [1]  - 提供读Microsoft Publisher格式档案的功能。 HSMF [1]  - 提供读Microsoft Outlook格式档案的功能。 2.3 ApachePOI官网说明 POI官网地址:https://poi.apache.org/index.html POI下载地址:https://poi.apache.org/download.html POI文档说明:https://poi.apache.org/apidocs/index.html  2.4 ApachePOI实现验证 Maven依赖配置          <!-- poi 相关 -->         <dependency>             <groupId>org.apache.poi</groupId>             <artifactId>poi</artifactId>             <version>4.0.1</version>         </dependency>         <dependency>             <groupId>org.apache.poi</groupId>             <artifactId>poi-ooxml</artifactId>             <version>4.0.1</version>         </dependency>         <dependency>             <groupId>org.apache.poi</groupId>             <artifactId>poi-ooxml-schemas</artifactId>             <version>4.0.1</version>         </dependency>  代码测试  package com.zrj.easyexcel.excel;  import com.google.common.collect.Lists; import org.junit.Test;  import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map;  /**  * 原生Excel测试类  *  * @author zrj  * @since 2021/12/29  **/ public class EasyExcelPoi {      @Test     public void createExcelTest() throws Exception {         //文件的基础路径         String fileBasePath = System.getProperty("user.dir") + File.separator + "excel" + File.separator;         System.out.println("文件的基础路径:" + fileBasePath);          String filepath = fileBasePath + "writeexcel.xlsx";         String sheetName = "模板名称";         List<String> titles = Lists.newArrayList("name", "sex", "age");         List<Map<String, Object>> values = Lists.newArrayList();          Map map2 = new HashMap(6);         map2.put("name", "王五");         map2.put("sex", "女");         map2.put("age", "20");         values.add(map2);          Map map3 = new HashMap(6);         map3.put("name", "阿萨");         map3.put("sex", "女");         map3.put("age", "66");         values.add(map3);          ExcelUtils.writeExcel(filepath, sheetName, titles, values);         System.out.println("创建Excel完成");     }      @Test     public void copyExcelTest() throws Exception {         //文件的基础路径         String fileBasePath = System.getProperty("user.dir") + File.separator + "excel" + File.separator;         System.out.println("文件的基础路径:" + fileBasePath);          String filepath = fileBasePath + "writeexcel.xlsx";         String fileNewPath = fileBasePath + "writeexcel2.xlsx";         ExcelUtils.writeExcel(filepath, fileNewPath);         System.out.println("复制Excel完成");     }      @Test     public void readExcelTest() throws Exception {         //文件的基础路径         String fileBasePath = System.getProperty("user.dir") + File.separator + "excel" + File.separator;         String filepath = fileBasePath + "writeexcel.xlsx";         System.out.println("文件路径:" + fileBasePath);          String readExcel = ExcelUtils.readExcel(filepath);         System.out.println(readExcel);         System.out.println("读取Excel完成");     }  } 3. HutoolExcel 3.1 Hutool简介 Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。 Hutool中的工具方法来自每个用户的精雕细琢,它涵盖了Java开发底层代码中的方方面面,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当; Hutool是项目中“util”包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务,同时可以最大限度的避免封装不完善带来的bug。  项目中使用非常广泛的一个小工具,没有依赖,非常干净好用。 官网地址:https://www.hutool.cn/  参考文档:https://www.hutool.cn/docs/#/ API文档:https://apidoc.gitee.com/dromara/hutool/  GITEE:https://gitee.com/dromara/hutool/ GITHUB:https://github.com/dromara/hutool/  视频介绍:https://player.bilibili.com/player.html?aid=710062843&bvid=BV1bQ4y1M7d9&cid=170377135&page=2  3.2 Hutool组件 3.1 HutoolExcel实现验证 Mave依赖配置  <dependency>     <groupId>cn.hutool</groupId>     <artifactId>hutool-all</artifactId>     <version>5.7.18</version> </dependency> 代码实现验证  package com.zrj.easyexcel.excel;  import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.poi.excel.ExcelReader; import cn.hutool.poi.excel.ExcelUtil; import cn.hutool.poi.excel.ExcelWriter; import com.zrj.easyexcel.utils.FileUtils; import org.junit.Test;  import java.io.ByteArrayOutputStream; import java.io.File; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map;  /**  * HutoolExcel测试类  * 注意:不要到错包了,easyexcel与hutool的类名一样!!!  *  * @author zrj  * @since 2021/12/29  **/ public class EasyExcelHutool {      /*******************************Excel读取-ExcelReader*******************************************/      /**      * 1. 读取Excel中所有行和列,都用列表表示      */     @Test     public void readExcelObjectTest() {         //定义文件路径         String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeBeanKeyTest.xlsx";         //通过工具类创建ExcelReader         ExcelReader reader = ExcelUtil.getReader(filepath);         List<List<Object>> readAll = reader.read();         System.out.println("Excel读取结果:" + readAll);     }      /**      * 2. 读取为Map列表,默认第一行为标题行,Map中的key为标题,value为标题对应的单元格值。      */     @Test     public void readExcelMapTest() {         //定义文件路径         String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeBeanKeyTest.xlsx";         //通过工具类创建ExcelReader         ExcelReader reader = ExcelUtil.getReader(filepath);         List<Map<String, Object>> readAll = reader.readAll();         System.out.println("Excel读取结果:" + readAll);     }      /**      * 3. 读取为Bean列表,Bean中的字段名为标题,字段值为标题对应的单元格值。      */     @Test     public void readExcelBeanTest() {         //定义文件路径         String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeBeanKeyTest.xlsx";         //通过工具类创建ExcelReader         ExcelReader reader = ExcelUtil.getReader(filepath);         List<UserBean> readAll = reader.readAll(UserBean.class);         System.out.println("Excel读取结果:" + readAll);     }      /*******************************Excel生成-ExcelWriter*******************************************/      /**      * 1. 将行列对象写出到Excel      */     @Test     public void writeExcelObjectTest() {         //定义一个嵌套的List,List的元素也是一个List,内层的一个List代表一行数据,每行都有4个单元格,最终list对象代表多行数据。         List<String> row1 = CollUtil.newArrayList("aa", "bb", "cc", "dd");         List<String> row2 = CollUtil.newArrayList("aa1", "bb1", "cc1", "dd1");         List<String> row3 = CollUtil.newArrayList("aa2", "bb2", "cc2", "dd2");         List<String> row4 = CollUtil.newArrayList("aa3", "bb3", "cc3", "dd3");         List<String> row5 = CollUtil.newArrayList("aa4", "bb4", "cc4", "dd4");          List<List<String>> rows = CollUtil.newArrayList(row1, row2, row3, row4, row5);          //定义文件路径         String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeTest.xlsx";         //通过工具类创建writer         ExcelWriter writer = ExcelUtil.getWriter(filepath);         //通过构造方法创建writer         //ExcelWriter writer = new ExcelWriter("d:/writeTest.xls");          //跳过当前行,既第一行,非必须,在此演示用         writer.passCurrentRow();          //合并单元格后的标题行,使用默认标题样式         writer.merge(row1.size() - 1, "测试标题");         //一次性写出内容,强制输出标题         writer.write(rows, true);         //关闭writer,释放内存         writer.close();         System.out.println("创建完成,文件路径:" + filepath);     }      /**      * 2. 写出Map数据      */     @Test     public void writeExcelMapTest() {         Map<String, Object> row1 = new LinkedHashMap<>();         row1.put("姓名", "张三");         row1.put("年龄", 23);         row1.put("成绩", 88.32);         row1.put("是否合格", true);         row1.put("考试日期", DateUtil.date());          Map<String, Object> row2 = new LinkedHashMap<>();         row2.put("姓名", "李四");         row2.put("年龄", 33);         row2.put("成绩", 59.50);         row2.put("是否合格", false);         row2.put("考试日期", DateUtil.date());          ArrayList<Map<String, Object>> rows = CollUtil.newArrayList(row1, row2);          //定义文件路径         String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeMapTest.xlsx";         // 通过工具类创建writer         ExcelWriter writer = ExcelUtil.getWriter(filepath);         // 合并单元格后的标题行,使用默认标题样式         writer.merge(row1.size() - 1, "一班成绩单");         // 一次性写出内容,使用默认样式,强制输出标题         writer.write(rows, true);         // 关闭writer,释放内存         writer.close();         System.out.println("创建完成,文件路径:" + filepath);     }      /**      * 3. 写出Bean数据      */     @Test     public void writeExcelBeanTest() {         UserBean bean1 = UserBean.builder().name("张三").age(22).isPass(true).score(66.30).examDate(DateUtil.date()).build();         UserBean bean2 = UserBean.builder().name("李四").age(30).isPass(false).score(38.50).examDate(DateUtil.date()).build();          List<UserBean> rows = CollUtil.newArrayList(bean1, bean2);          //定义文件路径         String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeBeanTest.xlsx";         // 通过工具类创建writer         ExcelWriter writer = ExcelUtil.getWriter(filepath);         // 合并单元格后的标题行,使用默认标题样式         writer.merge(4, "一班成绩单");         // 一次性写出内容,使用默认样式,强制输出标题         writer.write(rows, true);         // 关闭writer,释放内存         writer.close();         System.out.println("创建完成,文件路径:" + filepath);     }      /**      * 4. 自定义Bean的key别名(排序标题)      * 注意:与3的区别是,3中标题是英文,4中标题是自定义的别名      */     @Test     public void writeExcelBeanKeyTest() {         UserBean bean1 = UserBean.builder().name("张三").age(22).isPass(true).score(66.30).examDate(DateUtil.date()).build();         UserBean bean2 = UserBean.builder().name("李四").age(30).isPass(false).score(38.50).examDate(DateUtil.date()).build();          List<UserBean> rows = CollUtil.newArrayList(bean1, bean2);          //定义文件路径         String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeBeanKeyTest.xlsx";         // 通过工具类创建writer         ExcelWriter writer = ExcelUtil.getWriter(filepath);          //自定义标题别名         writer.addHeaderAlias("name", "姓名");         writer.addHeaderAlias("age", "年龄");         writer.addHeaderAlias("score", "分数");         writer.addHeaderAlias("isPass", "是否通过");         writer.addHeaderAlias("examDate", "考试时间");          // 默认的,未添加alias的属性也会写出,如果想只写出加了别名的字段,可以调用此方法排除之         writer.setOnlyAlias(true);          // 合并单元格后的标题行,使用默认标题样式         writer.merge(4, "一班成绩单");         // 一次性写出内容,使用默认样式,强制输出标题         writer.write(rows, true);         // 关闭writer,释放内存         writer.close();         System.out.println("创建完成,文件路径:" + filepath);     }      /**      * 5. 写出到流      */     @Test     public void writeExcelStreamTest() {         //定义内容         UserBean bean1 = UserBean.builder().name("张三").age(22).isPass(true).score(66.30).examDate(DateUtil.date()).build();         UserBean bean2 = UserBean.builder().name("李四").age(30).isPass(false).score(38.50).examDate(DateUtil.date()).build();         List<UserBean> rows = CollUtil.newArrayList(bean1, bean2);          //定义输出流         ByteArrayOutputStream out = FileUtils.cloneInputStream(IoUtil.toUtf8Stream(rows.toString()));          //定义文件路径         String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeStreamTest.xlsx";         if (!FileUtil.isFile(filepath)) {             FileUtil.touch(filepath);         }         //通过工具类创建writer         ExcelWriter writer = ExcelUtil.getWriter();         //创建xlsx格式的         //ExcelWriter writer = ExcelUtil.getWriter(true);         //一次性写出内容,使用默认样式,强制输出标题         writer.write(rows, true);         //out为OutputStream,需要写出到的目标流         writer.flush(out);         //关闭writer,释放内存         writer.close();         System.out.println("创建完成,文件路径:" + filepath);     }      /**      * 6. 写出到客户端下载(写出到Servlet)      */     @Test     public void writeExcelDownloadTest() {         //定义内容         UserBean bean1 = UserBean.builder().name("张三").age(22).isPass(true).score(66.30).examDate(DateUtil.date()).build();         UserBean bean2 = UserBean.builder().name("李四").age(30).isPass(false).score(38.50).examDate(DateUtil.date()).build();         List<UserBean> rows = CollUtil.newArrayList(bean1, bean2);          // 通过工具类创建writer,默认创建xls格式         ExcelWriter writer = ExcelUtil.getWriter();         // 一次性写出内容,使用默认样式,强制输出标题         writer.write(rows, true);         //out为OutputStream,需要写出到的目标流          //response为HttpServletResponse对象         //response.setContentType("application/vnd.ms-excel;charset=utf-8");         //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码         //response.setHeader("Content-Disposition", "attachment;filename=test.xls");         //ServletOutputStream out = response.getOutputStream();          //writer.flush(out, true);         //关闭writer,释放内存         writer.close();         //此处记得关闭输出Servlet流         //IoUtil.close(out);     }  } 4. EasyExcel 4.1 EasyExcel简介 EasyExcel是阿里巴巴开源的一款Excel操作工具,简单易用,功能强大,节省内存,性能彪悍。  语雀地址:https://www.yuque.com/easyexcel/doc/easyexcel Github地址: https://github.com/alibaba/easyexcel mvn依赖地址:https://mvnrepository.com/artifact/com.alibaba/easyexcel  4.2 HutoolExcel实现验证 Mave依赖配置          <dependency>             <groupId>com.alibaba</groupId>             <artifactId>easyexcel</artifactId>             <version>3.0.5</version>         </dependency> EasyExcel基本实现案例 Github案例地址:https://github.com/alibaba/easyexcel 项目路径:test -> read/write -> ReadTest与WriteTest类,实现基本Excel各种读写功能。  EasyExcel批量添加批注 ByteToInputStreamUtil  package com.zrj.easyexcel.excel;  import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream;  /**  * 输入流与字节转换  *  * @author zrj  * @since 2021/12/27  **/ public class ByteToInputStreamUtil {     /**      * 字节转输入流      *      * @param buf      * @return java.io.InputStream      */     public static InputStream byte2Input(byte[] buf) {         return new ByteArrayInputStream(buf);     }      /**      * 输入流转字节      *      * @param inStream      * @return byte[]      */     public static byte[] input2byte(InputStream inStream) throws Exception {         ByteArrayOutputStream swapStream = new ByteArrayOutputStream();         byte[] buff = new byte[100];         int rc = 0;         while ((rc = inStream.read(buff, 0, 100)) > 0) {             swapStream.write(buff, 0, rc);         }         byte[] in2b = swapStream.toByteArray();         return in2b;     } } CommentWriteHandler  package com.zrj.easyexcel.excel;  import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.excel.write.handler.AbstractRowWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; import org.apache.poi.hssf.usermodel.HSSFClientAnchor; import org.apache.poi.hssf.usermodel.HSSFRichTextString; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFRichTextString;  import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors;  /**  * 批注处理器  *  * @author zrj  * @since 2021/12/29  **/ public class CommentWriteHandler extends AbstractRowWriteHandler {      /**      * sheet名称KEY      */     public static final String SHEETNAME_NAME = "sheetName";      /**      * 文档后缀名      */     private String extension;      /**      * 列索引key      */     public static final String COLINDEX_NAME = "colIndex";      /**      * 行索引key      */     public static final String ROWINDEX_NAME = "rowIndex";      /**      * 批注内容key      */     public static final String COMMENTCONTENT_NAME = "commentContent";      /**      * sheet页名称列表      */     private List<String> sheetNameList;      /**      * 批注集合      */     List<Map<String, String>> commentList = new ArrayList<>(10);      /**      * CommentWriteHandler      *      * @param commentList      * @param extension      */     public CommentWriteHandler(List<Map<String, String>> commentList, String extension) {         this.commentList = commentList != null && commentList.size() > 0                 ? commentList.stream().filter(x ->                 x.keySet().contains(SHEETNAME_NAME) == true && x.get(SHEETNAME_NAME) != null && StrUtil.isNotBlank(x.get(SHEETNAME_NAME).toString())                         && x.keySet().contains(COLINDEX_NAME) == true && x.get(COLINDEX_NAME) != null && StrUtil.isNotBlank(x.get(COLINDEX_NAME).toString())                         && x.keySet().contains(ROWINDEX_NAME) == true && x.get(ROWINDEX_NAME) != null && StrUtil.isNotBlank(x.get(ROWINDEX_NAME).toString())                         && x.keySet().contains(COMMENTCONTENT_NAME) == true && x.get(COMMENTCONTENT_NAME) != null && StrUtil.isNotBlank(x.get(COMMENTCONTENT_NAME).toString())         ).collect(Collectors.toList()) : new ArrayList<>();         sheetNameList = this.commentList.stream().map(x -> x.get(SHEETNAME_NAME).toString()).collect(Collectors.toList());         this.extension = extension;     }      /**      * 生成批注信息      *      * @param sheetName      sheet页名称      * @param rowIndex       行号      * @param columnIndex    列号      * @param commentContent 批注内容      * @return      */     public static Map<String, String> createCommentMap(String sheetName, int rowIndex, int columnIndex, String commentContent) {         Map<String, String> map = new HashMap<>();         //sheet页名称         map.put(SHEETNAME_NAME, sheetName);         //行号         map.put(ROWINDEX_NAME, rowIndex + "");         //列号         map.put(COLINDEX_NAME, columnIndex + "");         //批注内容         map.put(COMMENTCONTENT_NAME, commentContent);         return map;     }      /**      * 功能描述      * @param writeSheetHolder      * @param writeTableHolder      * @param row      * @param relativeRowIndex      * @param isHead      * @return void      */     @Override     public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,                                 Integer relativeRowIndex, Boolean isHead) {         Sheet sheet = writeSheetHolder.getSheet();         //不需要添加批注,或者当前sheet页不需要添加批注         if (commentList == null || commentList.size() <= 0 || sheetNameList.contains(sheet.getSheetName()) == false) {             return;         }         //获取当前行的批注信息         List<Map<String, String>> rowCommentList = commentList.stream().filter(x ->                 StrUtil.equals(x.get(SHEETNAME_NAME).toString(), sheet.getSheetName())                         && relativeRowIndex == Integer.parseInt(x.get(ROWINDEX_NAME))).collect(Collectors.toList());         //当前行没有批注信息         if (rowCommentList == null || rowCommentList.size() <= 0) {             return;         }         List<String> colIndexList = rowCommentList.stream().map(x -> x.get(COLINDEX_NAME)).distinct().collect(Collectors.toList());         for (String colIndex : colIndexList) {             //同一单元格的批注信息             List<Map<String, String>> cellCommentList = rowCommentList.stream().filter(x ->                     StrUtil.equals(colIndex, x.get(COLINDEX_NAME))).collect(Collectors.toList());             if (CollectionUtil.isEmpty(cellCommentList)) {                 continue;             }             //批注内容拼成一条             String commentContent = cellCommentList.stream().map(x -> x.get(COMMENTCONTENT_NAME)).collect(Collectors.joining());             Cell cell = row.getCell(Integer.parseInt(colIndex));             addComment(cell, commentContent, extension);         }         //删除批注信息         commentList.remove(rowCommentList);         //重新获取要添加的sheet页姓名         sheetNameList = commentList.stream().map(x -> x.get(SHEETNAME_NAME).toString()).collect(Collectors.toList());     }      /**      * 给Cell添加批注      *      * @param cell      单元格      * @param value     批注内容      * @param extension 扩展名      */     public static void addComment(Cell cell, String value, String extension) {         Sheet sheet = cell.getSheet();         cell.removeCellComment();         if ("xls".equals(extension)) {             ClientAnchor anchor = new HSSFClientAnchor();             // 关键修改             anchor.setDx1(0);             anchor.setDx2(0);             anchor.setDy1(0);             anchor.setDy2(0);             anchor.setCol1(cell.getColumnIndex());             anchor.setRow1(cell.getRowIndex());             anchor.setCol2(cell.getColumnIndex() + 5);             anchor.setRow2(cell.getRowIndex() + 6);             // 结束             Drawing drawing = sheet.createDrawingPatriarch();             Comment comment = drawing.createCellComment(anchor);             // 输入批注信息             comment.setString(new HSSFRichTextString(value));             // 将批注添加到单元格对象中             cell.setCellComment(comment);         } else if ("xlsx".equals(extension)) {             ClientAnchor anchor = new XSSFClientAnchor();             // 关键修改             anchor.setDx1(0);             anchor.setDx2(0);             anchor.setDy1(0);             anchor.setDy2(0);             anchor.setCol1(cell.getColumnIndex());             anchor.setRow1(cell.getRowIndex());             anchor.setCol2(cell.getColumnIndex() + 5);             anchor.setRow2(cell.getRowIndex() + 6);             // 结束             Drawing drawing = sheet.createDrawingPatriarch();             Comment comment = drawing.createCellComment(anchor);             // 输入批注信息             comment.setString(new XSSFRichTextString(value));             // 将批注添加到单元格对象中             cell.setCellComment(comment);         }     } } EasyExcelUtils  package com.zrj.easyexcel.excel;  import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.write.builder.ExcelWriterBuilder; import com.alibaba.excel.write.metadata.WriteSheet; import com.zrj.easyexcel.entity.DemoData; import com.zrj.easyexcel.excel.read.ReadTest; import com.zrj.easyexcel.excel.write.WriteTest; import org.junit.Test;  import java.io.File; import java.util.*;  /**  * EasyExcel测试类  *  * @author zrj  * @since 2021/12/29  **/ public class EasyExcelUtils {     /**      * EasyExcel读写测试类      */     @Test     public void easyExcelReadWriteTest() {         //EasyExcel读测试类         ReadTest readTest = new ReadTest();         //EasyExcel写测试类         WriteTest writeTest = new WriteTest();     }     /**      * EasyExcel 批量添加批注      */     @Test     public void batchAddCommentTest() {         try {             //构建数据             List<DemoData> demoDataList = new ArrayList<>(10);             demoDataList.add(DemoData.builder().string("张三").date(new Date()).doubleData(3.14).build());             demoDataList.add(DemoData.builder().string("王五").date(new Date()).doubleData(6.68).build());             demoDataList.add(DemoData.builder().string("赵六").date(new Date()).doubleData(8.32).build());             demoDataList.add(DemoData.builder().string("李四").date(new Date()).doubleData(8.66).build());              //定义文件路径             String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "EasyExcelCommentWriteTest3.xlsx";             String sheetName = "批注模板";             List<Map<String, String>> commentList = new ArrayList<>();             commentList.add(CommentWriteHandler.createCommentMap(sheetName, 0, 1, "第一条批注。"));             commentList.add(CommentWriteHandler.createCommentMap(sheetName, 1, 1, "第二条批注。"));              // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭             // 这里要注意inMemory 要设置为true,才能支持批注。目前没有好的办法解决 不在内存处理批注。这个需要自己选择。             EasyExcel.write(filepath, DemoData.class).inMemory(Boolean.TRUE)                     .registerWriteHandler(new CommentWriteHandler(commentList, "xlsx"))                     .sheet(sheetName).doWrite(demoDataList);              System.out.println("批注模板完成,模板地址:" + filepath);         } catch (Exception e) {             e.printStackTrace();         }     } } 5. 源码地址 测试源码地址:https://gitee.com/rjzhu/opencode/tree/master/easyexcel ———————————————— 原文链接:https://blog.csdn.net/m0_37583655/article/details/122233640 
  • [问题求助] CodeArts IDE for java 安装完成异常
    不能登录不能创建项目java 文件不能执行
  • [技术干货] Java Stream流详解-转载
     学习目标 看玩这篇将会: 1.了解stream流 2.学会使用stream流 3.掌握stream流的使用场景 每个方法通过举例子的形式学习!  Stream API主要提供了两种类型的操作:中间操作 和 终止操作。  中间操作 中间操作是返回一个新的流,并在返回的流中包含所有之前的操作结果。它们总是延迟计算,这意味着它们只会在终止操作时执行,这样可以最大限度地优化资源使用。  Filter(过滤)  filter()方法接受一个谓词(一个返回boolean值的函数),并返回一个流,其中仅包含通过该谓词的元素。 建一个数组,帅选出长度大于4的元素 eg: public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         List<String> collect = names.stream().filter(item -> item.length() > 4).collect(Collectors.toList());         System.out.println(collect);     } } 这段代码创建了一个包含4个字符串的List集合,然后使用Stream()方法将其转化为一个Stream流。接下来使用filter()方法筛选出长度大于4的字符串,返回一个新的包含符合条件元素的Stream流collect。最后使用collect()方法将筛选后的结果转换成一个List集合。 使用Stream流中的filter()方法可以对流中的元素进行筛选过滤。在这段代码中,lambda表达式item -> item.length() > 4指定了筛选判断条件,即只保留长度大于4的字符串。collect(Collectors.toList())则将筛选后的结果转换成一个List集合返回。 通过这段代码,开发人员可以对包含字符串的数据进行快速的筛选和过滤,并且返回结果是一个新的可操作的集合,方便后续进行处理或展示。  Map(转换)  map()方法可将一个流的元素转换为另一个流。它接受一个函数,该函数映射流中的每个元素到另一个元素。 public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);         List<Integer> collect = numbers.stream().map(n ->         {             n = n * 2;             return n;         }).collect(Collectors.toList());         for (Integer integer : collect) {             System.out.println("integer = " + integer);         }     } } 这段代码使用了 Java 8 中的 Stream API 实现了一种对数字列表中的每个元素进行乘以 2 的操作,并将操作后的结果保存到新的列表中。  首先创建了一个包含数字 1~5 的列表。 然后利用 stream() 方法将列表转换成 Stream 对象。 接下来调用 map() 方法对每个元素进行操作,这里使用了 lambda 表达式对每个元素进行了乘以 2 的操作。 最后调用 collect() 方法将结果收集起来,并转换成 List。   转换为Map public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);         Map<Byte, Integer> collect = numbers.stream().collect(Collectors.toMap(Integer::byteValue, item -> item*2, (val1, val2) -> val2));         for (Map.Entry<Byte, Integer> byteIntegerEntry : collect.entrySet()) {             Byte key = byteIntegerEntry.getKey();             System.out.println("key = " + key);             System.out.println("Value = " + byteIntegerEntry.getValue());         }     } }  这段代码使用了 Java 8 中的 Stream API 实现了一种将数字列表转换成字节-整数键值对的方式。具体来说,代码中: 首先创建了一个包含数字 1~5 的列表。 然后利用 stream() 方法将列表转换成 Stream 对象。 接下来调用 collect(Collectors.toMap(…)) 方法将 Stream 转换成 Map<Byte, Integer>。 在 toMap 方法中,我们以每个整数的字节值为键,该整数乘以 2 为值,当遇到重复的键时取最后一个值。(这里实际上可以用任何能区分不同键的方式作为第一个参数,而不一定是 Integer::byteValue) 最后,在 for 循环中遍历了这个 Map 并打印出每个键值对的内容。 总的来说,通过 Stream API 可以方便地实现对集合数据进行筛选、映射、分组、统计等各种操作,相对于传统的循环遍历方式更为简洁、可读性更高,可以提高开发效率。  Sorted(排序)  sorted()方法可对流进行排序。它可以接受一个Comparator参数,也可以使用自然排序Ordering.natural()。默认排序是按升序排序。 public class Main {     public static void main(String[] args) {         int[] numbers = { 5, 2, 8, 3, 7 };         int[] sortedNumbers = Arrays.stream(numbers).sorted().toArray();         System.out.println(Arrays.toString(sortedNumbers));     } } 这段代码创建了一个包含整数的数组numbers,然后使用Arrays.stream()方法将其转化为一个IntStream流。接下来使用sorted()方法对流中的元素进行排序操作,返回一个新的排序后的IntStream流。最后,使用toArray()方法将排序后的结果转换为一个新的int类型数组sortedNumbers,并使用Arrays.toString()方法将结果输出到控制台。 使用Stream流可以简化代码,提高效率和可读性,方便开发人员对数据进行快速处理和排序。  Distinct(去重)  distinct()方法从流中返回所有不同的元素。在内部,它使用equals()方法来比较元素是否相同。因此,我们需要确保equals()方法已正确实现。 public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 2, 1);         List<Integer> collect = numbers.stream().distinct().collect(Collectors.toList());         System.out.println(collect);     } } 这段代码创建了一个包含整数的List集合numbers,其中包含了若干个重复的整数。接下来使用Stream()方法将其转化为一个Stream流。使用distinct()方法对流中的元素进行去重操作,返回一个新的不包含重复元素的Stream流collect。最后使用collect()方法将去重后的结果转换成一个List集合,并使用System.out.println()方法输出到控制台。 使用Stream流中的distinct()方法可以快速地对集合中的重复元素进行去重处理。在这段代码中,集合中的元素都是整数,使用distinct()方法去除了所有重复的整数,返回一个新的元素不重复且顺序不变的List集合。 运行该示例代码,输出结果为:[1, 2, 3],即去重后的不包含重复元素的整数List集合。  Limit(限制)  limit()方法可以将流限制为指定的元素数。 public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);         List<Integer> collect = numbers.stream().limit(3).collect(Collectors.toList());         System.out.println(collect);     } } 这段代码创建了一个包含整数的List集合numbers,其中包含了5个整数。接下来使用Stream()方法将其转化为一个Stream流。使用limit()方法对流中的元素进行限制操作,仅保留前3个元素,返回一个新的只包含前3个元素的Stream流collect。最后使用collect()方法将限制操作后的结果转化为一个新的List集合,并使用System.out.println()方法输出到控制台。 使用Stream流中的limit()方法可以快速地对集合中的元素进行截取操作,仅保留前N个元素。在这段代码中,集合中包含了5个整数,使用limit(3)方法仅保留了前3个整数,返回一个新的只包含前3个元素的List集合。 运行该示例代码,输出结果为:[1, 2, 3],即仅包含前3个元素的整数List集合。  Skip(跳过) public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);         List<Integer> collect = numbers.stream().skip(2).collect(Collectors.toList());         System.out.println(collect);     } }  skip()方法可跳过前N个元素。  这段代码创建了一个包含整数的List集合numbers,其中包含了5个整数。接下来使用Stream()方法将其转化为一个Stream流。使用skip()方法对流中的元素进行跳过操作,跳过前2个元素,返回一个新的不包含前2个元素的Stream流collect。最后使用collect()方法将跳过操作后的结果转化为一个新的List集合,并使用System.out.println()方法输出到控制台。 使用Stream流中的skip()方法可以快速地对集合中的元素进行跳过操作,跳过前N个元素。在这段代码中,集合中包含了5个整数,使用skip(2)方法跳过前2个元素,返回一个新的不包含前2个元素的List集合。 运行该示例代码,输出结果为:[3, 4, 5],即不包含前2个元素的整数List集合。  Peek(展示)  peek()方法可以用于在Stream流中获取元素同时执行一些操作,如打印、调试、观察等。通常会与其他的方法联合使用。 public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         List<String> filteredNames = names.stream()                 .peek(System.out::println)                 .filter(name -> name.startsWith("C"))                 .peek(name -> System.out.println("Filtered value: " + name))                 .collect(Collectors.toList());         System.out.println("-----------------------------------------------------------------");         System.out.println(filteredNames);     } } 代码中创建了一个列表 names 包含四个字符串元素,然后使用流式操作处理这个列表。首先使用 peek() 方法将每个元素打印到控制台,然后使用 filter() 方法过滤掉不符合条件的元素,即不以字母 C 开头的字符串。接下来再次使用 peek() 方法将符合条件的字符串打印到控制台,以便验证过滤操作的效果。最后使用 collect() 方法将符合条件的字符串收集到一个新的列表 filteredNames 中,并输出该列表。 注意到,控制台上先输出了列表中的四个字符串,但只有以字母 C 开头的字符串 Charles 才符合筛选条件,因此仅仅 Charles 被保存在了 filteredNames 列表中。第二个 peek() 方法也被用来打印筛选出的元素 Charles。  终止操作 终止操作返回一个结果或副作用(例如:显示控制台输出),并将流关闭。  forEach(循环)  forEach()方法可将给定的方法应用于流中的每个元素。该方法是一种消费流的方式,不会返回值。 public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         names.stream().forEach(System.out::println);     } } 这段代码创建了一个包含四个字符串元素的列表 names,使用流式操作将每个元素打印到控制台。具体来说,它使用 forEach() 方法遍历列表中的所有元素,并对每个元素执行打印操作。 其中,四个字符串元素按顺序打印到了控制台上。注意到,使用 forEach() 方法时并没有指定任何条件或谓词,因此它会对列表中的所有元素进行操作,以达到遍历、打印等目的。  Collect(收集)  collect()方法可以将流中的元素收集到一个集合中。一般与其他方法配合使用。 public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);         List<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());         System.out.println(evenNumbers);     } } 这段代码创建了一个包含整数的列表 numbers,使用流式操作筛选出所有偶数,然后将它们收集到一个新的列表 evenNumbers 中,并打印输出。具体来说,它使用了 filter() 方法过滤掉所有奇数元素,只保留所有偶数元素,并使用 collect() 方法将它们收集到一个新的列表 evenNumbers 中。 注意到,只有偶数元素被保留在了新列表 evenNumbers 中,而奇数元素全部被过滤掉了。而且,在筛选偶数元素时,使用了 lambda 表达式 n -> n % 2 == 0,其中 % 表示取模操作,判断当前数是否为偶数。如果 n % 2 的结果是 0,就把 n 这个数保留下来,否则就过滤掉。  Count(计数)  count()方法可以返回流中的元素数。 public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         long count = names.stream().count();         System.out.println(count);     } } 这段代码创建了一个包含四个字符串元素的列表 names,使用流式操作计算出它包含的元素数量(即列表大小),并将该数量打印到控制台。具体来说,它使用了 count() 方法统计列表中元素的个数。 注意到,count() 方法返回的是一个 long 类型的值,表示列表中元素的个数。因为列表 names 包含了四个元素,所以 count() 方法返回值为 4,最终被打印输出到了控制台。  Reduce(聚合)  reduce()方法可以将流元素聚合为单个结果。它接受一个BinaryOperator参数作为累加器。 public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);         Optional<Integer> sum = numbers.stream().reduce((a, b) -> a + b);         System.out.println(sum);     } } 这段代码创建了一个包含整数的列表 numbers,使用流式操作将它们累加起来得到总和,并将结果打印输出。具体来说,它使用了 reduce() 方法对列表中的所有元素进行累加操作。reduce() 方法接收一个 BinaryOperator 函数作为参数,用于指定如何处理相邻的两个元素并返回一个新的结果值。 注意到,reduce() 方法返回的是一个 Optional 类型的值,表示结果可能存在也可能不存在(例如当列表为空时)。由于列表 numbers 包含 1 到 5 共五个元素,因此 reduce() 方法的操作过程如下: 1 + 2 = 3 3 + 3 = 6 6 + 4 = 10 10 + 5 = 15 最终得到的结果 15 被包装成 Optional 类型的对象并打印输出到控制台。  AnyMatch(任意匹配)  anyMatch()方法如果至少有一个元素与给定的谓词匹配,则返回true。 public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         boolean anyStartsWithB = names.stream().anyMatch(name -> name.startsWith("B"));         System.out.println(anyStartsWithB);     } } 这段代码创建了一个包含四个字符串元素的列表 names,使用流式操作检查其中是否有任意一个元素以字母 “B” 开头,并将检查结果(布尔值)打印输出。具体来说,它使用了 anyMatch() 方法匹配列表中的所有元素,并依次对每个元素执行指定的谓词操作(这里是以 “B” 开头),只要有一个元素符合条件,就返回 true,否则返回 false。 注意到,列表 names 中包含了一个以字母 “B” 开头的元素 “Brian”,因此 anyMatch() 方法返回 true,最终被打印输出到了控制台。  AllMatch(全部匹配)  allMatch()方法如果所有元素都与给定谓词匹配,则返回true。 public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         boolean allStartsWithB = names.stream().allMatch(name -> name.startsWith("B"));         System.out.println(allStartsWithB);     } } 这段代码创建了一个包含四个字符串元素的列表 names,使用流式操作检查其中是否所有元素都以字母 “B” 开头,并将检查结果(布尔值)打印输出。具体来说,它使用了 allMatch() 方法匹配列表中的所有元素,并依次对每个元素执行指定的谓词操作(这里是以 “B” 开头),只有当所有元素都符合条件时,才返回 true,否则返回 false。 注意到,虽然列表 names 中包含了一个以字母 “B” 开头的元素 “Brian”,但是它不是所有元素都以 “B” 开头,因此 allMatch() 方法返回 false,最终被打印输出到了控制台。  NoneMatch(无匹配)  noneMatch()方法,如果没有任何元素与给定谓词匹配,则返回true。 public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         boolean noneStartsWithB = names.stream().noneMatch(name -> name.startsWith("E"));         System.out.println(noneStartsWithB);     } } 这段代码创建了一个包含四个字符串元素的列表 names,使用流式操作检查其中是否没有任意一个元素以字母 “E” 开头,并将检查结果(布尔值)打印输出。具体来说,它使用了 noneMatch() 方法匹配列表中的所有元素,并依次对每个元素执行指定的谓词操作(这里是以 “E” 开头),只有当所有元素都不符合条件时,才返回 true,否则返回 false。 注意到,列表 names 中不包含任何一个以字母 “E” 开头的元素,因此 noneMatch() 方法返回 true,最终被打印输出到了控制台。  以上就是Java Stream流的基础知识和操作方式。Stream API可以使Java程序员编写出高效,干净,紧凑的代码,使得代码易于阅读和维护。建议初学者多加练习,掌握基本操作。中级和高级程序员则需要深入研究Stream API的实现原理和运作机制,进一步提高代码的质量和效率。  使用Stream流的优缺点: 优点: Stream流可以帮助简化代码,减少样板代码,从而提高代码质量和可读性。 Stream流充分利用了现代多核处理器的优势,在多线程场景下可以获得更好的性能表现。 Stream流提供了丰富的操作方法,可以轻松地处理各种集合和数组的数据,从而降低程序员的编码难度和心理负担。 Stream流可以帮助开发人员更容易地写出函数式风格的代码,使代码更加健壮可维护。  缺点: Stream流有时候会让代码变得复杂,反而降低了可读性,因此在某些简单的情况下可能不需要使用Stream流。 Stream流可能会对程序的性能产生一定影响,尤其是在大型数据集或者复杂的业务逻辑的情况下,程序员需要根据具体的情况进行测试和分析,选择最优解。 Stream流可能会造成资源浪费,例如创建中间操作的临时对象,这些对象将占用存储空间,导致效率降低。 在实际开发中,应该根据具体情况来决定是否使用Stream流。一般建议在数据集较大或者需要进行复杂的数据处理操作时使用Stream流,而在一些简单的操作中则可以直接使用循环和传统的集合操作方法。此外,如果代码可读性受到影响,也可以考虑使用传统的集合操作方法来实现代码。 ———————————————— 版权声明:本文为CSDN博主「小尘要自信」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_54796785/article/details/131122376 
  • [技术干货] 【JavaScript】JS能力测试题:数组扁平化 | 判断质数 | 获取字符串的长度-转载
     数组扁平化 题目描述 请补全JavaScript代码,要求将数组参数中的多维数组扩展为一维数组并返回该数组。 注意:  数组参数中仅包含数组类型和数字类型 题解 递归  遍历数组 并 判断遍历的当前元素的数据类型,分为以下两种情况:  类型为数值:添加该元素到输出数组中。 类型为数组:重复上诉操作。   const _flatten = arr => {          return arr.reduce((target, item) => {     return target.concat(Array.isArray(item) ? _flatten(item) : item);  }, []) } 其中参数 target为输出数组,item为遍历的当前元素  字符串法  a. 利用toString()方法,将数组转为字符串,在使用split()方法,以","为分割变为数组。  b. 利用join()方法将数组转为字符串且用","分割,在利用上诉split()方法以","为分割变为数组。  const _flatten = arr => {     return arr.toString().split(",") } const _flatten = arr => {     return arr.join(",").split(",") } 简单粗暴法  思路:  使用while循环检测数组,只要数组中存在元素的数据类型为数组,就利用扩展运算符将该元素的可遍历属性追加到输出数组中展开直到数组元素的数据类型均为数值为止。  const _flatten = arr => {     while(arr.some(item=>{Array.isArray(item)})){         arr = [].concat(...arr)     }     return arr } some() :测试数组中是否至少有一个元素通过了由提供的函数实现的测试,在本题中用于测试数组中是否存在元素的数据类型为数组。  扩展运算符(…):取出参数对象中的所有可遍历属性,拷贝到当前对象之中。在本题中扩展运算符(…)用于取出被检测到元素类型为数组的对象内的所有可遍历属性,并使用concat方法拷贝到输出数组中。  判断质数 题目描述 请补全JavaScript代码,要求在Number对象的原型对象上添加"_isPrime"函数,该函数判断调用的对象是否为一个质数,是则返回true,否则返回false。  题解 质数的定义,不能被除了1和它本身的数字因式分解的数字,对应到数学的概念就是取余为0  双指针  Number.prototype._isPrime = function(){     let j=this-1;     for(let i=2; i<j;i++){                  if(i>=j)break         if(!(this%i) || !(this%j)){             return false         }         j--            }     return true } 平方根  假设Number数值为c, 若a * b = c 且(a<=b)则a,b需满足 a<=Math.sqrt©<=b,因此只用依次让[2,Math.sqrt©]范围内的数值对number进行取余数检验。  Number.prototype._isPrime = function(){          for(let i=2; i <= Math.sqrt(this); i++){         if(!(this%i)){             return false         }     }     return true } 获取字符串的长度 题目描述 如果第二个参数 bUnicode255For1 === true,则所有字符长度为 1 否则如果字符 Unicode 编码 > 255 则长度为 2  示例:  输入:  'hello world, 牛客', false 1 输出:  17 1 题解 function strLength(s, bUnicode255For1) {     if(bUnicode255For1){        return s.length      }else{         let length = 0         for(let i=0; i<s.length; i++){             if(s[i].codePointAt() > 255){                 length += 2             }else{                 length += 1             }         }         return length     } } codePointAt(index)  作用:返回一个非负整数,该整数是从给定索引开始的字符的 Unicode 码位值。  参数:index ———————————————— 版权声明:本文为CSDN博主「阿选不出来」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/m0_63300737/article/details/133319227 
  • [技术干货] 【JAVA】为什么要使用封装以及如何封装-转载
     前言 Java的封装指的是在一个类中将数据和方法进行封装,使其可以保护起来,只能在该类内部访问,而不允许外部直接访问和修改。这是Java面向对象编程的三个基本特性之一,另外两个是继承和多态。在此之前我们已经学习过关于继承的概念,今天我们来学习有关封装的内容。  封装的含义 封装是面向对象编程的一种重要概念,是将数据和对数据的操作封装在一个类中,使得数据对外部的访问受到限制,只能通过类中的公共方法来访问或操作。封装的目的是隐藏类的实现细节,并且保护数据不被随意修改,从而增强了代码的安全性和可维护性。另外,封装也使得代码的扩展和修改更加方便,只需在类内部进行修改而不需要修改其他代码。  通过封装,可以提高类的安全性和可维护性,使得类的实现细节被隐藏,只暴露出有限的接口和功能给外部使用,从而减少了类之间的耦合性。同时,封装还可以实现数据隐藏和数据保护,提高程序的可靠性和可扩展性。  四种访问控制符 Java中,采用访问控制符来控制类中数据成员和方法的访问权限,主要有四种访问控制符:public、private、protected和默认访问控制符(即不写访问控制符)。  public:表示该数据成员或方法可以被其他任何类访问。 private:表示该数据成员或方法只能在当前类内部访问。 protected:表示该数据成员或方法可以在当前类和其子类中访问。 默认访问控制符:表示该数据成员或方法只能在同一个包内的其他类中访问。 封装的两种经典实例 银行账户类 该类的主要属性包括账户名、账号、账户余额等。其中账户余额是一个私有属性,外部无法直接访问。类中提供了一系列操作账户的方法,例如存款、取款、查询余额等。这些方法都能够在保证账户余额正确的情况下,修改账户余额。  public class BankAccount {     private String accountNumber;     private double balance;      public BankAccount(String accountNumber, double balance) {         this.accountNumber = accountNumber;         this.balance = balance;     }      public String getAccountNumber() {         return accountNumber;     }      public double getBalance() {         return balance;     }      public void deposit(double amount) {         balance += amount;     }      public void withdraw(double amount) {         if (balance >= amount) {             balance -= amount;         } else {             System.out.println("Insufficient balance.");         }     } } accountNumber: 表示账户号码的字符串。 balance: 表示账户余额的双精度浮点数。 BankAccount(String accountNumber, double balance): 构造函数,用于创建一个新的银行账户对象。 getAccountNumber(): 返回账户号码的方法。 getBalance(): 返回账户余额的方法。 deposit(double amount): 存款方法,用于向账户中添加资金。 withdraw(double amount): 取款方法,用于从账户中扣除资金。如果账户余额不足,将输出一条错误信息。 学生类 public class Student {     // 属性     private String name;     private int age;     private String gender;     private String id;      // 构造方法     public Student(String name, int age, String gender, String id) {         this.name = name;         this.age = age;         this.gender = gender;         this.id = id;     }      // 方法     public void study() {         System.out.println("学生正在学习");     }      public void showInfo() {         System.out.println("姓名:" + name);         System.out.println("年龄:" + age);         System.out.println("性别:" + gender);         System.out.println("学号:" + id);     }      // Getter和Setter方法     public String getName() {         return name;     }      public void setName(String name) {         this.name = name;     }      public int getAge() {         return age;     }      public void setAge(int age) {         this.age = age;     }      public String getGender() {         return gender;     }      public void setGender(String gender) {         this.gender = gender;     }      public String getId() {         return id;     }      public void setId(String id) {         this.id = id;     } } 属性:学生类具有姓名、年龄、性别和学号这些属性。 构造方法:学生类具有一个带四个参数的构造方法,用于初始化学生对象。 方法:学生类具有学习方法和展示学生信息的方法。 Getter和Setter方法:学生类具有获取和设置属性值的Getter和Setter方法,用于保护属性的私有性。 ———————————————— 版权声明:本文为CSDN博主「许思王」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_73602725/article/details/133498865 
  • [技术干货] Java 超高频常见字符操作【建议收藏】-转载
     前言 为了巩固所学的知识,作者尝试着开始发布一些学习笔记类的博客,方便日后回顾。当然,如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚,文章中如果有记录错误,欢迎读者朋友们批评指正。 (博客的参考源码可以在我主页的资源里找到,如果在学习的过程中有什么疑问欢迎大家在评论区向我提出)  1. 字符串拼接 1. 使用 + 操作符:  String str1 = "Hello"; String str2 = ", "; String str3 = "World";  String result = str1 + str2 + str3; 这种方法是最简单的,通过使用 + 操作符可以将多个字符串连接在一起。Java会自动处理字符串拼接并创建一个新的字符串对象。  2. 使用 concat() 方法:  String str1 = "Hello"; String str2 = ", "; String str3 = "World";  String result = str1.concat(str2).concat(str3); concat() 方法用于将一个字符串连接到另一个字符串的末尾。可以连续调用 concat() 方法来连接多个字符串。  3. 使用 StringBuilder 类:  StringBuilder 是一个可变的字符串类,适用于需要频繁修改字符串的情况,因为它不会创建多个中间字符串对象,从而提高了性能。  StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("Hello"); stringBuilder.append(", "); stringBuilder.append("World");  String result = stringBuilder.toString(); 在这种情况下,我们首先创建一个 StringBuilder 对象,然后使用 append() 方法来添加需要拼接的字符串,最后通过 toString() 方法将 StringBuilder 转换为不可变的 String。  4. 使用 StringBuffer 类:  StringBuffer 类与 StringBuilder 类 类似,但是是线程安全的,适用于多线程环境。  StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("Hello"); stringBuffer.append(", "); stringBuffer.append("World");  String result = stringBuffer.toString(); 与 StringBuilder 一样,我们可以使用 append() 方法来构建最终的字符串,然后通过 toString() 方法获取不可变的 String。  5. 使用 String.join() 方法(Java 8+):  这种方法更加简洁,特别适用于将多个字符串与指定的分隔符连接起来的情况。  String.join()  String str1 = "Hello"; String str2 = ", "; String str3 = "World";  String result = String.join(str2, str1, str3); 2. 字符串查找 1.使用 indexOf() 方法查找子字符串的位置:  indexOf() 方法用于查找一个字符串是否包含另一个子字符串,并返回第一次出现的位置索引。如果未找到,它将返回 -1。  String mainString = "Hello, World!"; String subString = "World"; int position = mainString.indexOf(subString);  if (position != -1) {    System.out.println("子字符串在主字符串中的位置是:" + position); } else {     System.out.println("子字符串未找到!"); } 2.使用 lastIndexOf() 方法查找最后一次出现的位置:  lastIndexOf() 方法与 indexOf() 方法类似,但它返回最后一次出现的位置索引。  String mainString = "Java is a powerful programming language. Java is also fun!"; String subString = "Java"; int position = mainString.lastIndexOf(subString);  if (position != -1) {     System.out.println("最后一次出现的位置是:" + position); } else {     System.out.println("子字符串未找到!"); } 3.使用 contains() 方法检查字符串是否包含子字符串:  contains() 方法用于检查一个字符串是否包含另一个子字符串,它返回一个布尔值。  String mainString = "This is a Java example."; String subString = "Java"; boolean contains = mainString.contains(subString);  if (contains) {     System.out.println("主字符串包含子字符串!"); } else {     System.out.println("主字符串不包含子字符串!"); } 4.使用正则表达式进行高级搜索:  Java 也支持使用正则表达式来进行复杂的字符串搜索。你可以使用 Pattern 和 Matcher 类来实现这一点。这允许你执行更复杂的模式匹配操作。  import java.util.regex.*;  String text = "The quick brown fox jumps over the lazy dog."; String pattern = "fox";  Pattern compiledPattern = Pattern.compile(pattern); Matcher matcher = compiledPattern.matcher(text);  while (matcher.find()) {     System.out.println("找到匹配字符串: " + matcher.group()); } 3. 字符串截取 1.使用 substring() 方法:  substring() 方法用于从一个字符串中截取子串,你可以指定截取的起始位置和结束位置。这个方法有两种形式:一种只传入起始位置,另一种传入起始位置和结束位置。  String mainString = "Hello, World!";  // 截取从索引2到索引5之间的子串(包括索引2,但不包括索引5) String subString1 = mainString.substring(2, 5); System.out.println("截取子串1: " + subString1); // 输出 "llo"  // 从索引7开始截取到字符串末尾 String subString2 = mainString.substring(7); System.out.println("截取子串2: " + subString2); // 输出 "World!" 2.使用 split() 方法分割字符串:  split() 方法允许你根据某个分隔符将字符串拆分为子串,然后选择需要的子串。  String text = "apple,banana,grape";  // 使用逗号作为分隔符拆分字符串 String[] fruits = text.split(",");  for (String fruit : fruits) {     System.out.println("水果: " + fruit); } 3.使用正则表达式进行高级截取:  Java 的正则表达式库允许你根据复杂的模式来截取字符串。  import java.util.regex.*;  String text = "The quick brown fox jumps over the lazy dog."; String pattern = "\\b\\w+\\b"; // 匹配单词  Pattern compiledPattern = Pattern.compile(pattern); Matcher matcher = compiledPattern.matcher(text);  while (matcher.find()) {     System.out.println("匹配到的单词: " + matcher.group()); } 4. 字符串替換 1.使用 replace() 方法:  replace() 方法用于将指定的字符或子字符串替换为新的字符或子字符串。  String originalString = "Hello, World!"; String searchString = "World"; String replacementString = "Java";  String modifiedString = originalString.replace(searchString, replacementString); System.out.println("替换后的字符串: " + modifiedString); 输出:  替换后的字符串: Hello, Java! 1 2.使用 replaceAll() 方法进行正则表达式替换:  replaceAll() 方法允许你使用正则表达式进行更灵活的替换操作。  String originalString = "The quick brown fox jumps over the lazy dog."; String pattern = "fox"; String replacementString = "cat";  String modifiedString = originalString.replaceAll(pattern, replacementString); System.out.println("替换后的字符串: " + modifiedString); 输出:    替换后的字符串: The quick brown cat jumps over the lazy dog. 1 3.使用 StringBuilder 进行替换:  如果需要进行多次替换或性能要求较高,可以使用 StringBuilder 类。  StringBuilder stringBuilder = new StringBuilder("Java is easy. Java is fun."); String searchString = "Java"; String replacementString = "Python"; int index = stringBuilder.indexOf(searchString); while (index != -1) {     stringBuilder.replace(index, index + searchString.length(), replacementString);     index = stringBuilder.indexOf(searchString); }  String modifiedString = stringBuilder.toString(); System.out.println("替换后的字符串: " + modifiedString); 输出:    替换后的字符串: Python is easy. Python is fun. 1 5. 字符串分割 1. 使用 split() 方法:  split() 方法是Java中最常用的字符串分割方法。它使用指定的正则表达式作为分隔符,将字符串分割成一个字符串数组。  String inputString = "apple,orange,banana,grape"; String[] fruits = inputString.split(",");  System.out.println("分割后的水果:"); for (String fruit : fruits) {      System.out.println(fruit); } 输出:  分割后的水果: apple orange banana grape 2. 使用 StringTokenizer 类:  StringTokenizer 类是Java中另一种进行字符串分割的方式,它使用指定的分隔符将字符串分割成标记。  String inputString = "Java is a powerful programming language"; StringTokenizer tokenizer = new StringTokenizer(inputString);  System.out.println("分割后的单词:"); while (tokenizer.hasMoreTokens()) {      System.out.println(tokenizer.nextToken()); } 输出: 分割后的单词: Java is a powerful programming language 3. 使用正则表达式: 你也可以使用正则表达式作为分隔符,以实现更灵活的字符串分割。 String inputString = "Java123is456a789powerful"; String[] parts = inputString.split("\\d+"); System.out.println("分割后的部分:"); for (String part : parts) {      System.out.println(part); } 输出: 分割后的部分: Java is a 4. 使用 Apache Commons Lang 库:  Apache Commons Lang 库提供了 StringUtils 类,其中有一个方便的 split() 方法,可以更容易地处理字符串分割。  import org.apache.commons.lang3.StringUtils;  String inputString = "Java;C;C++;Python"; String[] languages = StringUtils.split(inputString, ';');  System.out.println("分割后的编程语言:"); for (String language : languages) {      System.out.println(language); 输出: 分割后的编程语言: Java C C++ 字符串比较 1. 使用 equals() 方法进行内容比较: equals() 方法用于比较两个字符串的内容是否相同。它比较字符串的每个字符,而不仅仅是比较引用是否相等。 String str1 = "Hello"; String str2 = "World"; String str3 = "Hello"; boolean isEqual1 = str1.equals(str2); // 返回 false boolean isEqual2 = str1.equals(str3); // 返回 true System.out.println("str1 和 str2 是否相等:" + isEqual1); 2. 使用 equalsIgnoreCase() 方法进行忽略大小写的内容比较: equalsIgnoreCase() 方法与 equals() 方法类似,但它会忽略字符串的大小写。 String str1 = "Hello"; String str2 = "hello"; boolean isEqualIgnoreCase = str1.equalsIgnoreCase(str2); // 返回 true 3. 使用 compareTo() 方法进行字典顺序比较: compareTo() 方法用于比较两个字符串的字典顺序。它返回一个整数,表示两个字符串之间的比较结果。 String str1 = "apple"; String str2 = "banana"; String str3 = "cherry";  int result1 = str1.compareTo(str2); // 返回负数 int result2 = str2.compareTo(str1); // 返回正数 int result3 = str1.compareTo(str3); // 返回负数  System.out.println("str1 和 str2 的比较结果:" + result1); System.out.println("str2 和 str1 的比较结果:" + result2); System.out.println("str1 和 str3 的比较结果:" + result3); 4. 使用 startsWith() 和 endsWith() 方法检查前缀和后缀: startsWith() 方法用于检查字符串是否以指定的前缀开头,而 endsWith() 方法用于检查字符串是否以指定的后缀结尾。 Strng str = "Hello, World"; boolean startsWithHello = str.startsWith("Hello"); // 返回 true boolean endsWithWorld = str.endsWith("World");     // 返回 false System.out.println("字符串是否以 Hello 开头:" + startsWithHello); System.out.println("字符串是否以 World 结尾:" + endsWithWorld); 5. 使用 compareToIgnoreCase() 方法进行忽略大小写的字典顺序比较: compareToIgnoreCase() 方法与 compareTo() 方法类似,但它会忽略字符串的大小写。 String str1 = "apple"; String str2 = "Banana"; int result = str1.compareToIgnoreCase(str2); // 返回正数 System.out.println("str1 和 str2 的比较结果(忽略大小写):" + result); 7. 字符串格式化 1. 使用 String.format() String.format() 方法允许您创建格式化的字符串,类似于C语言中的 printf() 函数。它使用占位符来指定要插入的数据以及它们的格式。占位符由百分号 (%) 后跟一个字符组成,该字符表示插入数据的类型。以下是一些常见的占位符及其用法: %s :字符串。 %d :整数。 %f :浮点数。 %n :换行符。 示例: String name = "Alice"; int age = 30; double salary = 50000.50; String formattedString = String.format("Name: %s, Age: %d, Salary: %.2f", name, age, salary); System.out.println(formattedString); 输出: Name: Alice, Age: 30, Salary: 50000.50 在上面的示例中,我们使用了 %s 、 %d 和 %.2f 占位符来插入字符串、整数和浮点数,并指定了浮点数保留两位小数。 2. 使用 printf() printf() 方法是 System.out 对象的一个方法,它用于将格式化的字符串输出到控制台。与 String.format() 类似,它使用相同的占位符来格式化输出。 示例: String name = "Bob"; int age = 25; double height = 1.75; 输出: Name: Bob, Age: 25, Height: 1.75 8. 字符串空格处理 1. 删除空格:  .使用 String 类的 trim() 方法删除字符串前后的空格。 trim() 返回一个新的字符串,其中删除了前导和尾随的空格。 String text = "  This is a text with spaces  "; String trimmedText = text.trim(); System.out.println(trimmedText); // 输出: "This is a text with spaces" 2. 替换空格:  使用 String 类的 replace() 方法替换字符串中的空格。 你可以将空格替换为其他字符或字符串。 String text = "Hello, World!"; String replacedText = text.replace(" ", "_"); System.out.println(replacedText); // 输出: "Hello,_World!" 3. 分割字符串:  使用 split() 方法将字符串拆分成字符串数组。 默认情况下,split() 使用空格作为分隔符,但你可以指定自定义分隔符。 String sentence = "This is a sample sentence"; String[] words = sentence.split(" "); // 使用空格分割成单词数组 4. 检查空格:  使用 contains() 方法检查字符串是否包含空格。 String text = "This has spaces"; boolean hasSpaces = text.contains(" "); // 返回 true 5. 统计空格的数量:  使用循环遍历字符串并计算空格的数量。 String text = "Count the spaces in this text"; int spaceCount = 0; for (char c : text.toCharArray()) {     if (c == ' ') {         spaceCount++;     } } System.out.println("空格数量:" + spaceCount); // 输出: "空格数量:5" 6. 替换多个连续空格:  使用正则表达式来替换连续的多个空格为单个空格。 String text = "Replace  multiple   spaces  with one."; String replacedText = text.replaceAll("\\s+", " "); System.out.println(replacedText); // 输出: "Replace multiple spaces with one." 7. 处理制表符和换行符:  空白字符不仅包括空格,还包括制表符 (\t) 和换行符 (\n) 等。 你可以使用 replaceAll() 来处理它们,就像处理空格一样。 String textWithTabs = "This\tis\ta\ttab\tseparated\ttext"; String textWithNewlines = "This\nis\na\nnewline\nseparated\ntext"; 总结 欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下。 (博客的参考源码可以在我主页的资源里找到,如果在学习的过程中有什么疑问欢迎大家在评论区向我提出) ———————————————— 版权声明:本文为CSDN博主「东离与糖宝」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/HHX_01/article/details/132499313 
  • [其他问题] .so动态库出现undefined symbol
    使用了一个第三方的动态库libgmssl.so来编写生成了一个新的动态库tlcp.so,但是使用时出现undefined symbol后面使用ldd -r tlcp.so查看,显示如下内容undefined symbol: tls_send    (./tlcp.so) undefined symbol: tls_ctx_cleanup    (./tlcp.so) undefined symbol: tls_socket_accept    (./tlcp.so) undefined symbol: tls_do_handshake    (./tlcp.so) undefined symbol: tls_ctx_set_certificate_and_key    (./tlcp.so) undefined symbol: tls_socket_connect    (./tlcp.so) 这些函数的实现在libgmssl.so内已经实现,且使用nm libgmssl.so显示
总条数:691 到第
上滑加载中