通用工具类
AES加密解密工具类
作用:防止明文前后端传输密码和账号
使用方法: 按照规则定义好key和iv,然后将定义的key和iv告知前端,让前端进行加密,对应的后端进行解密(代码里已写入测试方法,可测试key和iv是否可用)
import cn.hutool.crypto.digest.DigestUtil;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class AesDecryptUtil {
private static final String KEY_ALGORITHM = "AES";
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
/**
* AES解密
* @param content 待解密的内容,Base64编码字符串
* @param key 密钥,需要是16位、24位或32位长度的字符串
* @param iv 初始化向量,16位长度的字符串,CBC模式下需要
* @return 解密后的明文字符串
* @throws Exception 加解密异常
*/
public static String decrypt(String content, String key, String iv) throws Exception {
// 将密钥和初始化向量转换为字节数组
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
byte[] ivBytes = iv.getBytes(StandardCharsets.UTF_8);
// 创建密钥规范
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
// 创建初始化向量规范
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// 获取Cipher实例
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 初始化Cipher为解密模式,并设置密钥和初始化向量
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
// Base64解码待解密内容
byte[] encryptedBytes = Base64.getDecoder().decode(content);
// 执行解密操作
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
// 返回解密后的字符串
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
/**
* <h2>加密 - 自定义加密模式</h2>
* @param text 需要加密的文本内容
* @param key 加密的密钥 key
* @param iv 初始化向量
* */
public static String encrypt(String text, String key, String iv){
try {
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
byte[] ivBytes = iv.getBytes(StandardCharsets.UTF_8);
// 创建AES加密器
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(ivBytes));
// 加密字节数组
byte[] encryptedBytes = cipher.doFinal(text.getBytes());
// 将密文转换为 Base64 编码字符串
return Base64.getEncoder().encodeToString(encryptedBytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
try {
// String ciphertext = "UWs+WmaY3/bwsBW7oHXLAQ==";
// String key = "12345678ABCDEFGH"; // 请确保密钥长度为16、24或32位
// String iv = "12345678ABCDEFGH"; // 对于CBC模式,IV长度通常为16位
// String plaintext = decrypt(ciphertext, key, iv);
// System.out.println("解密后的明文: " + plaintext);
// 测试加密
String password = "123456abc";
String key2 = "12345678ABCDEFGH";
String iv2 = "12345678ABCDEFGH";
String encrypt = encrypt(password, key2, iv2);
System.out.println("加密后的密文: " + encrypt);
System.out.println("解密后的密文:" + decrypt("NL6KVcpTyjdATRWr0MeaEw==", key2, iv2));
System.out.println("对明文进行MD5加密: " + DigestUtil.md5Hex(password));
} catch (Exception e) {
e.printStackTrace();
}
}
}
脱敏工具类
作用:对一些敏感数据进行脱敏,例如身份证号,手机号,车牌号
使用:在对应的返回类的具体变量上加上注解,根据类型使用不同注解
例如
#这个代码是举例如何使用的参考代码,具体工具类代码在下面
@ApiModelProperty("手机号")
@DataMasking(type = DataMaskingType.MOBILE_PHONE)
private String phone;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.Objects;
/**
* @author Bummon
* @description
* @date 2023-09-01 18:14
*/
@AllArgsConstructor
@NoArgsConstructor
public class DataMaskingSerialize extends JsonSerializer implements ContextualSerializer {
private DataMaskingType type;
private Integer start;
private Integer end;
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
String value = (String) o;
switch (type) {
//中文名
case CHINESE_NAME:
if(value.length()>2){
jsonGenerator.writeString(StrUtil.isBlank(value) ? "" : StrUtil.hide(value, 1, value.length()-1));
}else {
jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.CHINESE_NAME));
}
break;
//身份证号
case ID_CARD:
jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.ID_CARD));
break;
//手机号
case MOBILE_PHONE:
jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.MOBILE_PHONE));
break;
//地址
case ADDRESS:
jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.ADDRESS));
break;
//密码
case PASSWORD:
jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.PASSWORD));
break;
//中国大陆车牌号
case CAR_LICENSE:
jsonGenerator.writeString(StrUtil.isBlank(value) ? "" : StrUtil.hide(value, 2, value.length()));
//jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.CAR_LICENSE));
break;
//自定义
case VIN:
jsonGenerator.writeString(CharSequenceUtil.hide(value, start, end));
break;
//自定义
case CUSTOM:
jsonGenerator.writeString(CharSequenceUtil.hide(value, start, end));
break;
default:
break;
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
if (Objects.nonNull(beanProperty)) {
//判断是否为string类型
if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
DataMasking anno = beanProperty.getAnnotation(DataMasking.class);
if (Objects.isNull(anno)) {
anno = beanProperty.getContextAnnotation(DataMasking.class);
}
if (Objects.nonNull(anno)) {
return new DataMaskingSerialize(anno.type(), anno.start(), anno.end());
}
}
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
return serializerProvider.findNullValueSerializer(null);
}
}
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Bummon
* @description 数据脱敏自定义注解
* @date 2023-09-01 18:01
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DataMaskingSerialize.class)
public @interface DataMasking {
/**
* 数据脱敏类型
*/
DataMaskingType type() default DataMaskingType.CUSTOM;
/**
* 脱敏开始位置(包含)
*/
int start() default 0;
/**
* 脱敏结束位置(不包含)
*/
int end() default 0;
}
public enum DataMaskingType {
/**
* 中文名
*/
CHINESE_NAME,
/**
* 身份证号
*/
ID_CARD,
/**
* 手机号
*/
MOBILE_PHONE,
/**
* 地址
*/
ADDRESS,
/**
* 密码
*/
PASSWORD,
/**
* 中国大陆车牌号
*/
CAR_LICENSE,
/**
* VIN
*/
VIN,
/**
* 自定义类型
*/
CUSTOM;
}
Http请求工具类
作用:向指定的url发送http请求
使用说明:直接调用工具类的对应方法,配置好相应请求参数
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.Map;
/**
* RequestHttp概要说明:Http请求工具类
*/
@Slf4j
public class RequestHttp {
/**
* 向指定URL发送GET方法的请求
*
* @param url 发送请求的URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return URL 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param) {
String result = "";
BufferedReader in = null;
try {
String urlNameString = url + "?" + param;
URL realUrl = new URL(urlNameString);
// 打开和URL之间的连接
URLConnection connection = realUrl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立实际的连接
connection.connect();
// 定义 BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF-8"));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
log.error("发送GET请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally块来关闭输入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(param);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送 POST 请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally块来关闭输出流、输入流
finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
/**
* asUrlParams方法慨述: 将map转化key=123&v=456格式 为只要确保你的编码输入是正确的,就可以忽略掉
* UnsupportedEncodingException 创 建 人:dyt 创建时间:2022年1月14日 上午10:13:42 修 改 人:dyt
* 修改日期:2022年1月14日 上午10:13:42 @param source @return String @throws
*/
public static String asUrlParams(Map<String, String> source) {
Iterator<String> it = source.keySet().iterator();
StringBuilder paramStr = new StringBuilder();
while (it.hasNext()) {
String key = it.next();
String value = source.get(key);
if (StringUtils.isBlank(value)) {
continue;
}
try {
// URL 编码
value = URLEncoder.encode(value, "utf-8");
} catch (UnsupportedEncodingException e) {
// do nothing
}
paramStr.append("&").append(key).append("=").append(value);
}
// 去掉第一个&
return paramStr.substring(1);
}
}
雪花算法工具类
使用:无需多言,要用的自然知道这个要做啥的
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
/**
* 描述: Twitter的分布式自增ID雪花算法snowflake
**/
public class SnowFlakeUtil {
/**
* 起始的时间戳
*/
private final static long START_STMP = 1480166465631L;
/**
* 每一部分占用的位数
*/
private final static long SEQUENCE_BIT = 12; //序列号占用的位数
private final static long MACHINE_BIT = 5; //机器标识占用的位数
private final static long DATA_CENTER_BIT = 5;//数据中心占用的位数
/**
* 每一部分的最大值
*/
private final static long MAX_DATA_CENTER_NUM = -1L ^ (-1L << DATA_CENTER_BIT);
private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
/**
* 每一部分向左的位移
*/
// private final static long MACHINE_LEFT = SEQUENCE_BIT;
// private final static long DATACENTER_LEFT = MACHINE_LEFT + MACHINE_BIT;
// private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATA_CENTER_BIT;
private final static long DATA_CENTER_LEFT = MACHINE_BIT;
private final static long SEQUENCE_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;
private final static long TIMESTMP_LEFT = SEQUENCE_LEFT + SEQUENCE_BIT;
private long datacenterId; //数据中心
private long machineId; //机器标识
private long sequence = 0L; //序列号
private long lastStmp = -1L;//上一次时间戳
public SnowFlakeUtil(long datacenterId, long machineId) {
if (datacenterId > MAX_DATA_CENTER_NUM || datacenterId < 0) {
throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
}
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}
/**
* 获取实例
*
* @return
*/
public static SnowFlakeUtil getInstance() {
return getInstance(true);
}
public static SnowFlakeUtil getInstance(boolean random) {
if (random) {
Random rand = new Random();
int min = 0;
int max = 30;
int s = rand.nextInt(max) % (max - min + 1) + min;
int e = rand.nextInt(max) % (max - min + 1) + min;
return getInstance(s, e);
} else {
return getInstance(1, 1);
}
}
private static final ConcurrentHashMap<String, SnowFlakeUtil> SNOW_MAP = new ConcurrentHashMap<>();
/**
* 获取实例
*
* @param datacenterId
* @param machineId
* @return
*/
public static SnowFlakeUtil getInstance(long datacenterId, long machineId) {
String key = new StringBuilder().append("datacenterId=").append(datacenterId).append(" machineId=").append(machineId).toString();
SNOW_MAP.putIfAbsent(key, new SnowFlakeUtil(datacenterId, machineId));
return SNOW_MAP.get(key);
}
/**
* 产生下一个ID
*
* @return
*/
public synchronized long nextId() {
long currStmp = getNewstmp();
if (currStmp < lastStmp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
if (currStmp == lastStmp) {
//相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列数已经达到最大
if (sequence == 0L) {
currStmp = getNextMill();
}
} else {
//不同毫秒内,序列号置为0
sequence = 0L;
}
lastStmp = currStmp;
// return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
// | datacenterId << DATACENTER_LEFT //数据中心部分
// | machineId << MACHINE_LEFT //机器标识部分
// | sequence; //序列号部分
return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
| sequence << SEQUENCE_LEFT //序列部分
| datacenterId << DATA_CENTER_LEFT //数据中心部分
| machineId; //机器部分
}
private long getNextMill() {
long mill = getNewstmp();
while (mill <= lastStmp) {
mill = getNewstmp();
}
return mill;
}
private long getNewstmp() {
return System.currentTimeMillis();
}
}
弱密码校验工具类
作用:登录的时候校验密码是否是弱密码
使用:根据对应的需求,修改对应的代码
import java.util.*;
public class HandlePasswordUtil {
/**
* 对密码进行校验判断,弱密码将返回true,强密码返回false
*
* 如果密码是以下几种情况将返回true,即是弱密码:
* 1.如果密码是admin、
* 2.密码位数小于等于6位、
* 3.密码全部是相同的字符(如:aaaaaaa、8888888)、
* 4.密码全部是空字符、
* 5.密码全是顺序字符(如:123456、987654、bcdefg、gfedcb)
*/
public static boolean isWeekPassword(String password) {
password = password.trim();
//判断密码是不是特殊的admin
if (password.length() <= 8) {
return true;
}
char[] chPassword = password.toCharArray();
LinkedHashSet<Character> link = new LinkedHashSet<>();
StringBuilder buff = new StringBuilder();
boolean result = false;
int count = 0;
for (int i = 0; i < chPassword.length; i++) {
//判断密码是否是连续的正序或降序数字或字母
int preNumber = i == 0 ? -999 : chPassword[i - 1] - 0; //前一字符对应的ascii码
int currentNumber = chPassword[i] - 0;//当前字符对应的ascii码
int asciiNum = preNumber + currentNumber;
if ((96 <= asciiNum && asciiNum <= 114) || (130 <= asciiNum && asciiNum <= 180) || (194 <= asciiNum && asciiNum <= 244)) {
//判断密码的相邻字符是否是正序或降序的数字或字符
if (Math.abs(preNumber - currentNumber) == 1) {
count += 1;
}
}
//处理不重复的字符
if (!link.contains(chPassword[i])) {
link.add(chPassword[i]);
buff.append(chPassword[i]);
}
}
//处理空字符串和密码全是同一字符的
String resultStr = String.valueOf(buff);
if (count == chPassword.length - 1 || resultStr.length() <= 1) {
result = true;
}
return result;
}
}
时间格式和日期工具类
使用:将一些时间格式和日期要求的工具类进行了汇总
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
public class DateUtils {
/** 一星期的天数 */
public static final int WEEK_DAYS = 7;
/** 一年的月份数 */
public static final int YEAR_MONTHS = 12;
/** 一天的小时数 */
public static final int DAY_HOURS = 24;
/** 一小时分钟数 */
public static final int HOUR_MINUTES = 60;
/** 一天分钟数 (24 * 60) */
public static final int DAY_MINUTES = 1440;
/** 一分钟的秒数 */
public static final int MINUTE_SECONDS = 60;
/** 一个小时的秒数 (60 * 60) */
public static final int HOUR_SECONDS = 3600;
/** 一天的秒数 (24 * 60 * 60) */
public static final int DAY_SECONDS = 86400;
/** 一秒的毫秒数 */
public static final long SECOND_MILLISECONDS = 1000L;
/** 一分钟的毫秒数(60 * 1000) */
public static final long MINUTE_MILLISECONDS = 60000L;
/** 一小时的毫秒数(60 * 60 * 1000) */
public static final long HOUR_MILLISECONDS = 3600000L;
/** 一天的毫秒数(24 * 60* 60* 1000) */
public static final long DAY_MILLISECONDS = 86400000L;
/** 星期一 */
public static final int WEEK_1_MONDAY = 1;
/** 星期二 */
public static final int WEEK_2_TUESDAY = 2;
/** 星期三 */
public static final int WEEK_3_WEDNESDAY = 3;
/** 星期四 */
public static final int WEEK_4_THURSDAY = 4;
/** 星期五 */
public static final int WEEK_5_FRIDAY = 5;
/** 星期六 */
public static final int WEEK_6_SATURDAY = 6;
/** 星期天 */
public static final int WEEK_7_SUNDAY = 7;
/** 一月 */
public static final int MONTH_1_JANUARY = 1;
/** 二月 */
public static final int MONTH_2_FEBRUARY = 2;
/** 三月 */
public static final int MONTH_3_MARCH = 3;
/** 四月 */
public static final int MONTH_4_APRIL= 4;
/** 五月 */
public static final int MONTH_5_MAY = 5;
/** 六月 */
public static final int MONTH_6_JUNE = 6;
/** 七月 */
public static final int MONTH_7_JULY = 7;
/** 八月 */
public static final int MONTH_8_AUGUST = 8;
/** 九月 */
public static final int MONTH_9_SEPTEMBER = 9;
/** 十月 */
public static final int MONTH_10_OCTOBER = 10;
/** 十一月 */
public static final int MONTH_11_NOVEMBER = 11;
/** 十二月 */
public static final int MONTH_12_DECEMBER= 12;
/** 显示到日期 */
public static final String FORMAT_DATE = "yyyy-MM-dd";
public static final String YEAR_DATE = "yyyy";
public static final String MOON_DATE = "MM-dd HH:mm:ss";
/** 显示到小时 */
public static final String FORMAT_HOUR = "yyyy-MM-dd HH";
/** 显示到分 */
public static final String FORMAT_MINUTE = "yyyy-MM-dd HH:mm";
/** 显示到秒 */
public static final String FORMAT_SECOND = "yyyy-MM-dd HH:mm:ss";
/** 显示到毫秒 */
public static final String FORMAT_MILLISECOND = "yyyy-MM-dd HH:mm:ss:SSS";
/** 显示到日期(数字格式) */
public static final String FORMAT_NO_DATE = "yyyyMMdd";
/** 显示到小时(数字格式) */
public static final String FORMAT_NO_HOUR = "yyyyMMddHH";
/** 显示到分(数字格式) */
public static final String FORMAT_NO_MINUTE = "yyyyMMddHHmm";
/** 显示到秒(数字格式) */
public static final String FORMAT_NO_SECOND = "yyyyMMddHHmmss";
/** 显示到毫秒(数字格式) */
public static final String FORMAT_NO_MILLISECOND = "yyyyMMddHHmmssSSS";
/**
* 获取指定时间格式化器
*
* @param formatStyle 时间格式
* @return 时间格式化器
*/
private static SimpleDateFormat getSimpleDateFormat(String formatStyle) {
return new SimpleDateFormat(formatStyle);
}
/**
* 将 Date 格式时间转化为指定格式时间
*
* @param date Date 格式时间
* @param formatStyle 转化指定格式(如: yyyy-MM-dd HH:mm:ss)
* @return 转化格式时间
*/
public static String format(Date date, String formatStyle) {
if (Objects.isNull(date)) {
return "";
}
return getSimpleDateFormat(formatStyle).format(date);
}
/**
* 将 Date 格式时间转化为 yyyy-MM-dd 格式时间
*
* @param date Date 格式时间
* @return yyyy-MM-dd 格式时间(如:2022-06-17)
*/
public static String formatDate(Date date) {
return format(date, FORMAT_DATE);
}
public static String contrastMessageDetail(Date date) {
if(null == date){
return format(new Date(), MOON_DATE);
}
String nowDate = format(new Date(), YEAR_DATE);
String contrastDate = format(date, YEAR_DATE);
if(Integer.parseInt(nowDate) <= Integer.parseInt(contrastDate)){
return format(date, MOON_DATE);
}
return format(date, FORMAT_SECOND);
}
/**
* 将 Date 格式时间转化为 yyyy-MM-dd HH:mm:ss 格式时间
*
* @param date Date 格式时间
* @return yyyy-MM-dd HH:mm:ss 格式时间(如:2022-06-17 16:06:17)
*/
public static String formatDateTime(Date date) {
return format(date, FORMAT_SECOND);
}
/**
* 将 Date 格式时间转化为 yyyy-MM-dd HH:mm:ss:SSS 格式时间
*
* @param date Date 格式时间
* @return yyyy-MM-dd HH:mm:ss:SSS 格式时间(如:2022-06-17 16:06:17:325)
*/
public static String formatDateTimeStamp(Date date) {
return format(date, FORMAT_MILLISECOND);
}
/**
* 将 yyyy-MM-dd 格式时间转化为 Date 格式时间
*
* @param dateString yyyy-MM-dd 格式时间(如:2022-06-17)
* @return Date 格式时间
*/
public static Date parseDate(String dateString) {
return parse(dateString, FORMAT_DATE);
}
/**
* 将 yyyy-MM-dd HH:mm:ss 格式时间转化为 Date 格式时间
*
* @param dateTimeStr yyyy-MM-dd HH:mm:ss 格式时间(如:2022-06-17 16:06:17)
* @return Date 格式时间
*/
public static Date parseDateTime(String dateTimeStr) {
return parse(dateTimeStr, FORMAT_SECOND);
}
/**
* 将 yyyy-MM-dd HH:mm:ss:SSS 格式时间转化为 Date 格式时间
*
* @param dateTimeStampStr yyyy-MM-dd HH:mm:ss:SSS 格式时间(如:2022-06-17 16:06:17)
* @return Date 格式时间
*/
public static Date parseDateTimeStamp(String dateTimeStampStr) {
return parse(dateTimeStampStr, FORMAT_MILLISECOND);
}
/**
* 将字符串格式时间转化为 Date 格式时间
*
* @param dateString 字符串时间(如:2022-06-17 16:06:17)
* @return formatStyle 格式内容
* @return Date 格式时间
*/
public static Date parse(String dateString, String formatStyle) {
String s = getString(dateString);
if (s.isEmpty()) {
return null;
}
try {
return getSimpleDateFormat(formatStyle).parse(dateString);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
/**
* 获取字符串有效内容
*
* @param s 字符串
* @return 有效内容
*/
private static String getString(String s) {
return Objects.isNull(s) ? "" : s.trim();
}
/**
* 获取一天的开始时间(即:0 点 0 分 0 秒 0 毫秒)
*
* @param date 指定时间
* @return 当天的开始时间
*/
public static Date getDateStart(Date date) {
if (Objects.isNull(date)) {
return null;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
/**
* 获取一天的截止时间(即:23 点 59 分 59 秒 999 毫秒)
*
* @param date 指定时间
* @return 当天的开始时间
*/
public static Date getDateEnd(Date date) {
if (Objects.isNull(date)) {
return null;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MILLISECOND, 999);
return calendar.getTime();
}
/**
* 获取一天的截止时间(即:23 点 59 分 59 秒 999 毫秒)
*
* @param date 指定时间
* @return 当天的开始时间
*/
public static Date getDateStr(Date date) {
if (Objects.isNull(date)) {
return null;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
return calendar.getTime();
}
/**
* 获取日期数字
*
* @param date 日期
* @return 日期数字
*/
public static int getDateNo(Date date) {
if (Objects.isNull(date)) {
return 0;
}
return Integer.valueOf(format(date, FORMAT_NO_DATE));
}
/**
* 获取日期时间数字(到秒)
*
* @param date 日期
* @return 日期数字
*/
public static long getDateTimeNo(Date date) {
if (Objects.isNull(date)) {
return 0L;
}
return Long.valueOf(format(date, FORMAT_NO_SECOND));
}
/**
* 获取日期时间数字(到毫秒)
*
* @param date 日期
* @return 日期数字
*/
public static long getDateTimeStampNo(Date date) {
if (Objects.isNull(date)) {
return 0L;
}
return Long.valueOf(format(date, FORMAT_NO_MILLISECOND));
}
/**
* 获取星期几
*
* @param date 时间
* @return 0(时间为空), 1(周一), 2(周二),3(周三),4(周四),5(周五),6(周六),7(周日)
*/
public static int getWeek(Date date) {
if (Objects.isNull(date)) {
return 0;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return getWeek(calendar);
}
/**
* 获取星期几
*
* @param calendar 时间
* @return 0(时间为空), 1(周一), 2(周二),3(周三),4(周四),5(周五),6(周六),7(周日)
*/
private static int getWeek(Calendar calendar) {
switch (calendar.get(Calendar.DAY_OF_WEEK)) {
case Calendar.MONDAY:
return 1;
case Calendar.TUESDAY:
return 2;
case Calendar.WEDNESDAY:
return 3;
case Calendar.THURSDAY:
return 4;
case Calendar.FRIDAY:
return 5;
case Calendar.SATURDAY:
return 6;
case Calendar.SUNDAY:
return 7;
default:
return 0;
}
}
/**
* 获取该日期是今年的第几周(以本年的周一为第1周,详见下面说明)<br>
*
* 【说明】<br>
* 比如 2022-01-01(周六)和 2022-01-02(周日)虽然在 2022 年里,但他们两天则属于 2021 年最后一周,<br>
* 那么这两天不会算在 2022 年第 1 周里,此时会返回 0 ;而 2022 年第 1 周将从 2022-01-03(周一) 开始计算。<br>
*
* @param date 时间
* @return -1(时间为空), 0(为上个年的最后一周),其他数字(今年的第几周)
*/
public static int getWeekOfYear(Date date) {
if (Objects.isNull(date)) {
return -1;
}
int weeks = getWeekOfYearIgnoreLastYear(date);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.MONTH, Calendar.JANUARY);
calendar.set(Calendar.DAY_OF_MONTH, 1);
int week = getWeek(calendar);
if (week == 1) {
return weeks;
}
return weeks - 1;
}
/**
* 获取今年的第几周(以本年的1月1日为第1周第1天)<br>
*
* @param date 时间
* @return -1(时间为空),其他数字(今年的第几周)
*/
public static int getWeekOfYearIgnoreLastYear(Date date) {
if (Objects.isNull(date)) {
return -1;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
int days = calendar.get(Calendar.DAY_OF_YEAR);
int weeks = days / 7;
// 如果是 7 的倍数,则表示恰好是多少周
if (days % 7 == 0) {
return weeks;
}
// 如果有余数,则需要再加 1
return weeks + 1;
}
/**
* 获取时间节点对象
*
* @param date 时间对象
* @return DateNode
*/
public static DateNode getDateNode(Date date) {
if (Objects.isNull(date)) {
return null;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
DateNode node = new DateNode();
node.setTime(format(date, FORMAT_MILLISECOND));
node.setYear(calendar.get(Calendar.YEAR));
node.setMonth(calendar.get(Calendar.MONTH) + 1);
node.setDay(calendar.get(Calendar.DAY_OF_MONTH));
node.setHour(calendar.get(Calendar.HOUR_OF_DAY));
node.setMinute(calendar.get(Calendar.MINUTE));
node.setSecond(calendar.get(Calendar.SECOND));
node.setMillisecond(calendar.get(Calendar.MILLISECOND));
node.setWeek(getWeek(calendar));
node.setDayOfYear(calendar.get(Calendar.DAY_OF_YEAR));
node.setWeekOfYear(getWeekOfYear(date));
node.setWeekOfYearIgnoreLastYear(getWeekOfYearIgnoreLastYear(date));
node.setMillisecondStamp(date.getTime());
node.setSecondStamp(node.getMillisecondStamp() / 1000);
return node;
}
/**
* 日期变更
*
* @param date 指定日期
* @param field 变更属性(如变更年份,则该值为 Calendar.DAY_OF_YEAR)
* @param amount 变更大小(大于 0 时增加,小于 0 时减少)
* @return 变更后的日期时间
*/
public static Date add(Date date, int field, int amount) {
if (Objects.isNull(date)) {
return null;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(field, amount);
return calendar.getTime();
}
/**
* 指定日期加减年份
*
* @param date 指定日期
* @param year 变更年份(大于 0 时增加,小于 0 时减少)
* @return 变更年份后的日期
*/
public static Date addYear(Date date, int year) {
return add(date, Calendar.YEAR, year);
}
/**
* 指定日期加减月份
*
* @param date 指定日期
* @param month 变更月份(大于 0 时增加,小于 0 时减少)
* @return 变更月份后的日期
*/
public static Date addMonth(Date date, int month) {
return add(date, Calendar.MONTH, month);
}
/**
* 指定日期加减天数
*
* @param date 指定日期
* @param day 变更天数(大于 0 时增加,小于 0 时减少)
* @return 变更天数后的日期
*/
public static Date addDay(Date date, int day) {
return add(date, Calendar.DAY_OF_YEAR, day);
}
/**
* 指定日期加减星期
*
* @param date 指定日期
* @param week 变更星期数(大于 0 时增加,小于 0 时减少)
* @return 变更星期数后的日期
*/
public static Date addWeek(Date date, int week) {
return add(date, Calendar.WEEK_OF_YEAR, week);
}
/**
* 指定日期加减小时
*
* @param date 指定日期时间
* @param hour 变更小时数(大于 0 时增加,小于 0 时减少)
* @return 变更小时数后的日期时间
*/
public static Date addHour(Date date, int hour) {
return add(date, Calendar.HOUR_OF_DAY, hour);
}
/**
* 指定日期加减分钟
*
* @param date 指定日期时间
* @param minute 变更分钟数(大于 0 时增加,小于 0 时减少)
* @return 变更分钟数后的日期时间
*/
public static Date addMinute(Date date, int minute) {
return add(date, Calendar.MINUTE, minute);
}
/**
* 指定日期加减秒
*
* @param date 指定日期时间
* @param second 变更秒数(大于 0 时增加,小于 0 时减少)
* @return 变更秒数后的日期时间
*/
public static Date addSecond(Date date, int second) {
return add(date, Calendar.SECOND, second);
}
/**
* 指定日期加减秒
*
* @param date 指定日期时间
* @param millisecond 变更毫秒数(大于 0 时增加,小于 0 时减少)
* @return 变更毫秒数后的日期时间
*/
public static Date addMillisecond(Date date, int millisecond) {
return add(date, Calendar.MILLISECOND, millisecond);
}
/**
* 获取该日期所在周指定星期的日期
*
* @param date 日期所在时间
* @return index 指定星期(1 - 7 分别对应星期一到星期天)
*/
public static Date getWeekDate(Date date, int index) {
if (index < WEEK_1_MONDAY || index > WEEK_7_SUNDAY) {
return null;
}
int week = getWeek(date);
return addDay(date, index - week);
}
/**
* 获取该日期所在周开始日期
*
* @param date 日期所在时间
* @return 所在周开始日期
*/
public static Date getWeekDateStart(Date date) {
return getDateStart(getWeekDate(date, WEEK_1_MONDAY));
}
/**
* 获取该日期所在周开始日期
*
* @param date 日期所在时间
* @return 所在周开始日期
*/
public static Date getWeekDateEnd(Date date) {
return getWeekDateEnd(getWeekDate(date, WEEK_7_SUNDAY));
}
/**
* 获取该日期所在周的所有日期(周一到周日)
*
* @param date 日期
* @return 该日照所在周的所有日期
*/
public static List<Date> getWeekDateList(Date date) {
if (Objects.isNull(date)) {
return Collections.emptyList();
}
// 获取本周开始时间
Date weekFromDate = getWeekDateStart(date);
// 获取本周截止时间
Date weekeEndDate = getWeekDateEnd(date);
return getBetweenDateList(weekFromDate, weekeEndDate, true);
}
public static List<Date> getWeekDates(Date date) {
List<Date> weekDates = new ArrayList<>();
// 将Date转换为LocalDate
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1; // 月份从0开始
int day = calendar.get(Calendar.DAY_OF_MONTH);
// 获取给定日期所在周的第一天(周一)
calendar.set(year, month - 1, day);
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
int daysUntilMonday = (dayOfWeek - Calendar.MONDAY + 7) % 7;
calendar.add(Calendar.DAY_OF_MONTH, -daysUntilMonday);
// 将周一到周日的日期添加到列表中
for (int i = 0; i < 7; i++) {
weekDates.add(calendar.getTime());
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
return weekDates;
}
/**
* 获取该日期所在周的所有日期(周一到周日)
*
* @param dateString
* @return 该日照所在周的所有日期
*/
public static List<String> getWeekDateList(String dateString) {
Date date = parseDate(dateString);
if (Objects.isNull(date)) {
return Collections.emptyList();
}
return getDateStrList(getWeekDateList(date));
}
/**
* 获取该日期所在月的所有日期
*
* @param date
* @return 该日照所月的所有日期
*/
public static List<Date> getMonthDateList(Date date) {
if (Objects.isNull(date)) {
return Collections.emptyList();
}
Date monthDateStart = getMonthDateStart(date);
Date monthDateEnd = getMonthDateEnd(date);
return getBetweenDateList(monthDateStart, monthDateEnd, true);
}
/**
* 获取该日期所在月的所有日期
*
* @param dateString
* @return 该日照所月的所有日期
*/
public static List<String> getMonthDateList(String dateString) {
Date date = parseDate(dateString);
if (Objects.isNull(date)) {
return Collections.emptyList();
}
return getDateStrList(getMonthDateList(date));
}
/**
* 获取本日期所在月第一天
*
* @param date 日期
* @return 本日期所在月第一天
*/
public static Date getMonthDateStart(Date date) {
if (Objects.isNull(date)) {
return null;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_MONTH, 1);
return getDateStart(calendar.getTime());
}
/**
* 获取本日期所在月最后一天
*
* @param date 日期
* @return 本日期所在月最后一天
*/
public static Date getMonthDateEnd(Date date) {
if (Objects.isNull(date)) {
return null;
}
Date monthDateStart = getMonthDateStart(date);
Date nextMonthDateStart = getMonthDateStart(addMonth(monthDateStart, 1));
return getDateEnd(addDay(nextMonthDateStart, -1));
}
/**
* 获取两个日期相差的天数(以日期为单位计算,不以24小时制计算,详见下面说明)<br>
*
* 【说明】比如 2022-06-17 23:00:00 和 2022-06-17 01:00:00,两者虽然只相差 2 个小时,但也算相差 1 天 <br>
*
* @param date1 日期1
* @param date2 日期2
* @return 相差天数(若返回 -1,则至少有一个日期存在为空,此时不能进行比较)
*/
public static int countBetweenDays(Date date1, Date date2) {
if (Objects.isNull(date1) || Objects.isNull(date2)) {
return -1;
}
// 获取两个日期 0 点 0 时 0 分 0 秒 0 毫秒时的时间戳(毫秒级)
long t1 = getDateStart(date1).getTime();
long t2 = getDateStart(date2).getTime();
// 相差天数 = 相差的毫秒数 / 一天的毫秒数
return (int) (Math.abs(t1 - t2) / DAY_MILLISECONDS);
}
/**
* 获取两个日期之间的所有日期
*
* @param date1 日期1
* @param date2 日期2
* @return 两个日期之间的所有日期的开始时间
*/
public static List<Date> getBetweenDateList(Date date1, Date date2) {
return getBetweenDateList(date1, date2, false);
}
/**
* 获取两个日期之间的所有日期
*
* @param date1 日期1
* @param date2 日期2
* @return 两个日期之间的所有日期的开始时间
*/
public static List<Date> getBetweenDateList(Date date1, Date date2, boolean isContainParams) {
if (Objects.isNull(date1) || Objects.isNull(date2)) {
return Collections.emptyList();
}
// 确定前后日期
Date fromDate = date1;
Date toDate = date2;
if (date2.before(date1)) {
fromDate = date2;
toDate = date1;
}
// 获取两个日期每天的开始时间
Date from = getDateStart(fromDate);
Date to = getDateStart(toDate);
// 获取日期,开始循环
List<Date> dates = new ArrayList<Date>();
if (isContainParams) {
dates.add(from);
}
Date date = from;
boolean isBefore = true;
while (isBefore) {
date = addDay(date, 1);
isBefore = date.before(to);
if (isBefore) {
dates.add(getDateStart(date));
}
}
if (isContainParams) {
dates.add(to);
}
return dates;
}
/**
* 获取两个日期之间的所有日期
*
* @param dateString1 日期1(如:2022-06-20)
* @param dateString2 日期2(如:2022-07-15)
* @return 两个日期之间的所有日期(不包含参数日期)
*/
public static List<String> getBetweenDateList(String dateString1, String dateString2) {
return getBetweenDateList(dateString1, dateString2, false);
}
/**
* 获取两个日期之间的所有日期
*
* @param dateString1 日期1(如:2022-06-20)
* @param dateString2 日期2(如:2022-07-15)
* @param isContainParams 是否包含参数的两个日期
* @return 两个日期之间的所有日期的开始时间
*/
public static List<String> getBetweenDateList(String dateString1, String dateString2, boolean isContainParams) {
Date date1 = parseDate(dateString1);
Date date2 = parseDate(dateString2);
List<Date> dates = getBetweenDateList(date1, date2, isContainParams);
return getDateStrList(dates);
}
/**
* List<Date> 转 List<String>
*
* @param dates 日期集合
* @return 日期字符串集合
*/
public static List<String> getDateStrList(List<Date> dates) {
if (dates.isEmpty()) {
return Collections.emptyList();
}
List<String> dateList = new ArrayList<String>();
for (Date date : dates) {
dateList.add(formatDate(date));
}
return dateList;
}
/**
* 日期计算年龄
* @param birthday
* @return
*/
public static Integer getAgeByBirthday(Date birthday) {
Calendar birthDate = Calendar.getInstance();
birthDate.setTime(birthday);
Calendar currentDate = Calendar.getInstance();
int age = currentDate.get(Calendar.YEAR) - birthDate.get(Calendar.YEAR);
// 可以考虑将当前月份和生日月份进行比较,以确定是否已过生日,进而确定是否需要减少1岁
if (currentDate.get(Calendar.MONTH) < birthDate.get(Calendar.MONTH)
|| (currentDate.get(Calendar.MONTH) == birthDate.get(Calendar.MONTH)
&& currentDate.get(Calendar.DAY_OF_MONTH) < birthDate.get(Calendar.DAY_OF_MONTH))) {
age--;
}
return age;
}
/**
* 传入时间与当天时间计算时间差 毫秒
* @param date
* @return
*/
public static long getNowAndEntDayDifference(Date date) {
LocalDateTime strTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
LocalDateTime endTime = LocalDateTime.ofInstant(Objects.requireNonNull(getDateEnd(date)).toInstant(), ZoneId.systemDefault());
Duration duration = Duration.between(strTime, endTime); // 获取持续时间对象
return duration.toMinutes() ;
}
static class DateNode {
/** 年 */
private int year;
/** 月 */
private int month;
/** 日 */
private int day;
/** 时 */
private int hour;
/** 分 */
private int minute;
/** 秒 */
private int second;
/** 毫秒 */
private int millisecond;
/** 星期几( 1 - 7 对应周一到周日) */
private int week;
/** 当年第几天 */
private int dayOfYear;
/** 当年第几周(本年周 1 为第 1 周,0 则表示属于去年最后一周) */
private int weekOfYear;
/** 当年第几周(本年周 1 为第 1 周,0 则表示属于去年最后一周) */
private int weekOfYearIgnoreLastYear;
/** 时间戳(秒级) */
private long secondStamp;
/** 时间戳(毫秒级) */
private long millisecondStamp;
/** 显示时间 */
private String time;
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public int getHour() {
return hour;
}
public void setHour(int hour) {
this.hour = hour;
}
public int getMinute() {
return minute;
}
public void setMinute(int minute) {
this.minute = minute;
}
public int getSecond() {
return second;
}
public void setSecond(int second) {
this.second = second;
}
public int getMillisecond() {
return millisecond;
}
public void setMillisecond(int millisecond) {
this.millisecond = millisecond;
}
public int getWeek() {
return week;
}
public void setWeek(int week) {
this.week = week;
}
public int getDayOfYear() {
return dayOfYear;
}
public void setDayOfYear(int dayOfYear) {
this.dayOfYear = dayOfYear;
}
public int getWeekOfYear() {
return weekOfYear;
}
public void setWeekOfYear(int weekOfYear) {
this.weekOfYear = weekOfYear;
}
public int getWeekOfYearIgnoreLastYear() {
return weekOfYearIgnoreLastYear;
}
public void setWeekOfYearIgnoreLastYear(int weekOfYearIgnoreLastYear) {
this.weekOfYearIgnoreLastYear = weekOfYearIgnoreLastYear;
}
public long getSecondStamp() {
return secondStamp;
}
public void setSecondStamp(long secondStamp) {
this.secondStamp = secondStamp;
}
public long getMillisecondStamp() {
return millisecondStamp;
}
public void setMillisecondStamp(long millisecondStamp) {
this.millisecondStamp = millisecondStamp;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
}
String字符串工具类
作用:对String类型字符串进行处理
使用:调用工具类里对应方法
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* String工具类.(来源spring框架)。
* 如果需要更多的功能,请使用apache-commons-lang的StringUtils
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Keith Donald
* @author Rob Harrop
* @author Rick Evans
* @author Arjen Poutsma
* @since 16 April 2001
*/
public abstract class StringUtils {
private static final Logger logger = LoggerFactory.getLogger(StringUtils.class);
private static final String FOLDER_SEPARATOR = "/";
private static final String WINDOWS_FOLDER_SEPARATOR = "\\";
private static final String TOP_PATH = "..";
private static final String CURRENT_PATH = ".";
private static final char EXTENSION_SEPARATOR = '.';
//---------------------------------------------------------------------
// General convenience methods for working with Strings
//---------------------------------------------------------------------
/**
* 将下划线风格替换为驼峰风格
*/
public static String underlineToCamelhump(String str) {
if(!hasText(str)){
return str;
}
Matcher matcher = Pattern.compile("_[a-z]").matcher(str.toLowerCase());
StringBuilder builder = new StringBuilder(str);
for (int i = 0; matcher.find(); i++) {
builder.replace(matcher.start() - i, matcher.end() - i, matcher.group().substring(1).toUpperCase());
}
if (Character.isUpperCase(builder.charAt(0))) {
builder.replace(0, 1, String.valueOf(Character.toLowerCase(builder.charAt(0))));
}
return builder.toString();
}
/**
* 将驼峰风格替换为下划线风格
*/
public static String camelhumpToUnderline(String str) {
final int size;
final char[] chars;
final StringBuilder sb = new StringBuilder(
(size = (chars = str.toCharArray()).length) * 3 / 2 + 1);
char c;
for (int i = 0; i < size; i++) {
c = chars[i];
if (isUppercaseAlpha(c)) {
sb.append('_').append(c);
} else {
sb.append(toUpperAscii(c));
}
}
return sb.charAt(0) == '_' ? sb.substring(1) : sb.toString();
}
public static boolean isUppercaseAlpha(char c) {
return (c >= 'A') && (c <= 'Z');
}
public static char toUpperAscii(char c) {
if (isUppercaseAlpha(c)) {
c -= (char) 0x20;
}
return c;
}
/**
* 判断字符串是否为空或null
*/
public static boolean isEmpty(Object str) {
return (str == null || "".equals(str));
}
public static boolean isNotEmpty(Object str){
return !isEmpty(str);
}
public static boolean isBlank(String str) {
return str == null || str.trim().isEmpty();
}
public static boolean isNotBlank(String str) {
return !isBlank(str);
}
/**
* 判断字符序列是否有内容:
* <p><pre class="code">
* StringUtils.hasLength(null) = false
* StringUtils.hasLength("") = false
* StringUtils.hasLength(" ") = true
* StringUtils.hasLength("Hello") = true
* </pre>
*/
public static boolean hasLength(CharSequence str) {
return (str != null && str.length() > 0);
}
/**
* 判断字符串是否有内容:
*/
public static boolean hasLength(String str) {
return hasLength((CharSequence) str);
}
/**
* 判断字符序列是否有非空的内容。
* <p><pre class="code">
* StringUtils.hasText(null) = false
* StringUtils.hasText("") = false
* StringUtils.hasText(" ") = false
* StringUtils.hasText("12345") = true
* StringUtils.hasText(" 12345 ") = true
* </pre>
*/
public static boolean hasText(CharSequence str) {
if (!hasLength(str)) {
return false;
}
int strLen = str.length();
for (int i = 0; i < strLen; i++) {
if (!Character.isWhitespace(str.charAt(i))) {
return true;
}
}
return false;
}
/**
* 判断字符串是否有非空的内容。
*/
public static boolean hasText(String str) {
return hasText((CharSequence) str);
}
/**
* 字符串中是否有空格
*/
public static boolean containsWhitespace(String str) {
if (!hasLength(str)) {
return false;
}
int strLen = str.length();
for (int i = 0; i < strLen; i++) {
if (Character.isWhitespace(str.charAt(i))) {
return true;
}
}
return false;
}
/**
* 减去字符串两端的空格
*/
public static String trimWhitespace(String str) {
if (!hasLength(str)) {
return str;
}
StringBuilder sb = new StringBuilder(str);
while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {
sb.deleteCharAt(0);
}
while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
/**
* 减去字符串两端及中间的所有空格
*/
public static String trimAllWhitespace(String str) {
if (!hasLength(str)) {
return str;
}
int len = str.length();
StringBuilder sb = new StringBuilder(str.length());
for (int i = 0; i < len; i++) {
char c = str.charAt(i);
if (!Character.isWhitespace(c)) {
sb.append(c);
}
}
return sb.toString();
}
/**
* 减去字符串开头的空格
*/
public static String trimLeadingWhitespace(String str) {
if (!hasLength(str)) {
return str;
}
StringBuilder sb = new StringBuilder(str);
while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {
sb.deleteCharAt(0);
}
return sb.toString();
}
/**
* 减去字符串末尾的空格
*/
public static String trimTrailingWhitespace(String str) {
if (!hasLength(str)) {
return str;
}
StringBuilder sb = new StringBuilder(str);
while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
/**
* 从字符串前端中减去指定的char。
*/
public static String trimLeadingCharacter(String str, char leadingCharacter) {
if (!hasLength(str)) {
return str;
}
StringBuilder sb = new StringBuilder(str);
while (sb.length() > 0 && sb.charAt(0) == leadingCharacter) {
sb.deleteCharAt(0);
}
return sb.toString();
}
/**
* 从字符串尾端减去指定的char
*/
public static String trimTrailingCharacter(String str, char trailingCharacter) {
if (!hasLength(str)) {
return str;
}
StringBuilder sb = new StringBuilder(str);
while (sb.length() > 0 && sb.charAt(sb.length() - 1) == trailingCharacter) {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
/**
* 判断字符串是否以prefix开头,忽略大小写
*/
public static boolean startsWithIgnoreCase(String str, String prefix) {
if (str == null || prefix == null) {
return false;
}
if (str.startsWith(prefix)) {
return true;
}
if (str.length() < prefix.length()) {
return false;
}
String lcStr = str.substring(0, prefix.length()).toLowerCase();
String lcPrefix = prefix.toLowerCase();
return lcStr.equals(lcPrefix);
}
/**
* 判断字符串是否以prefix结尾,忽略大小写
*/
public static boolean endsWithIgnoreCase(String str, String suffix) {
if (str == null || suffix == null) {
return false;
}
if (str.endsWith(suffix)) {
return true;
}
if (str.length() < suffix.length()) {
return false;
}
String lcStr = str.substring(str.length() - suffix.length()).toLowerCase();
String lcSuffix = suffix.toLowerCase();
return lcStr.equals(lcSuffix);
}
/**
* 计算sub在str中出现的次数.
*/
public static int countOccurrencesOf(String str, String sub) {
if (str == null || sub == null || str.length() == 0 || sub.length() == 0) {
return 0;
}
int count = 0;
int pos = 0;
int idx;
while ((idx = str.indexOf(sub, pos)) != -1) {
++count;
pos = idx + sub.length();
}
return count;
}
/**
* 在inString中,将所有的oldPattern替换为newPattern
*/
public static String replace(String inString, String oldPattern, String newPattern) {
if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) {
return inString;
}
StringBuilder sb = new StringBuilder();
int pos = 0; // our position in the old string
int index = inString.indexOf(oldPattern);
// the index of an occurrence we've found, or -1
int patLen = oldPattern.length();
while (index >= 0) {
sb.append(inString.substring(pos, index));
sb.append(newPattern);
pos = index + patLen;
index = inString.indexOf(oldPattern, pos);
}
sb.append(inString.substring(pos));
// remember to append any characters to the right of a match
return sb.toString();
}
/**
* 从inString中删除所有的pattern
*/
public static String delete(String inString, String pattern) {
return replace(inString, pattern, "");
}
/**
* 从inString中删除包含在charsToDelete中的所有char
*/
public static String deleteAny(String inString, String charsToDelete) {
if (!hasLength(inString) || !hasLength(charsToDelete)) {
return inString;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < inString.length(); i++) {
char c = inString.charAt(i);
if (charsToDelete.indexOf(c) == -1) {
sb.append(c);
}
}
return sb.toString();
}
//---------------------------------------------------------------------
// Convenience methods for working with formatted Strings
//---------------------------------------------------------------------
/**
* 为str打上单引号标记
*/
public static String quote(String str) {
return (str != null ? "'" + str + "'" : null);
}
/**
* obj如果是String类型,则为其打上单引号标记
*/
public static Object quoteIfString(Object obj) {
return (obj instanceof String ? quote((String) obj) : obj);
}
/**
* 获得最后一个‘.’后的字符串内容。例如:"this.name.is.qualified", 返回 "qualified".
*/
public static String unqualify(String qualifiedName) {
return unqualify(qualifiedName, '.');
}
/**
* 获得最后一个separator后的字符串内容。
*/
public static String unqualify(String qualifiedName, char separator) {
return qualifiedName.substring(qualifiedName.lastIndexOf(separator) + 1);
}
/**
* 字符串首字母大写
*/
public static String capitalize(String str) {
return changeFirstCharacterCase(str, true);
}
/**
* 字符串首字母小写
*/
public static String uncapitalize(String str) {
return changeFirstCharacterCase(str, false);
}
private static String changeFirstCharacterCase(String str, boolean capitalize) {
if (str == null || str.length() == 0) {
return str;
}
StringBuilder sb = new StringBuilder(str.length());
if (capitalize) {
sb.append(Character.toUpperCase(str.charAt(0)));
}
else {
sb.append(Character.toLowerCase(str.charAt(0)));
}
sb.append(str.substring(1));
return sb.toString();
}
/**
* 从path中解析出filename,
* 例如: "mypath/myfile.txt" -> "myfile.txt".
*/
public static String getFilename(String path) {
if (path == null) {
return null;
}
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path);
}
public static byte[] getBytes(String s) {
try {
return s.getBytes("UTF8");
} catch (UnsupportedEncodingException e) {
logger.error(e.getMessage(), e);
}
return null;
}
public static String fromBytes(byte[] b) {
try {
return new String(b, "UTF8");
} catch (UnsupportedEncodingException e) {
logger.error(e.getMessage(), e);
}
return null;
}
/**
* 从path中解析出文件的后缀名,
* 例如: "mypath/myfile.txt" -> "txt".
*/
public static String getFilenameExtension(String path) {
if (path == null) {
return null;
}
int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR);
if (extIndex == -1) {
return null;
}
int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);
if (folderIndex > extIndex) {
return null;
}
return path.substring(extIndex + 1);
}
/**
* 将relativePath加到path的最后一个层级之后,也即最后一级的“/”的后面。
*/
public static String applyRelativePath(String path, String relativePath) {
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
if (separatorIndex != -1) {
String newPath = path.substring(0, separatorIndex);
if (!relativePath.startsWith(FOLDER_SEPARATOR)) {
newPath += FOLDER_SEPARATOR;
}
return newPath + relativePath;
}
else {
return relativePath;
}
}
/**
* Normalize the path by suppressing sequences like "path/.." and
* inner simple dots.
* <p>The result is convenient for path comparison. For other uses,
* notice that Windows separators ("\") are replaced by simple slashes.
* @param path the original path
* @return the normalized path
*/
public static String cleanPath(String path) {
if (path == null) {
return null;
}
String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR);
// Strip prefix from path to analyze, to not treat it as part of the
// first path element. This is necessary to correctly parse paths like
// "file:core/../core/io/Resource.class", where the ".." should just
// strip the first "core" directory while keeping the "file:" prefix.
int prefixIndex = pathToUse.indexOf(":");
String prefix = "";
if (prefixIndex != -1) {
prefix = pathToUse.substring(0, prefixIndex + 1);
if (prefix.contains("/")) {
prefix = "";
}
else {
pathToUse = pathToUse.substring(prefixIndex + 1);
}
}
if (pathToUse.startsWith(FOLDER_SEPARATOR)) {
prefix = prefix + FOLDER_SEPARATOR;
pathToUse = pathToUse.substring(1);
}
String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR);
List<String> pathElements = new LinkedList<String>();
int tops = 0;
for (int i = pathArray.length - 1; i >= 0; i--) {
String element = pathArray[i];
if (CURRENT_PATH.equals(element)) {
// Points to current directory - drop it.
}
else if (TOP_PATH.equals(element)) {
// Registering top path found.
tops++;
}
else {
if (tops > 0) {
// Merging path element with element corresponding to top path.
tops--;
}
else {
// Normal path element found.
pathElements.add(0, element);
}
}
}
// Remaining top paths need to be retained.
for (int i = 0; i < tops; i++) {
pathElements.add(0, TOP_PATH);
}
return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);
}
/**
* 判断两个path是否相同
*/
public static boolean pathEquals(String path1, String path2) {
return cleanPath(path1).equals(cleanPath(path2));
}
//---------------------------------------------------------------------
// Convenience methods for working with String arrays
//---------------------------------------------------------------------
/**
* 将str添加到array的末尾
*/
public static String[] addStringToArray(String[] array, String str) {
if (ObjUtil.isEmpty(array)) {
return new String[] {str};
}
String[] newArr = new String[array.length + 1];
System.arraycopy(array, 0, newArr, 0, array.length);
newArr[array.length] = str;
return newArr;
}
/**
* 合并两个字符串数组
*/
public static String[] concatenateStringArrays(String[] array1, String[] array2) {
if (ObjUtil.isEmpty(array1)) {
return array2;
}
if (ObjUtil.isEmpty(array2)) {
return array1;
}
String[] newArr = new String[array1.length + array2.length];
System.arraycopy(array1, 0, newArr, 0, array1.length);
System.arraycopy(array2, 0, newArr, array1.length, array2.length);
return newArr;
}
/**
* 合并两个字符串数组,且过滤掉重复的元素
*/
public static String[] mergeStringArrays(String[] array1, String[] array2) {
if (ObjUtil.isEmpty(array1)) {
return array2;
}
if (ObjUtil.isEmpty(array2)) {
return array1;
}
List<String> result = new ArrayList<String>();
result.addAll(Arrays.asList(array1));
for (String str : array2) {
if (!result.contains(str)) {
result.add(str);
}
}
return toStringArray(result);
}
/**
* 对字符串数组进行排序
*/
public static String[] sortStringArray(String[] array) {
if (ObjUtil.isEmpty(array)) {
return new String[0];
}
Arrays.sort(array);
return array;
}
/**
* 将String类型的集合转为字符串数组
*/
public static String[] toStringArray(Collection<String> collection) {
if (collection == null) {
return null;
}
return collection.toArray(new String[collection.size()]);
}
/**
* 将字符串类型的Enumeration转换为字符串数组
*/
public static String[] toStringArray(Enumeration<String> enumeration) {
if (enumeration == null) {
return null;
}
List<String> list = Collections.list(enumeration);
return list.toArray(new String[list.size()]);
}
/**
* 对array的每个元素进行trim操作
*/
public static String[] trimArrayElements(String[] array) {
if (ObjUtil.isEmpty(array)) {
return new String[0];
}
String[] result = new String[array.length];
for (int i = 0; i < array.length; i++) {
String element = array[i];
result[i] = (element != null ? element.trim() : null);
}
return result;
}
/**
* 使用TreeSet实现对array中重复元素的过滤,并具备排序功能
*/
public static String[] removeDuplicateStrings(String[] array) {
if (ObjUtil.isEmpty(array)) {
return array;
}
Set<String> set = new TreeSet<String>();
for (String element : array) {
set.add(element);
}
return toStringArray(set);
}
/**
* 将toSplit从第一次出现delimiter的地方截断,不包含delimiter。
*/
public static String[] split(String toSplit, String delimiter) {
if (!hasLength(toSplit) || !hasLength(delimiter)) {
return null;
}
int offset = toSplit.indexOf(delimiter);
if (offset < 0) {
return null;
}
String beforeDelimiter = toSplit.substring(0, offset);
String afterDelimiter = toSplit.substring(offset + delimiter.length());
return new String[] {beforeDelimiter, afterDelimiter};
}
/**
* 将字符串数组转换为Properties。其每一个字符串元素通过delimiter进行截断,作为key和value值。
*/
public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) {
return splitArrayElementsIntoProperties(array, delimiter, null);
}
/**
* 将字符串数组转换为Properties。其每一个字符串元素通过delimiter进行截断,作为key和value值。
* 同时删除掉字符串中所有的包含charsToDelete的字符。
*/
public static Properties splitArrayElementsIntoProperties(
String[] array, String delimiter, String charsToDelete) {
if (ObjUtil.isEmpty(array)) {
return null;
}
Properties result = new Properties();
for (String element : array) {
if (charsToDelete != null) {
element = deleteAny(element, charsToDelete);
}
String[] splittedElement = split(element, delimiter);
if (splittedElement == null) {
continue;
}
result.setProperty(splittedElement[0].trim(), splittedElement[1].trim());
}
return result;
}
/**
* 将通过delimiter分隔的字符串转化为数组
*/
public static String[] delimitedListToStringArray(String str, String delimiter) {
return delimitedListToStringArray(str, delimiter, null);
}
/**
* Take a String which is a delimited list and convert it to a String array.
* <p>A single delimiter can consists of more than one character: It will still
* be considered as single delimiter string, rather than as bunch of potential
* delimiter characters - in contrast to {@code tokenizeToStringArray}.
* @param str the input String
* @param delimiter the delimiter between elements (this is a single delimiter,
* rather than a bunch individual delimiter characters)
* @param charsToDelete a set of characters to delete. Useful for deleting unwanted
* line breaks: e.g. "\r\n\f" will delete all new lines and line feeds in a String.
* @return an array of the tokens in the list
* @see #tokenizeToStringArray
*/
public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) {
if (str == null) {
return new String[0];
}
if (delimiter == null) {
return new String[] {str};
}
List<String> result = new ArrayList<String>();
if ("".equals(delimiter)) {
for (int i = 0; i < str.length(); i++) {
result.add(deleteAny(str.substring(i, i + 1), charsToDelete));
}
}
else {
int pos = 0;
int delPos;
while ((delPos = str.indexOf(delimiter, pos)) != -1) {
result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
pos = delPos + delimiter.length();
}
if (str.length() > 0 && pos <= str.length()) {
// Add rest of String, but not in case of empty input.
result.add(deleteAny(str.substring(pos), charsToDelete));
}
}
return toStringArray(result);
}
/**
* 将通过逗号分隔的字符串转化为数组。
*/
public static String[] commaDelimitedListToStringArray(String str) {
return delimitedListToStringArray(str, ",");
}
/**
* 将通过逗号分隔的字符串,转化为set集合。
*/
public static Set<String> commaDelimitedListToSet(String str) {
Set<String> set = new TreeSet<String>();
String[] tokens = commaDelimitedListToStringArray(str);
for (String token : tokens) {
set.add(token);
}
return set;
}
/**
* 将coll转换为字符串, 每个元素用delim分隔,并加上前后缀
*/
public static String collectionToDelimitedString(Collection<?> coll, String delim, String prefix, String suffix) {
if (CollUtil.isEmpty(coll)) {
return "";
}
StringBuilder sb = new StringBuilder();
Iterator<?> it = coll.iterator();
while (it.hasNext()) {
sb.append(prefix).append(it.next()).append(suffix);
if (it.hasNext()) {
sb.append(delim);
}
}
return sb.toString();
}
/**
* 将coll转换为字符串, 每个元素用delim分隔。
*/
public static String collectionToDelimitedString(Collection<?> coll, String delim) {
return collectionToDelimitedString(coll, delim, "", "");
}
/**
* 将coll转换为字符串, 每个元素用逗号分隔。
*/
public static String collectionToCommaDelimitedString(Collection<?> coll) {
return collectionToDelimitedString(coll, ",");
}
/**
* 将arr转换为字符串,每个元素用delim分隔。
*/
public static String arrayToDelimitedString(Object[] arr, String delim) {
if (ObjUtil.isEmpty(arr)) {
return "";
}
if (arr.length == 1) {
return ObjUtil.toString(arr[0]);
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
if (i > 0) {
sb.append(delim);
}
sb.append(arr[i]);
}
return sb.toString();
}
/**
* 将arr转换为字符串,每个元素用逗号分隔。
*/
public static String arrayToCommaDelimitedString(Object[] arr) {
return arrayToDelimitedString(arr, ",");
}
/**
* Convert byte[] to hex string.这里我们可以将byte转换成int,然后利用Integer.toHexString(int)来转换成16进制字符串。
* @param src byte[] data
* @return hex string
*/
public static String bytesToHexString(byte[] src){
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* Convert hex string to byte[]
* @param hexString the hex string
* @return byte[]
*/
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
/**
* Convert char to byte
* @param c char
* @return byte
*/
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
/**
* Tokenize the given String into a String array via a StringTokenizer.
* Trims tokens and omits empty tokens.
* <p>The given delimiters string is supposed to consist of any number of
* delimiter characters. Each of those characters can be used to separate
* tokens. A delimiter is always a single character; for multi-character
* delimiters, consider using <code>delimitedListToStringArray</code>
* <p/>
* <p>Copied from the Spring Framework while retaining all license, copyright and author information.
*
* @param str the String to tokenize
* @param delimiters the delimiter characters, assembled as String
* (each of those characters is individually considered as delimiter).
* @return an array of the tokens
* @see StringTokenizer
* @see String#trim()
*/
public static String[] tokenizeToStringArray(String str, String delimiters) {
return tokenizeToStringArray(str, delimiters, true, true);
}
/**
* Tokenize the given String into a String array via a StringTokenizer.
* <p>The given delimiters string is supposed to consist of any number of
* delimiter characters. Each of those characters can be used to separate
* tokens. A delimiter is always a single character; for multi-character
* delimiters, consider using <code>delimitedListToStringArray</code>
* <p/>
* <p>Copied from the Spring Framework while retaining all license, copyright and author information.
*
* @param str the String to tokenize
* @param delimiters the delimiter characters, assembled as String
* (each of those characters is individually considered as delimiter)
* @param trimTokens trim the tokens via String's <code>trim</code>
* @param ignoreEmptyTokens omit empty tokens from the result array
* (only applies to tokens that are empty after trimming; StringTokenizer
* will not consider subsequent delimiters as token in the first place).
* @return an array of the tokens (<code>null</code> if the input String
* was <code>null</code>)
* @see StringTokenizer
* @see String#trim()
*/
@SuppressWarnings({"unchecked"})
public static String[] tokenizeToStringArray(
String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
if (str == null) {
return null;
}
StringTokenizer st = new StringTokenizer(str, delimiters);
List tokens = new ArrayList();
while (st.hasMoreTokens()) {
String token = st.nextToken();
if (trimTokens) {
token = token.trim();
}
if (!ignoreEmptyTokens || token.length() > 0) {
tokens.add(token);
}
}
return toStringArray(tokens);
}
/**
* 首字母转大写
* @param s
* @return
*/
public static String toUpperCaseFirstOne(String s){
if(Character.isUpperCase(s.charAt(0))){
return s;
} else {
return (new StringBuilder()).append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)).toString();
}
}
}