feat(utils): 添加 AES 加密解密工具类
All checks were successful
Gitea Actions Build / Build (push) Successful in 51s

- 新增 AesUtils 类,提供 AES/GCM 模式的加密解密方法- 更新 .prettierrc配置,将 useTabs 设置为 true
- 重构 Base64Utils 和 Collection 类的代码结构- 简化 ComponentBean 注解定义
This commit is contained in:
Armamem0t 2025-08-17 19:12:08 +08:00
parent ceef51506d
commit 34dccd1895
Signed by: minglipro
GPG Key ID: 5F355A77B22AA93B
48 changed files with 4214 additions and 3952 deletions

View File

@ -4,5 +4,5 @@
"prettier-plugin-java"
],
"tabWidth": 4,
"useTabs": false
"useTabs": true
}

View File

@ -1,4 +1,4 @@
JDKVERSIONS=1.8
GROUPSID=com.mingliqiye.utils
ARTIFACTID=mingli-utils
VERSIONS=1.0.5
VERSIONS=1.0.7

View File

@ -1,14 +1,14 @@
{
"name": "maven-repository",
"version": "1.0.0",
"scripts": {
"build": "gradle build-jar",
"buildw": "gradlew build-jar",
"format": "prettier --write \"**/*.{js,ts,jsx,tsx,cjs,cts,mjs,mts,vue,astro,json,java}\""
},
"packageManager": "pnpm@10.4.1",
"devDependencies": {
"prettier-plugin-java": "^2.7.1",
"prettier": "^3.6.2"
}
"name": "maven-repository",
"version": "1.0.0",
"scripts": {
"build": "gradle build-jar",
"buildw": "gradlew build-jar",
"format": "prettier --write \"**/*.{js,ts,jsx,tsx,cjs,cts,mjs,mts,vue,astro,json,java}\""
},
"packageManager": "pnpm@10.4.1",
"devDependencies": {
"prettier-plugin-java": "^2.7.1",
"prettier": "^3.6.2"
}
}

View File

@ -4,36 +4,49 @@ import com.mingliqiye.utils.collection.Lists;
import com.mingliqiye.utils.string.StringUtil;
import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.Formatter;
import java.util.List;
public class Main {
public static void main(String[] args) {
// 字符串工具使用示例
System.out.println("=== 字符串工具使用示例 ===");
String formatted = StringUtil.format("你好 {},今天是{}年{}月{}日", "张三", 2025, 7, 25);
System.out.println(formatted);
List<String> fruits = Lists.newArrayList("苹果", "香蕉", "橙子");
String joined = StringUtil.join(", ", fruits);
System.out.println("水果列表: " + joined);
// 时间工具使用示例
System.out.println("\n=== 时间工具使用示例 ===");
DateTime now = DateTime.now();
System.out.println("当前时间: " + now);
System.out.println("标准格式: " + now.format(Formatter.STANDARD_DATETIME));
DateTime specificDate = DateTime.of(2025, 1, 1, 12, 0, 0);
System.out.println("指定时间: " + specificDate.format(Formatter.STANDARD_DATETIME));
// 集合工具使用示例
System.out.println("\n=== 集合工具使用示例 ===");
List<Integer> numbers = Lists.newArrayList(10, 20, 30);
System.out.println("数字列表: " + numbers);
List<String> linkedList = Lists.newLinkedList("第一个", "第二个", "第三个");
System.out.println("链表内容: " + linkedList);
}
}
public static void main(String[] args) {
// 字符串工具使用示例
System.out.println("=== 字符串工具使用示例 ===");
String formatted = StringUtil.format(
"你好 {},今天是{}年{}月{}日",
"张三",
2025,
7,
25
);
System.out.println(formatted);
List<String> fruits = Lists.newArrayList("苹果", "香蕉", "橙子");
String joined = StringUtil.join(", ", fruits);
System.out.println("水果列表: " + joined);
// 时间工具使用示例
System.out.println("\n=== 时间工具使用示例 ===");
DateTime now = DateTime.now();
System.out.println("当前时间: " + now);
System.out.println(
"标准格式: " + now.format(Formatter.STANDARD_DATETIME)
);
DateTime specificDate = DateTime.of(2025, 1, 1, 12, 0, 0);
System.out.println(
"指定时间: " + specificDate.format(Formatter.STANDARD_DATETIME)
);
// 集合工具使用示例
System.out.println("\n=== 集合工具使用示例 ===");
List<Integer> numbers = Lists.newArrayList(10, 20, 30);
System.out.println("数字列表: " + numbers);
List<String> linkedList = Lists.newLinkedList(
"第一个",
"第二个",
"第三个"
);
System.out.println("链表内容: " + linkedList);
}
}

View File

@ -0,0 +1,112 @@
package com.mingliqiye.utils.aes;
import com.mingliqiye.utils.base64.Base64Utils;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.SecureRandom;
public class AesUtils {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private static final int GCM_IV_LENGTH = 12;
private static final int GCM_TAG_LENGTH = 16;
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
/**
* AES加密方法使用GCM模式
* @param sSrc 待加密的字符串
* @param sKey 加密密钥
* @return 加密后的字符串格式为 IV:EncryptedData+Tag均为Base64编码
* @throws GeneralSecurityException 加密错误
*/
public static String encrypt(String sSrc, String sKey)
throws GeneralSecurityException {
if (sKey == null) {
return null;
}
// 生成密钥
SecretKeySpec secretKeySpec = createSecretKey(sKey);
// 生成安全随机IV
byte[] iv = new byte[GCM_IV_LENGTH];
SECURE_RANDOM.nextBytes(iv);
// 初始化加密器
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(
GCM_TAG_LENGTH * 8,
iv
);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);
// 执行加密
byte[] encrypted = cipher.doFinal(
sSrc.getBytes(StandardCharsets.UTF_8)
);
// 将IV和加密数据包含认证标签组合返回
return Base64Utils.encode(
(Base64Utils.encode(iv) +
":" +
Base64Utils.encode(encrypted)).getBytes()
);
}
/**
* AES解密方法使用GCM模式
* @param sSrc 待解密的字符串格式为 IV:EncryptedData+Tag均为Base64编码
* @param sKey 解密密钥
* @return 解密后的原始字符串
*/
public static String decrypt(String sSrc, String sKey) {
if (sKey == null) {
return null;
}
try {
// 分割IV和加密数据
String sSrcs = new String(Base64Utils.decode(sSrc));
String[] parts = sSrcs.split(":", 2);
if (parts.length != 2) {
return null;
}
byte[] iv = Base64Utils.decode(parts[0]);
byte[] encryptedData = Base64Utils.decode(parts[1]);
if (iv.length != GCM_IV_LENGTH) {
return null;
}
SecretKeySpec secretKeySpec = createSecretKey(sKey);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(
GCM_TAG_LENGTH * 8,
iv
);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec);
byte[] original = cipher.doFinal(encryptedData);
return new String(original, StandardCharsets.UTF_8);
} catch (Exception e) {
return null;
}
}
/**
* 创建AES密钥支持任意长度的密钥
* @param sKey 字符串密钥
* @return SecretKeySpec对象
* @throws Exception 可能抛出的异常
*/
private static SecretKeySpec createSecretKey(String sKey)
throws GeneralSecurityException {
byte[] key = sKey.getBytes(StandardCharsets.UTF_8);
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(key);
return new SecretKeySpec(digest, ALGORITHM);
}
}

View File

