常用的辅助类2(Object、StringBuilder、StringBuffer、处理时间相关的类、对象比较器)

Java知识点总结:想看的可以从这里进入

7.7、Object

7.7.1、Object类

Object类是所有类的超类,它是继承体系的顶端,所有的类都间接或直接的继承了Object类,所以java中任何一个对象都可以被视作Object类型,在定义类时,如果没有为类显示的指定父类,那么它就默认继承Object类。

它内部有几个常用的方法:

1、public final native Class<?> getClass():用于获得运行时的类型,返回的是此Object对象的类对象/运行时类对象(反射时使用)
2、public native int hashCode():返回对象的哈希码值,通常配合equals 一起使用(两者一起被重写)
3、public boolean equals(Object obj):本质上是用==,子类对其重写后,比较引用数据类型在堆中的值
4、protected native Object clone():特定的克隆操作。需实现接口Cloneable,否则抛出CloneNotSupportedException
5、public String toString():返回对象的字符串表示形式,类名和它的引用地址(重写后的toString会输出内容实体)
6、public final native void notify():唤醒正在此对象的监视器上等待的单个线程。
7、public final native void notifyAll():唤醒正在此对象的监视器上等待的所有线程
8、public final void wait():使当前线程等待直到它被唤醒
9、wait(long timeout):使当前线程等待直到它被唤醒或超过指定的时间量
10、protected void finalize():当垃圾收集确定不再有对该对象的引用时,由对象上的垃圾收集器调用

1、==和equals的比较

  • ==在比较基本数据类型时比较的是保存的值是否相同,而比较引用数据类型时比较存放的引用地址是否相同。

  • equals在底层也是使用==来比较的,但是一般都会对其进行重写,重写后的equals方法可以比较两个对象在堆中存放的值是否相同

    equals 的逻辑需要重写来决定,所以严格来说equals 的功能是根据业务逻辑来决定的,所以equals 中的代码不能随便写。

子类中如果重写了equals方法,在比较时,会先调用超类的equals方法,如果检测不相等,对象就会直接判定为不相等,否则再继续使用子类中重写的equals方法。

2、equals 方法具有下述性质:

  • 自反性:对于任何非 null的引用 x,x.equals(x) 应该返回 ture
  • 对称性:对于任何引用 x 和 y, 当且仅当 y.equals(x) 返回 true 时,x.equals(y) 也返回 true
  • 传递性;对于任何引用x、 y 、 z, 如果 x.equals(y) 返回 true,y.equals(z) 返回 true,则x.equals(z) 也应该返回 true
  • 一致性:如果 x 和 y 引用的对象没有发生变化,则反复调用 x.equals(y) 应该返回同样的结果。
  • 对于任意非 null引用 x, x.equals(null) 都应该返回 false。

3、equals和hashCode的比较

  • hashcode:主要是用来获取hash码,返回的是一个int,通常用来确定对象在hash表的位置。
  • equals:在发生hash冲突时(两个对象有相同的hash码),使用equals进行判断两个对象是否真的相同,返回值为Boolean

两者需要同时重写,在存入Hash数据时,先对key的hashcode进行判断,确定在hash表中的位置,如果此位置已经存在数据,再使用equals进行判断,如equals判断两者相同会将value替换,如果不同则创建新节点存放数据。这样会减少equals的使用频率,大大提高执行效率。

7.7.2、Objects类

从Java7开始,引入的Objects 工具类提供了一些工具方法来操作对象,这些方法大多是 “空指针” 安全的。比如我们在使用Object 中的toString 方法时,可能会出现空指针异常,但是使用Objects 中的 toString 方法时,就不会出现空指针的异常,但是它会返回一个 “null” 的字符串。

Objects 类的方法经常被用在需要进行对象比较、生成哈希码、返回字符串表示等场景中。

1、equals(Object a, Object b):比较两个对象的相等性。这个方法是 null-safe 的,如果两个对象都是 null,则返回      true;如果其中一个对象是 null,则返回 false;否则,调用第一个对象的 equals() 方法进行比较。

