正则表达式
基础语法
x|y:匹配单个字符x或者单个字符y
[xyz]:匹配指定(xyz)字符的任意一个
[^xyz]:匹配非(xyz)字符的任意一个
[0-9]:匹配范围;表示0到9的任意一个数字
[^a-z]:匹配范围;表示非小写字母(简单理解为非 a-z中的字母)
^:匹配开头;例如 :^[0-9]:表示以数字开头,并且只有一个数字,例如,字符串“1”匹配结果是true,字符串“12”匹配结果就是false
$:匹配结尾;例如:[0-9]$:表示以数字结尾,并且只有一个数字,同上
*:匹配0次或多次;例如:[0-9]*:表示匹配0次或多次数字
?:匹配0次或1次;例如:[0-9]?:表示匹配0次或一次数字
+:匹配1次或多次;例如:[0-9]+:表示匹配1次或多次数字
{n}:匹配确定的n次;例如:[0-9]{5};表示匹配5次数字
{n,m}:(n<=m)至少匹配n次,最多匹配m次;例如;[0-9]{1,2}:表示最少匹配一次数字,最多匹配2次数字
特殊字符
\d:表示一个十进制的数字 [0-9]
\D:表示非数字
\w:表示一个字符[0-9a-zA-Z_]
\W:表示除[0-9a-zA-Z_]之外的字符
\s:表示一个空白字符(空格,tab,换页符等)
\S:表示一个非空白字符
这些都是正则中能够表示一类字符的原子
常见写法
(1)匹配以某一个数字(字符)开头
^[1-9]:以1-9中的某一个数字开头,并且只有一个数字
(2)匹配固定格式开头
^(123):必须是123开头,并且这个串只有这三个数,例如,字符串“123”匹配结果是true,字符串“1234”匹配结果就是false,因为多了一个“4”,字符串“213”也是false,顺序不符合
(3)判断小数(转译字符相关java)
0\\.[1-9]:大于0小于1的小数,有且只有一个小数(这里重点关注哪个小数点的写法就行)
crontab表达式
详情参考:https://help.aliyun.com/document_detail/133509.html
基础语法
CRON 有如下两种语法格式:
秒 分 小时 日期 月份 星期 年
秒 分 小时 日期 月份 星期
一般都用下面这个(不带年的那个),“年”这个很少用到,可以暂时忽略
常用特殊字符
*:表示匹配域的任意值,在分这个域使用 *,即表示每分钟都会触发事件。
?:表示匹配域的任意值,但只能用在日期和星期两个域,因为这两个域会相互影响。示例:要在每月的 20 号触发调度,不管每个月的 20 号是星期几,则只能使用如下写法:0 0 0 20 * ?。其中,因为日期域已经指定了 20 号,最后一位星期域只能用 ?,不能使用 *。如果最后一位使用 *,则表示不管星期几都会触发,与日期域的 20 号相斥,此时表达式不正确。
-:表示起止范围,示例:在分这个域使用 5-20,表示从 5 分到 20 分钟每分钟触发一次。
/:表示起始时间开始触发,然后每隔固定时间触发一次,示例:在分这个域使用 5/20,表示从第5分钟开始触发,之后每 20 分钟触发一次,即 5、 25、45 等分别触发一次。
常用写法
*/5 * * * * ?:每隔 5 秒执行一次,等同于 0/5 * * * * ?
0 */1 * * * ?:每隔 1 分钟执行一次,等同于 0 0/1 * * * ?
0 0 2 1 * ?:每月 1 日的凌晨 2 点执行一次
0 15 10 ? * MON-FRI:周一到周五每天上午 10:15 执行作业
0 15 10 ? 6L 2002-2006:2002 年至 2006 年的每个月的最后一个星期五上午 10:15 执行作业
0 0 23 * * ?:每天 23 点执行一次
0 0 1 * * ?:每天凌晨 1 点执行一次
0 0 1 1 * ?:每月 1 日凌晨 1 点执行一次
0 0 23 L * ?:每月最后一天 23 点执行一次
0 0 1 ? * L:每周星期天凌晨 1 点执行一次
0 26,29,33 * * * ?:在 26 分、29 分、33 分执行一次
0 0 0,13,18,21 * * ?:每天的 0 点、13 点、18 点、21 点都执行一次
0 0 10,14,16 * * ?:每天上午 10 点,下午 2 点,4 点执行一次
0 0/30 9-17 * * ?:朝九晚五工作时间内每半小时执行一次
0 0 12 ? * WED:每个星期三中午 12 点执行一次
0 0 12 * * ?:每天中午 12 点触发
0 15 10 ? * *:每天上午 10:15 触发
0 15 10 * * ?:每天上午 10:15 触发
0 15 10 * * ? *:每天上午 10:15 触发
0 15 10 * * ? 2005:2005 年的每天上午 10:15 触发
0 * 14 * * ?:每天下午 2 点到 2:59 期间的每 1 分钟触发
0 0/5 14 * * ?:每天下午 2 点到 2:55 期间的每 5 分钟触发
0 0/5 14,18 * * ?:每天下午 2 点到 2:55 期间和下午 6 点到 6:55 期间的每 5 分钟触发
0 0-5 14 * * ?:每天下午 2 点到 2:05 期间的每 1 分钟触发
0 10,44 14 ? 3 WED:每年三月的星期三的下午 2:10 和 2:44 触发
0 15 10 ? * MON-FRI:周一至周五的上午 10:15 触发
0 15 10 15 * ?:每月 15 日上午 10:15 触发
0 15 10 L * ?:每月最后一日的上午 10:15 触发
0 15 10 ? * 6L:每月的最后一个星期五上午 10:15 触发
0 15 10 ? * 6L 2002-2005:2002 年至 2005 年的每月的最后一个星期五上午 10:15 触发
0 15 10 ? * 6#3:每月的第三个星期五上午 10:15 触发
常用小工具
(1)将电话号码的中间4位替换成*
public class Mobile {
public static void main(String[] args) {
String mobile = "13681467423";
mobile = mobile.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
System.out.println(mobile);
}
}
(2)姓名脱敏
脱敏规则: 只显示第一个汉字,比如李某某置换为李**, 李某置换为李*
private static String desensitizedName(String fullName){
if (!Strings.isNullOrEmpty(name)) {
String name = StringUtils.left(fullName, 1);
return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
}
return name;
}
(3)身份证号脱敏
脱敏规则: 保留前六后三, 适用于15位和18位身份证号
方式一: 需要判断身份证号位数
private static String desensitizedIdNumber(String idNumber){
if (!Strings.isNullOrEmpty(idNumber)) {
if (idNumber.length() == 15){
idNumber = idNumber.replaceAll("(\\w{6})\\w*(\\w{3})", "$1******$2");
}
if (idNumber.length() == 18){
idNumber = idNumber.replaceAll("(\\w{6})\\w*(\\w{3})", "$1*********$2");
}
}
return idNumber;
}
(4)地址脱敏
脱敏规则: 从第4位开始隐藏,隐藏8位
因地址位数是不确定的,所以结尾长度为总长度减去 前面保留长度和隐藏长度之和 address.length()-11
private static String desensitizedAddress(String address){
if (!Strings.isNullOrEmpty(address)) {
return StringUtils.left(address, 3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(address, address.length()-11), StringUtils.length(address), "*"), "***"));
}
return address;
}
(5)日期相关
获取指定日期的23:59:59
示例:date为new date(),如果传入-1,获取的就是前一天的23:59:59;如果传入的是0,获取的是就是当天的23:59:59
//new date(),-1
public static Date getEndOfDate(Date date, int amount){
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.add(Calendar.DAY_OF_MONTH, amount);
return calendar.getTime();
}
jdk自带的加解密工具
package com.jd.mcs.util;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.SecureRandom;
public class AesUtils {
/**
* 生成的key
*/
private static Key key;
/**
* 生成密钥的字符串(自己随便写)
*/
private static final String ENCRYPT_KEY = "test";
/**
* 加密
* @param encryptionStr
* @return
*/
public static String encrypt(String encryptionStr) {
try {
// 加密
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, getKey());
byte[] resultBytes = cipher.doFinal(encryptionStr.getBytes());
//使用Base64将加密后的字节数组变成字符串
return Base64.encodeBase64String(resultBytes);
} catch (Exception e) {
log.error("加密失败!", e);
return null;
}
}
/**
* 解密
* @param encryptionStr
* @return
*/
public static String decrypt(String encryptionStr) {
try {
// 解密
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, getKey());
byte[] result = cipher.doFinal(Base64.decodeBase64(encryptionStr));
return new String(result);
} catch (Exception e) {
log.error("解密失败!", e);
return null;
}
}
/**
* 生成密钥
* @return
*/
public static Key getKey() {
if (null != key) return key;
synchronized (AesUtils.class) {
if (null == key) {
try {
// 生成KEY ,AES 要求密钥长度为 128
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(ENCRYPT_KEY.getBytes());
keyGenerator.init(128, secureRandom);
SecretKey secretKey = keyGenerator.generateKey();
byte[] byteKey = secretKey.getEncoded();
// 转换KEY
key = new SecretKeySpec(byteKey, "AES");
} catch (Exception e) {
log.error("生成密钥失败!", e);
e.printStackTrace();
}
}
}
return key;
}
}