@ -10,163 +10,163 @@ import java.util.Base64;
*/
public class Base64Utils {
// Base64编码器实例
private static final Base64.Encoder BASE_64_ENCODER = Base64.getEncoder();
// Base64解码器实例
private static final Base64.Decoder BASE_64_DECODER = Base64.getDecoder();
// Base64编码器实例
private static final Base64.Encoder BASE_64_ENCODER = Base64.getEncoder();
// Base64解码器实例
private static final Base64.Decoder BASE_64_DECODER = Base64.getDecoder();
/**
* 对字节数组进行Base64编码
*
* @param bytes 待编码的字节数组
* @return 编码后的Base64字符串
*/
public static String encode(byte[] bytes) {
return BASE_64_ENCODER.encodeToString(bytes);
}
/**
* 对字节数组进行Base64编码
*
* @param bytes 待编码的字节数组
* @return 编码后的Base64字符串
*/
public static String encode(byte[] bytes) {
return BASE_64_ENCODER.encodeToString(bytes);
}
/**
* 对文件内容进行Base64编码
*
* @param file 待编码的文件对象
* @return 编码后的Base64字符串
* @throws RuntimeException 如果读取文件时发生IO异常
*/
public static String encode(File file) {
try {
byte[] bytes = java.nio.file.Files.readAllBytes(file.toPath());
return encode(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 对文件内容进行Base64编码
*
* @param file 待编码的文件对象
* @return 编码后的Base64字符串
* @throws RuntimeException 如果读取文件时发生IO异常
*/
public static String encode(File file) {
try {
byte[] bytes = java.nio.file.Files.readAllBytes(file.toPath());
return encode(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 根据文件路径对文件内容进行Base64编码
*
* @param filePath 文件路径
* @return 编码后的Base64字符串
*/
public static String encode(String filePath) {
return encode(new File(filePath));
}
/**
* 根据文件路径对文件内容进行Base64编码
*
* @param filePath 文件路径
* @return 编码后的Base64字符串
*/
public static String encode(String filePath) {
return encode(new File(filePath));
}
/**
* 安全地对文件内容进行Base64编码出错时返回null
*
* @param file 待编码的文件对象
* @return 编码后的Base64字符串出错时返回null
*/
public static String encodeSafe(File file) {
try {
return encode(file);
} catch (Exception e) {
return null;
}
}
/**
* 安全地对文件内容进行Base64编码出错时返回null
*
* @param file 待编码的文件对象
* @return 编码后的Base64字符串出错时返回null
*/
public static String encodeSafe(File file) {
try {
return encode(file);
} catch (Exception e) {
return null;
}
}
/**
* 安全地根据文件路径对文件内容进行Base64编码出错时返回null
*
* @param filePath 文件路径
* @return 编码后的Base64字符串出错时返回null
*/
public static String encodeSafe(String filePath) {
try {
return encode(filePath);
} catch (Exception e) {
return null;
}
}
/**
* 安全地根据文件路径对文件内容进行Base64编码出错时返回null
*
* @param filePath 文件路径
* @return 编码后的Base64字符串出错时返回null
*/
public static String encodeSafe(String filePath) {
try {
return encode(filePath);
} catch (Exception e) {
return null;
}
}
/**
* 对Base64字符串进行解码
*
* @param base64 待解码的Base64字符串
* @return 解码后的字节数组
*/
public static byte[] decode(String base64) {
return BASE_64_DECODER.decode(base64);
}
/**
* 对Base64字符串进行解码
*
* @param base64 待解码的Base64字符串
* @return 解码后的字节数组
*/
public static byte[] decode(String base64) {
return BASE_64_DECODER.decode(base64);
}
/**
* 安全地对Base64字符串进行解码出错时返回null
*
* @param base64 待解码的Base64字符串
* @return 解码后的字节数组出错时返回null
*/
public static byte[] decodeSafe(String base64) {
try {
return decode(base64);
} catch (Exception e) {
return null;
}
}
/**
* 安全地对Base64字符串进行解码出错时返回null
*
* @param base64 待解码的Base64字符串
* @return 解码后的字节数组出错时返回null
*/
public static byte[] decodeSafe(String base64) {
try {
return decode(base64);
} catch (Exception e) {
return null;
}
}
/**
* 将Base64字符串解码并写入指定文件
*
* @param base64 待解码的Base64字符串
* @param file 目标文件对象
* @throws RuntimeException 如果写入文件时发生IO异常
*/
public static void decodeToFile(String base64, File file) {
try (FileOutputStream fos = new FileOutputStream(file)) {
byte[] bytes = decode(base64);
fos.write(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 将Base64字符串解码并写入指定文件
*
* @param base64 待解码的Base64字符串
* @param file 目标文件对象
* @throws RuntimeException 如果写入文件时发生IO异常
*/
public static void decodeToFile(String base64, File file) {
try (FileOutputStream fos = new FileOutputStream(file)) {
byte[] bytes = decode(base64);
fos.write(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 将Base64字符串解码并写入指定路径的文件
*
* @param base64 待解码的Base64字符串
* @param filePath 目标文件路径
*/
public static void decodeToFile(String base64, String filePath) {
decodeToFile(base64, new File(filePath));
}
/**
* 将Base64字符串解码并写入指定路径的文件
*
* @param base64 待解码的Base64字符串
* @param filePath 目标文件路径
*/
public static void decodeToFile(String base64, String filePath) {
decodeToFile(base64, new File(filePath));
}
/**
* 安全地将Base64字符串解码并写入指定文件出错时返回false
*
* @param base64 待解码的Base64字符串
* @param file 目标文件对象
* @return 成功写入返回true否则返回false
*/
public static boolean decodeToFileSafe(String base64, File file) {
try {
decodeToFile(base64, file);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 安全地将Base64字符串解码并写入指定文件出错时返回false
*
* @param base64 待解码的Base64字符串
* @param file 目标文件对象
* @return 成功写入返回true否则返回false
*/
public static boolean decodeToFileSafe(String base64, File file) {
try {
decodeToFile(base64, file);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 安全地将Base64字符串解码并写入指定路径的文件出错时返回false
*
* @param base64 待解码的Base64字符串
* @param filePath 目标文件路径
* @return 成功写入返回true否则返回false
*/
public static boolean decodeToFileSafe(String base64, String filePath) {
return decodeToFileSafe(base64, new File(filePath));
}
/**
* 安全地将Base64字符串解码并写入指定路径的文件出错时返回false
*
* @param base64 待解码的Base64字符串
* @param filePath 目标文件路径
* @return 成功写入返回true否则返回false
*/
public static boolean decodeToFileSafe(String base64, String filePath) {
return decodeToFileSafe(base64, new File(filePath));
}
/**
* 对字节数组中指定范围的数据进行Base64编码
*
* @param bytes 源字节数组
* @param offset 起始偏移量
* @param length 要编码的数据长度
* @return 编码后的Base64字符串
*/
public static String encodeBytes(byte[] bytes, int offset, int length) {
byte[] data = new byte[length];
System.arraycopy(bytes, offset, data, 0, length);
return encode(data);
}
/**
* 对字节数组中指定范围的数据进行Base64编码
*
* @param bytes 源字节数组
* @param offset 起始偏移量
* @param length 要编码的数据长度
* @return 编码后的Base64字符串
*/
public static String encodeBytes(byte[] bytes, int offset, int length) {
byte[] data = new byte[length];
System.arraycopy(bytes, offset, data, 0, length);
return encode(data);
}
}

View File

@ -2,7 +2,6 @@ package com.mingliqiye.utils.bean;
import com.mingliqiye.utils.bean.annotation.ComponentBean;
import com.mingliqiye.utils.bean.annotation.InjectBean;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
@ -17,233 +16,233 @@ import java.util.concurrent.ConcurrentMap;
*/
public class Factory {
/**
* 存储所有已注册的Bean实例键为Bean名称值为Bean实例
*/
public static final ConcurrentMap<String, Object> beans =
new ConcurrentHashMap<>();
/**
* 存储所有已注册的Bean实例键为Bean名称值为Bean实例
*/
public static final ConcurrentMap<String, Object> beans =
new ConcurrentHashMap<>();
/**
* 存储按类型查找的Bean实例键为Bean的Class对象值为Bean实例
*/
private static final ConcurrentMap<Class<?>, Object> typeBeans =
new ConcurrentHashMap<>();
/**
* 存储按类型查找的Bean实例键为Bean的Class对象值为Bean实例
*/
private static final ConcurrentMap<Class<?>, Object> typeBeans =
new ConcurrentHashMap<>();
/**
* 私有构造函数防止外部实例化该类
*/
private Factory() {}
/**
* 私有构造函数防止外部实例化该类
*/
private Factory() {}
/**
* 自动扫描指定类所在包下的所有类并注册为Bean
*
* @param c 指定的类用于获取其所在的包
* @throws IllegalArgumentException 如果传入的类为null或位于默认包中
*/
public static void autoScan(Class<?> c) {
if (c == null) {
throw new IllegalArgumentException("Class cannot be null");
}
Package pkg = c.getPackage();
if (pkg == null) {
throw new IllegalArgumentException(
"Class is in the default package"
);
}
scan(pkg.getName());
}
/**
* 自动扫描指定类所在包下的所有类并注册为Bean
*
* @param c 指定的类用于获取其所在的包
* @throws IllegalArgumentException 如果传入的类为null或位于默认包中
*/
public static void autoScan(Class<?> c) {
if (c == null) {
throw new IllegalArgumentException("Class cannot be null");
}
Package pkg = c.getPackage();
if (pkg == null) {
throw new IllegalArgumentException(
"Class is in the default package"
);
}
scan(pkg.getName());
}
/**
* 扫描指定包路径下的所有类文件并注册其中带有@ComponentBean注解的类为Bean
*
* @param basePackage 要扫描的基础包名
* @throws RuntimeException 如果在扫描过程中发生异常
*/
public static void scan(String basePackage) {
try {
String path = basePackage.replace('.', '/');
ClassLoader classLoader =
Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = null;
resources = classLoader.getResources(path);
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
File file = new File(resource.toURI());
scanDirectory(file, basePackage);
}
injectDependencies();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 扫描指定包路径下的所有类文件并注册其中带有@ComponentBean注解的类为Bean
*
* @param basePackage 要扫描的基础包名
* @throws RuntimeException 如果在扫描过程中发生异常
*/
public static void scan(String basePackage) {
try {
String path = basePackage.replace('.', '/');
ClassLoader classLoader =
Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = null;
resources = classLoader.getResources(path);
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
File file = new File(resource.toURI());
scanDirectory(file, basePackage);
}
injectDependencies();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 递归扫描目录中的所有类文件并注册符合条件的类为Bean
*
* @param directory 当前要扫描的目录
* @param packageName 当前目录对应的包名
* @throws Exception 如果在扫描或类加载过程中发生异常
*/
private static void scanDirectory(File directory, String packageName)
throws Exception {
File[] files = directory.listFiles();
if (files == null) {
return;
}
/**
* 递归扫描目录中的所有类文件并注册符合条件的类为Bean
*
* @param directory 当前要扫描的目录
* @param packageName 当前目录对应的包名
* @throws Exception 如果在扫描或类加载过程中发生异常
*/
private static void scanDirectory(File directory, String packageName)
throws Exception {
File[] files = directory.listFiles();
if (files == null) {
return;
}
for (File file : files) {
if (file.isDirectory()) {
scanDirectory(file, packageName + "." + file.getName());
} else if (file.getName().endsWith(".class")) {
String className =
packageName + '.' + file.getName().replace(".class", "");
registerComponent(Class.forName(className));
}
}
}
for (File file : files) {
if (file.isDirectory()) {
scanDirectory(file, packageName + "." + file.getName());
} else if (file.getName().endsWith(".class")) {
String className =
packageName + '.' + file.getName().replace(".class", "");
registerComponent(Class.forName(className));
}
}
}
/**
* 注册一个带有@ComponentBean注解的类为Bean实例
*
* @param clazz 要注册的类
* @throws Exception 如果在实例化类或处理注解时发生异常
*/
private static void registerComponent(Class<?> clazz) throws Exception {
if (clazz.isAnnotationPresent(ComponentBean.class)) {
ComponentBean component = clazz.getAnnotation(ComponentBean.class);
String name = component.value().isEmpty()
? clazz.getName()
: component.value();
Object instance = clazz.getDeclaredConstructor().newInstance();
beans.put(name, instance);
typeBeans.put(clazz, instance);
/**
* 注册一个带有@ComponentBean注解的类为Bean实例
*
* @param clazz 要注册的类
* @throws Exception 如果在实例化类或处理注解时发生异常
*/
private static void registerComponent(Class<?> clazz) throws Exception {
if (clazz.isAnnotationPresent(ComponentBean.class)) {
ComponentBean component = clazz.getAnnotation(ComponentBean.class);
String name = component.value().isEmpty()
? clazz.getName()
: component.value();
Object instance = clazz.getDeclaredConstructor().newInstance();
beans.put(name, instance);
typeBeans.put(clazz, instance);
for (Class<?> interfaceClass : clazz.getInterfaces()) {
typeBeans.putIfAbsent(interfaceClass, instance);
}
}
}
for (Class<?> interfaceClass : clazz.getInterfaces()) {
typeBeans.putIfAbsent(interfaceClass, instance);
}
}
}
/**
* 对所有已注册的Bean进行依赖注入处理
*
* @throws Exception 如果在注入过程中发生异常
*/
private static void injectDependencies() throws Exception {
for (Object bean : beans.values()) {
for (Field field : bean.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(InjectBean.class)) {
InjectBean inject = field.getAnnotation(InjectBean.class);
Object dependency = findDependency(
field.getType(),
inject.value()
);
if (dependency == null) {
throw new IllegalStateException(
"No suitable dependency found for field " +
field.getName() +
" in class " +
bean.getClass().getName()
);
}
field.setAccessible(true);
field.set(bean, dependency);
}
}
}
}
/**
* 对所有已注册的Bean进行依赖注入处理
*
* @throws Exception 如果在注入过程中发生异常
*/
private static void injectDependencies() throws Exception {
for (Object bean : beans.values()) {
for (Field field : bean.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(InjectBean.class)) {
InjectBean inject = field.getAnnotation(InjectBean.class);
Object dependency = findDependency(
field.getType(),
inject.value()
);
if (dependency == null) {
throw new IllegalStateException(
"No suitable dependency found for field " +
field.getName() +
" in class " +
bean.getClass().getName()
);
}
field.setAccessible(true);
field.set(bean, dependency);
}
}
}
}
/**
* 根据类型和名称查找对应的依赖实例
*
* @param type 依赖的类型
* @param name 依赖的名称可为空
* @return 找到的依赖实例未找到则返回null
*/
private static Object findDependency(Class<?> type, String name) {
if (!name.isEmpty()) {
return beans.get(name);
}
/**
* 根据类型和名称查找对应的依赖实例
*
* @param type 依赖的类型
* @param name 依赖的名称可为空
* @return 找到的依赖实例未找到则返回null
*/
private static Object findDependency(Class<?> type, String name) {
if (!name.isEmpty()) {
return beans.get(name);
}
Object dependency = typeBeans.get(type);
if (dependency != null) {
return dependency;
}
Object dependency = typeBeans.get(type);
if (dependency != null) {
return dependency;
}
for (Class<?> interfaceType : typeBeans.keySet()) {
if (type.isAssignableFrom(interfaceType)) {
return typeBeans.get(interfaceType);
}
}
for (Class<?> interfaceType : typeBeans.keySet()) {
if (type.isAssignableFrom(interfaceType)) {
return typeBeans.get(interfaceType);
}
}
return null;
}
return null;
}
/**
* 将一个对象添加到Bean容器中使用其类名作为键
*
* @param object 要添加的对象
* @throws RuntimeException 如果在注入依赖时发生异常
*/
public static void add(Object object) {
Class<?> clazz = object.getClass();
String name = clazz.getName();
beans.put(name, object);
typeBeans.put(clazz, object);
try {
injectDependencies();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 将一个对象添加到Bean容器中使用其类名作为键
*
* @param object 要添加的对象
* @throws RuntimeException 如果在注入依赖时发生异常
*/
public static void add(Object object) {
Class<?> clazz = object.getClass();
String name = clazz.getName();
beans.put(name, object);
typeBeans.put(clazz, object);
try {
injectDependencies();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 将一个对象以指定名称添加到Bean容器中
*
* @param name Bean的名称
* @param object 要添加的对象
* @throws RuntimeException 如果在注入依赖时发生异常
*/
public static void add(String name, Object object) {
beans.put(name, object);
typeBeans.put(object.getClass(), object);
try {
injectDependencies();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 将一个对象以指定名称添加到Bean容器中
*
* @param name Bean的名称
* @param object 要添加的对象
* @throws RuntimeException 如果在注入依赖时发生异常
*/
public static void add(String name, Object object) {
beans.put(name, object);
typeBeans.put(object.getClass(), object);
try {
injectDependencies();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 根据类型获取对应的Bean实例
*
* @param objclass Bean的类型
* @param <T> Bean的泛型类型
* @return 对应类型的Bean实例未找到则返回null
*/
public static <T> T get(Class<T> objclass) {
return objclass.cast(typeBeans.get(objclass));
}
/**
* 根据类型获取对应的Bean实例
*
* @param objclass Bean的类型
* @param <T> Bean的泛型类型
* @return 对应类型的Bean实例未找到则返回null
*/
public static <T> T get(Class<T> objclass) {
return objclass.cast(typeBeans.get(objclass));
}
/**
* 根据名称和类型获取对应的Bean实例
*
* @param name Bean的名称
* @param objclass Bean的类型
* @param <T> Bean的泛型类型
* @return 对应名称和类型的Bean实例未找到则返回null
*/
public static <T> T get(String name, Class<T> objclass) {
return objclass.cast(beans.get(name));
}
/**
* 根据名称和类型获取对应的Bean实例
*
* @param name Bean的名称
* @param objclass Bean的类型
* @param <T> Bean的泛型类型
* @return 对应名称和类型的Bean实例未找到则返回null
*/
public static <T> T get(String name, Class<T> objclass) {
return objclass.cast(beans.get(name));
}
/**
* 根据名称获取对应的Bean实例
*
* @param name Bean的名称
* @return 对应名称的Bean实例未找到则返回null
*/
public static Object get(String name) {
return beans.get(name);
}
/**
* 根据名称获取对应的Bean实例
*
* @param name Bean的名称
* @return 对应名称的Bean实例未找到则返回null
*/
public static Object get(String name) {
return beans.get(name);
}
}

View File

@ -8,5 +8,5 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentBean {
String value() default "";
String value() default "";
}

View File

@ -8,5 +8,5 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectBean {
String value() default "";
String value() default "";
}

View File

@ -19,60 +19,57 @@ import org.springframework.stereotype.Component;
@Component
public class SpringBeanUtil implements ApplicationContextAware {
/**
* 获取applicationContext
*/
@Getter
private static ApplicationContext applicationContext;
/**
* 获取applicationContext
*/
@Getter
private static ApplicationContext applicationContext;
/**
* 通过bean名称获取Bean实例
*
* @param name bean名称
* @return bean实例对象
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过bean名称获取Bean实例
*
* @param name bean名称
* @return bean实例对象
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过bean类型获取Bean实例
*
* @param clazz bean的Class类型
* @param <T> 泛型类型
* @return 指定类型的bean实例
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过bean类型获取Bean实例
*
* @param clazz bean的Class类型
* @param <T> 泛型类型
* @return 指定类型的bean实例
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过bean名称和类型获取指定的Bean实例
*
* @param name bean名称
* @param clazz bean的Class类型
* @param <T> 泛型类型
* @return 指定名称和类型的bean实例
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
/**
* 通过bean名称和类型获取指定的Bean实例
*
* @param name bean名称
* @param clazz bean的Class类型
* @param <T> 泛型类型
* @return 指定名称和类型的bean实例
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
/**
* 设置ApplicationContext上下文对象
* 当Spring容器初始化时会自动调用此方法将ApplicationContext注入到本工具类中
* 通过判断避免重复赋值确保只设置一次ApplicationContext
*
* @param applicationContext Spring应用上下文对象
* @throws BeansException bean异常
*/
@Override
public void setApplicationContext(
@NotNull ApplicationContext applicationContext
) throws BeansException {
// 避免重复赋值确保只设置一次ApplicationContext
if (SpringBeanUtil.applicationContext == null) {
SpringBeanUtil.applicationContext = applicationContext;
}
}
/**
* 设置ApplicationContext上下文对象
* 当Spring容器初始化时会自动调用此方法将ApplicationContext注入到本工具类中
* 通过判断避免重复赋值确保只设置一次ApplicationContext
*
* @param applicationContext Spring应用上下文对象
* @throws BeansException bean异常
*/
@Override
public void setApplicationContext(
@NotNull ApplicationContext applicationContext
) throws BeansException {
SpringBeanUtil.applicationContext = applicationContext;
}
}

View File

@ -1,13 +1,12 @@
package com.mingliqiye.utils.collection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* 集合工具类提供对列表和数组的常用操作方法
@ -16,289 +15,289 @@ import java.util.function.Predicate;
*/
public class Collection {
/**
* 获取集合的第一个元素
*
* @param collection 集合
* @param <T> 元素类型
* @return 第一个元素如果集合为空或为null则返回 null
*/
@Nullable
public static <T> T getFirst(@Nullable java.util.Collection<T> collection) {
if (collection == null || collection.isEmpty()) {
return null;
}
/**
* 获取集合的第一个元素
*
* @param collection 集合
* @param <T> 元素类型
* @return 第一个元素如果集合为空或为null则返回 null
*/
@Nullable
public static <T> T getFirst(@Nullable java.util.Collection<T> collection) {
if (collection == null || collection.isEmpty()) {
return null;
}
// 对于List类型直接获取第一个元素
if (collection instanceof List) {
return ((List<T>) collection).get(0);
}
// 对于List类型直接获取第一个元素
if (collection instanceof List) {
return ((List<T>) collection).get(0);
}
// 对于其他Collection类型使用迭代器获取第一个元素
return collection.iterator().next();
}
// 对于其他Collection类型使用迭代器获取第一个元素
return collection.iterator().next();
}
/**
* 获取数组的第一个元素
*
* @param list 数组不能为空
* @param <T> 元素类型
* @return 第一个元素如果数组为空则返回 null
*/
@Nullable
public static <T> T getFirst(@NotNull T[] list) {
if (list.length == 0) {
return null;
}
return list[0];
}
/**
* 获取数组的第一个元素
*
* @param list 数组不能为空
* @param <T> 元素类型
* @return 第一个元素如果数组为空则返回 null
*/
@Nullable
public static <T> T getFirst(@NotNull T[] list) {
if (list.length == 0) {
return null;
}
return list[0];
}
/**
* 获取集合的最后一个元素
*
* @param collection 集合
* @param <T> 元素类型
* @return 最后一个元素如果集合为空或为null则返回 null
*/
@Nullable
public static <T> T getLast(@Nullable java.util.Collection<T> collection) {
if (collection == null || collection.isEmpty()) {
return null;
}
/**
* 获取集合的最后一个元素
*
* @param collection 集合
* @param <T> 元素类型
* @return 最后一个元素如果集合为空或为null则返回 null
*/
@Nullable
public static <T> T getLast(@Nullable java.util.Collection<T> collection) {
if (collection == null || collection.isEmpty()) {
return null;
}
// 对于List类型直接获取最后一个元素
if (collection instanceof List) {
List<T> list = (List<T>) collection;
return list.get(list.size() - 1);
}
// 对于List类型直接获取最后一个元素
if (collection instanceof List) {
List<T> list = (List<T>) collection;
return list.get(list.size() - 1);
}
// 对于其他Collection类型需要遍历到最后一个元素
T lastElement = null;
for (T element : collection) {
lastElement = element;
}
return lastElement;
}
// 对于其他Collection类型需要遍历到最后一个元素
T lastElement = null;
for (T element : collection) {
lastElement = element;
}
return lastElement;
}
/**
* 获取数组的最后一个元素
*
* @param list 数组不能为空
* @param <T> 元素类型
* @return 最后一个元素如果数组为空则返回 null
*/
@Nullable
public static <T> T getLast(@NotNull T[] list) {
if (list.length == 0) {
return null;
}
return list[list.length - 1];
}
/**
* 获取数组的最后一个元素
*
* @param list 数组不能为空
* @param <T> 元素类型
* @return 最后一个元素如果数组为空则返回 null
*/
@Nullable
public static <T> T getLast(@NotNull T[] list) {
if (list.length == 0) {
return null;
}
return list[list.length - 1];
}
/**
* 获取列表中指定索引的元素如果索引超出范围则返回默认值
*
* @param list 列表
* @param index 索引
* @param defaultValue 默认值
* @param <T> 元素类型
* @return 指定索引的元素或默认值
*/
@Nullable
public static <T> T getOrDefault(
@NotNull List<T> list,
int index,
@Nullable T defaultValue
) {
if (index < 0 || index >= list.size()) {
return defaultValue;
}
return list.get(index);
}
/**
* 获取列表中指定索引的元素如果索引超出范围则返回默认值
*
* @param list 列表
* @param index 索引
* @param defaultValue 默认值
* @param <T> 元素类型
* @return 指定索引的元素或默认值
*/
@Nullable
public static <T> T getOrDefault(
@NotNull List<T> list,
int index,
@Nullable T defaultValue
) {
if (index < 0 || index >= list.size()) {
return defaultValue;
}
return list.get(index);
}
/**
* 获取数组中指定索引的元素如果索引超出范围则返回默认值
*
* @param array 数组
* @param index 索引
* @param defaultValue 默认值
* @param <T> 元素类型
* @return 指定索引的元素或默认值
*/
@Nullable
public static <T> T getOrDefault(
@NotNull T[] array,
int index,
@Nullable T defaultValue
) {
if (index < 0 || index >= array.length) {
return defaultValue;
}
return array[index];
}
/**
* 获取数组中指定索引的元素如果索引超出范围则返回默认值
*
* @param array 数组
* @param index 索引
* @param defaultValue 默认值
* @param <T> 元素类型
* @return 指定索引的元素或默认值
*/
@Nullable
public static <T> T getOrDefault(
@NotNull T[] array,
int index,
@Nullable T defaultValue
) {
if (index < 0 || index >= array.length) {
return defaultValue;
}
return array[index];
}
/**
* 获取列表的安全子列表自动处理边界情况
*
* @param list 原始列表
* @param fromIndex 起始索引包含
* @param toIndex 结束索引不包含
* @param <T> 元素类型
* @return 子列表
*/
@NotNull
public static <T> List<T> safeSubList(
@NotNull List<T> list,
int fromIndex,
int toIndex
) {
int size = list.size();
if (size == 0) {
return Collections.emptyList();
}
/**
* 获取列表的安全子列表自动处理边界情况
*
* @param list 原始列表
* @param fromIndex 起始索引包含
* @param toIndex 结束索引不包含
* @param <T> 元素类型
* @return 子列表
*/
@NotNull
public static <T> List<T> safeSubList(
@NotNull List<T> list,
int fromIndex,
int toIndex
) {
int size = list.size();
if (size == 0) {
return Collections.emptyList();
}
// 调整边界
fromIndex = Math.max(0, fromIndex);
toIndex = Math.min(size, toIndex);
if (fromIndex >= toIndex) {
return Collections.emptyList();
}
// 调整边界
fromIndex = Math.max(0, fromIndex);
toIndex = Math.min(size, toIndex);
if (fromIndex >= toIndex) {
return Collections.emptyList();
}
return list.subList(fromIndex, toIndex);
}
return list.subList(fromIndex, toIndex);
}
/**
* 判断列表是否为空或null
*
* @param list 待检查的列表
* @return 如果列表为null或空则返回true否则返回false
*/
public static boolean isEmpty(@Nullable List<?> list) {
return list == null || list.isEmpty();
}
/**
* 判断列表是否为空或null
*
* @param list 待检查的列表
* @return 如果列表为null或空则返回true否则返回false
*/
public static boolean isEmpty(@Nullable List<?> list) {
return list == null || list.isEmpty();
}
/**
* 判断数组是否为空或null
*
* @param array 待检查的数组
* @return 如果数组为null或空则返回true否则返回false
*/
public static boolean isEmpty(@Nullable Object[] array) {
return array == null || array.length == 0;
}
/**
* 判断数组是否为空或null
*
* @param array 待检查的数组
* @return 如果数组为null或空则返回true否则返回false
*/
public static boolean isEmpty(@Nullable Object[] array) {
return array == null || array.length == 0;
}
/**
* 查找列表中第一个满足条件的元素
*
* @param list 列表
* @param predicate 条件谓词
* @param <T> 元素类型
* @return 第一个满足条件的元素如果没有则返回null
*/
@Nullable
public static <T> T findFirst(
@NotNull List<T> list,
@NotNull Predicate<T> predicate
) {
for (T item : list) {
if (predicate.test(item)) {
return item;
}
}
return null;
}
/**
* 查找列表中第一个满足条件的元素
*
* @param list 列表
* @param predicate 条件谓词
* @param <T> 元素类型
* @return 第一个满足条件的元素如果没有则返回null
*/
@Nullable
public static <T> T findFirst(
@NotNull List<T> list,
@NotNull Predicate<T> predicate
) {
for (T item : list) {
if (predicate.test(item)) {
return item;
}
}
return null;
}
/**
* 查找数组中第一个满足条件的元素
*
* @param array 数组
* @param predicate 条件谓词
* @param <T> 元素类型
* @return 第一个满足条件的元素如果没有则返回null
*/
@Nullable
public static <T> T findFirst(
@NotNull T[] array,
@NotNull Predicate<T> predicate
) {
for (T item : array) {
if (predicate.test(item)) {
return item;
}
}
return null;
}
/**
* 查找数组中第一个满足条件的元素
*
* @param array 数组
* @param predicate 条件谓词
* @param <T> 元素类型
* @return 第一个满足条件的元素如果没有则返回null
*/
@Nullable
public static <T> T findFirst(
@NotNull T[] array,
@NotNull Predicate<T> predicate
) {
for (T item : array) {
if (predicate.test(item)) {
return item;
}
}
return null;
}
/**
* 过滤列表中满足条件的元素
*
* @param list 原始列表
* @param predicate 条件谓词
* @param <T> 元素类型
* @return 包含满足条件元素的新列表
*/
@NotNull
public static <T> List<T> filter(
@NotNull List<T> list,
@NotNull Predicate<T> predicate
) {
List<T> result = new ArrayList<>();
for (T item : list) {
if (predicate.test(item)) {
result.add(item);
}
}
return result;
}
/**
* 过滤列表中满足条件的元素
*
* @param list 原始列表
* @param predicate 条件谓词
* @param <T> 元素类型
* @return 包含满足条件元素的新列表
*/
@NotNull
public static <T> List<T> filter(
@NotNull List<T> list,
@NotNull Predicate<T> predicate
) {
List<T> result = new ArrayList<>();
for (T item : list) {
if (predicate.test(item)) {
result.add(item);
}
}
return result;
}
/**
* 过滤数组中满足条件的元素
*
* @param array 原始数组
* @param predicate 条件谓词
* @param <T> 元素类型
* @return 包含满足条件元素的新列表
*/
@NotNull
public static <T> List<T> filter(
@NotNull T[] array,
@NotNull Predicate<T> predicate
) {
return filter(Arrays.asList(array), predicate);
}
/**
* 过滤数组中满足条件的元素
*
* @param array 原始数组
* @param predicate 条件谓词
* @param <T> 元素类型
* @return 包含满足条件元素的新列表
*/
@NotNull
public static <T> List<T> filter(
@NotNull T[] array,
@NotNull Predicate<T> predicate
) {
return filter(Arrays.asList(array), predicate);
}
/**
* 将列表转换为数组
*
* @param list 列表
* @param clazz 元素类型class
* @param <T> 元素类型
* @return 转换后的数组
*/
@SuppressWarnings("unchecked")
@NotNull
public static <T> T[] toArray(
@NotNull List<T> list,
@NotNull Class<T> clazz
) {
T[] array = (T[]) java.lang.reflect.Array.newInstance(
clazz,
list.size()
);
return list.toArray(array);
}
/**
* 将列表转换为数组
*
* @param list 列表
* @param clazz 元素类型class
* @param <T> 元素类型
* @return 转换后的数组
*/
@SuppressWarnings("unchecked")
@NotNull
public static <T> T[] toArray(
@NotNull List<T> list,
@NotNull Class<T> clazz
) {
T[] array = (T[]) java.lang.reflect.Array.newInstance(
clazz,
list.size()
);
return list.toArray(array);
}
/**
* 将集合转换为列表
*
* @param collection 集合
* @param <T> 元素类型
* @return 转换后的列表
*/
@NotNull
public static <T> List<T> toList(
@NotNull java.util.Collection<T> collection
) {
return new ArrayList<>(collection);
}
/**
* 将集合转换为列表
*
* @param collection 集合
* @param <T> 元素类型
* @return 转换后的列表
*/
@NotNull
public static <T> List<T> toList(
@NotNull java.util.Collection<T> collection
) {
return new ArrayList<>(collection);
}
}

View File

@ -10,192 +10,192 @@ import java.util.concurrent.ConcurrentMap;
*/
public class ForEach {
/**
* 对给定的集合执行指定的操作操作包含元素值和索引
* 根据集合类型选择最优的遍历方式以提高性能
*
* @param collection 要遍历的集合可以是 List 或其他 Collection 实现
* @param action 要对每个元素执行的操作接收元素值和索引作为参数
* @param <T> 集合中元素的类型
*/
public static <T> void forEach(
Collection<T> collection,
ForEach.Consumer<? super T> action
) {
// 参数校验如果集合或操作为空则直接返回
if (collection == null || action == null) {
return;
}
/**
* 对给定的集合执行指定的操作操作包含元素值和索引
* 根据集合类型选择最优的遍历方式以提高性能
*
* @param collection 要遍历的集合可以是 List 或其他 Collection 实现
* @param action 要对每个元素执行的操作接收元素值和索引作为参数
* @param <T> 集合中元素的类型
*/
public static <T> void forEach(
Collection<T> collection,
ForEach.Consumer<? super T> action
) {
// 参数校验如果集合或操作为空则直接返回
if (collection == null || action == null) {
return;
}
// 如果集合实现了 RandomAccess 接口 ArrayList使用索引访问优化性能
if (collection instanceof RandomAccess && collection instanceof List) {
List<T> list = (List<T>) collection;
for (int i = 0; i < list.size(); i++) {
action.call(list.get(i), i);
}
}
// 如果是普通 List使用迭代器遍历并手动维护索引
else if (collection instanceof List) {
int index = 0;
Iterator<T> it = collection.iterator();
while (it.hasNext()) {
action.call(it.next(), index);
index++;
}
}
// 其他类型的集合使用增强 for 循环并手动维护索引
else {
int index = 0;
for (T element : collection) {
action.call(element, index);
index++;
}
}
}
// 如果集合实现了 RandomAccess 接口 ArrayList使用索引访问优化性能
if (collection instanceof RandomAccess && collection instanceof List) {
List<T> list = (List<T>) collection;
for (int i = 0; i < list.size(); i++) {
action.call(list.get(i), i);
}
}
// 如果是普通 List使用迭代器遍历并手动维护索引
else if (collection instanceof List) {
int index = 0;
Iterator<T> it = collection.iterator();
while (it.hasNext()) {
action.call(it.next(), index);
index++;
}
}
// 其他类型的集合使用增强 for 循环并手动维护索引
else {
int index = 0;
for (T element : collection) {
action.call(element, index);
index++;
}
}
}
/**
* 对给定的集合执行指定的操作仅处理元素值
* 根据集合是否实现 RandomAccess 接口选择最优的遍历方式
*
* @param collection 要遍历的集合
* @param action 要对每个元素执行的操作只接收元素值作为参数
* @param <T> 集合中元素的类型
*/
public static <T> void forEach(
Collection<T> collection,
java.util.function.Consumer<? super T> action
) {
// 参数校验如果集合或操作为空则直接返回
if (collection == null || action == null) {
return;
}
/**
* 对给定的集合执行指定的操作仅处理元素值
* 根据集合是否实现 RandomAccess 接口选择最优的遍历方式
*
* @param collection 要遍历的集合
* @param action 要对每个元素执行的操作只接收元素值作为参数
* @param <T> 集合中元素的类型
*/
public static <T> void forEach(
Collection<T> collection,
java.util.function.Consumer<? super T> action
) {
// 参数校验如果集合或操作为空则直接返回
if (collection == null || action == null) {
return;
}
// 如果集合实现了 RandomAccess 接口使用索引访问提升性能
if (collection instanceof RandomAccess) {
List<T> list = (List<T>) collection;
for (int i = 0; i < list.size(); i++) {
action.accept(list.get(i));
}
}
// 否则使用增强 for 循环进行遍历
else {
for (T element : collection) {
action.accept(element);
}
}
}
// 如果集合实现了 RandomAccess 接口使用索引访问提升性能
if (collection instanceof RandomAccess) {
List<T> list = (List<T>) collection;
for (int i = 0; i < list.size(); i++) {
action.accept(list.get(i));
}
}
// 否则使用增强 for 循环进行遍历
else {
for (T element : collection) {
action.accept(element);
}
}
}
/**
* 对给定的映射执行指定的操作操作包含键值和索引
* 根据映射类型选择不同的遍历策略
*
* @param map 要遍历的映射
* @param action 要对每个键值对执行的操作接收键值和索引作为参数
* @param <K> 映射中键的类型
* @param <V> 映射中值的类型
*/
public static <K, V> void forEach(
Map<K, V> map,
BiConsumer<? super K, ? super V> action
) {
// 参数校验如果映射或操作为空则直接返回
if (map == null || action == null) {
return;
}
/**
* 对给定的映射执行指定的操作操作包含键值和索引
* 根据映射类型选择不同的遍历策略
*
* @param map 要遍历的映射
* @param action 要对每个键值对执行的操作接收键值和索引作为参数
* @param <K> 映射中键的类型
* @param <V> 映射中值的类型
*/
public static <K, V> void forEach(
Map<K, V> map,
BiConsumer<? super K, ? super V> action
) {
// 参数校验如果映射或操作为空则直接返回
if (map == null || action == null) {
return;
}
// 遍历 TreeMap 的条目集合并传递索引
if (map instanceof TreeMap) {
int index = 0;
for (Map.Entry<K, V> entry : map.entrySet()) {
action.call(entry.getKey(), entry.getValue(), index);
index++;
}
}
// 遍历 ConcurrentMap LinkedHashMap 的条目集合并传递索引
else if (map instanceof ConcurrentMap || map instanceof LinkedHashMap) {
int index = 0;
for (Map.Entry<K, V> entry : map.entrySet()) {
action.call(entry.getKey(), entry.getValue(), index);
index++;
}
}
// 遍历其他类型映射的条目集合并传递索引
else {
int index = 0;
for (Map.Entry<K, V> entry : map.entrySet()) {
action.call(entry.getKey(), entry.getValue(), index);
index++;
}
}
}
// 遍历 TreeMap 的条目集合并传递索引
if (map instanceof TreeMap) {
int index = 0;
for (Map.Entry<K, V> entry : map.entrySet()) {
action.call(entry.getKey(), entry.getValue(), index);
index++;
}
}
// 遍历 ConcurrentMap LinkedHashMap 的条目集合并传递索引
else if (map instanceof ConcurrentMap || map instanceof LinkedHashMap) {
int index = 0;
for (Map.Entry<K, V> entry : map.entrySet()) {
action.call(entry.getKey(), entry.getValue(), index);
index++;
}
}
// 遍历其他类型映射的条目集合并传递索引
else {
int index = 0;
for (Map.Entry<K, V> entry : map.entrySet()) {
action.call(entry.getKey(), entry.getValue(), index);
index++;
}
}
}
/**
* 对给定的映射执行指定的操作仅处理键和值
* 根据映射类型选择不同的遍历策略
*
* @param map 要遍历的映射
* @param action 要对每个键值对执行的操作接收键和值作为参数
* @param <K> 映射中键的类型
* @param <V> 映射中值的类型
*/
public static <K, V> void forEach(
Map<K, V> map,
java.util.function.BiConsumer<? super K, ? super V> action
) {
// 参数校验如果映射或操作为空则直接返回
if (map == null || action == null) {
return;
}
/**
* 对给定的映射执行指定的操作仅处理键和值
* 根据映射类型选择不同的遍历策略
*
* @param map 要遍历的映射
* @param action 要对每个键值对执行的操作接收键和值作为参数
* @param <K> 映射中键的类型
* @param <V> 映射中值的类型
*/
public static <K, V> void forEach(
Map<K, V> map,
java.util.function.BiConsumer<? super K, ? super V> action
) {
// 参数校验如果映射或操作为空则直接返回
if (map == null || action == null) {
return;
}
// 遍历 TreeMap 的条目集合
if (map instanceof TreeMap) {
for (Map.Entry<K, V> entry : map.entrySet()) {
action.accept(entry.getKey(), entry.getValue());
}
}
// 如果是 ConcurrentMap LinkedHashMap使用其内置的 forEach 方法
else if (map instanceof ConcurrentMap || map instanceof LinkedHashMap) {
map.forEach(action);
}
// 遍历其他类型映射的条目集合
else {
for (Map.Entry<K, V> entry : map.entrySet()) {
action.accept(entry.getKey(), entry.getValue());
}
}
}
// 遍历 TreeMap 的条目集合
if (map instanceof TreeMap) {
for (Map.Entry<K, V> entry : map.entrySet()) {
action.accept(entry.getKey(), entry.getValue());
}
}
// 如果是 ConcurrentMap LinkedHashMap使用其内置的 forEach 方法
else if (map instanceof ConcurrentMap || map instanceof LinkedHashMap) {
map.forEach(action);
}
// 遍历其他类型映射的条目集合
else {
for (Map.Entry<K, V> entry : map.entrySet()) {
action.accept(entry.getKey(), entry.getValue());
}
}
}
/**
* 自定义消费者接口用于接收元素值和索引
*
* @param <T> 元素类型
*/
@FunctionalInterface
public interface Consumer<T> {
/**
* 执行消费操作
*
* @param value 元素值
* @param index 元素在集合中的索引
*/
void call(T value, int index);
}
/**
* 自定义消费者接口用于接收元素值和索引
*
* @param <T> 元素类型
*/
@FunctionalInterface
public interface Consumer<T> {
/**
* 执行消费操作
*
* @param value 元素值
* @param index 元素在集合中的索引
*/
void call(T value, int index);
}
/**
* 自定义二元消费者接口用于接收键值和索引
*
* @param <K> 键类型
* @param <V> 值类型
*/
@FunctionalInterface
public interface BiConsumer<K, V> {
/**
* 执行消费操作
*
* @param key
* @param value
* @param index 键值对在映射中的索引
*/
void call(K key, V value, int index);
}
/**
* 自定义二元消费者接口用于接收键值和索引
*
* @param <K> 键类型
* @param <V> 值类型
*/
@FunctionalInterface
public interface BiConsumer<K, V> {
/**
* 执行消费操作
*
* @param key
* @param value
* @param index 键值对在映射中的索引
*/
void call(K key, V value, int index);
}
}

View File

@ -1,8 +1,7 @@
package com.mingliqiye.utils.collection;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import org.jetbrains.annotations.NotNull;
/**
* Lists工具类提供了一系列创建List实现的便捷方法
@ -11,239 +10,239 @@ import java.util.*;
*/
public class Lists {
/**
* 创建一个空的ArrayList实例
*
* @param <T> 列表元素的类型
* @return 新创建的空ArrayList实例
*/
public static <T> List<T> newArrayList() {
return new ArrayList<>();
}
/**
* 创建一个空的ArrayList实例
*
* @param <T> 列表元素的类型
* @return 新创建的空ArrayList实例
*/
public static <T> List<T> newArrayList() {
return new ArrayList<>();
}
/**
* 根据可变参数创建一个包含指定元素的ArrayList实例
*
* @param ts 要添加到列表中的元素可以为0个或多个
* @param <T> 列表元素的类型
* @return 包含指定元素的新ArrayList实例
*/
public static <T> List<T> newArrayList(T... ts) {
List<T> list = newArrayList();
list.addAll(Arrays.asList(ts));
return list;
}
/**
* 根据可变参数创建一个包含指定元素的ArrayList实例
*
* @param ts 要添加到列表中的元素可以为0个或多个
* @param <T> 列表元素的类型
* @return 包含指定元素的新ArrayList实例
*/
public static <T> List<T> newArrayList(T... ts) {
List<T> list = newArrayList();
list.addAll(Arrays.asList(ts));
return list;
}
/**
* 根据已有列表创建一个新的ArrayList实例
*
* @param list 要复制的列表
* @param <T> 列表元素的类型
* @return 包含原列表所有元素的新ArrayList实例
*/
public static <T> List<T> newArrayList(List<T> list) {
List<T> newList = newArrayList();
newList.addAll(list);
return newList;
}
/**
* 根据已有列表创建一个新的ArrayList实例
*
* @param list 要复制的列表
* @param <T> 列表元素的类型
* @return 包含原列表所有元素的新ArrayList实例
*/
public static <T> List<T> newArrayList(List<T> list) {
List<T> newList = newArrayList();
newList.addAll(list);
return newList;
}
/**
* 根据可迭代对象创建一个ArrayList实例
*
* @param iterable 可迭代对象
* @param <T> 列表元素的类型
* @return 包含可迭代对象中所有元素的新ArrayList实例
*/
public static <T> List<T> newArrayList(Iterable<T> iterable) {
List<T> list = newArrayList();
for (T t : iterable) {
list.add(t);
}
return list;
}
/**
* 根据可迭代对象创建一个ArrayList实例
*
* @param iterable 可迭代对象
* @param <T> 列表元素的类型
* @return 包含可迭代对象中所有元素的新ArrayList实例
*/
public static <T> List<T> newArrayList(Iterable<T> iterable) {
List<T> list = newArrayList();
for (T t : iterable) {
list.add(t);
}
return list;
}
/**
* 创建一个指定初始容量的空ArrayList实例
*
* @param size 初始容量大小
* @param <T> 列表元素的类型
* @return 指定初始容量的空ArrayList实例
*/
public static <T> List<T> newArrayList(int size) {
return new ArrayList<>(size);
}
/**
* 创建一个指定初始容量的空ArrayList实例
*
* @param size 初始容量大小
* @param <T> 列表元素的类型
* @return 指定初始容量的空ArrayList实例
*/
public static <T> List<T> newArrayList(int size) {
return new ArrayList<>(size);
}
/**
* 创建一个指定大小并用单个元素填充的ArrayList实例
*
* @param size 列表大小
* @param t 用于填充列表的元素
* @param <T> 列表元素的类型
* @return 指定大小且所有元素都相同的ArrayList实例
*/
public static <T> List<T> newArrayList(int size, T t) {
List<T> list = newArrayList(size);
for (int i = 0; i < size; i++) {
list.add(t);
}
return list;
}
/**
* 创建一个指定大小并用单个元素填充的ArrayList实例
*
* @param size 列表大小
* @param t 用于填充列表的元素
* @param <T> 列表元素的类型
* @return 指定大小且所有元素都相同的ArrayList实例
*/
public static <T> List<T> newArrayList(int size, T t) {
List<T> list = newArrayList(size);
for (int i = 0; i < size; i++) {
list.add(t);
}
return list;
}
/**
* 创建一个指定大小并交替使用两个元素填充的ArrayList实例
*
* @param size 列表大小
* @param t 第一个填充元素索引为偶数时使用
* @param t1 第二个填充元素索引为奇数时使用
* @param <T> 列表元素的类型
* @return 指定大小且交替填充两个元素的ArrayList实例
*/
public static <T> List<T> newArrayList(int size, T t, T t1) {
List<T> list = newArrayList(size);
for (int i = 0; i < size; i++) {
list.add(i % 2 == 0 ? t : t1);
}
return list;
}
/**
* 创建一个指定大小并交替使用两个元素填充的ArrayList实例
*
* @param size 列表大小
* @param t 第一个填充元素索引为偶数时使用
* @param t1 第二个填充元素索引为奇数时使用
* @param <T> 列表元素的类型
* @return 指定大小且交替填充两个元素的ArrayList实例
*/
public static <T> List<T> newArrayList(int size, T t, T t1) {
List<T> list = newArrayList(size);
for (int i = 0; i < size; i++) {
list.add(i % 2 == 0 ? t : t1);
}
return list;
}
/**
* 创建一个指定大小并循环使用三个元素填充的ArrayList实例
*
* @param size 列表大小
* @param t 第一个填充元素索引模3等于0时使用
* @param t1 第二个填充元素索引模3等于1时使用
* @param t2 第三个填充元素索引模3等于2时使用
* @param <T> 列表元素的类型
* @return 指定大小且循环填充三个元素的ArrayList实例
*/
public static <T> List<T> newArrayList(int size, T t, T t1, T t2) {
List<T> list = newArrayList(size);
for (int i = 0; i < size; i++) {
list.add(i % 3 == 0 ? t : i % 3 == 1 ? t1 : t2);
}
return list;
}
/**
* 创建一个指定大小并循环使用三个元素填充的ArrayList实例
*
* @param size 列表大小
* @param t 第一个填充元素索引模3等于0时使用
* @param t1 第二个填充元素索引模3等于1时使用
* @param t2 第三个填充元素索引模3等于2时使用
* @param <T> 列表元素的类型
* @return 指定大小且循环填充三个元素的ArrayList实例
*/
public static <T> List<T> newArrayList(int size, T t, T t1, T t2) {
List<T> list = newArrayList(size);
for (int i = 0; i < size; i++) {
list.add(i % 3 == 0 ? t : i % 3 == 1 ? t1 : t2);
}
return list;
}
/**
* 创建一个指定大小并循环使用四个元素填充的ArrayList实例
*
* @param size 列表大小
* @param t 第一个填充元素索引模4等于0时使用
* @param t1 第二个填充元素索引模4等于1时使用
* @param t2 第三个填充元素索引模4等于2时使用
* @param t3 第四个填充元素索引模4等于3时使用
* @param <T> 列表元素的类型
* @return 指定大小且循环填充四个元素的ArrayList实例
*/
public static <T> List<T> newArrayList(int size, T t, T t1, T t2, T t3) {
List<T> list = newArrayList(size);
for (int i = 0; i < size; i++) {
list.add(i % 4 == 0 ? t : i % 4 == 1 ? t1 : i % 4 == 2 ? t2 : t3);
}
return list;
}
/**
* 创建一个指定大小并循环使用四个元素填充的ArrayList实例
*
* @param size 列表大小
* @param t 第一个填充元素索引模4等于0时使用
* @param t1 第二个填充元素索引模4等于1时使用
* @param t2 第三个填充元素索引模4等于2时使用
* @param t3 第四个填充元素索引模4等于3时使用
* @param <T> 列表元素的类型
* @return 指定大小且循环填充四个元素的ArrayList实例
*/
public static <T> List<T> newArrayList(int size, T t, T t1, T t2, T t3) {
List<T> list = newArrayList(size);
for (int i = 0; i < size; i++) {
list.add(i % 4 == 0 ? t : i % 4 == 1 ? t1 : i % 4 == 2 ? t2 : t3);
}
return list;
}
/**
* 创建一个空的LinkedList实例
*
* @param <T> 列表元素的类型
* @return 新创建的空LinkedList实例
*/
public static <T> List<T> newLinkedList() {
return new LinkedList<>();
}
/**
* 创建一个空的LinkedList实例
*
* @param <T> 列表元素的类型
* @return 新创建的空LinkedList实例
*/
public static <T> List<T> newLinkedList() {
return new LinkedList<>();
}
/**
* 根据可变参数创建一个包含指定元素的LinkedList实例
*
* @param ts 要添加到列表中的元素可以为0个或多个
* @param <T> 列表元素的类型
* @return 包含指定元素的新LinkedList实例
*/
public static <T> List<T> newLinkedList(T... ts) {
List<T> list = newLinkedList();
list.addAll(Arrays.asList(ts));
return list;
}
/**
* 根据可变参数创建一个包含指定元素的LinkedList实例
*
* @param ts 要添加到列表中的元素可以为0个或多个
* @param <T> 列表元素的类型
* @return 包含指定元素的新LinkedList实例
*/
public static <T> List<T> newLinkedList(T... ts) {
List<T> list = newLinkedList();
list.addAll(Arrays.asList(ts));
return list;
}
/**
* 根据已有列表创建一个新的LinkedList实例
*
* @param list 要复制的列表
* @param <T> 列表元素的类型
* @return 包含原列表所有元素的新LinkedList实例
*/
public static <T> List<T> newLinkedList(List<T> list) {
List<T> newList = newLinkedList();
newList.addAll(list);
return newList;
}
/**
* 根据已有列表创建一个新的LinkedList实例
*
* @param list 要复制的列表
* @param <T> 列表元素的类型
* @return 包含原列表所有元素的新LinkedList实例
*/
public static <T> List<T> newLinkedList(List<T> list) {
List<T> newList = newLinkedList();
newList.addAll(list);
return newList;
}
/**
* 创建一个空的Vector实例
*
* @param <T> 列表元素的类型
* @return 新创建的空Vector实例
*/
public static <T> List<T> newVector() {
return new Vector<>();
}
/**
* 创建一个空的Vector实例
*
* @param <T> 列表元素的类型
* @return 新创建的空Vector实例
*/
public static <T> List<T> newVector() {
return new Vector<>();
}
/**
* 根据可变参数创建一个包含指定元素的Vector实例
*
* @param ts 要添加到列表中的元素可以为0个或多个
* @param <T> 列表元素的类型
* @return 包含指定元素的新Vector实例
*/
public static <T> List<T> newVector(T... ts) {
List<T> list = newVector();
list.addAll(Arrays.asList(ts));
return list;
}
/**
* 根据可变参数创建一个包含指定元素的Vector实例
*
* @param ts 要添加到列表中的元素可以为0个或多个
* @param <T> 列表元素的类型
* @return 包含指定元素的新Vector实例
*/
public static <T> List<T> newVector(T... ts) {
List<T> list = newVector();
list.addAll(Arrays.asList(ts));
return list;
}
/**
* 根据已有列表创建一个新的Vector实例
*
* @param list 要复制的列表
* @param <T> 列表元素的类型
* @return 包含原列表所有元素的新Vector实例
*/
public static <T> List<T> newVector(List<T> list) {
List<T> newList = newVector();
newList.addAll(list);
return newList;
}
/**
* 根据已有列表创建一个新的Vector实例
*
* @param list 要复制的列表
* @param <T> 列表元素的类型
* @return 包含原列表所有元素的新Vector实例
*/
public static <T> List<T> newVector(List<T> list) {
List<T> newList = newVector();
newList.addAll(list);
return newList;
}
/**
* 将指定列表中的每个元素转换为字符串表示形式
*
* @param <T> 列表元素的类型
* @param list 要转换的列表不能为空
* @return 包含原列表各元素字符串表示的新列表保持相同的顺序
*/
public static <T> List<String> toStringList(@NotNull List<T> list) {
// 创建与原列表相同大小的新列表用于存储字符串转换结果
List<String> newList = newArrayList(list.size());
for (T t : list) {
newList.add(t == null ? "null" : t.toString());
}
return newList;
}
/**
* 将指定列表中的每个元素转换为字符串表示形式
*
* @param <T> 列表元素的类型
* @param list 要转换的列表不能为空
* @return 包含原列表各元素字符串表示的新列表保持相同的顺序
*/
public static <T> List<String> toStringList(@NotNull List<T> list) {
// 创建与原列表相同大小的新列表用于存储字符串转换结果
List<String> newList = newArrayList(list.size());
for (T t : list) {
newList.add(t == null ? "null" : t.toString());
}
return newList;
}
/**
* 将指定数组中的每个元素转换为字符串表示形式
*
* @param <T> 数组元素的类型
* @param list 要转换的数组不能为空
* @return 包含原数组各元素字符串表示的新字符串数组
*/
public static <T> String[] toStringList(@NotNull T[] list) {
// 创建新的字符串列表用于存储转换后的结果
List<String> newList = newArrayList(list.length);
for (T t : list) {
newList.add(t == null ? "null" : t.toString());
}
return newList.toArray(new String[0]);
}
/**
* 将指定数组中的每个元素转换为字符串表示形式
*
* @param <T> 数组元素的类型
* @param list 要转换的数组不能为空
* @return 包含原数组各元素字符串表示的新字符串数组
*/
public static <T> String[] toStringList(@NotNull T[] list) {
// 创建新的字符串列表用于存储转换后的结果
List<String> newList = newArrayList(list.length);
for (T t : list) {
newList.add(t == null ? "null" : t.toString());
}
return newList.toArray(new String[0]);
}
}

View File

@ -10,178 +10,178 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public class Maps {
/**
* 创建一个空的HashMap实例
*
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 新创建的空HashMap实例
*/
public static <K, V> Map<K, V> newHashMap() {
return new HashMap<>();
}
/**
* 创建一个空的HashMap实例
*
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 新创建的空HashMap实例
*/
public static <K, V> Map<K, V> newHashMap() {
return new HashMap<>();
}
/**
* 创建一个指定初始容量的空HashMap实例
*
* @param size 初始容量大小
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 指定初始容量的空HashMap实例
*/
public static <K, V> Map<K, V> newHashMap(int size) {
return new HashMap<>(size);
}
/**
* 创建一个指定初始容量的空HashMap实例
*
* @param size 初始容量大小
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 指定初始容量的空HashMap实例
*/
public static <K, V> Map<K, V> newHashMap(int size) {
return new HashMap<>(size);
}
/**
* 根据已有Map创建一个新的HashMap实例
*
* @param map 要复制的Map
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 包含原Map所有元素的新HashMap实例
*/
public static <K, V> Map<K, V> newHashMap(Map<K, V> map) {
Map<K, V> newMap = newHashMap();
newMap.putAll(map);
return newMap;
}
/**
* 根据已有Map创建一个新的HashMap实例
*
* @param map 要复制的Map
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 包含原Map所有元素的新HashMap实例
*/
public static <K, V> Map<K, V> newHashMap(Map<K, V> map) {
Map<K, V> newMap = newHashMap();
newMap.putAll(map);
return newMap;
}
/**
* 创建一个空的LinkedHashMap实例
*
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 新创建的空LinkedHashMap实例
*/
public static <K, V> Map<K, V> newLinkedHashMap() {
return new LinkedHashMap<>();
}
/**
* 创建一个空的LinkedHashMap实例
*
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 新创建的空LinkedHashMap实例
*/
public static <K, V> Map<K, V> newLinkedHashMap() {
return new LinkedHashMap<>();
}
/**
* 创建一个指定初始容量的空LinkedHashMap实例
*
* @param size 初始容量大小
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 指定初始容量的空LinkedHashMap实例
*/
public static <K, V> Map<K, V> newLinkedHashMap(int size) {
return new LinkedHashMap<>(size);
}
/**
* 创建一个指定初始容量的空LinkedHashMap实例
*
* @param size 初始容量大小
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 指定初始容量的空LinkedHashMap实例
*/
public static <K, V> Map<K, V> newLinkedHashMap(int size) {
return new LinkedHashMap<>(size);
}
/**
* 根据已有Map创建一个新的LinkedHashMap实例
*
* @param map 要复制的Map
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 包含原Map所有元素的新LinkedHashMap实例
*/
public static <K, V> Map<K, V> newLinkedHashMap(Map<K, V> map) {
Map<K, V> newMap = newLinkedHashMap();
newMap.putAll(map);
return newMap;
}
/**
* 根据已有Map创建一个新的LinkedHashMap实例
*
* @param map 要复制的Map
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 包含原Map所有元素的新LinkedHashMap实例
*/
public static <K, V> Map<K, V> newLinkedHashMap(Map<K, V> map) {
Map<K, V> newMap = newLinkedHashMap();
newMap.putAll(map);
return newMap;
}
/**
* 创建一个空的TreeMap实例
*
* @param <K> Map键的类型必须实现Comparable接口
* @param <V> Map值的类型
* @return 新创建的空TreeMap实例
*/
public static <K extends Comparable<K>, V> Map<K, V> newTreeMap() {
return new TreeMap<>();
}
/**
* 创建一个空的TreeMap实例
*
* @param <K> Map键的类型必须实现Comparable接口
* @param <V> Map值的类型
* @return 新创建的空TreeMap实例
*/
public static <K extends Comparable<K>, V> Map<K, V> newTreeMap() {
return new TreeMap<>();
}
/**
* 根据已有Map创建一个新的TreeMap实例
*
* @param map 要复制的Map
* @param <K> Map键的类型必须实现Comparable接口
* @param <V> Map值的类型
* @return 包含原Map所有元素的新TreeMap实例
*/
public static <K extends Comparable<K>, V> Map<K, V> newTreeMap(
Map<K, V> map
) {
Map<K, V> newMap = newTreeMap();
newMap.putAll(map);
return newMap;
}
/**
* 根据已有Map创建一个新的TreeMap实例
*
* @param map 要复制的Map
* @param <K> Map键的类型必须实现Comparable接口
* @param <V> Map值的类型
* @return 包含原Map所有元素的新TreeMap实例
*/
public static <K extends Comparable<K>, V> Map<K, V> newTreeMap(
Map<K, V> map
) {
Map<K, V> newMap = newTreeMap();
newMap.putAll(map);
return newMap;
}
/**
* 创建一个空的Hashtable实例
*
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 新创建的空Hashtable实例
*/
public static <K, V> Map<K, V> newHashtable() {
return new Hashtable<>();
}
/**
* 创建一个空的Hashtable实例
*
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 新创建的空Hashtable实例
*/
public static <K, V> Map<K, V> newHashtable() {
return new Hashtable<>();
}
/**
* 创建一个指定初始容量的空Hashtable实例
*
* @param size 初始容量大小
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 指定初始容量的空Hashtable实例
*/
public static <K, V> Map<K, V> newHashtable(int size) {
return new Hashtable<>(size);
}
/**
* 创建一个指定初始容量的空Hashtable实例
*
* @param size 初始容量大小
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 指定初始容量的空Hashtable实例
*/
public static <K, V> Map<K, V> newHashtable(int size) {
return new Hashtable<>(size);
}
/**
* 根据已有Map创建一个新的Hashtable实例
*
* @param map 要复制的Map
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 包含原Map所有元素的新Hashtable实例
*/
public static <K, V> Map<K, V> newHashtable(Map<K, V> map) {
Map<K, V> newMap = newHashtable();
newMap.putAll(map);
return newMap;
}
/**
* 根据已有Map创建一个新的Hashtable实例
*
* @param map 要复制的Map
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 包含原Map所有元素的新Hashtable实例
*/
public static <K, V> Map<K, V> newHashtable(Map<K, V> map) {
Map<K, V> newMap = newHashtable();
newMap.putAll(map);
return newMap;
}
/**
* 创建一个空的ConcurrentHashMap实例
*
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 新创建的空ConcurrentHashMap实例
*/
public static <K, V> Map<K, V> newConcurrentHashMap() {
return new ConcurrentHashMap<>();
}
/**
* 创建一个空的ConcurrentHashMap实例
*
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 新创建的空ConcurrentHashMap实例
*/
public static <K, V> Map<K, V> newConcurrentHashMap() {
return new ConcurrentHashMap<>();
}
/**
* 创建一个指定初始容量的空ConcurrentHashMap实例
*
* @param size 初始容量大小
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 指定初始容量的空ConcurrentHashMap实例
*/
public static <K, V> Map<K, V> newConcurrentHashMap(int size) {
return new ConcurrentHashMap<>(size);
}
/**
* 创建一个指定初始容量的空ConcurrentHashMap实例
*
* @param size 初始容量大小
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 指定初始容量的空ConcurrentHashMap实例
*/
public static <K, V> Map<K, V> newConcurrentHashMap(int size) {
return new ConcurrentHashMap<>(size);
}
/**
* 根据已有Map创建一个新的ConcurrentHashMap实例
*
* @param map 要复制的Map
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 包含原Map所有元素的新ConcurrentHashMap实例
*/
public static <K, V> Map<K, V> newConcurrentHashMap(Map<K, V> map) {
Map<K, V> newMap = newConcurrentHashMap();
newMap.putAll(map);
return newMap;
}
/**
* 根据已有Map创建一个新的ConcurrentHashMap实例
*
* @param map 要复制的Map
* @param <K> Map键的类型
* @param <V> Map值的类型
* @return 包含原Map所有元素的新ConcurrentHashMap实例
*/
public static <K, V> Map<K, V> newConcurrentHashMap(Map<K, V> map) {
Map<K, V> newMap = newConcurrentHashMap();
newMap.putAll(map);
return newMap;
}
}

View File

@ -9,137 +9,137 @@ import java.util.*;
*/
public class Sets {
/**
* 创建一个空的HashSet实例
*
* @param <T> 集合元素的类型
* @return 新创建的空HashSet实例
*/
public static <T> Set<T> newHashSet() {
return new HashSet<>();
}
/**
* 创建一个空的HashSet实例
*
* @param <T> 集合元素的类型
* @return 新创建的空HashSet实例
*/
public static <T> Set<T> newHashSet() {
return new HashSet<>();
}
/**
* 根据可变参数创建一个包含指定元素的HashSet实例
*
* @param ts 要添加到集合中的元素可以为0个或多个
* @param <T> 集合元素的类型
* @return 包含指定元素的新HashSet实例
*/
public static <T> Set<T> newHashSet(T... ts) {
Set<T> set = newHashSet();
set.addAll(Arrays.asList(ts));
return set;
}
/**
* 根据可变参数创建一个包含指定元素的HashSet实例
*
* @param ts 要添加到集合中的元素可以为0个或多个
* @param <T> 集合元素的类型
* @return 包含指定元素的新HashSet实例
*/
public static <T> Set<T> newHashSet(T... ts) {
Set<T> set = newHashSet();
set.addAll(Arrays.asList(ts));
return set;
}
/**
* 根据已有集合创建一个新的HashSet实例
*
* @param set 要复制的集合
* @param <T> 集合元素的类型
* @return 包含原集合所有元素的新HashSet实例
*/
public static <T> Set<T> newHashSet(Set<T> set) {
Set<T> newSet = newHashSet();
newSet.addAll(set);
return newSet;
}
/**
* 根据已有集合创建一个新的HashSet实例
*
* @param set 要复制的集合
* @param <T> 集合元素的类型
* @return 包含原集合所有元素的新HashSet实例
*/
public static <T> Set<T> newHashSet(Set<T> set) {
Set<T> newSet = newHashSet();
newSet.addAll(set);
return newSet;
}
/**
* 根据可迭代对象创建一个HashSet实例
*
* @param iterable 可迭代对象
* @param <T> 集合元素的类型
* @return 包含可迭代对象中所有元素的新HashSet实例
*/
public static <T> Set<T> newHashSet(Iterable<T> iterable) {
Set<T> set = newHashSet();
for (T t : iterable) {
set.add(t);
}
return set;
}
/**
* 根据可迭代对象创建一个HashSet实例
*
* @param iterable 可迭代对象
* @param <T> 集合元素的类型
* @return 包含可迭代对象中所有元素的新HashSet实例
*/
public static <T> Set<T> newHashSet(Iterable<T> iterable) {
Set<T> set = newHashSet();
for (T t : iterable) {
set.add(t);
}
return set;
}
/**
* 创建一个指定初始容量的空HashSet实例
*
* @param size 初始容量大小
* @param <T> 集合元素的类型
* @return 指定初始容量的空HashSet实例
*/
public static <T> Set<T> newHashSet(int size) {
return new HashSet<>(size);
}
/**
* 创建一个指定初始容量的空HashSet实例
*
* @param size 初始容量大小
* @param <T> 集合元素的类型
* @return 指定初始容量的空HashSet实例
*/
public static <T> Set<T> newHashSet(int size) {
return new HashSet<>(size);
}
/**
* 创建一个空的LinkedHashSet实例
*
* @param <T> 集合元素的类型
* @return 新创建的空LinkedHashSet实例
*/
public static <T> Set<T> newLinkedHashSet() {
return new LinkedHashSet<>();
}
/**
* 创建一个空的LinkedHashSet实例
*
* @param <T> 集合元素的类型
* @return 新创建的空LinkedHashSet实例
*/
public static <T> Set<T> newLinkedHashSet() {
return new LinkedHashSet<>();
}
/**
* 根据可变参数创建一个包含指定元素的LinkedHashSet实例
*
* @param ts 要添加到集合中的元素可以为0个或多个
* @param <T> 集合元素的类型
* @return 包含指定元素的新LinkedHashSet实例
*/
public static <T> Set<T> newLinkedHashSet(T... ts) {
Set<T> set = newLinkedHashSet();
set.addAll(Arrays.asList(ts));
return set;
}
/**
* 根据可变参数创建一个包含指定元素的LinkedHashSet实例
*
* @param ts 要添加到集合中的元素可以为0个或多个
* @param <T> 集合元素的类型
* @return 包含指定元素的新LinkedHashSet实例
*/
public static <T> Set<T> newLinkedHashSet(T... ts) {
Set<T> set = newLinkedHashSet();
set.addAll(Arrays.asList(ts));
return set;
}
/**
* 根据已有集合创建一个新的LinkedHashSet实例
*
* @param set 要复制的集合
* @param <T> 集合元素的类型
* @return 包含原集合所有元素的新LinkedHashSet实例
*/
public static <T> Set<T> newLinkedHashSet(Set<T> set) {
Set<T> newSet = newLinkedHashSet();
newSet.addAll(set);
return newSet;
}
/**
* 根据已有集合创建一个新的LinkedHashSet实例
*
* @param set 要复制的集合
* @param <T> 集合元素的类型
* @return 包含原集合所有元素的新LinkedHashSet实例
*/
public static <T> Set<T> newLinkedHashSet(Set<T> set) {
Set<T> newSet = newLinkedHashSet();
newSet.addAll(set);
return newSet;
}
/**
* 创建一个空的TreeSet实例
*
* @param <T> 集合元素的类型必须实现Comparable接口
* @return 新创建的空TreeSet实例
*/
public static <T extends Comparable<T>> Set<T> newTreeSet() {
return new TreeSet<>();
}
/**
* 创建一个空的TreeSet实例
*
* @param <T> 集合元素的类型必须实现Comparable接口
* @return 新创建的空TreeSet实例
*/
public static <T extends Comparable<T>> Set<T> newTreeSet() {
return new TreeSet<>();
}
/**
* 根据可变参数创建一个包含指定元素的TreeSet实例
*
* @param ts 要添加到集合中的元素可以为0个或多个
* @param <T> 集合元素的类型必须实现Comparable接口
* @return 包含指定元素的新TreeSet实例
*/
public static <T extends Comparable<T>> Set<T> newTreeSet(T... ts) {
Set<T> set = newTreeSet();
set.addAll(Arrays.asList(ts));
return set;
}
/**
* 根据可变参数创建一个包含指定元素的TreeSet实例
*
* @param ts 要添加到集合中的元素可以为0个或多个
* @param <T> 集合元素的类型必须实现Comparable接口
* @return 包含指定元素的新TreeSet实例
*/
public static <T extends Comparable<T>> Set<T> newTreeSet(T... ts) {
Set<T> set = newTreeSet();
set.addAll(Arrays.asList(ts));
return set;
}
/**
* 根据已有集合创建一个新的TreeSet实例
*
* @param set 要复制的集合
* @param <T> 集合元素的类型必须实现Comparable接口
* @return 包含原集合所有元素的新TreeSet实例
*/
public static <T extends Comparable<T>> Set<T> newTreeSet(Set<T> set) {
Set<T> newSet = newTreeSet();
newSet.addAll(set);
return newSet;
}
/**
* 根据已有集合创建一个新的TreeSet实例
*
* @param set 要复制的集合
* @param <T> 集合元素的类型必须实现Comparable接口
* @return 包含原集合所有元素的新TreeSet实例
*/
public static <T extends Comparable<T>> Set<T> newTreeSet(Set<T> set) {
Set<T> newSet = newTreeSet();
newSet.addAll(set);
return newSet;
}
}

View File

@ -12,73 +12,73 @@ import java.util.concurrent.atomic.AtomicReference;
*/
public class IsChanged<T> {
/**
* 使用 AtomicReference 来保证对数据的原子操作
*/
private final AtomicReference<T> atomicReferenceData;
/**
* 使用 AtomicReference 来保证对数据的原子操作
*/
private final AtomicReference<T> atomicReferenceData;
/**
* 默认构造函数初始化数据为 null
*/
public IsChanged() {
this(null);
}
/**
* 默认构造函数初始化数据为 null
*/
public IsChanged() {
this(null);
}
/**
* 带参数的构造函数使用指定的初始值初始化
*
* @param data 初始数据值
*/
public IsChanged(T data) {
atomicReferenceData = new AtomicReference<>(data);
}
/**
* 带参数的构造函数使用指定的初始值初始化
*
* @param data 初始数据值
*/
public IsChanged(T data) {
atomicReferenceData = new AtomicReference<>(data);
}
/**
* 设置新的数据值不检查是否发生变化
*
* @param data 要设置的新数据值
*/
public void set(T data) {
atomicReferenceData.set(data);
}
/**
* 设置新的数据值不检查是否发生变化
*
* @param data 要设置的新数据值
*/
public void set(T data) {
atomicReferenceData.set(data);
}
/**
* 获取当前数据值
*
* @return 当前数据值
*/
public T get() {
return atomicReferenceData.get();
}
/**
* 获取当前数据值
*
* @return 当前数据值
*/
public T get() {
return atomicReferenceData.get();
}
/**
* 设置新的数据值并返回旧的数据值
*
* @param data 要设置的新数据值
* @return 设置前的旧数据值
*/
public T setAndGet(T data) {
return atomicReferenceData.getAndSet(data);
}
/**
* 设置新的数据值并返回旧的数据值
*
* @param data 要设置的新数据值
* @return 设置前的旧数据值
*/
public T setAndGet(T data) {
return atomicReferenceData.getAndSet(data);
}
/**
* 设置新的数据值如果新值与当前值不同则更新并返回 true否则返回 false
* 使用 CAS(Compare-And-Swap) 操作确保线程安全
*
* @param data 要设置的新数据值
* @return 如果值发生变化返回 true否则返回 false
*/
public boolean setAndChanged(T data) {
T currentData;
do {
currentData = get();
// 如果新值与当前值相等则认为没有变化直接返回 false
if (Objects.equals(data, currentData)) {
return false;
}
// 使用 CAS 操作尝试更新值如果失败则重试
} while (!atomicReferenceData.compareAndSet(currentData, data));
// 成功更新值返回 true 表示发生了变化
return true;
}
/**
* 设置新的数据值如果新值与当前值不同则更新并返回 true否则返回 false
* 使用 CAS(Compare-And-Swap) 操作确保线程安全
*
* @param data 要设置的新数据值
* @return 如果值发生变化返回 true否则返回 false
*/
public boolean setAndChanged(T data) {
T currentData;
do {
currentData = get();
// 如果新值与当前值相等则认为没有变化直接返回 false
if (Objects.equals(data, currentData)) {
return false;
}
// 使用 CAS 操作尝试更新值如果失败则重试
} while (!atomicReferenceData.compareAndSet(currentData, data));
// 成功更新值返回 true 表示发生了变化
return true;
}
}

View File

@ -10,74 +10,74 @@ package com.mingliqiye.utils.data;
*/
public class ThreadLocalDataHolder<T> {
private final ThreadLocal<T> threadLocal;
private final ThreadLocal<T> threadLocal;
/**
* 构造函数初始化 ThreadLocal 实例
*/
public ThreadLocalDataHolder() {
this.threadLocal = new ThreadLocal<>();
}
/**
* 构造函数初始化 ThreadLocal 实例
*/
public ThreadLocalDataHolder() {
this.threadLocal = new ThreadLocal<>();
}
/**
* 获取当前线程存储的值
*
* @return 当前线程存储的值如果没有则返回null
*/
public T get() {
return threadLocal.get();
}
/**
* 获取当前线程存储的值
*
* @return 当前线程存储的值如果没有则返回null
*/
public T get() {
return threadLocal.get();
}
/**
* 设置当前线程的值
*
* @param value 要存储的值
*/
public void set(T value) {
threadLocal.set(value);
}
/**
* 设置当前线程的值
*
* @param value 要存储的值
*/
public void set(T value) {
threadLocal.set(value);
}
/**
* 移除当前线程存储的值
* <p>
* 防止内存泄漏使用完毕后应调用此方法清理资源
*/
public void remove() {
threadLocal.remove();
}
/**
* 移除当前线程存储的值
* <p>
* 防止内存泄漏使用完毕后应调用此方法清理资源
*/
public void remove() {
threadLocal.remove();
}
/**
* 获取当前线程存储的值如果不存在则返回默认值
*
* @param defaultValue 默认值
* @return 当前线程存储的值或默认值
*/
public T getOrDefault(T defaultValue) {
T value = threadLocal.get();
return value != null ? value : defaultValue;
}
/**
* 获取当前线程存储的值如果不存在则返回默认值
*
* @param defaultValue 默认值
* @return 当前线程存储的值或默认值
*/
public T getOrDefault(T defaultValue) {
T value = threadLocal.get();
return value != null ? value : defaultValue;
}
/**
* 安全获取值避免NPE
* <p>
* 在某些异常情况下防止抛出异常直接返回 null
*
* @return 值或null
*/
public T safeGet() {
try {
return threadLocal.get();
} catch (Exception e) {
return null;
}
}
/**
* 安全获取值避免NPE
* <p>
* 在某些异常情况下防止抛出异常直接返回 null
*
* @return 值或null
*/
public T safeGet() {
try {
return threadLocal.get();
} catch (Exception e) {
return null;
}
}
/**
* 检查当前线程是否有值
*
* @return 是否有值
*/
public boolean isPresent() {
return threadLocal.get() != null;
}
/**
* 检查当前线程是否有值
*
* @return 是否有值
*/
public boolean isPresent() {
return threadLocal.get() != null;
}
}

View File

@ -1,9 +1,6 @@
package com.mingliqiye.utils.file;
import com.mingliqiye.utils.string.StringUtil;
import lombok.Getter;
import lombok.Setter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -15,6 +12,8 @@ import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
/**
* 文件工具类提供常用的文件操作方法
@ -23,322 +22,322 @@ import java.util.List;
*/
public class FileUtil {
/**
* 默认字符集
*/
@Getter
@Setter
private static Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
/**
* 默认字符集
*/
@Getter
@Setter
private static Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
/**
* 读取文件内容为字符串
*
* @param filePath 文件路径
* @return 文件内容字符串
* @throws IOException 读取文件时发生错误
*/
public static String readFileToString(String filePath) throws IOException {
return readFileToString(filePath, DEFAULT_CHARSET);
}
/**
* 读取文件内容为字符串
*
* @param filePath 文件路径
* @return 文件内容字符串
* @throws IOException 读取文件时发生错误
*/
public static String readFileToString(String filePath) throws IOException {
return readFileToString(filePath, DEFAULT_CHARSET);
}
/**
* 读取文件内容为字符串
*
* @param filePath 文件路径
* @param charset 字符集
* @return 文件内容字符串
* @throws IOException 读取文件时发生错误
*/
public static String readFileToString(String filePath, Charset charset)
throws IOException {
Path path = Paths.get(filePath);
byte[] bytes = Files.readAllBytes(path);
return new String(bytes, charset);
}
/**
* 读取文件内容为字符串
*
* @param filePath 文件路径
* @param charset 字符集
* @return 文件内容字符串
* @throws IOException 读取文件时发生错误
*/
public static String readFileToString(String filePath, Charset charset)
throws IOException {
Path path = Paths.get(filePath);
byte[] bytes = Files.readAllBytes(path);
return new String(bytes, charset);
}
/**
* 将字符串写入文件
*
* @param filePath 文件路径
* @param content 要写入的内容
* @throws IOException 写入文件时发生错误
*/
public static void writeStringToFile(String filePath, String content)
throws IOException {
writeStringToFile(filePath, content, DEFAULT_CHARSET);
}
/**
* 将字符串写入文件
*
* @param filePath 文件路径
* @param content 要写入的内容
* @throws IOException 写入文件时发生错误
*/
public static void writeStringToFile(String filePath, String content)
throws IOException {
writeStringToFile(filePath, content, DEFAULT_CHARSET);
}
/**
* 将字符串写入文件
*
* @param filePath 文件路径
* @param content 要写入的内容
* @param charset 字符集
* @throws IOException 写入文件时发生错误
*/
public static void writeStringToFile(
String filePath,
String content,
Charset charset
) throws IOException {
Path path = Paths.get(filePath);
Files.createDirectories(path.getParent());
Files.write(path, content.getBytes(charset));
}
/**
* 将字符串写入文件
*
* @param filePath 文件路径
* @param content 要写入的内容
* @param charset 字符集
* @throws IOException 写入文件时发生错误
*/
public static void writeStringToFile(
String filePath,
String content,
Charset charset
) throws IOException {
Path path = Paths.get(filePath);
Files.createDirectories(path.getParent());
Files.write(path, content.getBytes(charset));
}
/**
* 读取文件内容为字符串列表按行分割
*
* @param filePath 文件路径
* @return 文件内容按行分割的字符串列表
* @throws IOException 读取文件时发生错误
*/
public static List<String> readLines(String filePath) throws IOException {
return readLines(filePath, DEFAULT_CHARSET);
}
/**
* 读取文件内容为字符串列表按行分割
*
* @param filePath 文件路径
* @return 文件内容按行分割的字符串列表
* @throws IOException 读取文件时发生错误
*/
public static List<String> readLines(String filePath) throws IOException {
return readLines(filePath, DEFAULT_CHARSET);
}
/**
* 读取文件内容为字符串列表按行分割
*
* @param filePath 文件路径
* @param charset 字符集
* @return 文件内容按行分割的字符串列表
* @throws IOException 读取文件时发生错误
*/
public static List<String> readLines(String filePath, Charset charset)
throws IOException {
Path path = Paths.get(filePath);
return Files.readAllLines(path, charset);
}
/**
* 读取文件内容为字符串列表按行分割
*
* @param filePath 文件路径
* @param charset 字符集
* @return 文件内容按行分割的字符串列表
* @throws IOException 读取文件时发生错误
*/
public static List<String> readLines(String filePath, Charset charset)
throws IOException {
Path path = Paths.get(filePath);
return Files.readAllLines(path, charset);
}
/**
* 将字符串列表写入文件每行一个元素
*
* @param filePath 文件路径
* @param lines 要写入的行内容列表
* @throws IOException 写入文件时发生错误
*/
public static void writeLines(String filePath, List<String> lines)
throws IOException {
writeLines(filePath, lines, DEFAULT_CHARSET);
}
/**
* 将字符串列表写入文件每行一个元素
*
* @param filePath 文件路径
* @param lines 要写入的行内容列表
* @throws IOException 写入文件时发生错误
*/
public static void writeLines(String filePath, List<String> lines)
throws IOException {
writeLines(filePath, lines, DEFAULT_CHARSET);
}
/**
* 将字符串列表写入文件每行一个元素
*
* @param filePath 文件路径
* @param lines 要写入的行内容列表
* @param charset 字符集
* @throws IOException 写入文件时发生错误
*/
public static void writeLines(
String filePath,
List<String> lines,
Charset charset
) throws IOException {
Path path = Paths.get(filePath);
Files.createDirectories(path.getParent());
Files.write(path, lines, charset);
}
/**
* 将字符串列表写入文件每行一个元素
*
* @param filePath 文件路径
* @param lines 要写入的行内容列表
* @param charset 字符集
* @throws IOException 写入文件时发生错误
*/
public static void writeLines(
String filePath,
List<String> lines,
Charset charset
) throws IOException {
Path path = Paths.get(filePath);
Files.createDirectories(path.getParent());
Files.write(path, lines, charset);
}
/**
* 复制文件
*
* @param sourcePath 源文件路径
* @param targetPath 目标文件路径
* @throws IOException 复制文件时发生错误
*/
public static void copyFile(String sourcePath, String targetPath)
throws IOException {
Path source = Paths.get(sourcePath);
Path target = Paths.get(targetPath);
Files.createDirectories(target.getParent());
Files.copy(source, target);
}
/**
* 复制文件
*
* @param sourcePath 源文件路径
* @param targetPath 目标文件路径
* @throws IOException 复制文件时发生错误
*/
public static void copyFile(String sourcePath, String targetPath)
throws IOException {
Path source = Paths.get(sourcePath);
Path target = Paths.get(targetPath);
Files.createDirectories(target.getParent());
Files.copy(source, target);
}
/**
* 删除文件
*
* @param filePath 文件路径
* @return 如果文件删除成功返回true否则返回false
*/
public static boolean deleteFile(String filePath) {
try {
Path path = Paths.get(filePath);
return Files.deleteIfExists(path);
} catch (IOException e) {
return false;
}
}
/**
* 删除文件
*
* @param filePath 文件路径
* @return 如果文件删除成功返回true否则返回false
*/
public static boolean deleteFile(String filePath) {
try {
Path path = Paths.get(filePath);
return Files.deleteIfExists(path);
} catch (IOException e) {
return false;
}
}
/**
* 检查文件是否存在
*
* @param filePath 文件路径
* @return 如果文件存在返回true否则返回false
*/
public static boolean exists(String filePath) {
Path path = Paths.get(filePath);
return Files.exists(path);
}
/**
* 检查文件是否存在
*
* @param filePath 文件路径
* @return 如果文件存在返回true否则返回false
*/
public static boolean exists(String filePath) {
Path path = Paths.get(filePath);
return Files.exists(path);
}
/**
* 获取文件大小
*
* @param filePath 文件路径
* @return 文件大小字节如果文件不存在返回-1
*/
public static long getFileSize(String filePath) {
try {
Path path = Paths.get(filePath);
return Files.size(path);
} catch (IOException e) {
return -1;
}
}
/**
* 获取文件大小
*
* @param filePath 文件路径
* @return 文件大小字节如果文件不存在返回-1
*/
public static long getFileSize(String filePath) {
try {
Path path = Paths.get(filePath);
return Files.size(path);
} catch (IOException e) {
return -1;
}
}
/**
* 创建目录
*
* @param dirPath 目录路径
* @return 如果目录创建成功返回true否则返回false
*/
public static boolean createDirectory(String dirPath) {
try {
Path path = Paths.get(dirPath);
Files.createDirectories(path);
return true;
} catch (IOException e) {
return false;
}
}
/**
* 创建目录
*
* @param dirPath 目录路径
* @return 如果目录创建成功返回true否则返回false
*/
public static boolean createDirectory(String dirPath) {
try {
Path path = Paths.get(dirPath);
Files.createDirectories(path);
return true;
} catch (IOException e) {
return false;
}
}
/**
* 获取文件扩展名
*
* @param fileName 文件名
* @return 文件扩展名不包含点号如果无扩展名返回空字符串
*/
public static String getFileExtension(String fileName) {
if (StringUtil.isEmpty(fileName)) {
return "";
}
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex == -1 || lastDotIndex == fileName.length() - 1) {
return "";
}
return fileName.substring(lastDotIndex + 1);
}
/**
* 获取文件扩展名
*
* @param fileName 文件名
* @return 文件扩展名不包含点号如果无扩展名返回空字符串
*/
public static String getFileExtension(String fileName) {
if (StringUtil.isEmpty(fileName)) {
return "";
}
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex == -1 || lastDotIndex == fileName.length() - 1) {
return "";
}
return fileName.substring(lastDotIndex + 1);
}
/**
* 获取不带扩展名的文件名
*
* @param fileName 文件名
* @return 不带扩展名的文件名
*/
public static String getFileNameWithoutExtension(String fileName) {
if (StringUtil.isEmpty(fileName)) {
return "";
}
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex == -1) {
return fileName;
}
return fileName.substring(0, lastDotIndex);
}
/**
* 获取不带扩展名的文件名
*
* @param fileName 文件名
* @return 不带扩展名的文件名
*/
public static String getFileNameWithoutExtension(String fileName) {
if (StringUtil.isEmpty(fileName)) {
return "";
}
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex == -1) {
return fileName;
}
return fileName.substring(0, lastDotIndex);
}
/**
* 读取文件内容为字节数组
*
* @param filePath 文件路径
* @return 文件内容的字节数组
* @throws IOException 读取文件时发生错误
*/
public static byte[] readFileToByteArray(String filePath)
throws IOException {
Path path = Paths.get(filePath);
return Files.readAllBytes(path);
}
/**
* 读取文件内容为字节数组
*
* @param filePath 文件路径
* @return 文件内容的字节数组
* @throws IOException 读取文件时发生错误
*/
public static byte[] readFileToByteArray(String filePath)
throws IOException {
Path path = Paths.get(filePath);
return Files.readAllBytes(path);
}
/**
* 将字节数组写入文件
*
* @param filePath 文件路径
* @param data 要写入的字节数据
* @throws IOException 写入文件时发生错误
*/
public static void writeByteArrayToFile(String filePath, byte[] data)
throws IOException {
Path path = Paths.get(filePath);
Files.createDirectories(path.getParent());
Files.write(path, data);
}
/**
* 将字节数组写入文件
*
* @param filePath 文件路径
* @param data 要写入的字节数据
* @throws IOException 写入文件时发生错误
*/
public static void writeByteArrayToFile(String filePath, byte[] data)
throws IOException {
Path path = Paths.get(filePath);
Files.createDirectories(path.getParent());
Files.write(path, data);
}
/**
* 将字节数组追加到文件末尾
*
* @param filePath 文件路径
* @param data 要追加的字节数据
* @throws IOException 追加数据时发生错误
*/
public static void appendByteArrayToFile(String filePath, byte[] data)
throws IOException {
Path path = Paths.get(filePath);
Files.createDirectories(path.getParent());
Files.write(
path,
data,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND
);
}
/**
* 将字节数组追加到文件末尾
*
* @param filePath 文件路径
* @param data 要追加的字节数据
* @throws IOException 追加数据时发生错误
*/
public static void appendByteArrayToFile(String filePath, byte[] data)
throws IOException {
Path path = Paths.get(filePath);
Files.createDirectories(path.getParent());
Files.write(
path,
data,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND
);
}
/**
* 分块读取大文件为字节数组列表
*
* @param filePath 文件路径
* @param chunkSize 每块大小字节
* @return 文件内容按指定大小分割的字节数组列表
* @throws IOException 读取文件时发生错误
*/
public static List<byte[]> readFileToByteArrayChunks(
String filePath,
int chunkSize
) throws IOException {
List<byte[]> chunks = new ArrayList<>();
Path path = Paths.get(filePath);
/**
* 分块读取大文件为字节数组列表
*
* @param filePath 文件路径
* @param chunkSize 每块大小字节
* @return 文件内容按指定大小分割的字节数组列表
* @throws IOException 读取文件时发生错误
*/
public static List<byte[]> readFileToByteArrayChunks(
String filePath,
int chunkSize
) throws IOException {
List<byte[]> chunks = new ArrayList<>();
Path path = Paths.get(filePath);
try (InputStream inputStream = Files.newInputStream(path)) {
byte[] buffer = new byte[chunkSize];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
byte[] chunk = new byte[bytesRead];
System.arraycopy(buffer, 0, chunk, 0, bytesRead);
chunks.add(chunk);
}
}
try (InputStream inputStream = Files.newInputStream(path)) {
byte[] buffer = new byte[chunkSize];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
byte[] chunk = new byte[bytesRead];
System.arraycopy(buffer, 0, chunk, 0, bytesRead);
chunks.add(chunk);
}
}
return chunks;
}
return chunks;
}
/**
* 将字节数组列表写入文件
*
* @param filePath 文件路径
* @param chunks 字节数组列表
* @throws IOException 写入文件时发生错误
*/
public static void writeByteArrayChunksToFile(
String filePath,
List<byte[]> chunks
) throws IOException {
Path path = Paths.get(filePath);
Files.createDirectories(path.getParent());
/**
* 将字节数组列表写入文件
*
* @param filePath 文件路径
* @param chunks 字节数组列表
* @throws IOException 写入文件时发生错误
*/
public static void writeByteArrayChunksToFile(
String filePath,
List<byte[]> chunks
) throws IOException {
Path path = Paths.get(filePath);
Files.createDirectories(path.getParent());
try (OutputStream outputStream = Files.newOutputStream(path)) {
for (byte[] chunk : chunks) {
outputStream.write(chunk);
}
}
}
try (OutputStream outputStream = Files.newOutputStream(path)) {
for (byte[] chunk : chunks) {
outputStream.write(chunk);
}
}
}
}

View File

@ -9,63 +9,63 @@ import java.util.concurrent.*;
*/
public class Debouncer {
private final ScheduledExecutorService scheduler =
Executors.newSingleThreadScheduledExecutor();
private final ConcurrentHashMap<Object, Future<?>> delayedMap =
new ConcurrentHashMap<>();
private final long delay;
private final ScheduledExecutorService scheduler =
Executors.newSingleThreadScheduledExecutor();
private final ConcurrentHashMap<Object, Future<?>> delayedMap =
new ConcurrentHashMap<>();
private final long delay;
/**
* 构造函数创建一个防抖器实例
*
* @param delay 延迟时间
* @param unit 时间单位
*/
public Debouncer(long delay, TimeUnit unit) {
this.delay = unit.toMillis(delay);
}
/**
* 构造函数创建一个防抖器实例
*
* @param delay 延迟时间
* @param unit 时间单位
*/
public Debouncer(long delay, TimeUnit unit) {
this.delay = unit.toMillis(delay);
}
/**
* 执行防抖操作如果在指定延迟时间内再次调用相同key的任务则取消之前的任务并重新计时
*
* @param key 任务的唯一标识符用于区分不同任务
* @param task 要执行的任务
*/
public void debounce(final Object key, final Runnable task) {
// 提交新任务并获取之前可能存在的任务
final Future<?> prev = delayedMap.put(
key,
scheduler.schedule(
() -> {
try {
task.run();
} finally {
// 任务执行完成后从映射中移除
delayedMap.remove(key);
}
},
delay,
TimeUnit.MILLISECONDS
)
);
/**
* 执行防抖操作如果在指定延迟时间内再次调用相同key的任务则取消之前的任务并重新计时
*
* @param key 任务的唯一标识符用于区分不同任务
* @param task 要执行的任务
*/
public void debounce(final Object key, final Runnable task) {
// 提交新任务并获取之前可能存在的任务
final Future<?> prev = delayedMap.put(
key,
scheduler.schedule(
() -> {
try {
task.run();
} finally {
// 任务执行完成后从映射中移除
delayedMap.remove(key);
}
},
delay,
TimeUnit.MILLISECONDS
)
);
// 如果之前存在任务则取消它
if (prev != null) {
prev.cancel(true);
}
}
// 如果之前存在任务则取消它
if (prev != null) {
prev.cancel(true);
}
}
/**
* 关闭防抖器取消所有待执行的任务并关闭调度器
*/
public void shutdown() {
// 先取消所有延迟任务
for (Future<?> future : delayedMap.values()) {
future.cancel(true);
}
delayedMap.clear();
/**
* 关闭防抖器取消所有待执行的任务并关闭调度器
*/
public void shutdown() {
// 先取消所有延迟任务
for (Future<?> future : delayedMap.values()) {
future.cancel(true);
}
delayedMap.clear();
// 再关闭调度器
scheduler.shutdownNow();
}
// 再关闭调度器
scheduler.shutdownNow();
}
}

View File

@ -1,14 +1,13 @@
package com.mingliqiye.utils.hash;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.mindrot.jbcrypt.BCrypt;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.mindrot.jbcrypt.BCrypt;
/**
* 提供常用的哈希计算工具方法包括文件哈希值计算BCrypt 加密等
@ -17,77 +16,77 @@ import java.security.Security;
*/
public class HashUtils {
static {
Security.addProvider(new BouncyCastleProvider());
}
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* 计算指定文件的哈希值
*
* @param file 要计算哈希值的文件对象
* @param algorithm 使用的哈希算法名称 SHA-256MD5
* @return 文件的十六进制格式哈希值字符串
* @throws IOException 当文件不存在或读取过程中发生 I/O 错误时抛出
* @throws NoSuchAlgorithmException 当指定的哈希算法不可用时抛出
*/
public static String calculateFileHash(File file, String algorithm)
throws IOException, NoSuchAlgorithmException {
// 检查文件是否存在
if (!file.exists()) {
throw new IOException("File not found: " + file.getAbsolutePath());
}
/**
* 计算指定文件的哈希值
*
* @param file 要计算哈希值的文件对象
* @param algorithm 使用的哈希算法名称 SHA-256MD5
* @return 文件的十六进制格式哈希值字符串
* @throws IOException 当文件不存在或读取过程中发生 I/O 错误时抛出
* @throws NoSuchAlgorithmException 当指定的哈希算法不可用时抛出
*/
public static String calculateFileHash(File file, String algorithm)
throws IOException, NoSuchAlgorithmException {
// 检查文件是否存在
if (!file.exists()) {
throw new IOException("File not found: " + file.getAbsolutePath());
}
MessageDigest digest = MessageDigest.getInstance(algorithm);
MessageDigest digest = MessageDigest.getInstance(algorithm);
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[8192]; // 8KB 缓冲区
int bytesRead;
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[8192]; // 8KB 缓冲区
int bytesRead;
// 分块读取文件内容并更新摘要
while ((bytesRead = fis.read(buffer)) != -1) {
digest.update(buffer, 0, bytesRead);
}
}
// 分块读取文件内容并更新摘要
while ((bytesRead = fis.read(buffer)) != -1) {
digest.update(buffer, 0, bytesRead);
}
}
return bytesToHex(digest.digest());
}
return bytesToHex(digest.digest());
}
/**
* 将字节数组转换为十六进制字符串表示
*
* @param bytes 输入的字节数组
* @return 对应的十六进制字符串
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder(2 * bytes.length);
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
/**
* 将字节数组转换为十六进制字符串表示
*
* @param bytes 输入的字节数组
* @return 对应的十六进制字符串
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder(2 * bytes.length);
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
/**
* 使用 BCrypt 算法对字符串进行加密
*
* @param string 需要加密的明文字符串
* @return 加密后的 BCrypt 哈希字符串
*/
public static String bcrypt(String string) {
return BCrypt.hashpw(string, BCrypt.gensalt());
}
/**
* 使用 BCrypt 算法对字符串进行加密
*
* @param string 需要加密的明文字符串
* @return 加密后的 BCrypt 哈希字符串
*/
public static String bcrypt(String string) {
return BCrypt.hashpw(string, BCrypt.gensalt());
}
/**
* 验证给定字符串与 BCrypt 哈希是否匹配
*
* @param string 明文字符串
* @param bcrypted 已经使用 BCrypt 加密的哈希字符串
* @return 如果匹配返回 true否则返回 false
*/
public static boolean checkBcrypt(String string, String bcrypted) {
return BCrypt.checkpw(string, bcrypted);
}
/**
* 验证给定字符串与 BCrypt 哈希是否匹配
*
* @param string 明文字符串
* @param bcrypted 已经使用 BCrypt 加密的哈希字符串
* @return 如果匹配返回 true否则返回 false
*/
public static boolean checkBcrypt(String string, String bcrypted) {
return BCrypt.checkpw(string, bcrypted);
}
}

View File

@ -11,37 +11,37 @@ import lombok.ToString;
@Getter
public class Response<T> {
private final String time = DateTime.now().format(
Formatter.STANDARD_DATETIME_MILLISECOUND7
);
private String message;
private T data;
private int statusCode;
private final String time = DateTime.now().format(
Formatter.STANDARD_DATETIME_MILLISECOUND7
);
private String message;
private T data;
private int statusCode;
public Response(String message, T data, int statusCode) {
this.message = message;
this.data = data;
this.statusCode = statusCode;
}
public Response(String message, T data, int statusCode) {
this.message = message;
this.data = data;
this.statusCode = statusCode;
}
public static <T> Response<T> ok(T data) {
return new Response<>("操作成功", data, 200);
}
public static <T> Response<T> ok(T data) {
return new Response<>("操作成功", data, 200);
}
public Response<T> setMessage(String message) {
this.message = message;
return this;
}
public Response<T> setMessage(String message) {
this.message = message;
return this;
}
public Response<T> setData(T data) {
this.data = data;
return Response.ok(getData())
.setMessage(getMessage())
.setStatusCode(getStatusCode());
}
public Response<T> setData(T data) {
this.data = data;
return Response.ok(getData())
.setMessage(getMessage())
.setStatusCode(getStatusCode());
}
public Response<T> setStatusCode(int statusCode) {
this.statusCode = statusCode;
return this;
}
public Response<T> setStatusCode(int statusCode) {
this.statusCode = statusCode;
return this;
}
}

View File

@ -5,6 +5,6 @@ import lombok.Data;
@Data
public class Description {
private String text;
private Extra[] extra;
private String text;
private Extra[] extra;
}

View File

@ -5,8 +5,8 @@ import lombok.Data;
@Data
public class Extra {
private String text;
private String color;
private Boolean bold;
private Boolean italic;
private String text;
private String color;
private Boolean bold;
private Boolean italic;
}

View File

@ -5,11 +5,11 @@ import lombok.Data;
@Data
public class MinecraftServerStatus {
private Description description;
private Players players;
private Version version;
private String favicon;
private boolean enforcesSecureChat;
private boolean previewsChat;
private String jsonData;
private Description description;
private Players players;
private Version version;
private String favicon;
private boolean enforcesSecureChat;
private boolean previewsChat;
private String jsonData;
}

View File

@ -5,6 +5,6 @@ import lombok.Data;
@Data
public class PlayerSample {
private String name;
private String id;
private String name;
private String id;
}

View File

@ -5,7 +5,7 @@ import lombok.Data;
@Data
public class Players {
private int max;
private int online;
private PlayerSample[] sample;
private int max;
private int online;
private PlayerSample[] sample;
}

View File

@ -2,7 +2,6 @@ package com.mingliqiye.utils.minecraft.slp;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mingliqiye.utils.network.NetworkEndpoint;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
@ -17,182 +16,182 @@ import java.nio.ByteOrder;
*/
public class SLP {
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* int32 值截断为无符号 short2 字节并按大端序写入字节数组
*
* @param value 需要转换的整数int32
* @return 包含两个字节的数组表示无符号 short
*/
public static byte[] toUnsignedShort(int value) {
byte[] array = new byte[2];
ByteBuffer.wrap(array, 0, 2)
.order(ByteOrder.BIG_ENDIAN)
.putShort((short) (value & 0xFFFF));
return array;
}
/**
* int32 值截断为无符号 short2 字节并按大端序写入字节数组
*
* @param value 需要转换的整数int32
* @return 包含两个字节的数组表示无符号 short
*/
public static byte[] toUnsignedShort(int value) {
byte[] array = new byte[2];
ByteBuffer.wrap(array, 0, 2)
.order(ByteOrder.BIG_ENDIAN)
.putShort((short) (value & 0xFFFF));
return array;
}
/**
* 构造 Minecraft 握手包数据
* 握手包用于初始化客户端与服务器之间的连接
*
* @param serverIP 服务器 IP 地址或域名
* @param serverPort 服务器端口号
* @param type 连接类型通常为 1 表示获取状态
* @return 握手包的完整字节数组
* @throws IOException 如果构造过程中发生 IO 错误
*/
public static byte[] getHandshakePack(
String serverIP,
int serverPort,
int type
) throws IOException {
ByteArrayOutputStream pack = new ByteArrayOutputStream();
ByteArrayOutputStream byteArrayOutputStream =
new ByteArrayOutputStream();
pack.write(0x00); // 握手包标识符
pack.write(toVarInt(1156)); // 协议版本号示例值
byte[] sip = serverIP.getBytes();
pack.write(toVarInt(sip.length)); // 服务器地址长度
pack.write(sip); // 服务器地址
pack.write(toUnsignedShort(serverPort)); // 服务器端口
pack.write(toVarInt(type)); // 下一阶段类型1 表示状态请求
byteArrayOutputStream.write(toVarInt(pack.size())); // 包长度前缀
byteArrayOutputStream.write(pack.toByteArray());
/**
* 构造 Minecraft 握手包数据
* 握手包用于初始化客户端与服务器之间的连接
*
* @param serverIP 服务器 IP 地址或域名
* @param serverPort 服务器端口号
* @param type 连接类型通常为 1 表示获取状态
* @return 握手包的完整字节数组
* @throws IOException 如果构造过程中发生 IO 错误
*/
public static byte[] getHandshakePack(
String serverIP,
int serverPort,
int type
) throws IOException {
ByteArrayOutputStream pack = new ByteArrayOutputStream();
ByteArrayOutputStream byteArrayOutputStream =
new ByteArrayOutputStream();
pack.write(0x00); // 握手包标识符
pack.write(toVarInt(1156)); // 协议版本号示例值
byte[] sip = serverIP.getBytes();
pack.write(toVarInt(sip.length)); // 服务器地址长度
pack.write(sip); // 服务器地址
pack.write(toUnsignedShort(serverPort)); // 服务器端口
pack.write(toVarInt(type)); // 下一阶段类型1 表示状态请求
byteArrayOutputStream.write(toVarInt(pack.size())); // 包长度前缀
byteArrayOutputStream.write(pack.toByteArray());
return byteArrayOutputStream.toByteArray();
}
return byteArrayOutputStream.toByteArray();
}
/**
* 获取状态请求包的固定字节表示
* 此包用于向服务器请求当前状态信息
*
* @return 状态请求包的字节数组
*/
public static byte[] getStatusPack() {
return new byte[] { 0x01, 0x00 };
}
/**
* 获取状态请求包的固定字节表示
* 此包用于向服务器请求当前状态信息
*
* @return 状态请求包的字节数组
*/
public static byte[] getStatusPack() {
return new byte[] { 0x01, 0x00 };
}
/**
* 从输入流中读取服务器返回的状态 JSON 数据并解析为 MinecraftServerStatus 实体对象
*
* @param inputStream 输入流包含服务器响应的数据
* @return 解析后的 MinecraftServerStatus 对象
* @throws IOException 如果读取过程中发生 IO 错误
*/
public static MinecraftServerStatus getStatusJsonEntity(
DataInputStream inputStream
) throws IOException {
readVarInt(inputStream); // 忽略第一个 VarInt包长度
inputStream.readByte(); // 忽略包标识符
int lengthjson = readVarInt(inputStream); // 读取 JSON 数据长度
byte[] data = new byte[lengthjson];
inputStream.readFully(data); // 读取完整的 JSON 数据
MinecraftServerStatus serverStatus = objectMapper.readValue(
data,
MinecraftServerStatus.class
);
serverStatus.setJsonData(new String(data)); // 设置原始 JSON 字符串
return serverStatus;
}
/**
* 从输入流中读取服务器返回的状态 JSON 数据并解析为 MinecraftServerStatus 实体对象
*
* @param inputStream 输入流包含服务器响应的数据
* @return 解析后的 MinecraftServerStatus 对象
* @throws IOException 如果读取过程中发生 IO 错误
*/
public static MinecraftServerStatus getStatusJsonEntity(
DataInputStream inputStream
) throws IOException {
readVarInt(inputStream); // 忽略第一个 VarInt包长度
inputStream.readByte(); // 忽略包标识符
int lengthjson = readVarInt(inputStream); // 读取 JSON 数据长度
byte[] data = new byte[lengthjson];
inputStream.readFully(data); // 读取完整的 JSON 数据
MinecraftServerStatus serverStatus = objectMapper.readValue(
data,
MinecraftServerStatus.class
);
serverStatus.setJsonData(new String(data)); // 设置原始 JSON 字符串
return serverStatus;
}
/**
* 从输入流中读取一个 VarInt 类型的整数最多 5 个字节
*
* @param in 输入流
* @return 解码后的整数值
* @throws IOException 如果读取过程中发生 IO 错误
*/
public static int readVarInt(DataInputStream in) throws IOException {
int value = 0;
int length = 0;
byte currentByte;
do {
currentByte = in.readByte();
value |= (currentByte & 0x7F) << (length * 7);
length += 1;
if (length > 5) {
throw new RuntimeException("VarInt too long");
}
} while ((currentByte & 0x80) != 0);
return value;
}
/**
* 从输入流中读取一个 VarInt 类型的整数最多 5 个字节
*
* @param in 输入流
* @return 解码后的整数值
* @throws IOException 如果读取过程中发生 IO 错误
*/
public static int readVarInt(DataInputStream in) throws IOException {
int value = 0;
int length = 0;
byte currentByte;
do {
currentByte = in.readByte();
value |= (currentByte & 0x7F) << (length * 7);
length += 1;
if (length > 5) {
throw new RuntimeException("VarInt too long");
}
} while ((currentByte & 0x80) != 0);
return value;
}
/**
* 将一个 int32 整数编码为 VarInt 格式的字节数组1 5 个字节
*
* @param value 需要编码的整数
* @return 编码后的 VarInt 字节数组
*/
public static byte[] toVarInt(int value) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
while (true) {
if ((value & 0xFFFFFF80) == 0) {
buffer.write(value); // 最后一个字节
break;
}
buffer.write((value & 0x7F) | 0x80); // 写入带继续位的字节
value >>>= 7; // 右移 7 位继续处理
}
return buffer.toByteArray();
}
/**
* 将一个 int32 整数编码为 VarInt 格式的字节数组1 5 个字节
*
* @param value 需要编码的整数
* @return 编码后的 VarInt 字节数组
*/
public static byte[] toVarInt(int value) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
while (true) {
if ((value & 0xFFFFFF80) == 0) {
buffer.write(value); // 最后一个字节
break;
}
buffer.write((value & 0x7F) | 0x80); // 写入带继续位的字节
value >>>= 7; // 右移 7 位继续处理
}
return buffer.toByteArray();
}
/**
* 创建一个新的 Socket 连接到指定的网络端点并设置超时时间
*
* @param networkEndpoint 目标网络端点包括主机和端口
* @return 已连接的 Socket 实例
* @throws IOException 如果连接失败或发生 IO 错误
*/
public static Socket getNewConnect(NetworkEndpoint networkEndpoint)
throws IOException {
Socket socket = new Socket();
socket.setSoTimeout(5000); // 设置读取超时时间为 5
socket.connect(networkEndpoint.toInetSocketAddress()); // 执行连接操作
return socket;
}
/**
* 创建一个新的 Socket 连接到指定的网络端点并设置超时时间
*
* @param networkEndpoint 目标网络端点包括主机和端口
* @return 已连接的 Socket 实例
* @throws IOException 如果连接失败或发生 IO 错误
*/
public static Socket getNewConnect(NetworkEndpoint networkEndpoint)
throws IOException {
Socket socket = new Socket();
socket.setSoTimeout(5000); // 设置读取超时时间为 5
socket.connect(networkEndpoint.toInetSocketAddress()); // 执行连接操作
return socket;
}
/**
* 使用 "host:port" 格式的字符串连接到 Minecraft 服务器并获取其状态信息
*
* @param s 域名或 IP 地址加端口号组成的字符串例如 "127.0.0.1:25565"
* @return 服务器状态实体对象
* @throws IOException 如果连接失败或发生 IO 错误
*/
public static MinecraftServerStatus getServerStatus(String s)
throws IOException {
return getServerStatus(NetworkEndpoint.of(s));
}
/**
* 使用 "host:port" 格式的字符串连接到 Minecraft 服务器并获取其状态信息
*
* @param s 域名或 IP 地址加端口号组成的字符串例如 "127.0.0.1:25565"
* @return 服务器状态实体对象
* @throws IOException 如果连接失败或发生 IO 错误
*/
public static MinecraftServerStatus getServerStatus(String s)
throws IOException {
return getServerStatus(NetworkEndpoint.of(s));
}
/**
* 使用指定的主机名和端口号连接到 Minecraft 服务器并获取其状态信息
*
* @param s 主机名或 IP 地址
* @param i 端口号
* @return 服务器状态实体对象
* @throws IOException 如果连接失败或发生 IO 错误
*/
public static MinecraftServerStatus getServerStatus(String s, Integer i)
throws IOException {
return getServerStatus(NetworkEndpoint.of(s, i));
}
/**
* 使用指定的主机名和端口号连接到 Minecraft 服务器并获取其状态信息
*
* @param s 主机名或 IP 地址
* @param i 端口号
* @return 服务器状态实体对象
* @throws IOException 如果连接失败或发生 IO 错误
*/
public static MinecraftServerStatus getServerStatus(String s, Integer i)
throws IOException {
return getServerStatus(NetworkEndpoint.of(s, i));
}
/**
* 使用 NetworkEndpoint 实例连接到 Minecraft 服务器并获取其状态信息
*
* @param e 网络端点实例包含主机和端口信息
* @return 服务器状态实体对象
* @throws IOException 如果连接失败或发生 IO 错误
* @see NetworkEndpoint
*/
public static MinecraftServerStatus getServerStatus(NetworkEndpoint e)
throws IOException {
Socket socket = getNewConnect(e); // 建立 TCP 连接
OutputStream out = socket.getOutputStream(); // 获取输出流发送数据
DataInputStream in = new DataInputStream(socket.getInputStream()); // 获取输入流接收数据
out.write(getHandshakePack(e.getHost(), e.getPort(), 1)); // 发送握手包
out.write(getStatusPack()); // 发送状态请求包
return getStatusJsonEntity(in); // 读取并解析服务器响应
}
/**
* 使用 NetworkEndpoint 实例连接到 Minecraft 服务器并获取其状态信息
*
* @param e 网络端点实例包含主机和端口信息
* @return 服务器状态实体对象
* @throws IOException 如果连接失败或发生 IO 错误
* @see NetworkEndpoint
*/
public static MinecraftServerStatus getServerStatus(NetworkEndpoint e)
throws IOException {
Socket socket = getNewConnect(e); // 建立 TCP 连接
OutputStream out = socket.getOutputStream(); // 获取输出流发送数据
DataInputStream in = new DataInputStream(socket.getInputStream()); // 获取输入流接收数据
out.write(getHandshakePack(e.getHost(), e.getPort(), 1)); // 发送握手包
out.write(getStatusPack()); // 发送状态请求包
return getStatusJsonEntity(in); // 读取并解析服务器响应
}
}

View File

@ -5,6 +5,6 @@ import lombok.Data;
@Data
public class Version {
private String name;
private int protocol;
private String name;
private int protocol;
}

View File

@ -2,9 +2,9 @@ package com.mingliqiye.utils.network;
public class NetWorkUtil {
public static void main(String[] args) {
System.out.println(NetworkEndpoint.of("127.0.0.1", 25565));
System.out.println(NetworkEndpoint.of("127.0.0.1:25565"));
System.out.println(NetworkEndpoint.of("127.0.0.1:25565"));
}
public static void main(String[] args) {
System.out.println(NetworkEndpoint.of("127.0.0.1", 25565));
System.out.println(NetworkEndpoint.of("127.0.0.1:25565"));
System.out.println(NetworkEndpoint.of("127.0.0.1:25565"));
}
}

View File

@ -1,13 +1,12 @@
package com.mingliqiye.utils.network;
import com.mingliqiye.utils.string.StringUtil;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Pattern;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
/**
* 网络地址类用于表示一个网络地址IP或域名并提供相关操作
@ -17,180 +16,180 @@ import java.util.regex.Pattern;
*/
public class NetworkAddress implements Serializable {
/**
* IPv6标识
*/
public static int IPV6 = 6;
/**
* IPv6标识
*/
public static int IPV6 = 6;
/**
* IPv4标识
*/
public static int IPV4 = 4;
/**
* IPv4标识
*/
public static int IPV4 = 4;
/**
* IPv4地址正则表达式
*/
static String IPV4REG =
"^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2" +
"(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$";
/**
* IPv4地址正则表达式
*/
static String IPV4REG =
"^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2" +
"(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$";
/**
* 编译后的IPv4地址匹配模式
*/
private static final Pattern IPV4_PATTERN = Pattern.compile(IPV4REG);
/**
* 编译后的IPv4地址匹配模式
*/
private static final Pattern IPV4_PATTERN = Pattern.compile(IPV4REG);
/**
* IPv6地址正则表达式
*/
static String IPV6REG =
"^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|" +
"^(::([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})$" +
"|" +
"^(::)$|" +
"^([0-9a-fA-F]{1,4}::([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4})$|" +
"^(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})$|" +
"^(([0-9a-fA-F]{1,4}:){6}(([0-9]{1,3}\\.){3}[0-9]{1,3}))$|" +
"^::([fF]{4}:)?(([0-9]{1,3}\\.){3}[0-9]{1,3})$";
/**
* IPv6地址正则表达式
*/
static String IPV6REG =
"^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|" +
"^(::([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})$" +
"|" +
"^(::)$|" +
"^([0-9a-fA-F]{1,4}::([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4})$|" +
"^(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})$|" +
"^(([0-9a-fA-F]{1,4}:){6}(([0-9]{1,3}\\.){3}[0-9]{1,3}))$|" +
"^::([fF]{4}:)?(([0-9]{1,3}\\.){3}[0-9]{1,3})$";
/**
* 编译后的IPv6地址匹配模式
*/
private static final Pattern IPV6_PATTERN = Pattern.compile(IPV6REG);
/**
* 编译后的IPv6地址匹配模式
*/
private static final Pattern IPV6_PATTERN = Pattern.compile(IPV6REG);
/**
* IP地址类型4 表示 IPv46 表示 IPv6
*/
@Getter
private int IPv;
/**
* IP地址类型4 表示 IPv46 表示 IPv6
*/
@Getter
private int IPv;
/**
* IP地址字符串
*/
@Getter
private String ip;
/**
* IP地址字符串
*/
@Getter
private String ip;
/**
* 域名如果输入的是域名
*/
private String domain;
/**
* 域名如果输入的是域名
*/
private String domain;
/**
* 标识是否是域名解析来的IP
*/
private boolean isdom;
/**
* 标识是否是域名解析来的IP
*/
private boolean isdom;
/**
* 构造方法根据传入的字符串判断是IP地址还是域名并进行相应处理
*
* @param domip 可能是IP地址或域名的字符串
*/
NetworkAddress(String domip) {
try {
// 尝试将输入识别为IP地址
IPv = testIp(domip);
ip = domip;
} catch (NetworkException e) {
try {
// 如果不是有效IP则尝试作为域名解析
String ips = getHostIp(domip);
IPv = testIp(ips);
ip = ips;
isdom = true;
domain = domip;
} catch (UnknownHostException ex) {
throw new NetworkException(ex);
}
}
}
/**
* 构造方法根据传入的字符串判断是IP地址还是域名并进行相应处理
*
* @param domip 可能是IP地址或域名的字符串
*/
NetworkAddress(String domip) {
try {
// 尝试将输入识别为IP地址
IPv = testIp(domip);
ip = domip;
} catch (NetworkException e) {
try {
// 如果不是有效IP则尝试作为域名解析
String ips = getHostIp(domip);
IPv = testIp(ips);
ip = ips;
isdom = true;
domain = domip;
} catch (UnknownHostException ex) {
throw new NetworkException(ex);
}
}
}
/**
* 静态工厂方法创建 NetworkAddress 实例
*
* @param domip 可能是IP地址或域名的字符串
* @return 新建的 NetworkAddress 实例
*/
public static NetworkAddress of(String domip) {
return new NetworkAddress(domip);
}
/**
* 静态工厂方法创建 NetworkAddress 实例
*
* @param domip 可能是IP地址或域名的字符串
* @return 新建的 NetworkAddress 实例
*/
public static NetworkAddress of(String domip) {
return new NetworkAddress(domip);
}
/**
* 静态工厂方法通过 InetAddress 创建 NetworkAddress 实例
*
* @param inetAddress InetAddress 对象
* @return 新建的 NetworkAddress 实例
*/
public static NetworkAddress of(InetAddress inetAddress) {
return new NetworkAddress(inetAddress.getHostAddress());
}
/**
* 静态工厂方法通过 InetAddress 创建 NetworkAddress 实例
*
* @param inetAddress InetAddress 对象
* @return 新建的 NetworkAddress 实例
*/
public static NetworkAddress of(InetAddress inetAddress) {
return new NetworkAddress(inetAddress.getHostAddress());
}
/**
* 从DNS服务器解析域名获取对应的IP地址
*
* @param domain 域名
* @return 解析出的第一个IP地址
* @throws UnknownHostException 如果域名无法解析
*/
public static String getHostIp(@NotNull String domain)
throws UnknownHostException {
InetAddress[] addresses = InetAddress.getAllByName(domain.trim());
return addresses[0].getHostAddress();
}
/**
* 从DNS服务器解析域名获取对应的IP地址
*
* @param domain 域名
* @return 解析出的第一个IP地址
* @throws UnknownHostException 如果域名无法解析
*/
public static String getHostIp(@NotNull String domain)
throws UnknownHostException {
InetAddress[] addresses = InetAddress.getAllByName(domain.trim());
return addresses[0].getHostAddress();
}
/**
* 检测给定字符串是否为有效的IPv4或IPv6地址
*
* @param ip 要检测的IP地址字符串
* @return 4 表示IPv46 表示IPv6
* @throws NetworkException 如果IP格式无效
*/
public static int testIp(String ip) {
if (ip == null) {
throw new NetworkException("IP地址不能为null");
}
String trimmedIp = ip.trim();
/**
* 检测给定字符串是否为有效的IPv4或IPv6地址
*
* @param ip 要检测的IP地址字符串
* @return 4 表示IPv46 表示IPv6
* @throws NetworkException 如果IP格式无效
*/
public static int testIp(String ip) {
if (ip == null) {
throw new NetworkException("IP地址不能为null");
}
String trimmedIp = ip.trim();
// 判断是否匹配IPv4格式
if (IPV4_PATTERN.matcher(trimmedIp).matches()) {
return IPV4;
}
// 判断是否匹配IPv4格式
if (IPV4_PATTERN.matcher(trimmedIp).matches()) {
return IPV4;
}
// 判断是否匹配IPv6格式
if (IPV6_PATTERN.matcher(trimmedIp).matches()) {
return IPV6;
}
// 判断是否匹配IPv6格式
if (IPV6_PATTERN.matcher(trimmedIp).matches()) {
return IPV6;
}
// 不符合任一格式时抛出异常
throw new NetworkException(
StringUtil.format("[{}] 不是有效的IPv4或IPv6地址", ip)
);
}
// 不符合任一格式时抛出异常
throw new NetworkException(
StringUtil.format("[{}] 不是有效的IPv4或IPv6地址", ip)
);
}
/**
* 将当前 NetworkAddress 转换为 InetAddress 对象
*
* @return InetAddress 对象
*/
public InetAddress toInetAddress() {
try {
return InetAddress.getByName(ip != null ? ip : domain);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
/**
* 将当前 NetworkAddress 转换为 InetAddress 对象
*
* @return InetAddress 对象
*/
public InetAddress toInetAddress() {
try {
return InetAddress.getByName(ip != null ? ip : domain);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
/**
* 返回 NetworkAddress 的字符串表示形式
*
* @return 字符串表示
*/
public String toString() {
return isdom
? StringUtil.format(
"NetworkAddress(IP='{}',type='{}'," + "domain='{}')",
ip,
IPv,
domain
)
: StringUtil.format("NetworkAddress(IP='{}',type='{}')", ip, IPv);
}
/**
* 返回 NetworkAddress 的字符串表示形式
*
* @return 字符串表示
*/
public String toString() {
return isdom
? StringUtil.format(
"NetworkAddress(IP='{}',type='{}'," + "domain='{}')",
ip,
IPv,
domain
)
: StringUtil.format("NetworkAddress(IP='{}',type='{}')", ip, IPv);
}
}

View File

@ -1,10 +1,9 @@
package com.mingliqiye.utils.network;
import com.mingliqiye.utils.string.StringUtil;
import lombok.Getter;
import java.io.Serializable;
import java.net.InetSocketAddress;
import lombok.Getter;
/**
* IP和端口聚集类用于封装网络地址与端口信息
@ -15,128 +14,128 @@ import java.net.InetSocketAddress;
*/
public class NetworkEndpoint implements Serializable {
@Getter
private final NetworkAddress networkAddress;
@Getter
private final NetworkAddress networkAddress;
@Getter
private final NetworkPort networkPort;
@Getter
private final NetworkPort networkPort;
/**
* 构造函数使用指定的网络地址和端口创建NetworkEndpoint实例
*
* @param networkAddress 网络地址对象
* @param networkPort 网络端口对象
* @see NetworkAddress
* @see NetworkPort
*/
private NetworkEndpoint(
NetworkAddress networkAddress,
NetworkPort networkPort
) {
this.networkAddress = networkAddress;
this.networkPort = networkPort;
}
/**
* 构造函数使用指定的网络地址和端口创建NetworkEndpoint实例
*
* @param networkAddress 网络地址对象
* @param networkPort 网络端口对象
* @see NetworkAddress
* @see NetworkPort
*/
private NetworkEndpoint(
NetworkAddress networkAddress,
NetworkPort networkPort
) {
this.networkAddress = networkAddress;
this.networkPort = networkPort;
}
/**
* 根据给定的InetSocketAddress对象创建NetworkEndpoint实例
*
* @param address InetSocketAddress对象
* @return 新建的NetworkEndpoint实例
* @see InetSocketAddress
*/
public static NetworkEndpoint of(InetSocketAddress address) {
return new NetworkEndpoint(
new NetworkAddress(address.getHostString()),
new NetworkPort(address.getPort())
);
}
/**
* 根据给定的InetSocketAddress对象创建NetworkEndpoint实例
*
* @param address InetSocketAddress对象
* @return 新建的NetworkEndpoint实例
* @see InetSocketAddress
*/
public static NetworkEndpoint of(InetSocketAddress address) {
return new NetworkEndpoint(
new NetworkAddress(address.getHostString()),
new NetworkPort(address.getPort())
);
}
/**
* 根据主机名或IP字符串和端口号创建NetworkEndpoint实例
*
* @param s 主机名或IP地址字符串
* @param i 端口号
* @return 新建的NetworkEndpoint实例
*/
public static NetworkEndpoint of(String s, Integer i) {
NetworkAddress networkAddress = new NetworkAddress(s);
NetworkPort networkPort = new NetworkPort(i);
return new NetworkEndpoint(networkAddress, networkPort);
}
/**
* 根据主机名或IP字符串和端口号创建NetworkEndpoint实例
*
* @param s 主机名或IP地址字符串
* @param i 端口号
* @return 新建的NetworkEndpoint实例
*/
public static NetworkEndpoint of(String s, Integer i) {
NetworkAddress networkAddress = new NetworkAddress(s);
NetworkPort networkPort = new NetworkPort(i);
return new NetworkEndpoint(networkAddress, networkPort);
}
/**
* 根据"host:port"格式的字符串创建NetworkEndpoint实例
* 例如"127.0.0.1:8080"
*
* @param s "host:port"格式的字符串
* @return 新建的NetworkEndpoint实例
*/
public static NetworkEndpoint of(String s) {
// 查找最后一个冒号的位置以支持IPv6地址中的冒号
int lastColonIndex = s.lastIndexOf(':');
return of(
s.substring(0, lastColonIndex),
Integer.parseInt(s.substring(lastColonIndex + 1))
);
}
/**
* 根据"host:port"格式的字符串创建NetworkEndpoint实例
* 例如"127.0.0.1:8080"
*
* @param s "host:port"格式的字符串
* @return 新建的NetworkEndpoint实例
*/
public static NetworkEndpoint of(String s) {
// 查找最后一个冒号的位置以支持IPv6地址中的冒号
int lastColonIndex = s.lastIndexOf(':');
return of(
s.substring(0, lastColonIndex),
Integer.parseInt(s.substring(lastColonIndex + 1))
);
}
/**
* 将当前NetworkEndpoint转换为InetSocketAddress对象
*
* @return 对应的InetSocketAddress对象
* @see InetSocketAddress
*/
public InetSocketAddress toInetSocketAddress() {
return new InetSocketAddress(
networkAddress.toInetAddress(),
networkPort.getPort()
);
}
/**
* 将当前NetworkEndpoint转换为InetSocketAddress对象
*
* @return 对应的InetSocketAddress对象
* @see InetSocketAddress
*/
public InetSocketAddress toInetSocketAddress() {
return new InetSocketAddress(
networkAddress.toInetAddress(),
networkPort.getPort()
);
}
/**
* 将当前NetworkEndpoint转换为"host:port"格式的字符串
* 例如"127.0.0.1:25563"
*
* @return 格式化后的字符串
*/
public String toHostPortString() {
return StringUtil.format(
"{}:{}",
networkAddress.getIp(),
networkPort.getPort()
);
}
/**
* 将当前NetworkEndpoint转换为"host:port"格式的字符串
* 例如"127.0.0.1:25563"
*
* @return 格式化后的字符串
*/
public String toHostPortString() {
return StringUtil.format(
"{}:{}",
networkAddress.getIp(),
networkPort.getPort()
);
}
/**
* 返回NetworkEndpoint的详细字符串表示形式
* 格式NetworkEndpoint(IP=...,Port=...,Endpoint=...)
*
* @return 包含详细信息的字符串
*/
public String toString() {
return StringUtil.format(
"NetworkEndpoint(IP={},Port={},Endpoint={})",
networkAddress.getIp(),
networkPort.getPort(),
toHostPortString()
);
}
/**
* 返回NetworkEndpoint的详细字符串表示形式
* 格式NetworkEndpoint(IP=...,Port=...,Endpoint=...)
*
* @return 包含详细信息的字符串
*/
public String toString() {
return StringUtil.format(
"NetworkEndpoint(IP={},Port={},Endpoint={})",
networkAddress.getIp(),
networkPort.getPort(),
toHostPortString()
);
}
/**
* 获取主机名或IP地址字符串
*
* @return 主机名或IP地址
*/
public String getHost() {
return networkAddress.getIp();
}
/**
* 获取主机名或IP地址字符串
*
* @return 主机名或IP地址
*/
public String getHost() {
return networkAddress.getIp();
}
/**
* 获取端口号
*
* @return 端口号
*/
public Integer getPort() {
return networkPort.getPort();
}
/**
* 获取端口号
*
* @return 端口号
*/
public Integer getPort() {
return networkPort.getPort();
}
}

View File

@ -7,21 +7,21 @@ package com.mingliqiye.utils.network;
*/
public class NetworkException extends RuntimeException {
/**
* 构造一个带有指定详细消息的网络异常
*
* @param message 异常的详细消息
*/
public NetworkException(String message) {
super(message);
}
/**
* 构造一个带有指定详细消息的网络异常
*
* @param message 异常的详细消息
*/
public NetworkException(String message) {
super(message);
}
/**
* 构造一个网络异常指定原因异常
*
* @param e 导致此异常的原因异常
*/
public NetworkException(Exception e) {
super(e);
}
/**
* 构造一个网络异常指定原因异常
*
* @param e 导致此异常的原因异常
*/
public NetworkException(Exception e) {
super(e);
}
}

View File

@ -1,9 +1,8 @@
package com.mingliqiye.utils.network;
import com.mingliqiye.utils.string.StringUtil;
import lombok.Getter;
import java.io.Serializable;
import lombok.Getter;
/**
* 网络端口类
@ -12,31 +11,31 @@ import java.io.Serializable;
*/
public class NetworkPort implements Serializable {
@Getter
private final int port;
@Getter
private final int port;
/**
* 构造函数创建一个网络端口对象
*
* @param port 端口号必须在0-65535范围内
*/
public NetworkPort(int port) {
testPort(port);
this.port = port;
}
/**
* 构造函数创建一个网络端口对象
*
* @param port 端口号必须在0-65535范围内
*/
public NetworkPort(int port) {
testPort(port);
this.port = port;
}
/**
* 验证端口号是否合法
*
* @param port 待验证的端口号
* @throws NetworkException 当端口号不在合法范围(0-65535)内时抛出异常
*/
public static void testPort(int port) {
// 验证端口号范围是否在0-65535之间
if (!(0 <= port && 65535 >= port)) {
throw new NetworkException(
StringUtil.format("{} 不是正确的端口号", port)
);
}
}
/**
* 验证端口号是否合法
*
* @param port 待验证的端口号
* @throws NetworkException 当端口号不在合法范围(0-65535)内时抛出异常
*/
public static void testPort(int port) {
// 验证端口号范围是否在0-65535之间
if (!(0 <= port && 65535 >= port)) {
throw new NetworkException(
StringUtil.format("{} 不是正确的端口号", port)
);
}
}
}

View File

@ -1,7 +1,6 @@
package com.mingliqiye.utils.string;
import com.mingliqiye.utils.collection.Lists;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
@ -12,203 +11,203 @@ import java.util.List;
*/
public class StringUtil {
/**
* 将对象转换为字符串表示形式
*
* @param obj 需要转换的对象可以为null
* @return 如果对象为null则返回空字符串否则返回对象的字符串表示
*/
public static String toString(Object obj) {
// 如果对象为null返回空字符串否则调用对象的toString方法
return obj == null ? "" : obj.toString();
}
/**
* 将对象转换为字符串表示形式
*
* @param obj 需要转换的对象可以为null
* @return 如果对象为null则返回空字符串否则返回对象的字符串表示
*/
public static String toString(Object obj) {
// 如果对象为null返回空字符串否则调用对象的toString方法
return obj == null ? "" : obj.toString();
}
/**
* 格式化字符串将格式字符串中的占位符{}替换为对应的参数值<br>
* 示例输出 {} StringUtil.format("{},{},{}", "666", "{}", "777") - "666,{},777"<br>
* 示例 StringUtil.format("{},{},{},{}", "666", "{}", "777") - "666,{},777,"<br>
* 没有实际{} 会替换为 "" 空字符串
*
* @param format 格式字符串使用{}作为占位符如果为null则返回null
* @param args 用于替换占位符的参数数组
* @return 格式化后的字符串
*/
public static String format(String format, Object... args) {
if (format == null) {
return null;
}
/**
* 格式化字符串将格式字符串中的占位符{}替换为对应的参数值<br>
* 示例输出 {} StringUtil.format("{},{},{}", "666", "{}", "777") - "666,{},777"<br>
* 示例 StringUtil.format("{},{},{},{}", "666", "{}", "777") - "666,{},777,"<br>
* 没有实际{} 会替换为 "" 空字符串
*
* @param format 格式字符串使用{}作为占位符如果为null则返回null
* @param args 用于替换占位符的参数数组
* @return 格式化后的字符串
*/
public static String format(String format, Object... args) {
if (format == null) {
return null;
}
StringBuilder sb = new StringBuilder();
int placeholderCount = 0;
int lastIndex = 0;
int len = format.length();
StringBuilder sb = new StringBuilder();
int placeholderCount = 0;
int lastIndex = 0;
int len = format.length();
for (int i = 0; i < len - 1; i++) {
if (format.charAt(i) == '{' && format.charAt(i + 1) == '}') {
// 添加前面的部分
sb.append(format, lastIndex, i);
// 替换为 MessageFormat 占位符 {index}
sb.append('{').append(placeholderCount).append('}');
placeholderCount++;
i++; // 跳过 '}'
lastIndex = i + 1;
}
}
for (int i = 0; i < len - 1; i++) {
if (format.charAt(i) == '{' && format.charAt(i + 1) == '}') {
// 添加前面的部分
sb.append(format, lastIndex, i);
// 替换为 MessageFormat 占位符 {index}
sb.append('{').append(placeholderCount).append('}');
placeholderCount++;
i++; // 跳过 '}'
lastIndex = i + 1;
}
}
// 添加剩余部分
sb.append(format.substring(lastIndex));
// 添加剩余部分
sb.append(format.substring(lastIndex));
// 构造实际参数数组
Object[] actualArgs;
if (args.length < placeholderCount) {
actualArgs = new String[placeholderCount];
System.arraycopy(args, 0, actualArgs, 0, args.length);
for (int i = args.length; i < placeholderCount; i++) {
actualArgs[i] = "";
}
} else {
actualArgs = args;
}
// 构造实际参数数组
Object[] actualArgs;
if (args.length < placeholderCount) {
actualArgs = new String[placeholderCount];
System.arraycopy(args, 0, actualArgs, 0, args.length);
for (int i = args.length; i < placeholderCount; i++) {
actualArgs[i] = "";
}
} else {
actualArgs = args;
}
// 如果没有占位符直接返回格式化后的字符串
if (placeholderCount == 0) {
return sb.toString();
}
// 如果没有占位符直接返回格式化后的字符串
if (placeholderCount == 0) {
return sb.toString();
}
try {
return MessageFormat.format(
sb.toString(),
(Object[]) Lists.toStringList(actualArgs)
);
} catch (IllegalArgumentException e) {
// 返回原始格式化字符串或抛出自定义异常视业务需求而定
return sb.toString();
}
}
try {
return MessageFormat.format(
sb.toString(),
(Object[]) Lists.toStringList(actualArgs)
);
} catch (IllegalArgumentException e) {
// 返回原始格式化字符串或抛出自定义异常视业务需求而定
return sb.toString();
}
}
/**
* 判断字符串是否为空
*
* @param str 待检查的字符串
* @return 如果字符串为null或空字符串则返回true否则返回false
*/
public static boolean isEmpty(String str) {
return str == null || str.isEmpty();
}
/**
* 判断字符串是否为空
*
* @param str 待检查的字符串
* @return 如果字符串为null或空字符串则返回true否则返回false
*/
public static boolean isEmpty(String str) {
return str == null || str.isEmpty();
}
/**
* 使用指定的分隔符将多个对象连接成一个字符串
*
* @param spec 用作分隔符的字符串
* @param objects 要连接的对象数组
* @return 使用指定分隔符连接后的字符串
*/
public static String joinOf(String spec, String... objects) {
return join(spec, Arrays.asList(objects));
}
/**
* 使用指定的分隔符将多个对象连接成一个字符串
*
* @param spec 用作分隔符的字符串
* @param objects 要连接的对象数组
* @return 使用指定分隔符连接后的字符串
*/
public static String joinOf(String spec, String... objects) {
return join(spec, Arrays.asList(objects));
}
/**
* 将字符串按照指定分隔符分割成字符串列表并移除列表开头的空字符串元素
*
* @param str 待分割的字符串
* @param separator 用作分割符的字符串
* @return 分割后的字符串列表不包含开头的空字符串元素
*/
public static List<String> split(String str, String separator) {
List<String> data = new ArrayList<>(
Arrays.asList(str.split(separator))
);
// 移除列表开头的所有空字符串元素
while (!data.isEmpty() && data.get(0).isEmpty()) {
data.remove(0);
}
return data;
}
/**
* 将字符串按照指定分隔符分割成字符串列表并移除列表开头的空字符串元素
*
* @param str 待分割的字符串
* @param separator 用作分割符的字符串
* @return 分割后的字符串列表不包含开头的空字符串元素
*/
public static List<String> split(String str, String separator) {
List<String> data = new ArrayList<>(
Arrays.asList(str.split(separator))
);
// 移除列表开头的所有空字符串元素
while (!data.isEmpty() && data.get(0).isEmpty()) {
data.remove(0);
}
return data;
}
/**
* 将列表中的元素按照指定分隔符连接成字符串
*
* @param <P> 列表元素的类型
* @param separator 分隔符用于连接各个元素
* @param list 待连接的元素列表
* @param fun 转换函数用于将列表元素转换为字符串如果为null则使用toString()方法
* @return 连接后的字符串如果列表为空或null则返回空字符串
*/
public static <P> String join(
String separator,
List<P> list,
PRFunction<P, String> fun
) {
// 处理空列表情况
if (list == null || list.isEmpty()) {
return "";
}
/**
* 将列表中的元素按照指定分隔符连接成字符串
*
* @param <P> 列表元素的类型
* @param separator 分隔符用于连接各个元素
* @param list 待连接的元素列表
* @param fun 转换函数用于将列表元素转换为字符串如果为null则使用toString()方法
* @return 连接后的字符串如果列表为空或null则返回空字符串
*/
public static <P> String join(
String separator,
List<P> list,
PRFunction<P, String> fun
) {
// 处理空列表情况
if (list == null || list.isEmpty()) {
return "";
}
// 构建结果字符串
StringBuilder sb = StringUtil.stringBuilder(list.size() * 16);
for (int i = 0; i < list.size(); i++) {
P item = list.get(i);
// 将元素转换为字符串
String itemStr = fun == null
? (item == null ? "null" : item.toString())
: fun.call(item);
// 构建结果字符串
StringBuilder sb = StringUtil.stringBuilder(list.size() * 16);
for (int i = 0; i < list.size(); i++) {
P item = list.get(i);
// 将元素转换为字符串
String itemStr = fun == null
? (item == null ? "null" : item.toString())
: fun.call(item);
// 第一个元素直接添加其他元素先添加分隔符再添加元素
if (i == 0) {
sb.append(itemStr);
} else {
sb.append(separator).append(itemStr);
}
}
return sb.toString();
}
// 第一个元素直接添加其他元素先添加分隔符再添加元素
if (i == 0) {
sb.append(itemStr);
} else {
sb.append(separator).append(itemStr);
}
}
return sb.toString();
}
/**
* 使用指定分隔符连接字符串列表
*
* @param separator 分隔符不能为null
* @param list 字符串列表不能为null
* @return 连接后的字符串
* @throws IllegalArgumentException 当separator或list为null时抛出
*/
public static String join(String separator, List<String> list) {
if (separator == null) {
throw new IllegalArgumentException("Separator cannot be null");
}
if (list == null) {
throw new IllegalArgumentException("List cannot be null");
}
return join(separator, list, null);
}
/**
* 使用指定分隔符连接字符串列表
*
* @param separator 分隔符不能为null
* @param list 字符串列表不能为null
* @return 连接后的字符串
* @throws IllegalArgumentException 当separator或list为null时抛出
*/
public static String join(String separator, List<String> list) {
if (separator == null) {
throw new IllegalArgumentException("Separator cannot be null");
}
if (list == null) {
throw new IllegalArgumentException("List cannot be null");
}
return join(separator, list, null);
}
/**
* 创建一个新的StringBuilder实例
*
* @param i 指定StringBuilder的初始容量
* @return 返回一个新的StringBuilder对象其初始容量为指定的大小
*/
public static StringBuilder stringBuilder(int i) {
return new StringBuilder(i);
}
/**
* 创建一个新的StringBuilder实例
*
* @param i 指定StringBuilder的初始容量
* @return 返回一个新的StringBuilder对象其初始容量为指定的大小
*/
public static StringBuilder stringBuilder(int i) {
return new StringBuilder(i);
}
/**
* PRFunction接口表示一个接收参数P并返回结果R的函数式接口
* <p>
* 该接口使用@FunctionalInterface注解标记表明它是一个函数式接口
* 只包含一个抽象方法call可用于Lambda表达式和方法引用
* </p>
*
* @param <P> 函数接收的参数类型
* @param <R> 函数返回的结果类型
*/
@FunctionalInterface
public interface PRFunction<P, R> {
/**
* 执行函数调用
*
* @param p 输入参数
* @return 函数执行结果
*/
R call(P p);
}
/**
* PRFunction接口表示一个接收参数P并返回结果R的函数式接口
* <p>
* 该接口使用@FunctionalInterface注解标记表明它是一个函数式接口
* 只包含一个抽象方法call可用于Lambda表达式和方法引用
* </p>
*
* @param <P> 函数接收的参数类型
* @param <R> 函数返回的结果类型
*/
@FunctionalInterface
public interface PRFunction<P, R> {
/**
* 执行函数调用
*
* @param p 输入参数
* @return 函数执行结果
*/
R call(P p);
}
}

View File

@ -1,7 +1,6 @@
package com.mingliqiye.utils.system;
import com.mingliqiye.utils.collection.Lists;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
@ -17,182 +16,182 @@ import java.util.List;
*/
public class SystemUtil {
private static final String osName = System.getProperties().getProperty(
"os.name"
);
private static final String osName = System.getProperties().getProperty(
"os.name"
);
/**
* 判断当前操作系统是否为Windows系统
*
* @return 如果是Windows系统返回true否则返回false
*/
public static boolean isWindows() {
return osName != null && osName.startsWith("Windows");
}
/**
* 判断当前操作系统是否为Windows系统
*
* @return 如果是Windows系统返回true否则返回false
*/
public static boolean isWindows() {
return osName != null && osName.startsWith("Windows");
}
/**
* 判断当前操作系统是否为Mac系统
*
* @return 如果是Mac系统返回true否则返回false
*/
public static boolean isMac() {
return osName != null && osName.startsWith("Mac");
}
/**
* 判断当前操作系统是否为Mac系统
*
* @return 如果是Mac系统返回true否则返回false
*/
public static boolean isMac() {
return osName != null && osName.startsWith("Mac");
}
/**
* 判断当前操作系统是否为Unix/Linux系统
*
* @return 如果是Unix/Linux系统返回true否则返回false
*/
public static boolean isUnix() {
if (osName == null) {
return false;
}
return (
osName.startsWith("Linux") ||
osName.startsWith("AIX") ||
osName.startsWith("SunOS")
);
}
/**
* 判断当前操作系统是否为Unix/Linux系统
*
* @return 如果是Unix/Linux系统返回true否则返回false
*/
public static boolean isUnix() {
if (osName == null) {
return false;
}
return (
osName.startsWith("Linux") ||
osName.startsWith("AIX") ||
osName.startsWith("SunOS")
);
}
/**
* 获取JDK版本号
*
* @return JDK版本号字符串
*/
public static String getJdkVersion() {
return System.getProperty("java.specification.version");
}
/**
* 获取JDK版本号
*
* @return JDK版本号字符串
*/
public static String getJdkVersion() {
return System.getProperty("java.specification.version");
}
/**
* 获取Java版本号的整数形式
*
* @return Java版本号的整数形式81117等
*/
public static Integer getJavaVersionAsInteger() {
String version = getJdkVersion();
if (version == null || version.isEmpty()) {
throw new IllegalStateException(
"Unable to determine Java version from property 'java.specification.version'"
);
}
/**
* 获取Java版本号的整数形式
*
* @return Java版本号的整数形式81117等
*/
public static Integer getJavaVersionAsInteger() {
String version = getJdkVersion();
if (version == null || version.isEmpty()) {
throw new IllegalStateException(
"Unable to determine Java version from property 'java.specification.version'"
);
}
String uversion;
if (version.startsWith("1.")) {
if (version.length() < 3) {
throw new IllegalStateException(
"Invalid Java version format: " + version
);
}
uversion = version.substring(2, 3);
} else {
if (version.length() < 2) {
throw new IllegalStateException(
"Invalid Java version format: " + version
);
}
uversion = version.substring(0, 2);
}
return Integer.parseInt(uversion);
}
String uversion;
if (version.startsWith("1.")) {
if (version.length() < 3) {
throw new IllegalStateException(
"Invalid Java version format: " + version
);
}
uversion = version.substring(2, 3);
} else {
if (version.length() < 2) {
throw new IllegalStateException(
"Invalid Java version format: " + version
);
}
uversion = version.substring(0, 2);
}
return Integer.parseInt(uversion);
}
/**
* 判断当前JDK版本是否大于8
*
* @return 如果JDK版本大于8返回true否则返回false
*/
public static boolean isJdk8Plus() {
return getJavaVersionAsInteger() > 8;
}
/**
* 判断当前JDK版本是否大于8
*
* @return 如果JDK版本大于8返回true否则返回false
*/
public static boolean isJdk8Plus() {
return getJavaVersionAsInteger() > 8;
}
/**
* 获取本地IP地址数组
*
* @return 本地IP地址字符串数组
* @throws RuntimeException 当获取网络接口信息失败时抛出
*/
public static String[] getLocalIps() {
try {
List<String> ipList = new ArrayList<>();
Enumeration<NetworkInterface> interfaces =
NetworkInterface.getNetworkInterfaces();
/**
* 获取本地IP地址数组
*
* @return 本地IP地址字符串数组
* @throws RuntimeException 当获取网络接口信息失败时抛出
*/
public static String[] getLocalIps() {
try {
List<String> ipList = new ArrayList<>();
Enumeration<NetworkInterface> interfaces =
NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = interfaces.nextElement();
// 跳过回环接口和虚拟接口
if (
networkInterface.isLoopback() ||
networkInterface.isVirtual() ||
!networkInterface.isUp()
) {
continue;
}
while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = interfaces.nextElement();
// 跳过回环接口和虚拟接口
if (
networkInterface.isLoopback() ||
networkInterface.isVirtual() ||
!networkInterface.isUp()
) {
continue;
}
Enumeration<InetAddress> addresses =
networkInterface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
// 只获取IPv4地址
if (address instanceof Inet4Address) {
ipList.add(address.getHostAddress());
}
}
}
Enumeration<InetAddress> addresses =
networkInterface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
// 只获取IPv4地址
if (address instanceof Inet4Address) {
ipList.add(address.getHostAddress());
}
}
}
return ipList.toArray(new String[0]);
} catch (SocketException e) {
throw new RuntimeException("Failed to get local IP addresses", e);
}
}
return ipList.toArray(new String[0]);
} catch (SocketException e) {
throw new RuntimeException("Failed to get local IP addresses", e);
}
}
/**
* 获取本地IP地址列表
*
* @return 本地IP地址的字符串列表
*/
public static List<String> getLocalIpsByList() {
return Lists.newArrayList(getLocalIps());
}
/**
* 获取本地IP地址列表
*
* @return 本地IP地址的字符串列表
*/
public static List<String> getLocalIpsByList() {
return Lists.newArrayList(getLocalIps());
}
/**
* 获取本地回环地址
*
* @return 回环地址字符串通常为"127.0.0.1"
*/
public static String[] getLoopbackIps() {
List<String> strings = new ArrayList<>(3);
try {
Enumeration<NetworkInterface> interfaces =
NetworkInterface.getNetworkInterfaces();
/**
* 获取本地回环地址
*
* @return 回环地址字符串通常为"127.0.0.1"
*/
public static String[] getLoopbackIps() {
List<String> strings = new ArrayList<>(3);
try {
Enumeration<NetworkInterface> interfaces =
NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = interfaces.nextElement();
while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = interfaces.nextElement();
// 只处理回环接口
if (networkInterface.isLoopback() && networkInterface.isUp()) {
Enumeration<InetAddress> addresses =
networkInterface.getInetAddresses();
// 只处理回环接口
if (networkInterface.isLoopback() && networkInterface.isUp()) {
Enumeration<InetAddress> addresses =
networkInterface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
strings.add(address.getHostAddress());
}
}
}
return strings.toArray(new String[0]);
} catch (SocketException e) {
// 可考虑添加日志记录
return new String[] { "127.0.0.1" };
}
}
while (addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
strings.add(address.getHostAddress());
}
}
}
return strings.toArray(new String[0]);
} catch (SocketException e) {
// 可考虑添加日志记录
return new String[] { "127.0.0.1" };
}
}
/**
* 获取本地回环地址IP列表
*
* @return 本地回环地址IP字符串列表的副本
*/
public static List<String> getLoopbackIpsByList() {
// 将本地回环地址IP数组转换为列表并返回
return Lists.newArrayList(getLoopbackIps());
}
/**
* 获取本地回环地址IP列表
*
* @return 本地回环地址IP字符串列表的副本
*/
public static List<String> getLoopbackIpsByList() {
// 将本地回环地址IP数组转换为列表并返回
return Lists.newArrayList(getLoopbackIps());
}
}

View File

@ -1,14 +1,13 @@
package com.mingliqiye.utils.time;
import lombok.Getter;
import lombok.Setter;
import lombok.var;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import lombok.Getter;
import lombok.Setter;
import lombok.var;
/**
* 时间工具类用于处理日期时间的转换格式化等操作
@ -19,396 +18,396 @@ import java.util.Date;
*/
public final class DateTime {
@Getter
@Setter
private ZoneId zoneId = ZoneId.systemDefault();
@Getter
@Setter
private ZoneId zoneId = ZoneId.systemDefault();
@Getter
private LocalDateTime localDateTime;
@Getter
private LocalDateTime localDateTime;
/**
* 私有构造函数使用指定的 LocalDateTime 初始化实例
*
* @param time LocalDateTime 对象
*/
private DateTime(LocalDateTime time) {
setLocalDateTime(time);
}
/**
* 私有构造函数使用指定的 LocalDateTime 初始化实例
*
* @param time LocalDateTime 对象
*/
private DateTime(LocalDateTime time) {
setLocalDateTime(time);
}
/**
* 私有构造函数使用当前系统时间初始化实例
*/
private DateTime() {
setLocalDateTime(LocalDateTime.now());
}
/**
* 私有构造函数使用当前系统时间初始化实例
*/
private DateTime() {
setLocalDateTime(LocalDateTime.now());
}
/**
* 获取当前时间的 DateTime 实例
*
* @return 返回当前时间的 DateTime 实例
*/
public static DateTime now() {
return new DateTime();
}
/**
* 获取当前时间的 DateTime 实例
*
* @return 返回当前时间的 DateTime 实例
*/
public static DateTime now() {
return new DateTime();
}
/**
* Date 对象转换为 DateTime 实例
*
* @param zoneId 时区信息
* @param date Date 对象
* @return 返回对应的 DateTime 实例
*/
public static DateTime of(Date date, ZoneId zoneId) {
return new DateTime(date.toInstant().atZone(zoneId).toLocalDateTime());
}
/**
* Date 对象转换为 DateTime 实例
*
* @param zoneId 时区信息
* @param date Date 对象
* @return 返回对应的 DateTime 实例
*/
public static DateTime of(Date date, ZoneId zoneId) {
return new DateTime(date.toInstant().atZone(zoneId).toLocalDateTime());
}
/**
* Date 对象转换为 DateTime 实例使用系统默认时区
*
* @param date Date 对象
* @return 返回对应的 DateTime 实例
*/
public static DateTime of(Date date) {
return new DateTime(
date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
);
}
/**
* Date 对象转换为 DateTime 实例使用系统默认时区
*
* @param date Date 对象
* @return 返回对应的 DateTime 实例
*/
public static DateTime of(Date date) {
return new DateTime(
date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
);
}
/**
* 解析时间字符串并生成 DateTime 实例
*
* @param timestr 时间字符串
* @param formatter 格式化模板
* @param fillZero 是否补零到模板长度
* @return 返回解析后的 DateTime 实例
*/
public static DateTime parse(
String timestr,
String formatter,
boolean fillZero
) {
return new DateTime(
LocalDateTime.parse(
fillZero ? getFillZeroByLen(timestr, formatter) : timestr,
DateTimeFormatter.ofPattern(formatter)
)
);
}
/**
* 解析时间字符串并生成 DateTime 实例
*
* @param timestr 时间字符串
* @param formatter 格式化模板
* @param fillZero 是否补零到模板长度
* @return 返回解析后的 DateTime 实例
*/
public static DateTime parse(
String timestr,
String formatter,
boolean fillZero
) {
return new DateTime(
LocalDateTime.parse(
fillZero ? getFillZeroByLen(timestr, formatter) : timestr,
DateTimeFormatter.ofPattern(formatter)
)
);
}
/**
* 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例
*
* @param timestr 时间字符串
* @param formatter 格式化模板枚举
* @param fillZero 是否补零到模板长度
* @return 返回解析后的 DateTime 实例
*/
public static DateTime parse(
String timestr,
Formatter formatter,
boolean fillZero
) {
return parse(timestr, formatter.getValue(), fillZero);
}
/**
* 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例
*
* @param timestr 时间字符串
* @param formatter 格式化模板枚举
* @param fillZero 是否补零到模板长度
* @return 返回解析后的 DateTime 实例
*/
public static DateTime parse(
String timestr,
Formatter formatter,
boolean fillZero
) {
return parse(timestr, formatter.getValue(), fillZero);
}
/**
* 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例默认不补零
*
* @param timestr 时间字符串
* @param formatter 格式化模板枚举
* @return 返回解析后的 DateTime 实例
*/
public static DateTime parse(String timestr, Formatter formatter) {
return parse(timestr, formatter.getValue());
}
/**
* 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例默认不补零
*
* @param timestr 时间字符串
* @param formatter 格式化模板枚举
* @return 返回解析后的 DateTime 实例
*/
public static DateTime parse(String timestr, Formatter formatter) {
return parse(timestr, formatter.getValue());
}
/**
* 解析时间字符串并生成 DateTime 实例默认不补零
*
* @param timestr 时间字符串
* @param formatter 格式化模板
* @return 返回解析后的 DateTime 实例
*/
public static DateTime parse(String timestr, String formatter) {
return parse(timestr, formatter, false);
}
/**
* 解析时间字符串并生成 DateTime 实例默认不补零
*
* @param timestr 时间字符串
* @param formatter 格式化模板
* @return 返回解析后的 DateTime 实例
*/
public static DateTime parse(String timestr, String formatter) {
return parse(timestr, formatter, false);
}
/**
* 补零处理时间字符串以匹配格式化模板长度
*
* @param dstr 原始时间字符串
* @param formats 格式化模板
* @return 补零后的时间字符串
*/
private static String getFillZeroByLen(String dstr, String formats) {
if (dstr.length() == formats.length()) {
return dstr;
}
if (formats.length() > dstr.length()) {
if (dstr.length() == 19) {
dstr += ".";
}
var sb = new StringBuilder(dstr);
for (int i = 0; i < formats.length() - dstr.length(); i++) {
sb.append("0");
}
return sb.toString();
}
throw new IllegalArgumentException(
String.format(
"Text: '%s' len %s < %s %s",
dstr,
dstr.length(),
formats,
formats.length()
)
);
}
/**
* 补零处理时间字符串以匹配格式化模板长度
*
* @param dstr 原始时间字符串
* @param formats 格式化模板
* @return 补零后的时间字符串
*/
private static String getFillZeroByLen(String dstr, String formats) {
if (dstr.length() == formats.length()) {
return dstr;
}
if (formats.length() > dstr.length()) {
if (dstr.length() == 19) {
dstr += ".";
}
var sb = new StringBuilder(dstr);
for (int i = 0; i < formats.length() - dstr.length(); i++) {
sb.append("0");
}
return sb.toString();
}
throw new IllegalArgumentException(
String.format(
"Text: '%s' len %s < %s %s",
dstr,
dstr.length(),
formats,
formats.length()
)
);
}
/**
* 根据年日创建 DateTime 实例
*
* @param year 年份
* @param month 月份 (1-12)
* @param day 日期 (1-31)
* @return 返回指定日期的 DateTime 实例时间部分为 00:00:00
*/
public static DateTime of(int year, int month, int day) {
return new DateTime(LocalDateTime.of(year, month, day, 0, 0));
}
/**
* 根据年日创建 DateTime 实例
*
* @param year 年份
* @param month 月份 (1-12)
* @param day 日期 (1-31)
* @return 返回指定日期的 DateTime 实例时间部分为 00:00:00
*/
public static DateTime of(int year, int month, int day) {
return new DateTime(LocalDateTime.of(year, month, day, 0, 0));
}
/**
* 根据年分创建 DateTime 实例
*
* @param year 年份
* @param month 月份 (1-12)
* @param day 日期 (1-31)
* @param hour 小时 (0-23)
* @param minute 分钟 (0-59)
* @return 返回指定日期时间的 DateTime 实例秒部分为 00
*/
public static DateTime of(
int year,
int month,
int day,
int hour,
int minute
) {
return new DateTime(LocalDateTime.of(year, month, day, hour, minute));
}
/**
* 根据年分创建 DateTime 实例
*
* @param year 年份
* @param month 月份 (1-12)
* @param day 日期 (1-31)
* @param hour 小时 (0-23)
* @param minute 分钟 (0-59)
* @return 返回指定日期时间的 DateTime 实例秒部分为 00
*/
public static DateTime of(
int year,
int month,
int day,
int hour,
int minute
) {
return new DateTime(LocalDateTime.of(year, month, day, hour, minute));
}
/**
* 根据年秒创建 DateTime 实例
*
* @param year 年份
* @param month 月份 (1-12)
* @param day 日期 (1-31)
* @param hour 小时 (0-23)
* @param minute 分钟 (0-59)
* @param second (0-59)
* @return 返回指定日期时间的 DateTime 实例
*/
public static DateTime of(
int year,
int month,
int day,
int hour,
int minute,
int second
) {
return new DateTime(
LocalDateTime.of(year, month, day, hour, minute, second)
);
}
/**
* 根据年秒创建 DateTime 实例
*
* @param year 年份
* @param month 月份 (1-12)
* @param day 日期 (1-31)
* @param hour 小时 (0-23)
* @param minute 分钟 (0-59)
* @param second (0-59)
* @return 返回指定日期时间的 DateTime 实例
*/
public static DateTime of(
int year,
int month,
int day,
int hour,
int minute,
int second
) {
return new DateTime(
LocalDateTime.of(year, month, day, hour, minute, second)
);
}
/**
* 根据年纳秒创建 DateTime 实例
*
* @param year 年份
* @param month 月份 (1-12)
* @param day 日期 (1-31)
* @param hour 小时 (0-23)
* @param minute 分钟 (0-59)
* @param second (0-59)
* @param nano 纳秒 (0-999,999,999)
* @return 返回指定日期时间的 DateTime 实例
*/
public static DateTime of(
int year,
int month,
int day,
int hour,
int minute,
int second,
int nano
) {
return new DateTime(
LocalDateTime.of(year, month, day, hour, minute, second, nano)
);
}
/**
* 根据年纳秒创建 DateTime 实例
*
* @param year 年份
* @param month 月份 (1-12)
* @param day 日期 (1-31)
* @param hour 小时 (0-23)
* @param minute 分钟 (0-59)
* @param second (0-59)
* @param nano 纳秒 (0-999,999,999)
* @return 返回指定日期时间的 DateTime 实例
*/
public static DateTime of(
int year,
int month,
int day,
int hour,
int minute,
int second,
int nano
) {
return new DateTime(
LocalDateTime.of(year, month, day, hour, minute, second, nano)
);
}
/**
* 根据毫秒时间戳创建 DateTime 实例
*
* @param epochMilli 毫秒时间戳
* @return 返回对应时间的 DateTime 实例
*/
public static DateTime of(long epochMilli) {
return new DateTime(
Instant.ofEpochMilli(epochMilli)
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
);
}
/**
* 根据毫秒时间戳创建 DateTime 实例
*
* @param epochMilli 毫秒时间戳
* @return 返回对应时间的 DateTime 实例
*/
public static DateTime of(long epochMilli) {
return new DateTime(
Instant.ofEpochMilli(epochMilli)
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
);
}
/**
* 根据毫秒时间戳和时区创建 DateTime 实例
*
* @param epochMilli 毫秒时间戳
* @param zoneId 时区信息
* @return 返回对应时间的 DateTime 实例
*/
public static DateTime of(long epochMilli, ZoneId zoneId) {
return new DateTime(
Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime()
);
}
/**
* 根据毫秒时间戳和时区创建 DateTime 实例
*
* @param epochMilli 毫秒时间戳
* @param zoneId 时区信息
* @return 返回对应时间的 DateTime 实例
*/
public static DateTime of(long epochMilli, ZoneId zoneId) {
return new DateTime(
Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime()
);
}
/**
* 设置 LocalDateTime 实例
*
* @param localDateTime LocalDateTime 对象
*/
public void setLocalDateTime(LocalDateTime localDateTime) {
this.localDateTime = localDateTime;
}
/**
* 设置 LocalDateTime 实例
*
* @param localDateTime LocalDateTime 对象
*/
public void setLocalDateTime(LocalDateTime localDateTime) {
this.localDateTime = localDateTime;
}
/**
* 将当前 DateTime 转换为 Date 对象
*
* @return 返回对应的 Date 对象
*/
public Date toDate() {
return Date.from(localDateTime.atZone(getZoneId()).toInstant());
}
/**
* 将当前 DateTime 转换为 Date 对象
*
* @return 返回对应的 Date 对象
*/
public Date toDate() {
return Date.from(localDateTime.atZone(getZoneId()).toInstant());
}
/**
* 获取当前 DateTime 中的 LocalDateTime 实例
*
* @return 返回 LocalDateTime 对象
*/
public LocalDateTime toLocalDateTime() {
return localDateTime;
}
/**
* 获取当前 DateTime 中的 LocalDateTime 实例
*
* @return 返回 LocalDateTime 对象
*/
public LocalDateTime toLocalDateTime() {
return localDateTime;
}
/**
* 在当前时间基础上增加指定的时间偏移量
*
* @param dateTimeOffset 时间偏移对象
* @return 返回修改后的 DateTime 实例
*/
public DateTime add(DateTimeOffset dateTimeOffset) {
this.localDateTime = this.localDateTime.plus(
dateTimeOffset.getOffset(),
dateTimeOffset.getOffsetType()
);
return this;
}
/**
* 在当前时间基础上增加指定的时间偏移量
*
* @param dateTimeOffset 时间偏移对象
* @return 返回修改后的 DateTime 实例
*/
public DateTime add(DateTimeOffset dateTimeOffset) {
this.localDateTime = this.localDateTime.plus(
dateTimeOffset.getOffset(),
dateTimeOffset.getOffsetType()
);
return this;
}
/**
* 在当前时间基础上减少指定的时间偏移量
*
* @param dateTimeOffset 时间偏移对象
* @return 返回修改后的 DateTime 实例
*/
public DateTime sub(DateTimeOffset dateTimeOffset) {
this.localDateTime = this.localDateTime.plus(
-dateTimeOffset.getOffset(),
dateTimeOffset.getOffsetType()
);
return this;
}
/**
* 在当前时间基础上减少指定的时间偏移量
*
* @param dateTimeOffset 时间偏移对象
* @return 返回修改后的 DateTime 实例
*/
public DateTime sub(DateTimeOffset dateTimeOffset) {
this.localDateTime = this.localDateTime.plus(
-dateTimeOffset.getOffset(),
dateTimeOffset.getOffsetType()
);
return this;
}
/**
* 使用指定格式化模板将当前时间格式化为字符串
*
* @param formatter 格式化模板
* @return 返回格式化后的时间字符串
*/
public String format(String formatter) {
return format(formatter, false);
}
/**
* 使用指定格式化模板将当前时间格式化为字符串
*
* @param formatter 格式化模板
* @return 返回格式化后的时间字符串
*/
public String format(String formatter) {
return format(formatter, false);
}
/**
* 使用 Formatter 枚举将当前时间格式化为字符串
*
* @param formatter 格式化模板枚举
* @return 返回格式化后的时间字符串
*/
public String format(Formatter formatter) {
return format(formatter.getValue());
}
/**
* 使用 Formatter 枚举将当前时间格式化为字符串
*
* @param formatter 格式化模板枚举
* @return 返回格式化后的时间字符串
*/
public String format(Formatter formatter) {
return format(formatter.getValue());
}
/**
* 使用指定格式化模板将当前时间格式化为字符串并可选择是否去除末尾多余的零
*
* @param formatter 格式化模板
* @param repcZero 是否去除末尾多余的零
* @return 返回格式化后的时间字符串
*/
public String format(String formatter, boolean repcZero) {
var formatted = DateTimeFormatter.ofPattern(formatter).format(
toLocalDateTime()
);
if (repcZero) {
// 处理小数点后多余的0
formatted = formatted.replaceAll("(\\.\\d*?)0+\\b", "$1");
formatted = formatted.replaceAll("\\.$", "");
}
return formatted;
}
/**
* 使用指定格式化模板将当前时间格式化为字符串并可选择是否去除末尾多余的零
*
* @param formatter 格式化模板
* @param repcZero 是否去除末尾多余的零
* @return 返回格式化后的时间字符串
*/
public String format(String formatter, boolean repcZero) {
var formatted = DateTimeFormatter.ofPattern(formatter).format(
toLocalDateTime()
);
if (repcZero) {
// 处理小数点后多余的0
formatted = formatted.replaceAll("(\\.\\d*?)0+\\b", "$1");
formatted = formatted.replaceAll("\\.$", "");
}
return formatted;
}
/**
* 使用 Formatter 枚举将当前时间格式化为字符串并可选择是否去除末尾多余的零
*
* @param formatter 格式化模板枚举
* @param repcZero 是否去除末尾多余的零
* @return 返回格式化后的时间字符串
*/
public String format(Formatter formatter, boolean repcZero) {
return format(formatter.getValue(), repcZero);
}
/**
* 使用 Formatter 枚举将当前时间格式化为字符串并可选择是否去除末尾多余的零
*
* @param formatter 格式化模板枚举
* @param repcZero 是否去除末尾多余的零
* @return 返回格式化后的时间字符串
*/
public String format(Formatter formatter, boolean repcZero) {
return format(formatter.getValue(), repcZero);
}
/**
* 返回当前时间的标准字符串表示形式
*
* @return 返回标准格式的时间字符串
*/
@Override
public String toString() {
return String.format(
"DateTime(%s)",
format(Formatter.STANDARD_DATETIME_MILLISECOUND7, true)
);
}
/**
* 返回当前时间的标准字符串表示形式
*
* @return 返回标准格式的时间字符串
*/
@Override
public String toString() {
return String.format(
"DateTime(%s)",
format(Formatter.STANDARD_DATETIME_MILLISECOUND7, true)
);
}
/**
* 比较当前DateTime对象与指定对象是否相等
*
* @param obj 要比较的对象
* @return 如果对象相等则返回true否则返回false
*/
@Override
public boolean equals(Object obj) {
// 检查对象类型是否为DateTime
if (obj instanceof DateTime) {
// 比较两个DateTime对象转换为LocalDateTime后的值
return toLocalDateTime().equals(((DateTime) obj).toLocalDateTime());
}
return false;
}
/**
* 比较当前DateTime对象与指定对象是否相等
*
* @param obj 要比较的对象
* @return 如果对象相等则返回true否则返回false
*/
@Override
public boolean equals(Object obj) {
// 检查对象类型是否为DateTime
if (obj instanceof DateTime) {
// 比较两个DateTime对象转换为LocalDateTime后的值
return toLocalDateTime().equals(((DateTime) obj).toLocalDateTime());
}
return false;
}
public Instant toInstant() {
return localDateTime.atZone(zoneId).toInstant();
}
public Instant toInstant() {
return localDateTime.atZone(zoneId).toInstant();
}
}

View File

@ -11,33 +11,33 @@ import lombok.Getter;
@Getter
public class DateTimeOffset {
private final ChronoUnit offsetType;
private final Long offset;
private final ChronoUnit offsetType;
private final Long offset;
private DateTimeOffset(ChronoUnit offsetType, Long offset) {
this.offsetType = offsetType;
this.offset = offset;
}
private DateTimeOffset(ChronoUnit offsetType, Long offset) {
this.offsetType = offsetType;
this.offset = offset;
}
/**
* 创建一个新的DateTimeOffset实例
*
* @param offsetType 偏移量的单位类型指定偏移量的计算单位
* @param offset 偏移量的数值可以为正数负数或零
* @return 返回一个新的DateTimeOffset对象包含指定的偏移量信息
*/
public static DateTimeOffset of(ChronoUnit offsetType, Long offset) {
return new DateTimeOffset(offsetType, offset);
}
/**
* 创建一个新的DateTimeOffset实例
*
* @param offsetType 偏移量的单位类型指定偏移量的计算单位
* @param offset 偏移量的数值可以为正数负数或零
* @return 返回一个新的DateTimeOffset对象包含指定的偏移量信息
*/
public static DateTimeOffset of(ChronoUnit offsetType, Long offset) {
return new DateTimeOffset(offsetType, offset);
}
/**
* 创建一个 DateTimeOffset 实例
*
* @param offset 偏移量数值
* @param offsetType 偏移量的时间单位类型
* @return 返回一个新的 DateTimeOffset 实例
*/
public static DateTimeOffset of(Long offset, ChronoUnit offsetType) {
return new DateTimeOffset(offsetType, offset);
}
/**
* 创建一个 DateTimeOffset 实例
*
* @param offset 偏移量数值
* @param offsetType 偏移量的时间单位类型
* @return 返回一个新的 DateTimeOffset 实例
*/
public static DateTimeOffset of(Long offset, ChronoUnit offsetType) {
return new DateTimeOffset(offsetType, offset);
}
}

View File

@ -6,38 +6,38 @@ package com.mingliqiye.utils.time;
* @author MingLiPro
*/
public interface DateTimeUnit {
// 时间单位常量
String YEAR = "year";
String MONTH = "month";
String WEEK = "week";
String DAY = "day";
String HOUR = "hour";
String MINUTE = "minute";
String SECOND = "second";
String MILLISECOND = "millisecond";
String MICROSECOND = "microsecond";
String NANOSECOND = "nanosecond";
// 时间单位常量
String YEAR = "year";
String MONTH = "month";
String WEEK = "week";
String DAY = "day";
String HOUR = "hour";
String MINUTE = "minute";
String SECOND = "second";
String MILLISECOND = "millisecond";
String MICROSECOND = "microsecond";
String NANOSECOND = "nanosecond";
// 时间单位缩写
String YEAR_ABBR = "y";
String MONTH_ABBR = "M";
String WEEK_ABBR = "w";
String DAY_ABBR = "d";
String HOUR_ABBR = "h";
String MINUTE_ABBR = "m";
String SECOND_ABBR = "s";
String MILLISECOND_ABBR = "ms";
String MICROSECOND_ABBR = "μs";
String NANOSECOND_ABBR = "ns";
// 时间单位缩写
String YEAR_ABBR = "y";
String MONTH_ABBR = "M";
String WEEK_ABBR = "w";
String DAY_ABBR = "d";
String HOUR_ABBR = "h";
String MINUTE_ABBR = "m";
String SECOND_ABBR = "s";
String MILLISECOND_ABBR = "ms";
String MICROSECOND_ABBR = "μs";
String NANOSECOND_ABBR = "ns";
// 时间单位转换系数毫秒为基准
long MILLIS_PER_SECOND = 1000L;
long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
long MILLIS_PER_WEEK = 7 * MILLIS_PER_DAY;
// 时间单位转换系数毫秒为基准
long MILLIS_PER_SECOND = 1000L;
long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
long MILLIS_PER_WEEK = 7 * MILLIS_PER_DAY;
// 月份和年的毫秒数仅为近似值
long MILLIS_PER_MONTH = 30 * MILLIS_PER_DAY; // 近似值
long MILLIS_PER_YEAR = 365 * MILLIS_PER_DAY; // 近似值
// 月份和年的毫秒数仅为近似值
long MILLIS_PER_MONTH = 30 * MILLIS_PER_DAY; // 近似值
long MILLIS_PER_YEAR = 365 * MILLIS_PER_DAY; // 近似值
}

View File

@ -10,84 +10,84 @@ import lombok.Getter;
* </p>
*/
public enum Formatter {
/**
* 标准日期时间格式yyyy-MM-dd HH:mm:ss
*/
STANDARD_DATETIME("yyyy-MM-dd HH:mm:ss"),
/**
* 标准日期时间格式yyyy-MM-dd HH:mm:ss
*/
STANDARD_DATETIME("yyyy-MM-dd HH:mm:ss"),
/**
* 标准日期时间格式(7位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSSS
*/
STANDARD_DATETIME_MILLISECOUND7("yyyy-MM-dd HH:mm:ss.SSSSSSS"),
/**
* 标准日期时间格式(7位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSSS
*/
STANDARD_DATETIME_MILLISECOUND7("yyyy-MM-dd HH:mm:ss.SSSSSSS"),
/**
* 标准日期时间格式(6位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSS
*/
STANDARD_DATETIME_MILLISECOUND6("yyyy-MM-dd HH:mm:ss.SSSSSS"),
/**
* 标准日期时间格式(6位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSS
*/
STANDARD_DATETIME_MILLISECOUND6("yyyy-MM-dd HH:mm:ss.SSSSSS"),
/**
* 标准日期时间格式(5位毫秒)yyyy-MM-dd HH:mm:ss.SSSSS
*/
STANDARD_DATETIME_MILLISECOUND5("yyyy-MM-dd HH:mm:ss.SSSSS"),
/**
* 标准日期时间格式(5位毫秒)yyyy-MM-dd HH:mm:ss.SSSSS
*/
STANDARD_DATETIME_MILLISECOUND5("yyyy-MM-dd HH:mm:ss.SSSSS"),
/**
* 标准日期时间格式(4位毫秒)yyyy-MM-dd HH:mm:ss.SSSS
*/
STANDARD_DATETIME_MILLISECOUND4("yyyy-MM-dd HH:mm:ss.SSSS"),
/**
* 标准日期时间格式(4位毫秒)yyyy-MM-dd HH:mm:ss.SSSS
*/
STANDARD_DATETIME_MILLISECOUND4("yyyy-MM-dd HH:mm:ss.SSSS"),
/**
* 标准日期时间格式(3位毫秒)yyyy-MM-dd HH:mm:ss.SSS
*/
STANDARD_DATETIME_MILLISECOUND3("yyyy-MM-dd HH:mm:ss.SSS"),
/**
* 标准日期时间格式(3位毫秒)yyyy-MM-dd HH:mm:ss.SSS
*/
STANDARD_DATETIME_MILLISECOUND3("yyyy-MM-dd HH:mm:ss.SSS"),
/**
* 标准日期时间格式(2位毫秒)yyyy-MM-dd HH:mm:ss.SS
*/
STANDARD_DATETIME_MILLISECOUND2("yyyy-MM-dd HH:mm:ss.SS"),
/**
* 标准日期时间格式(2位毫秒)yyyy-MM-dd HH:mm:ss.SS
*/
STANDARD_DATETIME_MILLISECOUND2("yyyy-MM-dd HH:mm:ss.SS"),
/**
* 标准日期时间格式(1位毫秒)yyyy-MM-dd HH:mm:ss.S
*/
STANDARD_DATETIME_MILLISECOUND1("yyyy-MM-dd HH:mm:ss.S"),
/**
* 标准日期时间格式(1位毫秒)yyyy-MM-dd HH:mm:ss.S
*/
STANDARD_DATETIME_MILLISECOUND1("yyyy-MM-dd HH:mm:ss.S"),
/**
* 标准ISO格式yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
*/
STANDARD_ISO("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
/**
* 标准ISO格式yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
*/
STANDARD_ISO("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
/**
* 标准日期时间秒格式yyyy-MM-dd HH:mm:ss
*/
STANDARD_DATETIME_SECOUND("yyyy-MM-dd HH:mm:ss"),
/**
* 标准日期时间秒格式yyyy-MM-dd HH:mm:ss
*/
STANDARD_DATETIME_SECOUND("yyyy-MM-dd HH:mm:ss"),
/**
* 标准日期格式yyyy-MM-dd
*/
STANDARD_DATE("yyyy-MM-dd"),
/**
* 标准日期格式yyyy-MM-dd
*/
STANDARD_DATE("yyyy-MM-dd"),
/**
* ISO8601格式yyyy-MM-dd'T'HH:mm:ss.SSS'000'
*/
ISO8601("yyyy-MM-dd'T'HH:mm:ss.SSS'000'"),
/**
* ISO8601格式yyyy-MM-dd'T'HH:mm:ss.SSS'000'
*/
ISO8601("yyyy-MM-dd'T'HH:mm:ss.SSS'000'"),
/**
* 紧凑型日期时间格式yyyyMMddHHmmss
*/
COMPACT_DATETIME("yyyyMMddHHmmss");
/**
* 紧凑型日期时间格式yyyyMMddHHmmss
*/
COMPACT_DATETIME("yyyyMMddHHmmss");
@Getter
private final String value;
@Getter
private final String value;
@Getter
private final int len;
@Getter
private final int len;
/**
* 构造函数
*
* @param value 格式化模式字符串
*/
Formatter(String value) {
this.value = value;
this.len = value.length();
}
/**
* 构造函数
*
* @param value 格式化模式字符串
*/
Formatter(String value) {
this.value = value;
this.len = value.length();
}
}

View File

@ -11,7 +11,6 @@ import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.Formatter;
import java.io.IOException;
/**
@ -21,168 +20,168 @@ import java.io.IOException;
*/
public class Jackson {
/**
* yyyy-MM-dd HH:mm:ss.SSSSSSS 的反序列化适配器
* <p>
* JSON 字符串按照指定格式解析为 DateTime 对象
*/
public static class DateTimeJsonDeserializerM7
extends DateTimeJsonDeserializer {
/**
* yyyy-MM-dd HH:mm:ss.SSSSSSS 的反序列化适配器
* <p>
* JSON 字符串按照指定格式解析为 DateTime 对象
*/
public static class DateTimeJsonDeserializerM7
extends DateTimeJsonDeserializer {
/**
* 获取当前使用的日期时间格式化器
*
* @return 返回标准的 7 位毫秒时间格式化器
*/
@Override
public Formatter getFormatter() {
return Formatter.STANDARD_DATETIME_MILLISECOUND7;
}
}
/**
* 获取当前使用的日期时间格式化器
*
* @return 返回标准的 7 位毫秒时间格式化器
*/
@Override
public Formatter getFormatter() {
return Formatter.STANDARD_DATETIME_MILLISECOUND7;
}
}
/**
* 默认日期时间反序列化器
* <p>
* 提供基础的日期时间反序列化功能支持自定义格式化器
*/
public static class DateTimeJsonDeserializer
extends JsonDeserializer<DateTime> {
/**
* 默认日期时间反序列化器
* <p>
* 提供基础的日期时间反序列化功能支持自定义格式化器
*/
public static class DateTimeJsonDeserializer
extends JsonDeserializer<DateTime> {
/**
* 获取当前使用的日期时间格式化器
*
* @return 返回标准的日期时间格式化器
*/
public Formatter getFormatter() {
return Formatter.STANDARD_DATETIME;
}
/**
* 获取当前使用的日期时间格式化器
*
* @return 返回标准的日期时间格式化器
*/
public Formatter getFormatter() {
return Formatter.STANDARD_DATETIME;
}
/**
* 获取格式化器对应的字符串表达式
*
* @return 格式化器的字符串值
*/
public String getFormatterString() {
return getFormatter().getValue();
}
/**
* 获取格式化器对应的字符串表达式
*
* @return 格式化器的字符串值
*/
public String getFormatterString() {
return getFormatter().getValue();
}
/**
* 反序列化方法 JSON 解析为 DateTime 对象
*
* @param p JSON 解析器对象
* @param ctxt 反序列化上下文
* @return 解析后的 DateTime 对象若输入为 NaN 则返回 null
* @throws IOException 当解析过程中发生 IO 异常时抛出
*/
@Override
public DateTime deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
// 如果是 NaN 值则返回 null
if (p.isNaN()) {
return null;
}
// 使用指定格式将字符串解析为 DateTime 对象
return DateTime.parse(
p.getValueAsString(),
getFormatterString(),
true
);
}
}
/**
* 反序列化方法 JSON 解析为 DateTime 对象
*
* @param p JSON 解析器对象
* @param ctxt 反序列化上下文
* @return 解析后的 DateTime 对象若输入为 NaN 则返回 null
* @throws IOException 当解析过程中发生 IO 异常时抛出
*/
@Override
public DateTime deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
// 如果是 NaN 值则返回 null
if (p.isNaN()) {
return null;
}
// 使用指定格式将字符串解析为 DateTime 对象
return DateTime.parse(
p.getValueAsString(),
getFormatterString(),
true
);
}
}
/**
* yyyy-MM-dd HH:mm:ss.SSSSSSS 的序列化适配器
* <p>
* DateTime 对象按指定格式转换为 JSON 字符串
*/
public static class DateTimeJsonSerializerM7
extends DateTimeJsonSerializer {
/**
* yyyy-MM-dd HH:mm:ss.SSSSSSS 的序列化适配器
* <p>
* DateTime 对象按指定格式转换为 JSON 字符串
*/
public static class DateTimeJsonSerializerM7
extends DateTimeJsonSerializer {
/**
* 获取当前使用的日期时间格式化器
*
* @return 返回标准的 7 位毫秒时间格式化器
*/
@Override
public Formatter getFormatter() {
return Formatter.STANDARD_DATETIME_MILLISECOUND7;
}
}
/**
* 获取当前使用的日期时间格式化器
*
* @return 返回标准的 7 位毫秒时间格式化器
*/
@Override
public Formatter getFormatter() {
return Formatter.STANDARD_DATETIME_MILLISECOUND7;
}
}
/**
* 默认日期时间序列化器
* <p>
* 提供基础的日期时间序列化功能支持自定义格式化器
*/
public static class DateTimeJsonSerializer
extends JsonSerializer<DateTime> {
/**
* 默认日期时间序列化器
* <p>
* 提供基础的日期时间序列化功能支持自定义格式化器
*/
public static class DateTimeJsonSerializer
extends JsonSerializer<DateTime> {
/**
* 获取当前使用的日期时间格式化器
*
* @return 返回标准的日期时间格式化器
*/
public Formatter getFormatter() {
return Formatter.STANDARD_DATETIME;
}
/**
* 获取当前使用的日期时间格式化器
*
* @return 返回标准的日期时间格式化器
*/
public Formatter getFormatter() {
return Formatter.STANDARD_DATETIME;
}
/**
* 获取格式化器对应的字符串表达式
*
* @return 格式化器的字符串值
*/
public String getFormatterString() {
return getFormatter().getValue();
}
/**
* 获取格式化器对应的字符串表达式
*
* @return 格式化器的字符串值
*/
public String getFormatterString() {
return getFormatter().getValue();
}
/**
* 序列化方法 DateTime 对象写入 JSON 生成器
*
* @param value 要序列化的 DateTime 对象
* @param gen JSON 生成器
* @param serializers 序列化提供者
* @throws IOException 当写入过程中发生 IO 异常时抛出
*/
@Override
public void serialize(
DateTime value,
JsonGenerator gen,
SerializerProvider serializers
) throws IOException {
// 若值为 null则直接写入 null
if (value == null) {
gen.writeNull();
return;
}
// 按照指定格式将 DateTime 写入为字符串
gen.writeString(value.format(getFormatterString(), true));
}
/**
* 序列化方法 DateTime 对象写入 JSON 生成器
*
* @param value 要序列化的 DateTime 对象
* @param gen JSON 生成器
* @param serializers 序列化提供者
* @throws IOException 当写入过程中发生 IO 异常时抛出
*/
@Override
public void serialize(
DateTime value,
JsonGenerator gen,
SerializerProvider serializers
) throws IOException {
// 若值为 null则直接写入 null
if (value == null) {
gen.writeNull();
return;
}
// 按照指定格式将 DateTime 写入为字符串
gen.writeString(value.format(getFormatterString(), true));
}
/**
* 带类型信息的序列化方法用于支持多态类型处理
*
* @param value 要序列化的 DateTime 对象
* @param gen JSON 生成器
* @param serializers 序列化提供者
* @param typeSer 类型序列化器
* @throws IOException 当写入过程中发生 IO 异常时抛出
*/
@Override
public void serializeWithType(
DateTime value,
JsonGenerator gen,
SerializerProvider serializers,
TypeSerializer typeSer
) throws IOException {
// 写入类型前缀
WritableTypeId typeId = typeSer.writeTypePrefix(
gen,
typeSer.typeId(value, JsonToken.VALUE_STRING)
);
// 执行实际序列化
serialize(value, gen, serializers);
// 写入类型后缀
typeSer.writeTypeSuffix(gen, typeId);
}
}
/**
* 带类型信息的序列化方法用于支持多态类型处理
*
* @param value 要序列化的 DateTime 对象
* @param gen JSON 生成器
* @param serializers 序列化提供者
* @param typeSer 类型序列化器
* @throws IOException 当写入过程中发生 IO 异常时抛出
*/
@Override
public void serializeWithType(
DateTime value,
JsonGenerator gen,
SerializerProvider serializers,
TypeSerializer typeSer
) throws IOException {
// 写入类型前缀
WritableTypeId typeId = typeSer.writeTypePrefix(
gen,
typeSer.typeId(value, JsonToken.VALUE_STRING)
);
// 执行实际序列化
serialize(value, gen, serializers);
// 写入类型后缀
typeSer.writeTypeSuffix(gen, typeId);
}
}
}

View File

@ -2,13 +2,12 @@ package com.mingliqiye.utils.time.typehandlers;
import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.Formatter;
import java.sql.*;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import java.sql.*;
/**
* DateTime类型处理器类
* 用于在MyBatis中处理DateTime类型与数据库VARCHAR类型之间的转换
@ -17,91 +16,91 @@ import java.sql.*;
@MappedJdbcTypes(JdbcType.VARCHAR)
public class DateTimeTypeHandler extends BaseTypeHandler<DateTime> {
/**
* 设置非空参数值
* 将DateTime对象转换为Timestamp并设置到PreparedStatement中
*
* @param ps PreparedStatement对象
* @param i 参数索引位置
* @param parameter DateTime参数值
* @param jdbcType JDBC类型
* @throws SQLException SQL异常
*/
@Override
public void setNonNullParameter(
PreparedStatement ps,
int i,
DateTime parameter,
JdbcType jdbcType
) throws SQLException {
ps.setTimestamp(i, Timestamp.valueOf(parameter.getLocalDateTime()));
}
/**
* 设置非空参数值
* 将DateTime对象转换为Timestamp并设置到PreparedStatement中
*
* @param ps PreparedStatement对象
* @param i 参数索引位置
* @param parameter DateTime参数值
* @param jdbcType JDBC类型
* @throws SQLException SQL异常
*/
@Override
public void setNonNullParameter(
PreparedStatement ps,
int i,
DateTime parameter,
JdbcType jdbcType
) throws SQLException {
ps.setTimestamp(i, Timestamp.valueOf(parameter.getLocalDateTime()));
}
/**
* 从ResultSet中获取可为空的结果值
* 根据列名获取字符串值并解析为DateTime对象
*
* @param rs ResultSet对象
* @param columnName 列名
* @return DateTime对象如果值为null则返回null
* @throws SQLException SQL异常
*/
@Override
public DateTime getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return parse(rs.getString(columnName));
}
/**
* 从ResultSet中获取可为空的结果值
* 根据列名获取字符串值并解析为DateTime对象
*
* @param rs ResultSet对象
* @param columnName 列名
* @return DateTime对象如果值为null则返回null
* @throws SQLException SQL异常
*/
@Override
public DateTime getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return parse(rs.getString(columnName));
}
/**
* 从ResultSet中获取可为空的结果值
* 根据列索引获取字符串值并解析为DateTime对象
*
* @param rs ResultSet对象
* @param columnIndex 列索引
* @return DateTime对象如果值为null则返回null
* @throws SQLException SQL异常
*/
@Override
public DateTime getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return parse(rs.getString(columnIndex));
}
/**
* 从ResultSet中获取可为空的结果值
* 根据列索引获取字符串值并解析为DateTime对象
*
* @param rs ResultSet对象
* @param columnIndex 列索引
* @return DateTime对象如果值为null则返回null
* @throws SQLException SQL异常
*/
@Override
public DateTime getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return parse(rs.getString(columnIndex));
}
/**
* 从CallableStatement中获取可为空的结果值
* 根据列索引获取字符串值并解析为DateTime对象
*
* @param cs CallableStatement对象
* @param columnIndex 列索引
* @return DateTime对象如果值为null则返回null
* @throws SQLException SQL异常
*/
@Override
public DateTime getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return parse(cs.getString(columnIndex));
}
/**
* 从CallableStatement中获取可为空的结果值
* 根据列索引获取字符串值并解析为DateTime对象
*
* @param cs CallableStatement对象
* @param columnIndex 列索引
* @return DateTime对象如果值为null则返回null
* @throws SQLException SQL异常
*/
@Override
public DateTime getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return parse(cs.getString(columnIndex));
}
/**
* 解析字符串为DateTime对象
*
* @param s 待解析的字符串
* @return DateTime对象如果字符串为null则返回null
*/
public DateTime parse(String s) {
if (s == null) {
return null;
}
return DateTime.parse(s, Formatter.STANDARD_DATETIME_MILLISECOUND7);
}
/**
* 解析字符串为DateTime对象
*
* @param s 待解析的字符串
* @return DateTime对象如果字符串为null则返回null
*/
public DateTime parse(String s) {
if (s == null) {
return null;
}
return DateTime.parse(s, Formatter.STANDARD_DATETIME_MILLISECOUND7);
}
/**
* 格式化DateTime对象为字符串
*
* @param t DateTime对象
* @return 格式化后的字符串
*/
public String format(DateTime t) {
return t.format(Formatter.STANDARD_DATETIME_MILLISECOUND7);
}
/**
* 格式化DateTime对象为字符串
*
* @param t DateTime对象
* @return 格式化后的字符串
*/
public String format(DateTime t) {
return t.format(Formatter.STANDARD_DATETIME_MILLISECOUND7);
}
}

View File

@ -0,0 +1,66 @@
package com.mingliqiye.utils.uuid;
import lombok.var;
/**
* MySQL UUID格式与标准UUID格式相互转换工具类
* <p>
* MySQL使用不同的字节顺序存储UUID此类提供了在MySQL格式和标准UUID格式之间转换的方法
* @author MingLiPro
*/
public class MysqlUUIDv1 {
/**
* 将MySQL格式的UUID转换为标准UUID格式
*
* @param uuid MySQL格式的UUID字节数组长度必须为16字节
* @return 标准UUID格式的字节数组长度为16字节
*/
public static byte[] mysqlToUuid(byte[] uuid) {
var reuuid = new byte[16];
// 转换时间戳低位部分
reuuid[4] = uuid[0];
reuuid[5] = uuid[1];
reuuid[6] = uuid[2];
reuuid[7] = uuid[3];
// 转换时间戳中位部分
reuuid[2] = uuid[4];
reuuid[3] = uuid[5];
// 转换时间戳高位部分
reuuid[0] = uuid[6];
reuuid[1] = uuid[7];
// 复制时钟序列和节点标识部分
System.arraycopy(uuid, 8, reuuid, 8, 8);
return reuuid;
}
/**
* 将标准UUID格式转换为MySQL格式的UUID
*
* @param uuid 标准UUID格式的字节数组长度必须为16字节
* @return MySQL格式的UUID字节数组长度为16字节
*/
public static byte[] uuidToMysql(byte[] uuid) {
var reuuid = new byte[16];
// 转换时间戳高位部分
reuuid[6] = uuid[0];
reuuid[7] = uuid[1];
// 转换时间戳中位部分
reuuid[4] = uuid[2];
reuuid[5] = uuid[3];
// 转换时间戳低位部分
reuuid[0] = uuid[4];
reuuid[1] = uuid[5];
reuuid[2] = uuid[6];
reuuid[3] = uuid[7];
// 复制时钟序列和节点标识部分
System.arraycopy(uuid, 8, reuuid, 8, 8);
return reuuid;
}
}

View File

@ -4,8 +4,7 @@ import com.github.f4b6a3.uuid.UuidCreator;
import com.mingliqiye.utils.string.StringUtil;
import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.DateTimeOffset;
import com.mingliqiye.utils.time.Formatter;
import lombok.Setter;
import lombok.Data;
import java.io.Serializable;
import java.nio.ByteBuffer;
@ -19,234 +18,237 @@ import java.util.Objects;
*
* @author MingLiPro
*/
@Setter
@Data
public class UUID implements Serializable {
/**
* 内部封装的 java.util.UUID 实例
*/
private java.util.UUID uuid;
/**
* 内部封装的 java.util.UUID 实例
*/
private java.util.UUID uuid;
/**
* 构造一个由指定高位和低位组成的 UUID
*
* @param msb 高64位
* @param lsb 低64位
*/
public UUID(long msb, long lsb) {
uuid = new java.util.UUID(msb, lsb);
}
/**
* 构造一个由指定高位和低位组成的 UUID
*
* @param msb 高64位
* @param lsb 低64位
*/
public UUID(long msb, long lsb) {
uuid = new java.util.UUID(msb, lsb);
}
/**
* 构造一个基于当前时间的时间戳型 UUID版本1
*/
public UUID() {
uuid = UuidCreator.getTimeBased();
}
/**
* 构造一个基于当前时间的时间戳型 UUID版本1
*/
public UUID() {
uuid = UuidCreator.getTimeBased();
}
/**
* 使用给定的 java.util.UUID 对象构造一个新的 UUID 实例
*
* @param uuid java.util.UUID 实例
*/
public UUID(java.util.UUID uuid) {
this.uuid = uuid;
}
/**
* 使用给定的 java.util.UUID 对象构造一个新的 UUID 实例
*
* @param uuid java.util.UUID 实例
*/
public UUID(java.util.UUID uuid) {
this.uuid = uuid;
}
/**
* 根据字符串表示的 UUID 构造一个新的 UUID 实例
*
* @param uuid 字符串形式的 UUID
*/
public UUID(String uuid) {
this.uuid = java.util.UUID.fromString(uuid);
}
/**
* 根据字符串表示的 UUID 构造一个新的 UUID 实例
*
* @param uuid 字符串形式的 UUID
*/
public UUID(String uuid) {
this.uuid = java.util.UUID.fromString(uuid);
}
/**
* 将字节数组转换为 UUID 实例
*
* @param bytes 表示 UUID 16 字节数据
* @return 新建的 UUID 实例
*/
public static UUID of(byte[] bytes) {
ByteBuffer bb = ByteBuffer.wrap(bytes);
long msb = bb.getLong();
long lsb = bb.getLong();
return new UUID(msb, lsb);
}
/**
* 将字节数组转换为 UUID 实例
*
* @param bytes 表示 UUID 16 字节数据
* @return 新建的 UUID 实例
*/
public static UUID of(byte[] bytes) {
if (bytes == null) {
return null;
}
ByteBuffer bb = ByteBuffer.wrap(bytes);
long msb = bb.getLong();
long lsb = bb.getLong();
return new UUID(msb, lsb);
}
/**
* 将字符串解析为 UUID 实例如果解析失败则抛出 UUIDException
*
* @param data UUID 字符串
* @return 解析后的 UUID 实例
* @throws UUIDException 如果解析失败
*/
public static UUID ofString(String data) {
try {
java.util.UUID uuid1 = java.util.UUID.fromString(data);
UUID uuid = new UUID();
uuid.setUuid(uuid1);
return uuid;
} catch (Exception e) {
throw new UUIDException(e.getMessage(), e);
}
}
/**
* 将字符串解析为 UUID 实例如果解析失败则抛出 UUIDException
*
* @param data UUID 字符串
* @return 解析后的 UUID 实例
* @throws UUIDException 如果解析失败
*/
public static UUID of(String data) {
if (data == null) {
return null;
}
try {
java.util.UUID uuid1 = java.util.UUID.fromString(data);
UUID uuid = new UUID();
uuid.setUuid(uuid1);
return uuid;
} catch (Exception e) {
throw new UUIDException(e.getMessage(), e);
}
}
/**
* UUID 转换为 16 字节的字节数组
*
* @return 表示该 UUID 的字节数组
*/
public byte[] toBytes() {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
}
/**
* UUID 转换为 16 字节的字节数组
*
* @return 表示该 UUID 的字节数组
*/
public byte[] toBytes() {
if (this.uuid == null) {
return null;
}
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
}
/**
* 获取内部封装的 java.util.UUID 实例
*
* @return java.util.UUID 实例
*/
public java.util.UUID GetUUID() {
return uuid;
}
/**
* 获取内部封装的 java.util.UUID 实例
*
* @return java.util.UUID 实例
*/
public java.util.UUID GetUUID() {
return uuid;
}
/**
* UUID 转换为字符串表示默认使用小写格式
*
* @return UUID 字符串
*/
public String toUUIDString() {
return toUUIDString(false);
}
/**
* UUID 转换为字符串表示默认使用小写格式
*
* @return UUID 字符串
*/
public String toUUIDString() {
return toUUIDString(false);
}
/**
* UUID 转换为字符串表示并可选择是否使用大写
*
* @param u 是否使用大写格式
* @return UUID 字符串
* @throws UUIDException 如果 uuid null
*/
public String toUUIDString(boolean u) {
if (uuid == null) {
throw new UUIDException("uuid is null : NullPointerException");
}
if (u) {
return uuid.toString().toUpperCase(Locale.ROOT);
}
return uuid.toString();
}
/**
* UUID 转换为字符串表示并可选择是否使用大写
*
* @param u 是否使用大写格式
* @return UUID 字符串
* @throws UUIDException 如果 uuid null
*/
public String toUUIDString(boolean u) {
if (uuid == null) {
throw new UUIDException("uuid is null : NullPointerException");
}
if (u) {
return uuid.toString().toUpperCase(Locale.ROOT);
}
return uuid.toString();
}
/**
* 计算此 UUID 的哈希码
*
* @return 哈希码值
*/
@Override
public int hashCode() {
return Objects.hash(uuid);
}
/**
* 计算此 UUID 的哈希码
*
* @return 哈希码值
*/
@Override
public int hashCode() {
return Objects.hash(uuid);
}
/**
* 判断两个 UUID 是否相等
*
* @param o 比较对象
* @return 如果相等返回 true否则返回 false
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
UUID uuid = (UUID) o;
return Objects.equals(this.uuid, uuid.uuid);
}
/**
* 判断两个 UUID 是否相等
*
* @param o 比较对象
* @return 如果相等返回 true否则返回 false
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
UUID uuid = (UUID) o;
return Objects.equals(this.uuid, uuid.uuid);
}
/**
* 返回此 UUID 的字符串表示包含版本信息和时间戳如果是版本1
*
* @return UUID 的详细字符串表示
*/
@Override
public String toString() {
if (uuid == null) {
return "UUID(null)";
}
if (uuid.version() == 1) {
return StringUtil.format(
"UUID(uuid={},time={},mac={},version={})",
toUUIDString(true),
getDateTime().format(Formatter.STANDARD_DATETIME),
extractMACFromUUID(),
uuid.version()
);
}
return StringUtil.format(
"UUID(uuid={},version={})",
toUUIDString(true),
uuid.version()
);
}
/**
* 返回此 UUID 的字符串表示包含版本信息和时间戳如果是版本1
*
* @return UUID 的详细字符串表示
*/
@Override
public String toString() {
if (uuid == null) {
return "UUID(null)";
}
return StringUtil.format(
"UUID(uuid={},version={})",
toUUIDString(true),
uuid.version()
);
}
/**
* 从时间戳型 UUID 中提取时间戳并转换为 DateTime 对象
*
* @return 对应的 DateTime 对象如果 uuid null则返回 null
*/
public DateTime getDateTime() {
if (uuid == null) {
return null;
}
return DateTime.of(uuid.timestamp() / 10_000).add(
DateTimeOffset.of(-141427L, ChronoUnit.DAYS)
);
}
/**
* 从时间戳型 UUID 中提取时间戳并转换为 DateTime 对象
*
* @return 对应的 DateTime 对象如果 uuid null则返回 null
*/
public DateTime getDateTime() {
if (uuid == null) {
return null;
}
if (uuid.version() != 1) {
return null;
}
return DateTime.of(uuid.timestamp() / 10_000).add(
DateTimeOffset.of(-141427L, ChronoUnit.DAYS)
);
}
/**
* 从时间戳型 UUID 中提取 MAC 地址默认使用冒号分隔符
*
* @return MAC 地址字符串
* @throws UUIDException 如果 uuid null
*/
public String extractMACFromUUID() {
return extractMACFromUUID(null);
}
/**
* 从时间戳型 UUID 中提取 MAC 地址默认使用冒号分隔符
*
* @return MAC 地址字符串
* @throws UUIDException 如果 uuid null
*/
public String extractMACFromUUID() {
return extractMACFromUUID(null);
}
/**
* 从时间戳型 UUID 中提取 MAC 地址并允许自定义分隔符
*
* @param spec 分隔符字符默认为 ":"
* @return MAC 地址字符串
* @throws UUIDException 如果 uuid null
*/
public String extractMACFromUUID(String spec) {
if (uuid == null) {
throw new UUIDException("uuid is null : NullPointerException");
}
if (spec == null) {
spec = ":";
}
long leastSigBits = uuid.getLeastSignificantBits();
long macLong = leastSigBits & 0xFFFFFFFFFFFFL;
byte[] macBytes = new byte[6];
// 提取 MAC 地址的每个字节
for (int i = 0; i < 6; i++) {
macBytes[5 - i] = (byte) (macLong >> (8 * i));
}
StringBuilder mac = new StringBuilder();
// 构造 MAC 地址字符串
for (int i = 0; i < 6; i++) {
mac.append(String.format("%02X", macBytes[i]));
if (i < 5) {
mac.append(spec);
}
}
return mac.toString();
}
/**
* 从时间戳型 UUID 中提取 MAC 地址并允许自定义分隔符
*
* @param spec 分隔符字符默认为 ":"
* @return MAC 地址字符串
* @throws UUIDException 如果 uuid null
*/
public String extractMACFromUUID(String spec) {
if (uuid == null) {
throw new UUIDException("uuid is null : NullPointerException");
}
if (spec == null) {
spec = ":";
}
long leastSigBits = uuid.getLeastSignificantBits();
long macLong = leastSigBits & 0xFFFFFFFFFFFFL;
byte[] macBytes = new byte[6];
// 提取 MAC 地址的每个字节
for (int i = 0; i < 6; i++) {
macBytes[5 - i] = (byte) (macLong >> (8 * i));
}
StringBuilder mac = new StringBuilder();
// 构造 MAC 地址字符串
for (int i = 0; i < 6; i++) {
mac.append(String.format("%02X", macBytes[i]));
if (i < 5) {
mac.append(spec);
}
}
return mac.toString();
}
}

View File

@ -2,11 +2,11 @@ package com.mingliqiye.utils.uuid;
public class UUIDException extends RuntimeException {
public UUIDException(String message) {
super(message);
}
public UUIDException(String message) {
super(message);
}
public UUIDException(String message, Throwable cause) {
super(message, cause);
}
public UUIDException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -11,7 +11,6 @@ import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.mingliqiye.utils.uuid.UUID;
import com.mingliqiye.utils.uuid.UUIDException;
import java.io.IOException;
/**
@ -21,89 +20,89 @@ import java.io.IOException;
*/
public class Jackson {
/**
* UUID 反序列化器
* <p>
* JSON 字符串反序列化为自定义 UUID 对象
*/
public static class UUIDJsonDeserializer extends JsonDeserializer<UUID> {
/**
* UUID 反序列化器
* <p>
* JSON 字符串反序列化为自定义 UUID 对象
*/
public static class UUIDJsonDeserializer extends JsonDeserializer<UUID> {
/**
* 反序列化方法 JSON 解析为 UUID 对象
*
* @param p JSON 解析器对象
* @param ctxt 反序列化上下文
* @return 解析后的 UUID 对象若输入为 NaN 则返回 null
* @throws IOException 当解析过程中发生 IO 异常时抛出
*/
@Override
public UUID deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
// 如果是 NaN 值则返回 null
if (p.isNaN()) {
return null;
}
// 使用指定字符串值创建新的 UUID 对象
return new UUID(p.getValueAsString());
}
}
/**
* 反序列化方法 JSON 解析为 UUID 对象
*
* @param p JSON 解析器对象
* @param ctxt 反序列化上下文
* @return 解析后的 UUID 对象若输入为 NaN 则返回 null
* @throws IOException 当解析过程中发生 IO 异常时抛出
*/
@Override
public UUID deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
// 如果是 NaN 值则返回 null
if (p.isNaN()) {
return null;
}
// 使用指定字符串值创建新的 UUID 对象
return new UUID(p.getValueAsString());
}
}
/**
* UUID 序列化器
* <p>
* 将自定义 UUID 对象序列化为 JSON 字符串
*/
public static class UUIDJsonSerializer extends JsonSerializer<UUID> {
/**
* UUID 序列化器
* <p>
* 将自定义 UUID 对象序列化为 JSON 字符串
*/
public static class UUIDJsonSerializer extends JsonSerializer<UUID> {
/**
* 序列化方法 UUID 对象写入 JSON 生成器
*
* @param uuid 要序列化的 UUID 对象
* @param jsonGenerator JSON 生成器
* @param serializerProvider 序列化提供者
* @throws UUIDException UUID 处理过程中发生异常时抛出
* @throws IOException 当写入过程中发生 IO 异常时抛出
*/
@Override
public void serialize(
UUID uuid,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider
) throws UUIDException, IOException {
// 若值为 null则直接写入 null
if (uuid == null) {
jsonGenerator.writeNull();
return;
}
// UUID 写入为字符串
jsonGenerator.writeString(uuid.toUUIDString());
}
/**
* 序列化方法 UUID 对象写入 JSON 生成器
*
* @param uuid 要序列化的 UUID 对象
* @param jsonGenerator JSON 生成器
* @param serializerProvider 序列化提供者
* @throws UUIDException UUID 处理过程中发生异常时抛出
* @throws IOException 当写入过程中发生 IO 异常时抛出
*/
@Override
public void serialize(
UUID uuid,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider
) throws UUIDException, IOException {
// 若值为 null则直接写入 null
if (uuid == null) {
jsonGenerator.writeNull();
return;
}
// UUID 写入为字符串
jsonGenerator.writeString(uuid.toUUIDString());
}
/**
* 带类型信息的序列化方法用于支持多态类型处理
*
* @param value 要序列化的 UUID 对象
* @param gen JSON 生成器
* @param serializers 序列化提供者
* @param typeSer 类型序列化器
* @throws IOException 当写入过程中发生 IO 异常时抛出
*/
@Override
public void serializeWithType(
UUID value,
JsonGenerator gen,
SerializerProvider serializers,
TypeSerializer typeSer
) throws IOException {
// 写入类型前缀
WritableTypeId typeId = typeSer.writeTypePrefix(
gen,
typeSer.typeId(value, JsonToken.VALUE_STRING)
);
// 执行实际序列化
serialize(value, gen, serializers);
// 写入类型后缀
typeSer.writeTypeSuffix(gen, typeId);
}
}
/**
* 带类型信息的序列化方法用于支持多态类型处理
*
* @param value 要序列化的 UUID 对象
* @param gen JSON 生成器
* @param serializers 序列化提供者
* @param typeSer 类型序列化器
* @throws IOException 当写入过程中发生 IO 异常时抛出
*/
@Override
public void serializeWithType(
UUID value,
JsonGenerator gen,
SerializerProvider serializers,
TypeSerializer typeSer
) throws IOException {
// 写入类型前缀
WritableTypeId typeId = typeSer.writeTypePrefix(
gen,
typeSer.typeId(value, JsonToken.VALUE_STRING)
);
// 执行实际序列化
serialize(value, gen, serializers);
// 写入类型后缀
typeSer.writeTypeSuffix(gen, typeId);
}
}
}

View File

@ -6,7 +6,6 @@ import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import java.nio.ByteBuffer;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@ -22,90 +21,64 @@ import java.sql.SQLException;
@MappedJdbcTypes(JdbcType.BINARY)
public class UUIDBinaryTypeHandler extends BaseTypeHandler<UUID> {
/**
* UUID 对象转换为二进制字节数组
*
* @param uuid 要转换的 UUID 对象
* @return 包含 UUID 数据的 16 字节二进制数组
*/
public static byte[] UUID_TO_BIN(UUID uuid) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.GetUUID().getMostSignificantBits());
bb.putLong(uuid.GetUUID().getLeastSignificantBits());
return bb.array();
}
/**
* 设置非空参数到 PreparedStatement
*
* @param ps PreparedStatement 对象
* @param i 参数在 SQL 语句中的位置索引
* @param parameter 要设置的 UUID 参数值
* @param jdbcType JDBC 类型信息
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public void setNonNullParameter(
PreparedStatement ps,
int i,
UUID parameter,
JdbcType jdbcType
) throws SQLException {
ps.setBytes(i, UUIDConverter.UUID_TO_BIN(parameter));
}
/**
* 将二进制字节数组转换为 UUID 对象
*
* @param bytes 包含 UUID 数据的二进制字节数组
* @return 转换后的 UUID 对象如果输入为 null 则返回 null
*/
public static UUID BIN_TO_UUID(byte[] bytes) {
if (bytes == null) {
return null;
}
return UUID.of(bytes);
}
/**
* ResultSet 中根据列名获取可为空的 UUID 结果
*
* @param rs ResultSet 对象
* @param columnName 数据库列名
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return UUIDConverter.BIN_TO_UUID(rs.getBytes(columnName));
}
/**
* 设置非空参数到 PreparedStatement
*
* @param ps PreparedStatement 对象
* @param i 参数在 SQL 语句中的位置索引
* @param parameter 要设置的 UUID 参数值
* @param jdbcType JDBC 类型信息
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public void setNonNullParameter(
PreparedStatement ps,
int i,
UUID parameter,
JdbcType jdbcType
) throws SQLException {
ps.setBytes(i, UUID_TO_BIN(parameter));
}
/**
* ResultSet 中根据列索引获取可为空的 UUID 结果
*
* @param rs ResultSet 对象
* @param columnIndex 数据库列索引
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return UUIDConverter.BIN_TO_UUID(rs.getBytes(columnIndex));
}
/**
* ResultSet 中根据列名获取可为空的 UUID 结果
*
* @param rs ResultSet 对象
* @param columnName 数据库列名
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return BIN_TO_UUID(rs.getBytes(columnName));
}
/**
* ResultSet 中根据列索引获取可为空的 UUID 结果
*
* @param rs ResultSet 对象
* @param columnIndex 数据库列索引
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return BIN_TO_UUID(rs.getBytes(columnIndex));
}
/**
* CallableStatement 中根据参数索引获取可为空的 UUID 结果
*
* @param cs CallableStatement 对象
* @param columnIndex 参数索引
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return BIN_TO_UUID(cs.getBytes(columnIndex));
}
/**
* CallableStatement 中根据参数索引获取可为空的 UUID 结果
*
* @param cs CallableStatement 对象
* @param columnIndex 参数索引
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return UUIDConverter.BIN_TO_UUID(cs.getBytes(columnIndex));
}
}

View File

@ -0,0 +1,31 @@
package com.mingliqiye.utils.uuid.typehandlers;
import com.mingliqiye.utils.uuid.MysqlUUIDv1;
import com.mingliqiye.utils.uuid.UUID;
public class UUIDConverter {
public static String UUID_TO_STR(UUID uuid) {
return uuid.toUUIDString();
}
public static UUID STR_TO_UUID(String string) {
return UUID.of(string);
}
public static byte[] UUID_TO_BIN(UUID uuid) {
return uuid.toBytes();
}
public static UUID BIN_TO_UUID(byte[] bytes) {
return UUID.of(bytes);
}
public static byte[] MYSQL_UUID_TO_BIN(UUID uuid) {
return MysqlUUIDv1.uuidToMysql(uuid.toBytes());
}
public static UUID BIN_TO_MYSQL_UUID(byte[] bytes) {
return UUID.of(MysqlUUIDv1.mysqlToUuid(bytes));
}
}

View File

@ -0,0 +1,83 @@
package com.mingliqiye.utils.uuid.typehandlers;
import com.mingliqiye.utils.uuid.UUID;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
/**
* UUIDBinaryTypeHandler 类用于处理 UUID 类型与数据库 BINARY 类型之间的转换
* 该类继承自 BaseTypeHandler专门处理 UUID 对象的序列化和反序列化
*
* @author MingLiPro
*/
@MappedTypes({ UUID.class })
@MappedJdbcTypes(JdbcType.VARCHAR)
public class UUIDStringTypeHandler extends BaseTypeHandler<UUID> {
/**
* 设置非空参数到 PreparedStatement
*
* @param ps PreparedStatement 对象
* @param i 参数在 SQL 语句中的位置索引
* @param parameter 要设置的 UUID 参数值
* @param jdbcType JDBC 类型信息
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public void setNonNullParameter(
PreparedStatement ps,
int i,
UUID parameter,
JdbcType jdbcType
) throws SQLException {
ps.setString(i, UUIDConverter.UUID_TO_STR(parameter));
}
/**
* ResultSet 中根据列名获取可为空的 UUID 结果
*
* @param rs ResultSet 对象
* @param columnName 数据库列名
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return UUIDConverter.STR_TO_UUID(rs.getString(columnName));
}
/**
* ResultSet 中根据列索引获取可为空的 UUID 结果
*
* @param rs ResultSet 对象
* @param columnIndex 数据库列索引
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return UUIDConverter.STR_TO_UUID(rs.getString(columnIndex));
}
/**
* CallableStatement 中根据参数索引获取可为空的 UUID 结果
*
* @param cs CallableStatement 对象
* @param columnIndex 参数索引
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return UUIDConverter.STR_TO_UUID(cs.getString(columnIndex));
}
}