2、hashCode(Object o):生成对象的哈希码。这个方法也是 null-safe 的,如果对象是 null,则返回 0;否则,调用对象的 hashCode() 方法。

3、toString(Object o):返回对象的字符串表示。如果对象是 null,则返回字符串 "null";否则,调用对象的 toString() 方法。

4、toString(Object o, String nullDefault):返回对象的字符串表示,但允许为 null 对象指定默认的字符串表示。

5、requireNonNull(T obj):检查指定的对象引用不是 null。如果是 null,则抛出 NullPointerException。

6、requireNonNull(T obj, String message):检查指定的对象引用不是 null,并在抛出 NullPointerException 时提供自定义的错误消息。

7、compare(T a, T b, Comparator<? super T> c):使用提供的 Comparator 比较两个对象。这个方法是 null-safe 的,可以优雅地处理 null 值。

8、deepEquals(Object a, Object b):对两个对象进行深度比较。这对于比较可能包含数组的对象尤其有用,因为它会考虑数组内部的元素是否相等。

7.8、字符串相关类

7.8.1、介绍

在Java操作字符串的除了String外,还有两个类,分别是:StringBuffer、StringBuilder。它们创建的字符串其值是可以改的,这得益于内部的动态数组。

  • StringBuffer:JDK1.0出现,字符串类,其值可修改,线程安全,速度相对StringBuilder慢。

  • StringBuilder:JDK 5.0出现,字符串类,其值可修改,线程不安全,但是速度最快。

    //三种构造方法
    StringBuffer():初始容量为16的字符串缓冲区
    StringBuffer(int size):构造指定容量的字符串缓冲区
    StringBuffer(String str):将内容初始化为指定字符串内容
    

StringBuilder和StringBuffderde都继承了AbstractStringBuilder类,它和String一样(java9之前的版本),其底层是一个字符数组。每个字符占2个字节。而Java9之后String采用 byte[] 和 encoding-flag字段来保存字符,每个字符占一个字节,所以Java9之后的字符串更加节省空间。

在这里插入图片描述

底层的字符数组,初始大小是 16 个字符(除非在构造时指定了其他大小),这意味着如果不进行扩容,它可以存储 16 个字符。

在这里插入图片描述

当添加的字符超出了当前数组容量时,就会发生扩容。扩容过程如下:

如果添加字符串过程中超出数组范围,就会对底层数组进行扩容,首先创建一个新的数组大小为原来的2倍加2,将原数组中的内容复制到新数组中,再将指针指向新创建的数组地址。

  1. jdk8中的扩容方法:

    private int newCapacity(int minCapacity) {
        // 计算新容量,当前容量的两倍再加2
        int newCapacity = (value.length << 1) + 2;
        // 如果计算得到的新容量仍然小于所需最小容量,直接使用所需最小容量
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        // 检查新容量是否溢出或超过数组最大限制
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }
    private int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // 溢出
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) 
            ? Integer.MAX_VALUE 
            : MAX_ARRAY_SIZE;
    }
    
  2. jdk17中的扩容方法

    private int newCapacity(int minCapacity) {
        int oldLength = value.length;              // 当前数组长度
        int newLength = minCapacity << coder;      // 根据编码调整最小容量需求
        int growth = newLength - oldLength;        // 计算增长量
        // 计算新长度,确保至少能容纳增长量,同时预留一些额外空间
        int length = ArraysSupport.newLength(oldLength, growth, oldLength + (2 << coder));
        // 如果计算结果为 Integer.MAX_VALUE,抛出内存溢出错误
        if (length == Integer.MAX_VALUE) {
            throw new OutOfMemoryError("Required length exceeds implementation limit");
        }
        return length >> coder;                    // 根据编码调整计算后的长度,并返回
    }
    //计算新数组的长度
    public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
        // 计算首选长度。首选长度是旧长度加上最小增长和首选增长中的较大值。
        // 这里可能会发生整数溢出
        int prefLength = oldLength + Math.max(minGrowth, prefGrowth); 
    
        // 如果计算出的首选长度是正数且不超过软最大数组长度(SOFT_MAX_ARRAY_LENGTH),则直接返回这个长度
        if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
            return prefLength;
        } else {
            // 如果首选长度是负数或超过软最大长度,调用hugeLength方法来处理这种极端情况
            // 这部分代码因为很少执行,所以被放在一个单独的方法中以优化性能
            return hugeLength(oldLength, minGrowth);
        }
    }
    
    • Coder影响:coder的值决定了字符数组中每个字符的字节大小。因此,minCapacity << coder实际上是根据编码对最小容量需求进行调整。
    • 动态扩容策略:ArraysSupport.newLength 方法用于动态决定数组的新长度。它考虑了当前长度、需要增加的长度以及一个预期的最大长度。这里oldLength + (2 << coder)表示预期增长的长度,这个增长是基于当前编码方式的。
    • 内存溢出检查:如果新的长度计算结果为Integer.MAX_VALUE,则抛出OutOfMemoryError,说明请求的数组长度超过了Java数组能够支持的最大限制。
    • 返回调整后的长度:返回值length >> coder是将计算出来的长度根据编码方式进行调整,确保它反映的是字符数而非字节数。
