1. 使用合适的数据结构
选择合适的数据结构对提高代码效率至关重要。
示例:使用HashMap优化查找
// 不推荐的做法:使用List进行查找 List 查找元素,是通过索引慢
List<String> list = new ArrayList<>(Arrays.asList("apple", "banana", "cherry"));
if (list.contains("banana")) {
// 处理逻辑
}
// 推荐的做法:使用HashMap提高查找效率 HashSet 的底层是 HashMap ,直接相等与
Set<String> set = new HashSet<>(Arrays.asList("apple", "banana", "cherry"));
if (set.contains("banana")) {
// 处理逻辑
}
set.contains("banana") == map.get("banana")
2. 避免重复代码
重复代码会使得程序难以维护,使用方法或类重构可以有效解决这一问题。
示例:提取重复代码到方法
// 重复代码示例
public void processUser(User user) {
if (user != null && user.isActive()) {
// 处理逻辑
}
}
// 优化后
public void processUser(User user) {
if (isValidUser(user)) {
// 处理逻辑
}
}
private boolean isValidUser(User user) {
return user != null && user.isActive();
}
3. 利用Java 8特性
Java 8引入了许多新特性,如Lambda表达式和Stream API,它们可以使代码更加简洁高效。
示例:使用Stream API处理集合
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> filteredNames = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
4. 理解并发编程
在多线程和并发编程中,正确管理资源和线程是提高性能的关键。
示例:可以使用Java 8 的 CompletableFuture
// 异步把上个版本的数据 批量插入到 历史表
CompletableFuture.runAsync(() -> {
MyBatisBatchExecutorUtil.batchCommit(PzryMapper.class, INSERT_HISTORY, historyList);
MyBatisBatchExecutorUtil.batchCommit(PzryMapper.class, BATCH_HISTORY_INSERT_LD, historyLdData);
});
// 异步调用一些方法(记录操作日志)
CompletableFuture.runAsync(() -> logService.addLog(LogOperationYeMianEnum.getContent(2), logType, builder, isSucess, userId));
5.使用全局异常类
可以使用全局异常处理类来替换写不完的 try catch
示例: @RestControllerAdvice
原本:
@GetMapping("/add2")
public String add2() {
String result = "成功";
try {
int a = 10 / 0;
} catch (Exception e) {
result = "数据异常";
}
return result;
}
使用后:
@GetMapping("/add2")
public String add2() {
String result = "成功";
int a = 10 / 0;
return result;
}
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
if (e instanceof ArithmeticException) {
return "数据异常";
}
if (e != null) {
return "服务器内部异常";
}
return null;
}
}
6. 多使用工具类方法
可以多使用一些工具类方法来判断集合,字符串,对象是否为空
示例: Objects,Strings,StringUtils, CollectionUtils,ObjectUtils
Objects.nonNull(new Ld());
Objects.isNull(new Ld());
Objects.equals(new Ld(),new Ld());
Strings.isNullOrEmpty("");
StringUtils.isEmpty();
CollectionUtils.isEmpty(new HashMap<>());
CollectionUtils.isEmpty(new ArrayList<>());
CollectionUtils.isEmpty(new HashSet<>());
List<Object> list = new ArrayList<>();
list.add("111");
System.out.println(ObjectUtils.isEmpty(list));
7. 字符串拼接
替换传统的 + “” +
示例:String.format
String.format("%s(%s)", name.getName(), name.getNetId())
8. 优化一些码值的转换
替换传统的if else 判断存值
示例:使用 map 或 枚举
//如果是 例行= 0 非例行= 1 OA申请=2
Map<String, String> map= new HashMap<>(16);
map.put("例行", "0");
map.put("非例行", "1");
map.put("OA申请", "2");
yxThingsType.setIsRoutine(map.getOrDefault(yxThingsType.getIsRoutine(), ""));
9. 使用静态常量替换魔法值,字符串
private static final String INSERT_HISTORY = "insertHistory";
private static final Integer MAX_DRAFT_COUNT = 5;
10. 批量插入几百条,几千条数据库时使用工具类
穿透的批量插入,当数据量过多的时候批量插入的时间多长
示例:使用底层 sqlSessionFactory 的批量插入
工具类1:
package com.ly.cloud.util;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import com.google.common.collect.Lists;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @Author
* @Date Created in 2023/12/25 15:57
* @DESCRIPTION: mybatis快速批量插入 数据 工具类
* @Version V1.0
*/
@Component
public class MyBatisBatchExecutorUtil {
private static final Logger logger = LoggerFactory.getLogger(MyBatisBatchExecutorUtil.class);
@Resource
private SqlSessionFactory sqlSessionFactory;
private static MyBatisBatchExecutorUtil utils;
@PostConstruct
public void init() {
utils = this;
utils.sqlSessionFactory = this.sqlSessionFactory;
}
/**
* 批量提交数据
*
* @param mapperClass Mapper 类
* @param dataList 要提交的数据列表
*/
public static <T> void batchCommit(Class<?> mapperClass, String mybatisSqlId, List<T> dataList) {
if (dataList == null || dataList.isEmpty()) {
return;
}
SqlSession session = null;
int commitCountEveryTime = 500;
try {
long startTime = System.currentTimeMillis();
session = utils.sqlSessionFactory.openSession(ExecutorType.BATCH, false);
List<List<T>> groupList = Lists.partition(dataList, commitCountEveryTime);
for (List<T> tempList : groupList) {
session.insert(mapperClass.getName() + "." + mybatisSqlId, tempList);
}
session.commit();
session.clearCache(); // 移动到循环结束后执行
long endTime = System.currentTimeMillis();
logger.info("批量插入数据耗时:" + (endTime - startTime) + "毫秒");
} catch (Exception e) {
logger.error("batchCommit error!", e);
if (session != null) {
session.rollback();
}
} finally {
if (session != null) {
session.close();
}
}
}
}
使用示例:
INSERT_HISTORY === 》 对应的mapper 类的方法名 (mybatis 的id )
currentData === 》 对应的数据集合
MyBatisBatchExecutorUtil.batchCommit(NewYxThingsTypeMapper.class, INSERT_HISTORY, currentData);
工具类2:
package com.example.juc.utils.dataUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import java.util.function.BiFunction;
/**
* @author 12926
* @CreatTime: 2022/7/27 10:53
*/
@Component
public class MybatisBatchUtils {
/**
* 每次处理1000条
*/
private static final int BATCH_SIZE = 1000;
@Resource
private SqlSessionFactory sqlSessionFactory;
private static MybatisBatchUtils utils;
@PostConstruct
public void init() {
utils = this;
utils.sqlSessionFactory = this.sqlSessionFactory;
}
/**
* 批量处理修改或者插入
*
* @param data 需要被处理的数据
* @param mapperClass Mybatis的Mapper类
* @param function 自定义处理逻辑
* @return int 影响的总行数
*/
public static <T,U,R> int batchUpdateOrInsert(List<T> data, Class<U> mapperClass, BiFunction<T, U, R> function) {
int i = 1;
SqlSession batchSqlSession = utils.sqlSessionFactory.openSession();
batchSqlSession.getConfiguration().setDefaultExecutorType(ExecutorType.BATCH);
try {
U mapper = batchSqlSession.getMapper(mapperClass);
int size = data.size();
for (T element : data) {
function.apply(element,mapper);
if ((i % BATCH_SIZE == 0) || i == size) {
System.out.println(batchSqlSession.flushStatements());
}
i++;
}
// 非事务环境下强制commit,事务情况下该commit相当于无效
batchSqlSession.commit(!TransactionSynchronizationManager.isSynchronizationActive());
} catch (Exception e) {
batchSqlSession.rollback();
throw new RuntimeException(e);
} finally {
batchSqlSession.close();
}
return i - 1;
}
}
//使用方法
MybatisBatchUtils.batchUpdateOrInsert(list, UserMapper.class,
(user, userMapper) -> userMapper.insert(user));
@Mapper
public interface UserMapper {
List<User> getUsersList();
int insert(@Param("user") User user);
}
11. 获取接口的请求头信息
使用工具类获取,每次在只需要一行代码
示例:
原来每个请求:@RequestHeader("loginUserId") String loginUserId
@ApiOperation("查询流程[类型]列表(本地数据源)")
@PostMapping("/listInstancesTypeByLocal")
public WebResult<List<InstancesStatusVo>> listInstancesTypeByLocal(@RequestBody FlowGetInstanceByTypeDto flowGetInstanceByTypeDto,
@RequestHeader("loginUserId") String loginUserId) {
return WebResult.ok(yxMyThingsService.listInstancesTypeByLocal(flowGetInstanceByTypeDto, loginUserId));
工具类:
public class LoginUserUtil {
public static String getLoginUserId() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = null;
if (requestAttributes != null) {
request = ((ServletRequestAttributes) requestAttributes).getRequest();
}
// 子线程共享
RequestContextHolder.setRequestAttributes(requestAttributes, true);
if (request != null) {
return request.getHeader("loginUserId");
}
return null;
}
}
//调用:
LoginUserUtil.getLoginUserId
12. 遍历Map 的时候,使用 EntrySet 方法
使用 EntrySet 方法,可以直接返回 set 对象,直接拿来用即可;而使用 KeySet 方法,获得的是key 的集合,需要再进行一次 get 操作,多了一个操作步骤,所以更推荐使用 EntrySet 方式遍历 Map。
Set<Map.Entry<String, String>> entryseSet = nmap.entrySet();
for (Map.Entry<String, String> entry : entryseSet) {
System.out.println(entry.getKey()+","+entry.getValue());
}
13.为集合对象中的每个元素赋值(序号)
IntStream.rangeClosed(1, departmentList.size())
.forEach(i ->
departmentList.get(i - 1).setMyNumber(String.valueOf(i)));
14. 减少集合循环次数
在开发比较俩个集合的案例很多
示例:
反例
for(User user: userList) {
for(Role role: roleList) {
if(user.getRoleId().equals(role.getId())) {
user.setRoleName(role.getName());
}
}
}
正例
Map<Long, List<Role>> roleMap = roleList.stream().collect(Collectors.groupingBy(Role::getId));
for (User user : userList) {
List<Role> roles = roleMap.get(user.getRoleId());
if(CollectionUtils.isNotEmpty(roles)) {
user.setRoleName(roles.get(0).getName());
}
}
15. 使用BigDecimal 进行小数点的加减
private String covertString(String pzrySmallBbh) {
if (!StringUtils.isEmpty(pzrySmallBbh)) {
// 将字符串转换为 BigDecimal 类型
BigDecimal number = new BigDecimal(pzrySmallBbh);
// 每次加 0.01
number = number.add(new BigDecimal("0.01"));
// 将结果转换回字符串
return number.toString();
}
return null;
}
16. 多个线程往同一个集合中写数据时
使用:CopyOnWriteArrayList
List<Object> list = new CopyOnWriteArrayList<>();
Set<Object> list = new CopyOnWriteArraySet<>();
Map<String,String> map2 = new ConcurrentHashMap<>()
17. 集合复制时修改复制后的集合
第一种简单的:
反例: 这样会把 yxAdminList 这个集合的 setYxThingNo 也值为空;
List<YxAdmin> yxAdminListTwo = new ArrayList<>(yxAdminList);
yxAdminListTwo.forEach(vo -> vo.setYxThingNo(""));
正例:
List<ListCopy> addALL = new ArrayList<>();
addALL.addAll(list);
addALL.forEach(vo -> vo
.setName(""));
或者使用for循环
List<YxAdmin> yxAdminListTwo = new ArrayList<>();
for (YxAdmin vo : yxAdminList) {
YxAdmin copiedAdmin = new YxAdmin();
copiedAdmin.setName(vo.getName());
copiedAdmin.setDepartment(vo.getDepartment());
copiedAdmin.setYxThingNo("");
yxAdminListTwo.add(copiedAdmin);
}
第二种工具类库
package com.example.juc.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
/**
* @Author
* @Date Created in 2024/4/2 16:48
* @DESCRIPTION: 对象赋值工具类
* @Version V1.0
*/
public class BeanCopyUtil {
private static final Logger logger = LoggerFactory.getLogger(BeanCopyUtil.class);
/**
* 相同对象合并,将原对象的非空属性的值赋值给目标对象
*
* @param origin 源对象
* @param destination 目标对象
* @param <T> 对象的类型
*/
public static <T> void merge(T origin, T destination) {
if (origin == null || destination == null) {
return;
}
if (!origin.getClass().equals(destination.getClass())) {
return;
}
Field[] fields = origin.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
try {
Object value = field.get(origin);
if (null != value) {
field.set(destination, value);
}
} catch (IllegalAccessException e) {
logger.error("访问对象异常", e);
}
field.setAccessible(false);
}
}
}
ListCopy listCopy = new ListCopy();
listCopy.setName("qqqqqqq");
listCopy.setAge(18);
ListCopy listCopy2 = new ListCopy();
BeanCopyUtil.merge(listCopy, listCopy2);
listCopy2.setName("2222222");
System.out.println(listCopy);
System.out.println(listCopy2);

