util常用类

  1. 字符串转日期

    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);
    }
    
    1. 将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

加密过程:

  1. 获得编码器
Base64.Encoder encoder = Base64.getEncoder();
  1. 加密(编码)
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);
}

解码过程:

  1. 获得解码器
 Base64.Decoder decoder = Base64.getDecoder();
  1. 解密(解码)
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是不可逆的。

posted @ 2022-11-16 19:20  Liku007  阅读(40)  评论(0编辑  收藏  举报