7.8.2、常用方法

StringBuilder和StringBuffder提供的方法基本类似:

StringBuilderStringBuffder常用方法:
1、增:追加字符串   .append("").append("").....;
2、删:删除指定范围的内容[start,end)     .delete(int start,int end)
3、改:替换指定范围的内容[start,end)    .replace(int start, int end, String str)
      修改指定索引处的字符:public void setCharAt(int n ,char ch) 
4、查:获取指定索引处的字符     public char charAt(int n ) 
5、在指定位置插入字符串:在指定位置插入指定的内容      .insert(int offset, "")
6、返回指定子字符串在当前字符串中第一次出现处的索引:public int indexOf(String str) 
7、截取字符串[start,end)public String substring(int start,int end) 
8、返回字符串的长度:public int length() 
9、把当前字符序列逆转:.reverse() 
public static void main(String[] args) {
    StringBuilder builder = new StringBuilder();
    //增
    builder.append("hello").append("world");
    System.out.println("追加字符串:"+builder);
    //删
    builder.delete(0,2);
    System.out.println("删除字符串:"+builder);
    //改
    builder.replace(3,7,"java");
    System.out.println("替换字符串:"+builder);
    //查、
    System.out.println("索引3的位置:"+builder.charAt(3));
    builder.setCharAt(3,'a');
    System.out.println("修改字符串:"+builder);
    //插入字符串
    builder.insert(0,"he");
    System.out.println(builder);
    //返回指定子字符串在当前字符串中第一次出现处的索引
    System.out.println(builder.indexOf("a"));
    //返回指定子字符串在当前字符串中最后一次出现处的索引
    System.out.println(builder.lastIndexOf("a"));
    //截取字符串[start,end)
    System.out.println(builder.substring(0,3));
    //把当前字符序列逆转
    System.out.println(builder.reverse());
    //判断字符串是否为空
    System.out.println(builder.isEmpty());
}

image-20240424115425278

7.8.3、速度对比

对比一下使用String和StringBuilder在速度上的区别

计算一下追加 100,000,0次字符所用的时间

  1. 使用String

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        String str = "";
        for(int i=0; i<1000000; i++){
            str += 1;
        }
        long end = System.currentTimeMillis();
        System.out.printf("使用String用时:%d",end-start);
    }
    

在这里插入图片描述

  1. 换成StringBuilder

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        StringBuilder builder = new StringBuilder();
        for(int i=0; i<1000000; i++){
            builder.append(1);
        }
        long end = System.currentTimeMillis();
        System.out.printf("使用StringBuilder用时:%d",end-start);
    }
    

    在这里插入图片描述

  2. 使用StringBuffder

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        StringBuffer buffer = new StringBuffer();
        for(int i=0; i<1000000; i++){
            buffer.append(1);
        }
        long end = System.currentTimeMillis();
        System.out.printf("使用StringBuffer用时:%d",end-start);
    }
    

    在这里插入图片描述