18. 将list<String> 转成字符串
List<String> objects = new ArrayList<>();
String name = String.join(",", objects);
19. 学会使用三目运算符
反例
String title;
if (isMember(phone)) {
title = "会员";
} else {
title = "游客";
}
正例
String title = isMember(phone) ? "会员" : "游客";
20. 使用增强for 替换普通for 循环
for (JsonElement element : dataArray) {
JsonObject itemObject = element.getAsJsonObject();
String title = itemObject.get("Title").getAsString();
System.out.println("Title:----" + title);
}
21. 利用 Map 的 computeIfAbsent 方法
利用 Map 的 computeIfAbsent 方法,可以保证获取到的对象非空,从而避免了不必要的空判断和重新设置值。
普通写法:
Map<Long, List<UserDO>> roleUserMap = new HashMap<>();
for (UserDO userDO : userDOList) {
Long roleId = userDO.getRoleId();
List<UserDO> userList = roleUserMap.get(roleId);
if (Objects.isNull(userList)) {
userList = new ArrayList<>();
roleUserMap.put(roleId, userList);
}
userList.add(userDO);
}
使用利用 Map 的 computeIfAbsent 方法
Map<Long, List<UserDO>> roleUserMap = new HashMap<>();
for (UserDO userDO : userDOList) {
roleUserMap.computeIfAbsent(userDO.getRoleId(), key -> new ArrayList<>())
.add(userDO);
}
22.stream 并行流适时使用
// 使用stream 并行流处理查询结果,检查是否有重复数据 提高查询效率
if (admins.parallelStream().anyMatch(vo -
> !CollectionUtils.isEmpty(
pzPeopleMapper.isExitYxAdmin(vo.getName(), vo.getDepartment())))) {
// 只要匹配到有一条重复数据,直接返回 false
return false;
}
23:使用map 优化 if else 语法
List<String> changeRecords = new ArrayList<>();
Map<String, String[]> changes = new HashMap<>();
changes.put("讲座名称", new String[]{changeFontName, changeEndName});
changes.put("讲座副标题", new String[]{lectureMessage.getLectureSubtitle(), vo.getLectureSubtitle()});
changes.put("讲座类型", new String[]{lectureMessage.getLectureType(), vo.getLectureType()});
changes.put("讲座详情", new String[]{lectureMessage.getLectureDetail(), vo.getLectureDetail()});
changes.put("举办地点", new String[]{lectureMessage.getHoldPlace(), vo.getHoldPlace()});
changes.put("举办开始时间", new String[]{String.valueOf(lectureMessage.getHoldStartTime()), String.valueOf(vo.getHoldStartTime())});
changes.put("举办结束时间", new String[]{String.valueOf(lectureMessage.getHoldEndTime()), String.valueOf(vo.getHoldEndTime())});
changes.put("主讲人", new String[]{isForeign(lectureMessage.getForeignRelations()), isForeign(vo.getForeignRelations())});
for (Map.Entry<String, String[]> entry : changes.entrySet()) {
String key = entry.getKey();
String[] values = entry.getValue();
if (!values[0].equals(values[1])) {
changeRecords.add(formatChangeRecord(key, values[0], values[1]));
}
}
24:优化Map 的put方法
ImmutableMap<String, String> paramMap = ImmutableMap.of(
"year", place.substring(0, 4),
"month", place.substring(5, 10),
"day", place.substring(11, 24),
"weekday", chineseDayOfWeek
);
25:如何把集合数据根据不同的条件分割
注: 根据不同的条件true,false 来分割 mainSpeaker.getForeignSchool() 是否 为0 的数据
public List<MainSpeaker> partitioningBy(List<MainSpeaker> list, Boolean isTrue) {
Map<Boolean, List<MainSpeaker>> booleanListMap = list.stream()
.collect(Collectors.partitioningBy(mainSpeaker -> "0".equals(mainSpeaker.getForeignSchool())));
return booleanListMap.get(isTrue);
}
26:枚举算法
列举出来所有的 数据结果
package com.example.juc.factory;
import com.example.juc.entity.ListCopy;
import com.example.juc.utils.BeanCopyUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
public class test{
// 枚举算法
public List<String> permute(String str) {
List<String> result = new ArrayList<>();
backtrack(result, "", str);
return result;
}
private void backtrack(List<String> result, String current, String remaining) {
if (remaining.isEmpty()) {
result.add(current);
return;
}
for (int i = 0; i < remaining.length(); i++) {
String next = current + remaining.charAt(i);
String left = remaining.substring(0, i) + remaining.substring(i + 1);
backtrack(result, next, left);
}
}
public static void main(String[] args) {
test permutations = new test();
List<String> results = permutations.permute("16");
System.out.println(results);
}
}
27:二分查找~迭代
public class BinarySearch {
public static int binarySearchIterative(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2; // 防止溢出
if (arr[mid] == target) {
return mid; // 找到目标,返回索引
} else if (arr[mid] < target) {
left = mid + 1; // 在右半部分继续搜索
} else {
right = mid - 1; // 在左半部分继续搜索
}
}
return -1; // 没有找到目标
}
public static void main(String[] args) {
int[] sortedArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int target = 5;
int result = binarySearchIterative(sortedArray, target);
if (result != -1) {
System.out.println("Element found at index: " + result);
} else {
System.out.println("Element not found.");
}
}
}
28:二分查找 - 递归实现
public class BinarySearch {
public static int binarySearchRecursive(int[] arr, int target, int left, int right) {
if (left > right) {
return -1; // 基本情况:没有找到目标
}
int mid = left + (right - left) / 2; // 防止溢出
if (arr[mid] == target) {
return mid; // 找到目标,返回索引
} else if (arr[mid] < target) {
return binarySearchRecursive(arr, target, mid + 1, right); // 在右半部分继续搜索
} else {
return binarySearchRecursive(arr, target, left, mid - 1); // 在左半部分继续搜索
}
}
public static void main(String[] args) {
int[] sortedArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int target = 5;
int result = binarySearchRecursive(sortedArray, target, 0, sortedArray.length - 1);
if (result != -1) {
System.out.println("Element found at index: " + result);
} else {
System.out.println("Element not found.");
}
}
}
28: multipartFile --> fileInputStream
public static FileInputStream convertMultipartFileToInputStream(MultipartFile multipartFile) throws IOException {
// 创建临时文件
File tempFile = File.createTempFile("temp", null);
// 将MultipartFile的数据写入临时文件
try (FileOutputStream fos = new FileOutputStream(tempFile)) {
fos.write(multipartFile.getBytes());
}
// 将临时文件转换为FileInputStream
FileInputStream fileInputStream = new FileInputStream(tempFile);
// 删除临时文件
tempFile.delete();
return fileInputStream;
}
29:在调用第三方接口时如何设置超时时间
private CloseableHttpClient buildHttpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
// 创建 SSL 上下文,跳过证书认证
SSLContext sslContext = SSLContextBuilder.create().loadTrustMaterial((chain, authType) -> true).build();
// 创建 SSL 连接套接字工厂
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
// 创建RequestConfig并设置读取超时时间为10秒
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(30000) // 读取超时
.setConnectTimeout(30000) // 连接超时
.setConnectionRequestTimeout(30000) // 从连接池获取连接的超时
.build();
// 创建CloseableHttpClient并应用RequestConfig
return HttpClients.custom().setSSLSocketFactory(sslSocketFactory).setDefaultRequestConfig(requestConfig).build();
}
30:如何比较俩个新旧实体类的区别
List<String> changeRecords = new ArrayList<>();
Map<String, String[]> changes = new HashMap<>();
changes.put("讲座名称", new String[]{changeFontName, changeEndName});
changes.put("讲座副标题", new String[]{lectureMessage.getLectureSubtitle(), vo.getLectureSubtitle()});
changes.put("讲座类型", new String[]{lectureMessage.getLectureType(), vo.getLectureType()});
changes.put("讲座详情", new String[]{lectureMessage.getLectureDetail(), vo.getLectureDetail()});
changes.put("举办地点", new String[]{lectureMessage.getHoldPlace(), vo.getHoldPlace()});
changes.put("举办开始时间", new String[]{String.valueOf(lectureMessage.getHoldStartTime()), String.valueOf(vo.getHoldStartTime())});
changes.put("举办结束时间", new String[]{String.valueOf(lectureMessage.getHoldEndTime()), String.valueOf(vo.getHoldEndTime())});
changes.put("主讲人", new String[]{isForeign(lectureMessage.getForeignRelations()), isForeign(vo.getForeignRelations())});
for (Map.Entry<String, String[]> entry : changes.entrySet()) {
String key = entry.getKey();
String[] values = entry.getValue();
if (!values[0].equals(values[1])) {
changeRecords.add(formatChangeRecord(key, values[0], values[1]));
}
}
private String formatChangeRecord(String oldValue, String newValue, String label) {
if (!oldValue.equalsIgnoreCase(newValue)) {
return String.format("【%s由:%s 改为 %s】", label, oldValue, newValue);
}
return "";
}
31:FileUtils常用的一些方法
package com.ly.cloud.util;
import cn.hutool.core.util.IdUtil;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.util.ArrayList;
/**
* @Author
* @Date Created in 2024/4/11 上午11:13
* @DESCRIPTION: 将 multipartFile --> file
* @Version V1.0
*/
public class FileUtil {
private final static Log logger = LogFactory.getLog(FileUtil.class);
/**
* 将输入流输出到页面
*/
public static void writeFile(HttpServletResponse resp, InputStream inputStream) {
OutputStream out = null;
try {
out = resp.getOutputStream();
int len = 0;
byte[] b = new byte[1024];
while ((len = inputStream.read(b)) != -1) {
out.write(b, 0, len);
}
out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* BufferedImage ---> InputStream
*/
public static InputStream convertBufferedImageToInputStream(BufferedImage poster) throws IOException {
// 创建一个字节输出流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(poster, "png", byteArrayOutputStream);
// 从字节输出流中获取字节数组
byte[] imageData = byteArrayOutputStream.toByteArray();
// 创建一个 ByteArrayInputStream 来提供 InputStream
return new ByteArrayInputStream(imageData);
}
public static BufferedImage convert(String temp , BufferedImage backgroundImage) throws IOException {
// 创建输出文件
File outputFile = new File(temp); // 设置文件路径和文件名
// 将 BufferedImage 写入文件
ImageIO.write(backgroundImage, "jpg", outputFile);
File file = new File(temp);
FileInputStream payload = new FileInputStream(file);
// step2: 压缩图片
byte[] imageBytes = compressImage(payload);
outputFile.delete();
return byteArrayToBufferedImage(imageBytes);
}
// 将字节数组转换回 BufferedImage
public static BufferedImage byteArrayToBufferedImage(byte[] imageData) throws IOException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(imageData);
return ImageIO.read(byteArrayInputStream);
}
public static byte[] compressImage(InputStream payload) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] bytes;
try {
// scale:设置缩略图的缩放系数,大于0.0
// outputQuality:设置将缩略图写入外部目标(如文件或输出流)时用于压缩缩略图的压缩算法的输出质量,该值是一个介于0.0f和1.0f之间的浮点数,其中0.0f表示最低质量,1.0f表示压缩编解码器应该使用的最大质量设置。
Thumbnails.of(payload).scale(0.2f).outputQuality(0.2f).toOutputStream(outputStream);
bytes = outputStream.toByteArray();
} catch (IOException e) {
bytes = new byte[]{};
} finally {
try {
outputStream.close();
} catch (IOException e) {
}
}
return bytes;
}
/**
* 将 multipartFile --> fileInputStream
*/
public static FileInputStream convertMultipartFileToInputStream(MultipartFile multipartFile) throws IOException {
// 创建临时文件
File tempFile = File.createTempFile("temp", null);
// 将MultipartFile的数据写入临时文件
try (FileOutputStream fos = new FileOutputStream(tempFile)) {
fos.write(multipartFile.getBytes());
}
// 将临时文件转换为FileInputStream
FileInputStream fileInputStream = new FileInputStream(tempFile);
// 删除临时文件
tempFile.delete();
return fileInputStream;
}
/**
* 将上传的 logo图片保存在本地;
*/
public static File convertInputStreamToFile(InputStream inputStream, String fileName) throws IOException {
String imagesPath = System.getProperty("user.dir") + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator + "static" + File.separator + "imagesLogo" + File.separator;
logger.info("我的路径是:{}" + imagesPath);
File directory = new File(imagesPath);
// 如果目标文件夹不存在,则创建它
if (!directory.exists()) {
directory.mkdirs();
}
File file = new File(directory, fileName);
try (OutputStream outputStream = Files.newOutputStream(file.toPath())) {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
}
}
// deleteImage(imagesPath,fileName);
directory.delete();
return file;
}
/**
* 删除图片
*/
public static void deleteImage(String directoryPath, String imageName) {
File directory = new File(directoryPath);
if (!directory.exists() || !directory.isDirectory()) {
return;
}
File imageFile = new File(directory, imageName);
if (imageFile.exists() && imageFile.isFile()) {
if (imageFile.delete()) {
logger.info("删除图片成功!");
} else {
logger.error("删除图片失败!");
}
} else {
System.out.println("Image file " + imageName + " does not exist.");
}
}
/**
* 修改图片的宽高 等比例扩大 海报背景图和 logo
*/
public static byte[] resizeImage(byte[] imageData, int width, int height) throws IOException {
ByteArrayInputStream bis = new ByteArrayInputStream(imageData);
BufferedImage image = ImageIO.read(bis);
Image resizedImage = image.getScaledInstance(width, height, Image.SCALE_SMOOTH);
BufferedImage bufferedResizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
bufferedResizedImage.getGraphics().drawImage(resizedImage, 0, 0, null);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(bufferedResizedImage, "jpg", bos);
byte[] resizedImageData = bos.toByteArray();
bis.close();
bos.close();
return resizedImageData;
}
public static void main(String[] args) throws IOException {
System.out.println(IdUtil.simpleUUID());
String sql = "SELECT COUNT(1) FROM ( SELECT A.BH AS id, D.ZJRXM AS lectureTeacher, A.JZLX AS lectureType, A.JZZT AS lectureStatus, A.JZMC AS lectureName, A.JTDD AS place, A.JBDD AS holdPlace, A.JBKSSJ AS holdStartTime, A.JBJSSJ AS holdEndTime, TO_CHAR(A.JBKSSJ, 'YYYY-MM-DD') AS lectureDate, CASE WHEN (A.JBJSSJ - A.JBKSSJ) < INTERVAL '1' DAY THEN TO_CHAR(A.JBKSSJ, 'YYYY-MM-DD HH24:MI') || ' - ' || TO_CHAR(A.JBJSSJ, 'HH24:MI') ELSE TO_CHAR(A.JBKSSJ, 'YYYY-MM-DD HH24:MI') || ' - ' || TO_CHAR(A.JBJSSJ, 'YYYY-MM-DD HH24:MI') END AS holdTime, A.ZBDW AS DWMC, B.BMMC AS organizer, A.JZBH AS lectureNumber, TO_CHAR(A.CZSJ,'YYYY-MM-DD HH:SS:MM') AS operationTime, C.YHMC AS publishPeople, A.WDSJBS AS wedaDataId, A.LSH AS serialNumber, A.ZBDZ AS liveBroadcastLink , A.HYLJ AS meetingLink FROM LY_SJS_WDJZ_JZXJ A LEFT JOIN LY_XTGL_BM B ON A.ZBDW = B.BMBH LEFT JOIN LY_XTGL_YH C ON A.FBRY = C.YHBH LEFT JOIN ( SELECT A.BH, LISTAGG(C.ZJRXM, ',') WITHIN GROUP (ORDER BY B.SFDYZJR, C.ZJRXM) AS ZJRXM FROM LY_SJS_WDJZ_JZXJ A LEFT JOIN LY_SJS_WDJZ_JZXJ_ZJRXX B ON A.BH = B.JZBH LEFT JOIN LY_SJS_WDJZ_ZJRGL C ON B.ZJRBH = C.BH WHERE A.SFSC = '1' AND B.SFSC = '1' AND C.SFSC = '1' AND C.SFYX = '1' GROUP BY A.BH ) D ON A.BH = D.BH WHERE A.SFSC = '1' AND A.LSH IN ( ? , ? , ? ) ORDER BY A.CZSJ DESC ) TOTAL";
String params = "1846773565878845441(String), 1846778466331672577(String), 1847168643654893569(String)";
System.out.println(concatSQL(sql,params));
}
public static String concatSQL(String sql,String params){
String[] p = params.split(", ");
System.out.println();
for (String s : p) {
System.out.println(s);
String param = s.substring(0,s.lastIndexOf('('));
sql = sql.replaceFirst("[?]","'"+param+"'");
}
return sql;
}
}
32:AES加密工具类
package com.ly.cloud.util;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import cn.hutool.core.io.resource.ClassPathResource;
import com.alibaba.druid.util.Base64;
import org.apache.commons.codec.binary.Hex;
public class AESUtils {
private static final String AES = "AES";
private static final String UTF8 = "UTF-8";
private static final String CIPHERALGORITHM = "AES/ECB/PKCS5Padding";
private static final String Key = "9!#95hsup*&$1zq7";
/**
* AES加密+Base64转码
*
* @param data 明文(16进制)
* @param key 密钥
* @return
*/
public static String encrypt(String data, String key) {
byte[] keyb = null;
keyb = Base64.base64ToByteArray(key);
SecretKeySpec sKeySpec = new SecretKeySpec(keyb, "AES");
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
try {
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] bjiamihou = null;
String miwen = "";
try {
bjiamihou = cipher.doFinal(data.getBytes("utf-8"));
// byte加密后
miwen = Base64.byteArrayToBase64(bjiamihou);// 密文用base64加密
} catch (IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return miwen;
}
/**
* Base64解码 + AES解码
*
* @param data 密文 (16进制)
* @param key 密钥
* @return
*/
public static String decrypt(String data, String key) {
byte[] keyb = null;
keyb = Base64.base64ToByteArray(key);
byte[] miwen = Base64.base64ToByteArray(data);
SecretKeySpec sKeySpec = new SecretKeySpec(keyb, "AES");
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
try {
cipher.init(Cipher.DECRYPT_MODE, sKeySpec);
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] bjiemihou = null;
String mingwen = "";
try {
bjiemihou = cipher.doFinal(miwen);
// byte加密后
mingwen = new String(bjiemihou, "utf-8");
} catch (IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return mingwen;
}
public static void main(String[] args) throws IOException {
// String name = "{\"userId\":\"179135\",\"keyword\":\"讲座副标题\",\"pageNum\":\"1\",\"pageSize\":\"10\"}";
String name = "{\"pageNum\":\"1\",\"pageSize\":\"10\"}";
String encrypt = encrypt(name, "");
// String name = "{\"userId\":\"179135\",\"pageNum\":\"1\",\"pageSize\":\"20\"}";
// String name1 = "{\"userId\":\"179135\",\"organizerId\":\"02020\"}";
// String name1 = "{\"pageNum\":\"1\",\"pageSize\":\"20\"}";
// String encrypt = encrypt(name, "");
System.out.println(encrypt);
// System.out.println("---------");
// System.out.println(decrypt("", ""));
}
/**
* 生成base 64 作为AES加密后的密钥
*/
public static String encrypts(String content) {
try {
byte[] encodeFormat = Key.getBytes();
SecretKeySpec key = new SecretKeySpec(encodeFormat, AES);
// Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance(CIPHERALGORITHM);
// 加密内容进行编码
byte[] byteContent = content.getBytes(UTF8);
// 用密匙初始化Cipher对象
cipher.init(Cipher.ENCRYPT_MODE, key);
// 正式执行加密操作
byte[] result = cipher.doFinal(byteContent);
return Hex.encodeHexString(result);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String dataToAES(String password, String data) {
String hexStr = encrypt(data, password);
return hexStr;
}
public static String AESToData(String password, String data) {
return decrypt(data, password);
}
}
33:ThreadLocal 的简单使用方法
private static ThreadLocal<Integer> LECTURE_SPEAKER_INDEX = ThreadLocal.withInitial(() -> 1); // 默认值为 1
int i = LECTURE_SPEAKER_INDEX.get();
LECTURE_SPEAKER_INDEX.set(i+1);
if (LECTURE_SPEAKER_INDEX.get().equals(2)) {
LECTURE_SPEAKER_INDEX.set(1);
return combiner.getCombinedImageStream();
}
34:Iterator 遍历移除某个元素
List<PosterSettings> posterSettingsList = lectureMessageMapper.getPosterSettings(posterId);
// 获取绑定的海报设置的海报信息
Iterator<PosterSettings> iterator = posterSettingsList.iterator();
while (iterator.hasNext()) {
PosterSettings vo = iterator.next();
String lectureNumber = vo.getLectureNumber();
LectureMessageVO lecture = lectureMessageMapper.getISHasLectureOver(lectureNumber);
if ("5".equals(lecture.getLectureStatus())) {
// 移除
iterator.remove();
}
}
35:如何获取今天周几的方法
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd");
LocalDate date = LocalDate.parse(place.substring(0, 10), formatter);
// 获取星期几
DayOfWeek dayOfWeek = date.getDayOfWeek();
// 将 DayOfWeek 转换为中文
String[] weekDays = {"", "周一", "周二", "周三", "周四", "周五", "周六", "周日"};
String chineseDayOfWeek = weekDays[dayOfWeek.getValue()];
36:批量下载zip压缩包附件
@Override
public void downloadAttachmentsZip(BankCardCriteria criteria, HttpServletResponse response) throws IOException, InterruptedException {
criteria.unPaged();
criteria.setBankCardType(true);
List<BankCardDTO> pageList = listPaged(criteria).getPage();
List<CustomerBankCardExcelDTO> excelList = myBankCardMapper.dtoToCustomerExcel(pageList);
List<CustomerBankCardExcelDTO> ordered = excelList.stream()
.filter(excel -> excel != null && excel.getCredentialAttachmentId() != null)
.collect(Collectors.toList());
if (CollUtil.isEmpty(ordered)) {
throw new CustomException("");
}
// ---------- 队列初始化 ----------
BlockingQueue<ZipItem> queue = new LinkedBlockingQueue<>(20); // 可调节缓冲区大小
int cores = Runtime.getRuntime().availableProcessors();
// 生产者线程池(I/O密集型:从 数据库 读取附件)
ThreadPoolExecutor producerPool = new ThreadPoolExecutor(
cores * 2,
cores * 4,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(200),
new ThreadPoolExecutor.CallerRunsPolicy()
);
CountDownLatch latch = new CountDownLatch(ordered.size());
AtomicBoolean hasError = new AtomicBoolean(false);
// ---------- 启动生产者(并发下载附件) ----------
for (int i = 0; i < ordered.size(); i++) {
final int index = i + 1;
producerPool.submit(() -> {
try {
if (hasError.get()) return; // 有错误则不再继续生产
CustomerBankCardExcelDTO dto = ordered.get(index - 1);
Long id = dto.getCredentialAttachmentId();
AttachmentDTO attachment = attachmentExternalClient.get(id);
if (attachment == null) {
log.warn("附件[id:{}]不存在", id);
return;
}
byte[] encoded = attachment.getContent();
if (encoded == null || encoded.length == 0) return;
String ext = getFileExtensionOrDefault(attachment.getName());
String rawName = String.join("_",
Optional.ofNullable(dto.getPartyCode()).orElse(""),
Optional.ofNullable(dto.getName()).orElse(""),
Optional.ofNullable(dto.getBankAccount()).orElse("")
) + ext;
String fileNameWithIndex = String.format("%02d_", index) + sanitizeFileName(rawName);
// 放入队列
queue.put(new ZipItem(fileNameWithIndex, encoded));
} catch (Exception e) {
log.error("附件下载失败", e);
hasError.set(true);
} finally {
latch.countDown();
}
});
}
// ---------- 设置响应头 ----------
String zipName = "客户卡片凭证附件.zip";
FileUtils.setDownloadFileResponse(response, zipName);
// ---------- 启动消费者(边写边出队) ----------
try (ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()))) {
Set<String> usedNames = new HashSet<>();
while (true) {
// 取出队列中的数据(阻塞等待)
ZipItem item = queue.poll(1, TimeUnit.SECONDS);
// 若所有生产者结束且队列为空,则跳出
if (item == null && latch.getCount() == 0) break;
if (item == null) continue;
String finalName = makeUniqueFileName(item.fileName, usedNames);
zos.putNextEntry(new ZipEntry(finalName));
try (InputStream in = new ByteArrayInputStream(item.bytes)) {
byte[] buffer = new byte[8192];
int len;
while ((len = in.read(buffer)) != -1) {
zos.write(buffer, 0, len);
}
}
zos.closeEntry();
response.flushBuffer();
}
zos.finish();
log.info("ZIP 文件写入完成,共写入 {} 个文件", usedNames.size());
} catch (Exception e) {
hasError.set(true);
log.error("ZIP 压缩或写入出错", e);
throw new CustomException("附件打包下载失败");
} finally {
// 等待所有任务结束
latch.await();
producerPool.shutdownNow();
}
}
@Override
public void downloadAttachmentsZip(BankCardCriteria criteria, HttpServletResponse response) throws IOException {
criteria.unPaged();
criteria.setBankCardType(true);
List<BankCardDTO> pageList = listPaged(criteria).getPage();
// 并发拉取附件,按读取顺序写入 ZIP
List<CustomerBankCardExcelDTO> excelList = myBankCardMapper.dtoToCustomerExcel(pageList);
List<CustomerBankCardExcelDTO> ordered = excelList.stream()
.filter(excel -> excel != null && excel.getCredentialAttachmentId() != null)
.collect(Collectors.toList());
if (CollUtil.isEmpty(ordered)) {
throw new CustomException("暂无可下载的银行凭证附件");
}
log.info("开始下载银行凭证附件,数量:{}", ordered.size());
// 使用 IntStream 来遍历索引和内容,并加上顺序编号
List<CompletableFuture<ZipItem>> futures = new ArrayList<>(ordered.size());
for (int i = 0; i < ordered.size(); i++) {
final int index = i + 1;
CompletableFuture<ZipItem> future = CompletableFuture.supplyAsync(() -> {
CustomerBankCardExcelDTO dto = ordered.get(index - 1); // 获取对应的 DTO
Long id = dto.getCredentialAttachmentId();
AttachmentDTO attachment = attachmentExternalClient.get(id);
if (attachment == null) {
log.warn("附件[id:{}]不存在", id);
return null;
}
byte[] encoded = attachment.getContent();
String ext = getFileExtensionOrDefault(attachment.getName());
String rawName = String.join("_",
Optional.ofNullable(dto.getPartyCode()).orElse(""),
Optional.ofNullable(dto.getName()).orElse(""),
Optional.ofNullable(dto.getBankAccount()).orElse("")
) + ext;
// 在文件名前加上顺序编号,例如 "01_", "02_" 等
String fileNameWithIndex = String.format("%02d_", index) + sanitizeFileName(rawName);
return new ZipItem(fileNameWithIndex, encoded);
}, commonPoolExecutor).exceptionally(throwable -> {
logger.error("附件下载失败:", throwable);
throw new CustomException("附件下载失败");
});
futures.add(future);
}
// 设置文件名
String zipName = "客户卡片凭证附件.zip";
FileUtils.setDownloadFileResponse(response, zipName);
try (ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()))) {
Set<String> usedNames = new HashSet<>();
for (CompletableFuture<ZipItem> future : futures) {
try {
ZipItem item = future.join();
if (item == null || item.bytes == null || item.bytes.length == 0) continue;
String finalName = makeUniqueFileName(item.fileName, usedNames);
zos.putNextEntry(new ZipEntry(finalName));
try (InputStream in = new ByteArrayInputStream(item.bytes)) {
byte[] buffer = new byte[8192];
int len;
while ((len = in.read(buffer)) != -1) {
zos.write(buffer, 0, len);
}
}
zos.closeEntry();
} catch (Exception e) {
throw new CustomException("附件下载失败");
}
}
zos.finish();
response.flushBuffer();
}
}
后续。。。。持续更新
本文介绍了如何通过选择合适的数据结构(如HashMap代替List)、消除重复代码、利用Java8新特性(如Lambda和StreamAPI)、处理并发编程和使用全局异常类等方法,来提高代码效率。同时,还涉及工具类的使用、字符串操作优化以及并发编程的最佳实践。



1318

被折叠的 条评论
为什么被折叠?



