util常用类
-
字符串转日期
Date/Calendar类
date类
现阶段讲的date是位于util包下的一个类,他拥有的方法有(其他方法都弃用了):
//常用构造: Date(); Date(long date); //常用方法: long getTime(); private static void demo11() { Date date=new Date(); System.out.println(date);//得到当前时间日期:Sun Nov 13 15:28:15 CST 2022 System.out.println(System.currentTimeMillis());//获得当前时间的毫秒数:1668324639580 Date date1 = new Date(System.currentTimeMillis());//参数为毫秒数 System.out.println(date1.getTime());//获得当前时间的毫秒数:1668324639580 }
calendar类
该类为一个抽象类,一般用里面的静态方法,getInstance()来创建一个calendar的实例对象,然后利用对象来调用方法。常用方法都有以下:
对象.get(Calendar.YEAR): 获得对象当前年份
对象.get(Calendar.MONTH)+1: 获得对象当前月份,因为month是0~11因此需要+1
对象.set(Calendar.MONTH,Calendar.OCTOBER): 修改年月日等,这里修改月份
对象.get(Calendar.DATE): 获得日期
对象.get(Calendar.DAY_OF_WEEK)-1): 获得星期数,国外从周日算第一天,因此需要-1
对象.getActualMaximum(Calendar.DATE): 获得对象当前月份的最大天数
private static void demo12() { Calendar calendar=Calendar.getInstance(); System.out.println("1.对象: "+calendar); //get(int filed) System.out.println("2. 当前年份:"+calendar.get(Calendar.YEAR));//得到属性值用get System.out.println("3. 修改之前月份为:"+(calendar.get(Calendar.MONTH)+1));//从0-11,月份需要+1 //修改时间,利用set calendar.set(Calendar.MONTH,Calendar.OCTOBER);//修改属性值用set System.out.println("8. 修改之后月份为:"+(calendar.get(Calendar.MONTH)+1)); System.out.println("4.当前日期: "+calendar.get(Calendar.DATE)); System.out.println("5. 当前日期"+calendar.get(Calendar.DAY_OF_MONTH)); //获得今天是周几? System.out.println("6.获得10月13号是周几?"+(calendar.get(Calendar.DAY_OF_WEEK)-1));//从周日开始算第一天 //获得一个月份的最大天数 System.out.println("7.获得一个月份的最大天数 "+calendar.getActualMaximum(Calendar.DATE)); //显示calendar中month+1的最大天数,因为前面说了calendar的month是0-11 }
输出内容:
1.对象: java.util.GregorianCalendar[time=1668326595534,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=10,WEEK_OF_YEAR=47,WEEK_OF_MONTH=3,DAY_OF_MONTH=13,DAY_OF_YEAR=317,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=3,SECOND=15,MILLISECOND=534,ZONE_OFFSET=28800000,DST_OFFSET=0] 2. 当前年份:2022 3. 修改之前月份为:11 8. 修改之后月份为:10 4.当前日期: 13 5. 当前日期13 6.获得10月13号是周几?4 7.获得一个月份的最大天数 31
LocalDate/LocalDateTime
jdk1.8之后将时间日期类或者接口都放在java.time包下,构造都是私有化,不可new,线程安全,值不可变
LocalDate只有年月日,LocalDateTime既有年月日又有时分秒
常用方法1. 将string类型转换成日期,也是用的parse:
LocalDate.parse(string date,DateTimeFormatter.ofPattern("yyyy年MM月dd")
private static void demo13() { UserInfor1 userInfor1=new UserInfor1(); //需要类型转换string to localdate 类型 System.out.println("input your birthday"); userInfor1.setBirthday(LocalDate.parse(input.next(),DateTimeFormatter.ofPattern("yyyy年MM月dd"))); userInfor1.setCreateDay(LocalDateTime.now()); System.out.println(userInfor1); }
- 将date类型转化string类型,用的format方法:
createDay.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
在用户类里面我们将localdate类型转换成string类型,重写tostring:
public class UserInfor1 { private Integer id; private String name; private LocalDate birthday; private LocalDateTime createDay; @Override public String toString() { return "UserInfor1{" + "id=" + id + ", name='" + name + '\'' + ", birthday=" + birthday + ", createDay=" + ((createDay==null)?"":createDay.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) )+ '}'; } }
LocalDateTime类
常用方法:
获得当前日期时间:LocalDateTime.now()
LocalDateTime now = LocalDateTime.now();
System.out.println(now);//2022-11-14T08:05:19.374
设定指定日期和时间:LocalDateTime.of()
LocalDateTime localDateTimeOf = LocalDateTime.of(2022, Month.NOVEMBER, 20, 8, 07);//设定时间
System.out.println(localDateTimeOf);//2022-11-20T08:07
获得年份:用get方法
get(TemporalField field),然而TemporalField是一个接口,因此需要靠接口实现类public enum ChronoField implements TemporalField 来调用接口的属性,
System.out.println(now.get(ChronoField.YEAR));//2022
System.out.println(now.getYear());//2022
修改日期:
plus(TemporalAmount amountToAdd)、minus
plus里面的TemporalAmount也是接口,可以通过period或者duration来实现接口
now = now.plus(Duration.of(10, ChronoUnit.DAYS));//往后加十天
now = now.plus(Period.ofDays(10));//往后加十天
System.out.println(now);//2022-11-24T08:20:30.200
LocalDateTime minus = now.minus(10, ChronoUnit.DAYS);//往前减十天
System.out.println(minus);//2022-11-14T08:20:30.200
求间隔天数:until()
long until = now.until(localDateTimeOf, ChronoUnit.DAYS);
//localDateTimeOf与现在的日期相减得到-4,可以绝对值一下
System.out.println(until);//-4
LocalDate类
方法跟LocalDateTime差不多但是没有具体时间,只有年月日
LocalDate now1 = LocalDate.now();//获得当前年月日
LocalDate localDate=LocalDate.of(2021,10,9);//设定年月日
Period until1 = now1.until(localDate);
//获得相差的年月日:P-1Y-1M-5D
System.out.println(until1);
System.out.println(until1.getMonths());//获得相差月数-1
System.out.println(until1.getYears());//获得相差年数-1
System.out.println(until1.getDays());//获得相差天数-5
日期类instant:
private static void demo2() {
Instant instant=Instant.now();
System.out.println(instant);//输出日期会跟时区有关系:2022-11-30T08:02:36.591Z
Instant instant1 = instant.plusMillis(8 * 3600 * 1000);
System.out.println(instant1);//2022-11-30T16:02:36.591Z
}
格式化类Format
DateFormat
使用子类:SimpleDateFormat(线程不安全的类,在单线程没有问题,但是在并发编程会出现问题)
常用构造:
//利用多态来创建对象:
DateFormat DateFormat=new SimpleDateFormat(PATTERN);
//直接使用dateformat的子类来创建对象
SimpleDateFormat simpleDateFormat=new SimpleDateFormat(PATTERN);
常用方法:
//下面利用dateformat对象来调用parse方法,将字符串转成date
Date date=dateformat.parse(String str);//将字符串转成date
//下面利用dateformat对象来调用format方法,将日期转换成string
dateformat.format(Date date);//将date转成string
例题:在控制台输入用户信息name,birthday,createDay并将输入的信息展示出来。
上面问题可以看出,有两个是date类型的,这个date类是在util包下的类,我们可以用DateFormat里面的方法来将date与string相互转换:
下面我将封装一个含有字符串转日期,与日期转字符串的类,作为我们以后将字符串与日期进行转换的一个工具类
public class DateUtil {
private static final String PATTERN="yyyy-mm-dd";
private static final String PATTERN1="yyyy-mm-dd HH:MM:SS";
private DateUtil() {
}
public Date strToDate(String date,boolean flag){
Objects.nonNull(date);
String type=PATTERN;
if(!flag)type=PATTERN1;
DateFormat dateFormat=new SimpleDateFormat(type);
try {
return dateFormat.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
public static String dateToStr(Date date,boolean flag){
Objects.nonNull(date);
String type=PATTERN;
if(!flag)type=PATTERN1;
DateFormat dateFormat=new SimpleDateFormat(type);
return dateFormat.format(date);
}
}
下面的类是用户信息类:
@AllArgsConstructor
@Setter
@Getter
@NoArgsConstructor
public class UserInfor {
private String name;
private Date birthday;
private Date createDay;
@Override
public String toString() {
return "{" +
"name='" + name + '\'' +
", birthday=" + (birthday==null?"null":DateUtil.dateToStr(birthday,true))+
", createDay=" + (createDay==null?"null":DateUtil.dateToStr(createDay,false))+
'}';
}
}
通过控制台,我们来给用户的属性赋值并且展示:
private static void demo10() {
UserInfor userInfor=new UserInfor();
System.out.println("input your id");
userInfor.setId(input.nextInt());
System.out.println("input your name");
input.nextLine();//读取多余的一个回车
userInfor.setName(input.nextLine());
System.out.println("input your birthday");
userInfor.setBirthday(DateUtil.strToDate(input.nextLine(),true));
System.out.println("input your create time");
userInfor.setCreateDay(DateUtil.strToDate(input.nextLine(),true));
System.out.println(userInfor);
}
以上我们就实现了字符串与日期之间的相互转换,看结果:
input your name
liku
input your birthday
2022-01-01
input your createday
2022-11-12
DateUtil{name='liku', birthday=2022-01-01, createDay=2022-11-12 00:01:00}
SimpleDateFormat在并发里面SimpleDateFormat不能充当成员来使用,只能在局部里面创建
public class SimpleFormatDemo {
private static DateFormat dateFormat;
static {
dateFormat=new SimpleDateFormat("yyyy-mm-dd");
}
public static Date dateToStr(String date) {
Objects.nonNull(date);
try {
return dateFormat.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String[] date={"2022-10-01","2022-10-01",
"2022-10-01","2022-10-01","2022-10-01",
"2022-10-01","2022-10-01","2022-10-01",
"2022-10-01","2022-10-01","2022-10-01"};
new Thread(()->{
for (String s : date) {
System.out.println(Thread.currentThread()+": "+dateToStr(s));
}
}).start();
new Thread(()->{
for (String s : date) {
System.out.println(Thread.currentThread()+": "+dateToStr(s));
}
}).start();
}
}
//输出结果会出现异常,因为simpleformat所有线程都用了同一个日期格式化对象
/*Exception in thread "Thread-0" java.lang.NumberFormatException: For input string: ""
Thread[Thread-1,5,main]: Sat Jan 01 00:11:00 CST 2022
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
Thread[Thread-1,5,main]: Sat Jan 01 00:10:00 CST 2022
at java.lang.Long.parseLong(Long.java:601)
Thread[Thread-1,5,main]: Sat Jan 01 00:10:00 CST 2022
Thread[Thread-1,5,main]: Sat Jan 01 00:10:00 CST 2022
at java.lang.Long.parseLong(Long.java:631)
Thread[Thread-1,5,main]: Sat Jan 01 00:10:00 CST 2022
at java.text.DigitList.getLong(DigitList.java:195)
Thread[Thread-1,5,main]: Sat Jan 01 00:10:00 CST 2022
Thread[Thread-1,5,main]: Sat Jan 01 00:10:00 CST 2022
at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
Thread[Thread-1,5,main]: Sat Jan 01 00:10:00 CST 2022
Thread[Thread-1,5,main]: Sat Jan 01 00:10:00 CST 2022
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162)
Thread[Thread-1,5,main]: Sat Jan 01 00:10:00 CST 2022
Thread[Thread-1,5,main]: Sat Jan 01 00:10:00 CST 2022
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.java.util.SimpleFormatDemo.dateToStr(SimpleFormatDemo.java:23)
at com.java.util.SimpleFormatDemo.lambda$main$0(SimpleFormatDemo.java:38)
at java.lang.Thread.run(Thread.java:745)*/
synchronized
尽量少占内存,而且要解决线程安全的方法:如果作为成员的话可以用synchronized 来修饰,但是这时候就不属于并发线程,两个线程同时拿到该方法,一个线程先拿到该方法,做完之后才会释放出该方法给另外一个线程,但是此时效率会比较低
private static DateFormat dateFormat;
static {
dateFormat=new SimpleDateFormat("yyyy-mm-dd");
}
public static synchronized Date dateToStr(String date) {
Objects.nonNull(date);
try {
return dateFormat.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
ThreadLocal
出现异常的根本原因是因为所个线程使用了同一个dateFormat,因此需要解决线程安全,尽量少占据堆内存,提高程序的运行效率: 用ThreadLocal来为每个线程来分配各自一个对象(格式化日期的对象)。
方法:ThreadLocal.get(),得到
private static final ThreadLocal<SimpleDateFormat> LOCAL=new ThreadLocal<>();
//拿数据的话得用LOCAL.get();返回值类型正好是SimpleDateFormat
public static synchronized Date dateToStr(String date) {
Objects.nonNull(date);
try {
return LOCAL.get().parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
但此时代码还是会出现空指针异常,因为此时SimpleDateFormat对象是个空,没有创建一个实例,底层返回值为null,因此需要写一个匿名内部类,重写initialValue方法,将方法返回SimpleDateFormat的new对象,因此需要将上面代码更改成下面:
private static final String PATTERN="yyyy-MM-dd";
private static final ThreadLocal<SimpleDateFormat> LOCAL=new ThreadLocal(){
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(PATTERN);
}
};
public static synchronized Date dateToStr(String date) {
Objects.nonNull(date);
try {
return LOCAL.get().parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
Numberformat
抽象类,因此用子类:DecimalFormat来创建对象
NumberFormat numberFormat = new DecimalFormat("格式");
private static void demo16() {
//保留小数点后面几位
double num=3.1415926535;
NumberFormat numberFormat=new DecimalFormat("0000.###");
NumberFormat numberFormat1=new DecimalFormat("####.0000");
System.out.println(numberFormat.format(num));//0003.142
System.out.println(numberFormat1.format(num));//0003.142
// 返回值类型都是string类型
//将小数转换百分数
double num1=0.2456673;
NumberFormat numberFormat2 = new DecimalFormat(".##%");
System.out.println(numberFormat2.format(num1));//24.57%
//格式化金钱
double money=1389378954589.325789;
NumberFormat numberFormat3=new DecimalFormat(",###.####");
System.out.println(numberFormat3.format(money));//1,389,378,954,589.3257
}
随机数类Random
之前学的随机数都是真正的随机数:
int random1=(int)(Math.random()*9000+1000);
Math.random()底层默认调用的random.nextDouble(),每次调用该方法都会在底层new一个random类的对象,生成的随机数无法预测。
但是random类是一个伪随机,生成的随机数可以控制的
//常用构造和方法:
Random();
Random(long seed)
random.nextInt(int bound)//随机生成一个[0-bound)的整数,范围为int的范围
可以利用带参构造方法里面的种子来控制随机数的生成:
private static void demo18() {
for (int i = 0; i < 5; i++) {
Random random=new Random();//即使每次用的是同一个,种子也不会相同
for (int i1 = 0; i1 < 5; i1++) {
int num=random.nextInt(9000)+1000;
System.out.print(num+", ");
}
System.out.println();
}
}
private static void demo17() {
for (int i = 0; i < 5; i++) {
Random random=new Random(100);
//每次都生成随机数:2915, 4250, 1874, 3988, 5291,
for (int i1 = 0; i1 < 5; i1++) {
int num=random.nextInt(9000)+1000;
System.out.print(num+", ");
}
System.out.println();
}
}
private static void demo15() {
Random random=new Random();
for (int i = 0; i < 5; i++) {
for (int i1 = 0; i1 < 5; i1++) {
int num=random.nextInt(9000)+1000;
System.out.print(num+", ");
}
System.out.println();
}
}
- java.util.Random的
java.util.Random
是线程安全的。 但是,跨线程的同时使用java.util.Random
实例可能会遇到争用,从而导致性能下降。 在多线程设计中考虑使用[ThreadLocalRandom
在创建ThreadLocalRandom对象的时候不能new,需要通过ThreadLocalRandom的静态方法来返回ThreadLocalRandom类的对象。
private static void demo19() {
ThreadLocalRandom threadLocalRandom=ThreadLocalRandom.current();
int num=threadLocalRandom.nextInt(100,9000);
//从100到9000
System.out.println(num);
}
并且ThreadLocalRandom不支持修改种子,因此也是真随机,不能生成伪随机数
Base64/MD5
编码/解码---加密/解密
Base64
可逆的,能够加密也可以解密,加密之后都是字符串类型,一般用于前端,结果都是字符型数组a-zA-Z0-9
加密过程:
- 获得编码器
Base64.Encoder encoder = Base64.getEncoder();
- 加密(编码)
private static String demo1() {
String str="1234789";
byte[] bytes = new byte[0];
try {
bytes = str.getBytes("utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Base64.Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(bytes);
}
解码过程:
- 获得解码器
Base64.Decoder decoder = Base64.getDecoder();
- 解密(解码)
private static String demo2() {
Base64.Decoder decoder = Base64.getDecoder();
byte[] decode = decoder.decode("MTIzNDc4OQ==");
String str= null;
try {
str = new String(decode,PATTERN);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return str;
}
MD5
全程MessageDigest后面数字是版本号,信息摘要算法的一种,不可逆的加密解密
public class MD5Util {
private static final char[] ARRAY={
'1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F'
};
private static final String ENCODETYPE="utf-8";
private static String msgEncode(String str){
Objects.nonNull(str);
try {
MessageDigest messageDigest=MessageDigest.getInstance("MD5");
messageDigest.update(str.getBytes(Charset.forName(ENCODETYPE)));
byte[] digest = messageDigest.digest();
String string=new String(digest,Charset.forName(ENCODETYPE));
System.out.println(string);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String str="12345";
msgEncode(str);
}
}
输出的是一串乱码,因为MD5返回的是byte类型的数组,用string的构造来走是不对的, 要将byte字节数组转成16进制的内容,byte为一种128位的16进制的字符,由于16进制0-9-F转成2进制只能表示从0~15的数字,因此16进制占4个bit,而byte从-128到127,占八个bit,一个byte等于两个16进制的内容
public class MD5Util {
private static final char[] ARRAY={
'1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F'
};
private static final String ENCODETYPE="utf-8";
private static String msgEncode(String str){
Objects.nonNull(str);
StringBuilder builder=new StringBuilder();
try {
MessageDigest messageDigest=MessageDigest.getInstance("MD5");
//将需要加密的内容放入算法-->更新信息摘要的对象
messageDigest.update(str.getBytes(Charset.forName(ENCODETYPE)));
//加密
byte[] digest = messageDigest.digest();
for (byte b : digest) {
builder.append(byteToHex(b));
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return builder.toString();
}
private static String byteToHex(byte b) {
int num=b;
if(num<0)num+=256;
int highIndex=num/16;
int lowIndex=num%16;
return ARRAY[highIndex] +""+ARRAY[lowIndex];
}
public static void main(String[] args) {
System.out.println(msgEncode("1234"));
}
}
或者直接调用BigInteger方法来将十进制转成16进制:
byte[] digest = messageDigest.digest();
BigInteger bigInteger=new BigInteger(1,digest);//1表示正数,-1表示负数,0表示0
bigInteger.toString(16);
还可以用盐值来进行加密,定义一个盐值常量,
private static final String SALT="LIKU--->3627";
在将加密信息放到算法的时候,将加密内容拼接该盐值,就可以得到新的加密,这时候将加密内容放到解密网站破解的时候是无法得到解密内容的,因此MD5是不可逆的。