从上面可以明显的看出来使用String的时候相比较后两种速度慢的不是一点半点的,而是很多倍的差距了,而StringBuilder相比较StringBuffder也快了一些。

7.9、时间处理

Java原本提供的时间处理类为 Date 和 Calendar两个类,但是Date不仅无法实现国际化,且它对不同属性也使用了前后矛盾偏移量,如月份小时从0开始,天数从1开始。而Calendar操作过于复杂,所以再Java8之后,提供了一套全新的时间、日期库。

7.9.1、JDK8之前
  1. 计算时间差:

    System类提供的 public static long currentTimeMillis() 用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。通常用来计算某个程序的运行时间,进行快慢的对比

    在这里插入图片描述

  2. java.util.Date类

    Date中既包含日期,也包含时间。目前Date中大多数构造器都不再推荐使用,仅仅留下2个构造器。

在这里插入图片描述

在这里插入图片描述

  1. SimpleDateFormat类:用于格式化和解析日期的一个类,属于 java.text 包。

    在这里插入图片描述

    在这里插入图片描述

  2. Calendar类:是一个抽象类,用于表示日历,是一个日历类的模板,提供一些日历相关的通用方法。在Java中提供一个其子类GregorianCalendar就是公历。主要用于操作日历字段,如年、月、日、时、分、秒等。

    //创建方式
    1、创建子类GregorianCalendar的的对象:Calendar calendar = new GregorianCalendar();
    2、通过静态方法:Calendar calendar = Calendar.getInstance();  (底层也是创建GregorianCalendar对象)
    
    • 设置日期:set(int field, int value)、setTimeInMillis(long millis)

      calendar.set(Calendar.YEAR, 2023);		//设置年份,2023年
      calendar.set(Calendar.MONTH, Calendar.JANUARY);	//设置月份 1月
      calendar.set(Calendar.DAY_OF_MONTH, 1);		//设置日期
      
    • 获取日期:get(int field)、getTimeInMillis()

      int year = calendar.get(Calendar.YEAR);		//获取年份
      int month = calendar.get(Calendar.MONTH);	//获取月份
      int day = calendar.get(Calendar.DAY_OF_MONTH);	//获取日期
      
    • 添加或减少时间:add(int field, int amount)

      calendar.add(Calendar.YEAR, 1);  // 在当前年份上加1年
      calendar.add(Calendar.DAY_OF_MONTH, -10);  // 当前日期减10天
      
    • 设置时间到最大或最小值:getActualMaximum(int field)和 getActualMinimum(int field)

      getActualMaximum(int field):用于确定一个月可以有的最大天数等
      getActualMinimum(int field):通常用于确定一个月可以有的最小天数等
      
    • 比较时间:

      before(Object when):用于比较两个日期,查看当前实例是否在参数表示的日期之前
      after(Object when):用于比较两个日期,查看当前实例是否在参数表示的日期之后。
      equals(Object obj):用于比较两个日历对象是否代表同一时刻。
      

    Calendar类中常用的常量字段如下:

    常量字段说明
    Calendar.ERA表示纪元,只能为0或1。0表示BC(“before Christ”,即公元前);1表示AD(拉丁语“Anno Domini”,即公元)。
    Calendar.YEAR表示年份
    Calendar.MONTH表示月份,0表示1月,1表示2月………
    Calendar.DAY_OF_MONTH表示一个月中的某天字段
    Calendar.WEEK_OF_YEAR`即本年中第几个星期
    Calendar.WEEK_OF_MONTH当前月中的星期数,即本月中第几个星期
    Calendar.DAY_OF_YEAR一年中第几天
    Calendar.DAY_OF_WEEK一周中第几天,注意,周日是1,周一是2,…
    Calendar.DAY_OF_WEEK_IN_MONTH表示当前月份中的星期几
    Calendar.DATE一月中第几天,同DAY_OF_MONTH的值是一样的
    Calendar.HOUR小时(12小时制)
    Calendar.AM_PM用于指示HOUR是在中午之前还是在中午之后。在中午12点之前返回0,在中午12点(包括12点)之后返回1
    Calendar.HOUR_OF_DAY一天中第几个小时(24小时制)
    Calendar.MINUTE分钟
    Calendar.SECOND
    Calendar.MILLISECOND毫秒
    Calendar.JANUARY1月
    Calendar.FEBRUARY2月
    Calendar.MARCH3月
    Calendar.APRIL4月
    Calendar.MAY5月
    Calendar.JUNE6月
    Calendar.JULY7月
    Calendar.AUGUST8月
    Calendar.SEPTEMBER9月
    Calendar.OCTOBER10月
    Calendar.NOVEMBER11月
    Calendar.DECEMBER12月
    public static void main(String[] args) throws ParseException {
        Calendar calendar = Calendar.getInstance();
        //查看是公元前(0)还是公元后(1)
        System.out.println("判断公元:"+calendar.get(Calendar.ERA));
        //获取月份:返回值+1
        System.out.println("获取月份:"+calendar.get(Calendar.MONTH));
        //将年份设置为1999
        calendar.set(Calendar.YEAR,1999);
        System.out.println("修改年份为1999:"+calendar.get(Calendar.YEAR));
        //将年份+1
        calendar.add(Calendar.YEAR,1);
        System.out.println("将年份+1:"+calendar.get(Calendar.YEAR));
        //返回Date格式日期
        System.out.println("获取date格式日期:"+calendar.getTime());
        //设置时间
        calendar.setTime(new Date("Tue Feb 08 18:29:23 CST 2222"));
        System.out.println("设置时间:"+calendar.getTime());;
    }
    

    在这里插入图片描述

7.9.2、JDK8后
1、时间日期类

在JDK8以前,不论是Date类、还是Calendar类,都不是特别好用,所以时期时间问题一直都是一个让人头疼的问题。所以在JDK8中第三次引入了关于日期时间的API,新的 java.time包 中包含了如下类:

  1. LocalDate:表示一个没有时区的日期(年月日),用于处理日期。

    LocalDate localDate = LocalDate.now();  //获取当前日期
    System.out.println(localDate);
    LocalDate localDate2 = LocalDate.of(2000,1,1);  //指定日期
    System.out.println(localDate2);
    int year = localDate2.getYear();//获取年 2000
    Month month = localDate2.getMonth();//获取月 JANUARY
    int dayOfMonth = localDate2.getDayOfMonth(); //获取日 1
    DayOfWeek week = localDate2.getDayOfWeek();//获取星期几 SATURDAY
    int dayOfYear = localDate2.getDayOfYear();//获取一年中的第几天 1
    LocalDate plussedDays = localDate2.plusDays(10);//增加天数    2000-01-21
    LocalDate minusedDays = localDate2.minusDays(10);//减少天数  1999-12-22
    LocalDate plussedMonths = localDate2.plusMonths(10);//增加月   2000-11-01
    LocalDate minusedMonths = localDate2.minusMonths(10);//减少月  1999-03-01
    LocalDate plussedYears = localDate2.plusYears(10);//增加年    2010-01-01
    LocalDate minusedYears = localDate2.minusYears(10);//减少年   1990-01-01
    LocalDate withYear = localDate2.withYear(2020);//设置年   2020-01-01
    
  2. LocalTime:表示一个没有日期的时间(时分秒和纳秒),用于处理时间。

    System.out.println(LocalTime.now()); //获取当前时间
    LocalTime localTime2 = LocalTime.of(10,10,10);  //指定时间
    System.out.println(localTime2);
    int hour = localTime2.getHour();//获取时 10
    int minute = localTime2.getMinute();//获取分 10
    int second = localTime2.getSecond();//获取秒 10
    LocalTime plusHours = localTime2.plusHours(1);//增加小时   11:10:10
    LocalTime minusHours = localTime2.minusHours(1);//减少小时  09:10:10
    LocalTime plusMinutes = localTime2.plusMinutes(1);//增加分钟  10:11:10
    LocalTime minusMinutes = localTime2.minusMinutes(1);//减少分钟 10:09:10
    LocalTime plusSeconds = localTime2.plusSeconds(1);//增加秒 10:10:11
    LocalTime minusSeconds = localTime2.minusSeconds(1);//减少秒 10:10:09
    LocalTime withHour = localTime2.withHour(20);//设置时 20:10:10
    LocalTime withMinute = localTime2.withMinute(20);//设置分 10:20:10
    LocalTime withSecond = localTime2.withSecond(20);//设置秒 10:10:20
    
  3. LocalDateTime:表示没有时区的日期和时间,是LocalDateLocalTime的组合,适用于需要同时处理日期和时间但不涉及时区的情况。

    System.out.println(LocalDateTime.now());        //获取当前日期时间
    LocalDateTime localDateTime2 = LocalDateTime.of(2000,1,1,10,10,10);//指定日期时间
    System.out.println(localDateTime2);
    int year = localDateTime2.getYear();//获取年 2000
    Month month = localDateTime2.getMonth();//获取月 JANUARY
    int dayOfMonth = localDateTime2.getDayOfMonth();//获取日 1
    DayOfWeek dayOfWeek = localDateTime2.getDayOfWeek();//获取星期几 SATURDAY
    int dayOfYear = localDateTime2.getDayOfYear();//获取一年中的第几天 1
    int hour = localDateTime2.getHour();//获取时 10
    int minute = localDateTime2.getMinute();//获取分 10
    int second = localDateTime2.getSecond();//获取秒 10
    LocalDateTime plusDays = localDateTime2.plusDays(10);//增加天数   2000-01-21T10:10:10
    LocalDateTime minusDays = localDateTime2.minusDays(10);//减少天数 2000-01-01T10:10:10
    LocalDateTime plusMonths = localDateTime2.plusMonths(10);//增加月  2000-11-01T10:10:10
    LocalDateTime minusMonths = localDateTime2.minusMonths(10);//减少月 2000-03-01T10:10:10
    LocalDateTime plusYears = localDateTime2.plusYears(10);//增加年   2010-01-01T10:10:10
    LocalDateTime minusYears = localDateTime2.minusYears(10);//减少年 1990-01-01T10:10:10
    LocalDateTime withYear = localDateTime2.withYear(2020);//设置年  2020-01-01T10:10:10
    LocalDateTime withHour = localDateTime2.withHour(20);//设置时 20:10:10
    LocalDateTime withMinute = localDateTime2.withMinute(20);//设置分 10:20:10
    LocalDateTime withSecond = localDateTime2.withSecond(20);//设置秒 10:10:20
    
  4. ZoneId:代表一个时区

    在这里插入图片描述

  5. ZonedDateTime:代表一个时区化的日期、时间

    System.out.println(ZonedDateTime.now());    //获取当前带时区的日期和时间。
    ZonedDateTime zonedDateTime2 = ZonedDateTime.of(localDateTime2,ZoneId.of("Asia/Shanghai"));//根据给定的 LocalDateTime 和时区创建 ZonedDateTime。
    System.out.println(zonedDateTime2);
    int year = zonedDateTime2.getYear();//获取年 2000
    Month month = zonedDateTime2.getMonth();//获取月 JANUARY
    int dayOfMonth = zonedDateTime2.getDayOfMonth();//获取日 1
    DayOfWeek dayOfWeek = zonedDateTime2.getDayOfWeek();//获取星期几 SATURDAY
    int dayOfYear = zonedDateTime2.getDayOfYear();//获取一年中的第几天 1
    int zonedDateTime2Hour = zonedDateTime2.getHour();//获取时 10
    int zonedDateTime2Minute = zonedDateTime2.getMinute();//获取分 10
    int second = zonedDateTime2.getSecond();//获取秒 10
    
  6. Duration :类用于处理时间的持续性,以秒和纳秒为单位。它主要用于计算两个时间点之间的差异,并且是不可变且线程安全的。

    1between(Temporal startInclusive, Temporal endExclusive)	//计算两个时间点之间的持续时间。
    2ofDays(long days)	//创建表示指定天数的持续时间。
    3ofHours(long hours)	//创建表示指定小时数的持续时间。
    4ofMinutes(long minutes) //创建表示指定分钟数的持续时间。
    5ofSeconds(long seconds):创建表示指定秒数的持续时间。
    6plus(Duration durationToAdd) //返回一个新的 Duration,将当前 Duration 与另一个 Duration 相加。
    7minus(Duration durationToSubtract) //返回一个新的 Duration,从当前 Duration中减去另一个 Duration。
    
  7. Period:用于处理日期之间的期间,以年、月、日为单位。它也是不可变和线程安全的,适用于日期单位的计算。

    1between(LocalDate startDateInclusive, LocalDate endDateExclusive)	//计算两个日期之间的期间。
    2ofYears(int years)	//创建表示指定年数的期间。
    3ofMonths(int months)	//创建表示指定月数的期间。
    4ofWeeks(int weeks)	//创建表示指定周数的期间。
    5ofDays(int days)	//创建表示指定天数的期间。
    6plus(Period periodToAdd)	//返回一个新的 Period,将当前 Period 与另一个 Period 相加。
    7minus(Period periodToSubtract)	//返回一个新的 Period,从当前 Period 中减去另一个 Period。
    
  8. InstantSource:Java17中引入的规范性接口,是一个可以获取时刻(Instant)的通用接口。Clock就是其实现类。

    • Clock:获取指定时区的当前日期、时间,可取代currentTimeMillis() 方法。
  9. Instant:表示一个具体的时刻,可精确到纳秒。其静态方法now获取当前时刻,now(Clock)获取Clock对应的时刻。还可以通过一系列的minusXxx()方法在当前时刻基础上减去一段时间,也可以通过plusXxx()方法加上一段时间。

  10. Monthday:概念代表日月。其静态方法now获取当前月份和日期,now(Clock)获取Clock对应的月份和日期。

  11. Year:代表年,其静态方法now获取当前的年份,now(Clock)获取Clock对应的年份。还可以通过minusYears()方法在当前年份基础上减去几年,也可以通过plusYears()方法加上几年。

  12. YearMonth:代表年月,其静态方法now获取当前的年月,now(Clock)获取Clock对应的年月。

  13. DayOfWeek:是一个枚举类,定义了周日到周六的枚举值

  14. Month:一个枚举类,定义了一到十二月的枚举值

  15. TemporalAdjuster :时间校正器。

2、格式化日期

java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法:

  1. 预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME

    在这里插入图片描述

  2. 本地化相关的格式。

在这里插入图片描述

在这里插入图片描述

  1. 自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss“)
//时间
LocalDateTime dateTime2 = LocalDateTime.of(1999, 12, 2, 12, 22, 22);
System.out.println(dateTime2);
//第一种格式化
DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
String format1 = dateTimeFormatter1.format(dateTime2);
System.out.println(format1);
//第二种格式化
DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
String format2 = dateTimeFormatter2.format(dateTime2);
System.out.println(format2);
//第三种格式化
DateTimeFormatter dateTimeFormatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
String format3 = dateTimeFormatter3.format(dateTime2);
System.out.println(format3);

image-20230209101748401

7.10、对象比较器

在Java中,对对象进行排序和比较是一项常见的需求,尤其是在处理集合时。Java提供了两种主要方式来比较对象:实现Comparable接口和使用Comparator接口。

7.10.1、Comparable

java.lang.Comparable接口,用于定义对象自然排序的方式。类通过实现Comparable接口的compareTo方法来定义其对象的排序顺序,默认从小到大。

public interface Comparable<T> {
    int compareTo(T o);
}

实现Comparable接口的类可以被Java的任何基于比较的标准排序方法使用,如Collections.sort()Arrays.sort()。此外,这些对象可以用于自动排序的数据结构,如TreeSetTreeMap

在这里插入图片描述

  1. 类实现Comparable接口

  2. 重写接口的compareTo(Object obj) 方法,通过此方法对比两个对象的大小(如果当前对象this大于形参对象,则返回正整数,如果当前对象this小于形参对象,则返回负整数,如果当前对象this等于形参对象,则返回零。)

    public class Person implements Comparable<Person> {
        String name;
        int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public int compareTo(@NotNull Person p) {
            //(x < y) ? -1 : ((x == y) ? 0 : 1); 此处xy为age的值
            return Integer.compare(this.age, p.age);
        }
    
        @Override
        public String toString() {
            return "Person{" + "name='" + name + '\'' +", age=" + age +'}';
        }
    }
    
    public static void main(String[] args) {
        List<Person> list  =new ArrayList<>();
        list.add(new Person("张三", 25));
        list.add(new Person("李四", 19));
        list.add(new Person("王五", 32));
        list.add(new Person("赵六", 18));
        //排序
        Collections.sort(list);
        list.forEach(System.out::println);
    }
    

    image-20240421160319696

7.10.2、Comparator

定制排序:java.util.Comparator接口,用于定义对象比较规则的工具,尤其是当这些对象的类没有实现Comparable接口或你需要除类的自然排序之外的其他排序方式时。Comparator提供了一种灵活的策略,使你能够控制多种类型的比较逻辑。

Comparator是一个功能接口(functional interface),它定义了一个比较两个对象的方法和一些静态方法,其中compare方法的用法和compareTo一样,需要子类重写。

除了compare外,还可以直接使用Comparator的静态方法来进行排序。

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    // 其他默认和静态方法
}

重写compare(Object o1,Object o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。

这是一个功能接口,内部定义了一些比较规则:

  1. comparing():一个静态辅助方法,用于生成一个比较器,该比较器根据提供的函数对对象的某个属性进行比较。

    Comparator<Student> byName = Comparator.comparing(Student::getName);
    
  2. comparingInt()、comparingLong()、comparingDouble():用于处理基本类型字段的比较,避免了装箱操作,提高性能。

    Comparator<Student> byScore = Comparator.comparingInt(Student::getScore);
    
  3. thenComparing():进行多级排序,如果主比较器认为两个对象相等,那么可以使用另一个比较器来进行进一步的比较。

    Comparator<Student> byNameThenByScore = Comparator.comparing(Student::getName)
                                                      .thenComparingInt(Student::getScore);
    
  4. reversed():逆序排序

    Comparator<Student> reverseByName = Comparator.comparing(Student::getName).reversed();
    
  5. nullsFirst() 和 nullsLast():用于处理比较中的null值,可以生成一个新的比较器,该比较器将null视为小于或大于非null值。

    Comparator<Student> byNameNullFirst = Comparator.comparing(Student::getName, Comparator.nullsFirst(String::compareTo));
    
  6. naturalOrder()和 reverseOrder():提供了对自然排序和其逆序的快捷访问。

    Comparator<Integer> natural = Comparator.naturalOrder();
    Comparator<Integer> reverse = Comparator.reverseOrder();
    

类除了实现Comparator的compare方法外,还能直接使用其静态方法,这样类可以不用实现Comparator也能进行排序。

public class Person  {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
	//get、set、toString等方法省略
}
public static void main(String[] args) {
    List<Person> list = new ArrayList<>();
    list.add(new Person("张三", 25));
    list.add(new Person("李四", 10));
    list.add(new Person("王五", 21));
    list.add(new Person("赵六", 32));

    //使用Comparator提供的静态方法,按年龄进行排序
    Comparator<Person> comparator = Comparator.comparingInt(Person::getAge);
    list.sort(comparator);
    list.forEach(System.out::println);
    //使用Comparator提供的静态方法,按名字逆序排序
    System.out.println("按名字排序:");
    Comparator<Person> comparator1 = Comparator.comparing(Person::getName);
    list.sort(comparator1.reversed());      //逆序
    list.forEach(System.out::println);
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰 羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值