编辑
2024-02-21
学习记录
00

前提

在项目中有时候会遇到需要多个对象,但是他们的大部分属性的内容都是相同的,那么我们就可以使用克隆的方式来克隆对象,再单独修改不同部分的属性即可,精简多余的set代码。

简介

克隆是指创建一个对象的副本,使得新创建的对象在内容上与原始对象相同。在编程中,克隆是常用的技术之一,它具有以下几个重要用途和优势:

  • 复制对象:使用克隆可以创建一个与原始对象相同的新对象,包括对象的属性和状态。这样可以在不影响原始对象的情况下,对新对象进行修改、操作、传递等。这在某些场景下非常有用,可以避免重新创建和初始化一个对象。

  • 隔离性与保护:通过克隆,可以创建一个独立于原始对象的副本。这样,修改克隆对象时,不会影响到原始对象,从而实现了对象之间的隔离性。这对于多线程环境下的并发操作或者保护重要数据具有重要意义。

  • 性能优化:有时候,通过克隆对象可以提高程序的性能。在某些场景下,对象的创建和初始化过程可能较为耗时,如果需要多次使用这个对象,通过克隆原始对象可以避免重复的创建和初始化过程,从而提高程序的执行效率。

  • 原型模式:克隆在设计模式中有一个重要的角色,即原型模式。原型模式通过克隆来创建对象的实例,而不是使用传统的构造函数。这样可以提供更灵活的对象创建方式,并且避免了频繁的子类化。

在编程中,通常通过实现Cloneable接口和重写clone方法来实现对象的克隆。然而,需要注意的是克隆操作可能存在深拷贝和浅拷贝的区别,在使用时需要根据实际需求选择合适的克隆方式。

编辑
2024-01-09
学习记录
00

依赖

xml
<dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.23</version> </dependency>

代码

java
//单页pdf public static String pdfToImage(String pdfBase64) throws IOException { ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(pdfBase64)); PDDocument document = PDDocument.load(inputStream); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); PDFRenderer renderer = new PDFRenderer(document); for(int i = 0; i < document.getNumberOfPages(); i++) { BufferedImage image = renderer.renderImageWithDPI(i, 300); ImageIO.write(image, "png", outputStream); } byte[] imageBytes = outputStream.toByteArray(); String imageBase64 = Base64.getEncoder().encodeToString(imageBytes); document.close(); return imageBase64; }
java
//多页pdf public static String[] pdfToImages(String pdfBase64) throws IOException { ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(pdfBase64)); PDDocument document = PDDocument.load(inputStream); String imageBase64[] = new String[document.getNumberOfPages()]; for(int i = 0; i < document.getNumberOfPages(); i++) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); PDFRenderer renderer = new PDFRenderer(document); BufferedImage image = renderer.renderImageWithDPI(i, 300); ImageIO.write(image, "png", outputStream); byte[] imageBytes = outputStream.toByteArray(); imageBase64[i] = Base64.getEncoder().encodeToString(imageBytes); } document.close(); return imageBase64; }
java
//多线程的方式 public static String[] pdfToImagesByMultiThreading(String pdfBase64) throws IOException, ExecutionException, InterruptedException { ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(pdfBase64)); PDDocument document = PDDocument.load(inputStream); int pages = document.getNumberOfPages(); int core=Runtime.getRuntime().availableProcessors(); // 创建一个固定大小的线程池 ExecutorService executorService = Executors.newFixedThreadPool(core>pages?pages:core); List<CompletableFuture<String>> futures = new ArrayList<>(); for (int i = 0; i < pages; i++) { final int pageIndex = i; CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); PDFRenderer renderer = new PDFRenderer(document); try { BufferedImage image = renderer.renderImageWithDPI(pageIndex, 300); ImageIO.write(image, "jpg", outputStream); } catch (IOException e) { e.printStackTrace(); throw new BusinessException(ResultCodeEnum.ERR_0x1000.getCode(),"pdf转换出错"+e.getMessage()); } byte[] imageBytes = outputStream.toByteArray(); return Base64.getEncoder().encodeToString(imageBytes); }, executorService); futures.add(future); } // 等待所有任务完成并收集结果 CompletableFuture<String>[] arrayFutures = futures.toArray(new CompletableFuture[0]); CompletableFuture.allOf(arrayFutures).join(); // 等待所有任务完成 // 关闭线程池(在实际应用中考虑在服务停止时关闭) executorService.shutdown(); document.close(); // 等待所有任务完成并收集结果 String[] imageBase64 = new String[pages]; for (int i = 0; i < pages; i++) { imageBase64[i] = futures.get(i).get(); } return imageBase64; }
编辑
2023-12-25
学习记录
00

介绍

在JDK1.7中引入了一种新的Fork/Join线程池,它可以将一个大的任务拆分成多个小的任务并行执行并汇总执行结果。 Fork/Join采用的是分而治之的基本思想,分而治之就是将一个复杂的任务,按照规定的阈值划分成多个简单的小任务,然后将这些小任务的结果再进行汇总返回,得到最终的任务。

ForkJoinPool

ForkJoinPool是用于运行ForkJoinTasks的线程池,实现了Executor接口。 可以通过new ForkJoinPool()直接创建ForkJoinPool对象。

java
public ForkJoinPool() { this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()), defaultForkJoinWorkerThreadFactory, null, false); } public ForkJoinPool(int parallelism, ForkJoinWorkerThreadFactory factory, UncaughtExceptionHandler handler, boolean asyncMode){ this(checkParallelism(parallelism), checkFactory(factory), handler, asyncMode ? FIFO_QUEUE : LIFO_QUEUE, "ForkJoinPool-" + nextPoolId() + "-worker-"); checkPermission(); }

通过查看构造方法源码我们可以发现,在创建ForkJoinPool时,有以下4个参数:

  • parallelism:期望并发数。默认会使用Runtime.getRuntime().availableProcessors()的值
  • factory:创建ForkJoin工作线程的工厂,默认为defaultForkJoinWorkerThreadFactory
  • handler:执行任务时遇到不可恢复的错误时的处理程序,默认为null
  • asyncMode:工作线程获取任务使用FIFO模式还是LIFO模式,默认为LIFO
编辑
2023-12-25
实用工具
00

主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master),后者称为从节点(Slave);数据的复制是单向的,只能由主节点到从节点。 默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

作用

  • 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  • 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  • 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
编辑
2023-12-25
遇到的问题
00

问题

使用redis集群时,从机在redis.windows.conf中修改port为6380,启动后还是使用的是6379,导致窗口闪退,启动失败

原因

未读取到redis.windows.conf中的信息,还是使用的是默认的配置信息

解决

进入redis目录下,打开命令行输入:

redis-server.exe ./redis.windows.conf

即可启动成功