8.常用API--黑马程序员

常用API

在这里插入图片描述

D:\code\黑马\code\API\src\com\LMH

Object

作用

Object类是Java中所有类的祖宗类,因此,Java中所有类的对象都可以直接使用Object类中提供的一些方法。

常见方法

  • public String toString( )

    • 基本作用:返回对象的字符串表示

    • 存在的意义:让子类重写,以便返回子类对象的内容

      public class Student {
          public String name;
          public int age;
      
          public Student(String name, int age) {
              this.name = name;
              this.age = age;
          }
          
      //重写的toString方法
          @Override
          public String toString() {
              return "name='" + name + '\'' +
                      ", age=" + age;
          }
      }
      
      public class Test1 {
          public static void main(String[] args) {
              Student student = new Student("jack", 20);
              
              //student等价于student.toString(),在Student类中重写了toString()方法
              //如果没有重写toString()方法,那么输出的是对象的地址
              System.out.println(student);
              System.out.println(student.toString());
          }
      }
      /*
      result:
              name='jack', age=20
              name='jack', age=20
      */
      
  • public boolean equals(Object o)

    • 基本作用:判断两个对象的地址是否相等,相当于 “==”

    • 存在的意义:让子类重写,以便用于比较对象的内容是否相同

      public class Student {
          public String name;
          public int age;
      
          public Student(String name, int age) {
              this.name = name;
              this.age = age;
          }
          
      //重写的equals方法,在IDEA中输入equ即可自动生成,选择第二个
          @Override
          public boolean equals(Object o) {
              //如果对象的地址一样,则认为相同,防止输入s1.equals(s1)
              if (this == o) return true;
              //如果传入的对象为空,或者类型不是Student,则认为不同
              if (o == null || getClass() != o.getClass()) return false;
              //转换为当前类型
              Student student = (Student) o;
              return age == student.age && Objects.equals(name, student.name);
          }
      
          //下面的hashCode方法是IDEA生成equals方法时自动生成的
          @Override
          public int hashCode() {
              return Objects.hash(name, age);
          }
      }
      
      public class Test1 {
          public static void main(String[] args) {
              Student s1 = new Student("jack", 20);
              Student s2 = new Student("jack", 20);
      
              //==比较的是两个对象的地址,等价于Student类重写前的equals()方法
              System.out.println(s1 == s2);//false
              System.out.println(s1.equals(s2));//true
          }
      }
      
      
  • protected Object clone( )

    • 作用:对象克隆,是浅克隆,必须重写后才能使用

    • 在类中重写后,该类的对象就可以使用,无需修改代码

      public class User implements Cloneable{
          private int id;
          private String userName;
          private String password;
          private double[] scores;
      
          public User(int id, String userName, String password, double[] scores) {
              this.id = id;
              this.userName = userName;
              this.password = password;
              this.scores = scores;
          }
      
          @Override
          protected Object clone() throws CloneNotSupportedException {
              return super.clone();
          }
      
          public double[] getScores() {
              return scores;
          }
      }
      
      public class Test2 {
          public static void main(String[] args) throws CloneNotSupportedException {
              User u1 = new User(1001, "jack", "123456", new double[] { 67, 89, 100 });
              User u2 = (User)u1.clone();
              //浅克隆中引用类型是共享的,u2只是复制的引用类型的地址
              System.out.println(u1.getScores() == u2.getScores());
              System.out.println(u1 == u2);
          }
      }
      /*
      result:
              true
              false
      */
      

在这里插入图片描述

重写clone方法,使其深克隆

```java
//修改上文重写后的clone()
@Override
    protected Object clone() throws CloneNotSupportedException {
        User u2 = (User) super.clone();
        u2.scores = u2.scores.clone();
        return u2;
    }

```

```java
public class Test2{
    public static void main(String[] args) throws CloneNotSupportedException {
        User u1 = new User(1001, "jack", "123456", new double[] { 67, 89, 100 });
        User u2 = (User)u1.clone();
        //浅克隆中引用类型是共享的,u2只是复制的引用类型的地址
        System.out.println(u1.getScores() == u2.getScores());
        System.out.println(u1 == u2);
    }
}
/*
result:
        false
        false
*/
```

在这里插入图片描述

Objects

工具类,提供操作对象的许多静态方法

常见方法

  • public static boolean equals(Object a,Object b) 先做非空判断,再比较两个对象是否相等
  • public static boolean isNull(Object obj) 判断对象是否为null
  • public static boolean nonNull(Object obj) 判断对象是否不为null
public class Test {
    public static void main(String[] args) {
        String s1 = null;
        String s2 = "hello";
        
        
//        System.out.println(s1.equals(s2));//会报错,因为s1为null
        System.out.println(Objects.equals(s1, s2));
        //不会报错,因为Objects.equals()方法会判断是否为null
        //相比于直接调用对象的equals方法更安全

        System.out.println(Objects.isNull(s1));//判断是否为null
        System.out.println(s1 == null);//等价于上面的方法
        
        
        System.out.println(Objects.nonNull(s1));//判断是否不为null
        System.out.println(s1 != null);//等价于上面的方法
    }
}
/*
result:
        false
        true
        true
        false
        false
*/
//Objects.equals()的源码
    public static boolean equals(Object a, Object b) {
        return (a == b) || (a != null && a.equals(b));
    }

包装类

将基本类型数据包装成对象

基本数据类型包装类(引用类型)
byteByte
shortShort
intInteger
longLong
charCharacter
floatFloat
doubleDouble
booleanBoolean

以下内容以Integer为例,其他包装类用法与其相同

  • 将int类型转为Integer对象

    //将int类型的数据转换为Integer类型对象的三种方法
    public class Test {
        public static void main(String[] args) {
    
            //方法一:已过时
            Integer i1 = new Integer(100);
            
            //方法二:不常用
            Integer i2 = Integer.valueOf(100);
            
            //方法三:常用
            Integer i3 = 100;
            //java的自动装箱机制,编译器会自动帮我们改进代码:
            //Integer i3 = Integer.valueOf(100);
            int i4 = i3;
            //java的自动拆箱机制,编译器会自动帮我们改进代码:int i4 = i3.intValue();
            
            //自动装箱与拆箱机制的好处:使得我们在使用的时候更加方便
            ArrayList<Integer> list = new ArrayList<>();
            list.add(100);
            //自动装箱,编译器会帮我们改进代码:list.add(Integer.valueOf(100));
            int i = list.get(0);
            //自动拆箱,编译器会帮我们改进代码:list.get(0).intValue();
        }
    }
    
  • 两个相同类型之间的大小比较

            int a = 10;
            int b = 14;
            System.out.println(Integer.compare(a, b));//-1
            //Integer.compare(a, b)会比较a和b的大小
            // 如果a>b,返回1,如果a<b,返回-1,如果a=b,返回0
    
  • 常见应用:基本数据类型与字符串类型之间的转换

    • 将基本数据类型转为字符串类型
      • public static Sting toString(int a)
      • public Sting toString( )
    public class Test1 {
        public static void main(String[] args) {
            //将int类型的数据转换为String类型的数据的方法
            Integer a = 10;
            
            //方法一:
            String s = Integer.toString(a);
            //a会先自动拆箱为int类型,然后调用Integer.toString(int i)方法
            
            //方法二
            String s1 = a.toString();
            
            //方法3
            String s2 = a + "";
        }
    }
    
    • 将字符串类型转为数值本身对应的数据类型
      • public static int toString(String s)
      • public static Interger valueOf(String s)
    public class Test1 {
        public static void main(String[] args) {
            //将String类型的数据转换为int类型的数据的方法
            
            //方法一:
            String s2 = "100";//s2必须是数字格式的字符串,否则会报NumberFormatException异常
            int i1 = Integer.parseInt(s2);
            
            //方法二:
            int i2 = Integer.valueOf(s2);
            //Integer.valueOf(s2)方法返回的是Integer对象,再自动拆箱为int类型
        }
    }
    

StringBuilder

StringBuilder表示可变的字符串对象,里面的字符串对象可变,用于操作字符串

构造器

构造器说明
public StringBuilder( )创建一个空白的可变字符串对象
public StringBuilder(String str)创建一个带内容的可变字符串对象

常用方法

方法说明
public StringBuilder append(任意类型)将数据拼接到对象中并返回对象本身
public StringBuilder reverse( )将内容反转
public int length( )返回对象中字符串的长度
public String toString将StringBuilder 类型转换为String
public class Test {
    public static void main(String[] args) {
        //StringBuilder的构造方法
        StringBuilder sb1 = new StringBuilder();
        StringBuilder sb2 = new StringBuilder("1");

        //StringBuilder的常用方法
        //拼接字符串
        sb1.append("hello");
        sb1.append(9);
        System.out.println(sb1);
        //result:hello9
        
        //获取对象的类型
        System.out.println(sb1.getClass());
        //result:class java.lang.StringBuilder
        
        //获取对象的长度
        System.out.println(sb1.length());
        //result:6
        
        //将StringBuilder转换为String
        System.out.println(sb1.toString());
        System.out.println(sb1.toString().getClass());
        //result:hello9		class java.lang.String
        
        //将StringBuilder的内容反转
        System.out.println(sb1.reverse());
        //result:9olleh
        System.out.println(sb1);
        //上面直接对sb1进行了反转,不需要新的变量存储,之后所有的sb1都是反转之后的
        //result:9olleh
    }
}

StringBuilder与String的对比

  • 如果操作字符串较少,或者不需要操作,以及定义字符串变量,使用String
  • 对于字符串相关的操作,如频繁的拼接、修改等,建议用StringBuidler,会极大的提高效率
    • 因为StringBuidler直接对当前对象操作,而不像String会为每次拼接后的字符串在堆内存中重新生成新的存储空间,并需要变量指向新的存储空间的地址
public class Test2 {
    public static void main(String[] args) {
        String s1 = "hello";
        StringBuilder sb1 = new StringBuilder(s1);
        
        //String执行时长
        long stime1 = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            s1 = s1 + i;
        }
        long etime1 = System.currentTimeMillis();
        System.out.printf("String执行时长:%d 毫秒.",(etime1 - stime1));
        System.out.println();
        
	    //StringBuilder执行时长
        long stime2 = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            sb1.append(i);
        }
        long etime2 = System.currentTimeMillis();
        System.out.printf("StringBuilder执行时长:%d 毫秒.", (etime2 - stime2));
    }
}
/*
result:
        String执行时长:16949 毫秒.
        StringBuilder执行时长:2 毫秒.
*/

由运行时间可以看出,使用StringBuilder进行字符串操作会大大提高运算效率,因为无需开辟新的存储空间

StringBuffer

构造器与方法

StringBuffer的构造器与方法与StringBuilder完全相同

StringBuilder与StringBuffer的对比

  • StringBuilder是线程不安全的,效率高

    • 即效率更高,但是多用户同时操作容易出现bug
  • StringBuffer是线程安全的,效率低

    • 多用户同时操作不会出现bug,效率没有StringBuilder高,但是同样高于String
    public class Test3 {
        public static void main(String[] args) {
            String s1 = "hello";
            StringBuilder sb1 = new StringBuilder(s1);
            StringBuffer sb2 = new StringBuffer(s1);
            
    		//StringBuilder执行时长
            long stime2 = System.currentTimeMillis();
            for (int i = 0; i < 10000000; i++) {
                sb1.append(i);
            }
            long etime2 = System.currentTimeMillis();
            System.out.printf("StringBuilder执行时长:%d 毫秒.", (etime2 - stime2));
            System.out.println();
    		
            //StringBuffer执行时长
            long stime3 = System.currentTimeMillis();
            for (int i = 0; i < 10000000; i++) {
                sb2.append(i);
            }
            long etime3 = System.currentTimeMillis();
            System.out.printf("StringBuffer执行时长:%d 毫秒.", (etime3 - stime3));
        }
    }
    /*
    result:
            StringBuilder执行时长:212 毫秒.
            StringBuffer执行时长:330 毫秒.
    */
    

    案例

    返回任意整型数组的内容,返回的格式:[11, 22, 33]

    public class Test4 {
        public static void main(String[] args) {
            int[] a = {1,2,3};
            String s = getList(a);
            System.out.println(s);
        }
    
        //由于StringBuffer与StringBuilder用法完全相同,
        //将下面方法中的StringBuffer改为StringBuilder同样可以实现该案例
        public static String getList(int[] a){
            StringBuffer sb = new StringBuffer();
            sb.append("[");
            for (int i = 0; i < a.length; i++) {
                if(i == a.length - 1){
                    sb.append(a[i]);
                }else{
                    sb.append(a[i]).append(",");
                }
            }
            sb.append("]");
            return sb.toString();
        }
    }
    /*
    result:[1,2,3]
    */
    

StringJoiner

StringJoiner与StringBuilder、StringBuffer类似,也是用于操作字符串,本身是一个可变的字符串容器,唯一不同的是StringJoiner添加数据的方法只能接受字符串类型的参数,不能像StringBuilder、StringBuffer一样添加所有类型的数据

构造器

构造器说明
StringJoiner(CharSequence delimiter)创建一个StringJoiner对象,参数为拼接时的间隔符号
StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix)创建一个StringJoiner对象,参数为拼接时的间隔符号、开始符号和结束符号

常用方法

方法说明
public StringJoiner add(String类型数据)将字符串类型数据拼接到对象中并返回对象本身
public int length( )返回对象中字符串的长度
public String toString将StringBuilder 类型转换为String
public class Test5 {
    public static void main(String[] args) {
        StringJoiner sj1 = new StringJoiner("__");
        StringJoiner sj2 = new StringJoiner(",","[","]");

        //sj1
        sj1.add("hello");
        sj1.add("world");
        System.out.println(sj1);
        System.out.println(sj1.getClass());
        System.out.println(sj1.toString());
        System.out.println(sj1.toString().getClass());
        System.out.println(sj1.length());
        System.out.println("===========================");
        //sj2
        sj2.add("hello");
        sj2.add("world");
        System.out.println(sj2);
        System.out.println(sj2.getClass());
        System.out.println(sj2.toString());
        System.out.println(sj2.toString().getClass());
        System.out.println(sj2.length());
    }
}
/*
result:
        hello__world
        class java.util.StringJoiner
        hello__world
        class java.lang.String
        12
        ===========================
        [hello,world]
        class java.util.StringJoiner
        [hello,world]
        class java.lang.String
	    13
*/

StringJoiner与StringBuilder、StringBuffer的对比

StringJoiner在操作字符串时与StringBuilder、StringBuffer相同,都提高了字符串的操作效率,但是StringJoiner独特的优势在于它可以使代码更简洁

下面代码为StringBuffer的案例简化后的代码

public class Test6 {
    public static void main(String[] args) {
        int[] a = {1,2,3};
        String s = getList(a);
        System.out.println(s);
    }

    public static String getList(int[] a){
        StringJoiner sj = new StringJoiner(",","[","]");
        for (int i = 0; i < a.length; i++) {
            sj.add(Integer.toString(a[i]));
        }
        return sj.toString();
    }
}
/*
result:[1,2,3]
*/

Math

有关数学的工具类

常用方法

方法名说明
public static int abs(int a)获取参数绝对值
public static double ceil(double a)向上取整
public static double floor(double a)向下取整
public static int round(double a)四舍五入
public static int max(int a, int b)获取两个int值中的较大值
public static double pow(double a, double b)返回a的b次幂的值
public static double random( )返回值为doub1e的随机值,范围[0.0, 1.0)
public class MathTest {
    public static void main(String[] args) {
        // Math类中的常用方法
        // abs():绝对值,参数可以是int、long、float、double
        System.out.println(Math.abs(-10));// 10
        System.out.println(Math.abs(-22.5));// 22.5

        // ceil():向上取整,无论正负,都往大了取
        System.out.println(Math.ceil(12.3));// 13.0
        System.out.println(Math.ceil(12.9));// 13.0
        System.out.println(Math.ceil(-12.3));// -12.0
        // floor():向下取整,无论正负都往值小的取
        System.out.println(Math.floor(12.3));// 12.0
        System.out.println(Math.floor(12.9));// 12.0
        System.out.println(Math.floor(-12.3));// -13.0
        // round():四舍五入
        System.out.println(Math.round(12.3));// 12
        System.out.println(Math.round(12.9));// 13
        System.out.println(Math.round(-12.3));// -12
	    System.out.println(Math.round(-12.9));// -13
        
        // max():最大值,参数可以是int、long、float、double
        System.out.println(Math.max(12.3, 12.9));// 12.9
        // min():最小值,参数可以是int、long、float、double
        System.out.println(Math.min(12, 13));// 12

        // pow(a, b):a的b次幂
        System.out.println(Math.pow(2.3, 3));// 12.167999999999998
        System.out.println(Math.pow(2, 3));// 8.0

        //random():随机数[0.0, 1.0)
        System.out.println(Math.random());// 0.0~1.0之间的随机数,取不到1.0
    }
}

system

代表程序所在的系统,是一个工具类

常见方法

方法说明
public static void exit(int status)终止当前运行的java虚拟机
public static long currentTimeMillis( )返回当前系统时间,毫秒形式 1000ms = 1s
//public static void exit(int status)
public class SystemTest {
    public static void main(String[] args) {
//        public static void exit(int status)
//        作用:终止当前运行的Java虚拟机。
//        该参数用作状态代码:按照惯例,非零状态代码表示异常终止,0表示人为终止。
        System.exit(0);//人为终止虚拟机.
        //该代码之后的代码都不会执行,虚拟机已经终止了。
        System.out.println("hello");//不会执行
    }
}
//public static long currentTimeMillis( )
public class SystemTest2 {
    public static void main(String[] args) {
//        public static long currentTimeMillis()
//        作用:获取当前系统的时间
//        返回的是long类型的时间毫秒值:指的是从1970-1-10:0:0开始走到此刻的总的毫秒值
//		  1s=1000ms
//        常用于测试程序的效率
        long start = System.currentTimeMillis();
        int s = 1;
        for (int i = 0; i < 1000000; i++) {
            s += i;
        }
        System.out.println(s);
        long end = System.currentTimeMillis();
        System.out.println("共耗时:" + (end - start)/1000 + "秒");
    }
}

Runtime

  • 代表程序所在的运行环境
  • 是一个单例类,只能通过get方法获取唯一的对象,不能使用构造方法new

常用方法

方法说明
public static Runtime getRuntime()返回与当前Java应用程序关联的运行时对象
public void exit(int status)终止当前运行的虚拟机
public int availableProcessors()返回Java虚拟机可用的处理器数。
public long totalMemory()返回Java虚拟机中的内存总量
public long freeMemory()返回Java虚拟机中的可用内存
public Process exec(String command)启动某个程序,并返回代表该程序的对象
public class RunTimeTest {
    public static void main(String[] args) {
        //获取RunTime类的对象
        Runtime rt = Runtime.getRuntime();//因为是单例类,所以不能直接new

        //获取Java虚拟机可用的处理器数量
        int i = rt.availableProcessors();
        System.out.println("可用的处理器数量:" + i);//可以同时执行多少个程序

        //获取Java虚拟机中的内存总量
        long l = rt.totalMemory();//默认单位是字节  1M = 1024KB = 1024 *1024B 
        l = l / 1024 / 1024;//转换为MB
        System.out.println("Java虚拟机中的内存总量:" + l + "MB");

        //获取Java虚拟机中的空闲内存量
        long l1 = rt.freeMemory();//默认单位是字节
        l1 = l1 / 1024;//转换为KB
        System.out.println("Java虚拟机中的空闲内存量:" + l1 + "KB");

        try {
            //启动某个程序
            Process exe = rt.exec("notepad.exe");//启动记事本
            //系统休眠3秒
            Thread.sleep(3000);
            //杀死进程
            exe.destroy();
        } catch (Exception e) {
            e.printStackTrace();
        }

        //退出Java虚拟机
        rt.exit(0);
        System.out.println("====");//不会执行
    }
}
/*
可用的处理器数量:20
Java虚拟机中的内存总量:241MB
Java虚拟机中的空闲内存量:243423KB
*/

BigDecimal

解决浮点型运算时的结果失真问题

结果失真

在计算机中使用二进制存储,所以在将十进制转换为二进制时可能会出现结果失真问题

public class Test {
    public static void main(String[] args) {
        System.out.println(0.1 + 0.2);
        System.out.println(1.0 - 0.32);
        System.out.println(1.015 * 100);
        System.out.println(1.301 / 100);
    }
}
/*
result:
        0.30000000000000004
        0.6799999999999999
        101.49999999999999
        0.013009999999999999
*/

构造器

构造器说明
public BigDecimal(double val) 不要用,仍然会存在精度问题将double转换为BigDecimal
public BigDecimal(String val)把String转成BigDecimal

常用方法

常用方法说明
public static BigDecimal valueof(double val)转换一个double成BigDecimal
public BigDecimal add(BigDecimal b)加法
public BigDecimal subtract(BigDecimal b)减法
public BigDecimal multiply(BigDecimal b)乘法
public BigDecimal divide(BigDecimal b)除法
public BigDecimal divide(另一个BigDecimali对象,精确几位,舍入模式)除法、可以控制精确到小数几位
public double doublevalue( )将BigDecimal转换为double
public class Test1 {
    public static void main(String[] args) {
        //生成一个BigDecimal对象的两种方法
        //1.使用BigDecimal的构造器
        BigDecimal a = new BigDecimal(Double.toString(0.1));
        //2.使用BigDecimal的静态方法valueOf
        BigDecimal b = BigDecimal.valueOf(0.2);

        //使用BigDecimal的add方法进行加法运算
        BigDecimal c1 = a.add(b);
        System.out.println("加法 " + c1);

        //使用BigDecimal的subtract方法进行减法运算
        BigDecimal c2 = a.subtract(b);
        System.out.println("减法 " + c2);

        //使用BigDecimal的multiply方法进行乘法运算
        BigDecimal c3 = a.multiply(b);
        System.out.println("乘法 " + c3);

        //使用BigDecimal的divide方法进行除法运算,如果结果无限循环,会报错
        BigDecimal c4 = a.divide(b);
        System.out.println("除法 " + c4);

        //使用BigDecimal的doubleValue方法进行类型转换,将BigDecimal转换为double
        double v = c1.doubleValue();
    }
}
/*
result:
        加法 0.3
        减法 -0.1
        乘法 0.02
        除法 0.5
*/
//public BigDecimal divide(另一个BigDecimali对象,精确几位,舍入模式)
//解决除法结果无限循环报错的问题
public class Test2 {
    public static void main(String[] args) {
        BigDecimal a = new BigDecimal(Double.toString(0.1));
        BigDecimal b = BigDecimal.valueOf(0.2);
        BigDecimal c = a.divide(b, 2, RoundingMode.ROUND_HALF_UP);
        System.out.println(c);
    }
}
//0.50

八种舍入模式

  • RoundingMode.UP
    • 向远离原点的方向舍入
    • 无论结果是正数还是负数都会对结果的最后一位的绝对值加1
  • RoundingMode.DOWN
    • 向接近原点的方向舍入
    • 无论结果是正数还是负数直接截断,无需其他操作
  • RoundingMode.CEILING
    • 向上取整,向正无穷方向舍入
    • 即正数最后一位的绝对值加1,负数最后一位不变
  • RoundingMode.FLOOR
    • 向下取整,向负无穷方向舍入
    • 即正数最后一位不变,负数最后一位的绝对值加1
  • RoundingMode.HALF_UP
    • 四舍五入
    • 无论正数还是负数最后一位如果大于等于5则加1,小于5则不变
  • RoundingMode.HALF_DOWN
    • 五舍六入
    • 无论正数还是负数最后一位如果大于6则加1,小于等于5则不变
  • RoundingMode.HALF_EVEN
    • 向“最接近”的整数方向舍入,如果与两个相邻的整数距离相等,则向相邻的偶数舍入
    • 无论正数还是负数最后一位如果大于6则加1,小于5则不变
    • 等于5时,如果5前是偶数则不变,是奇数则加1
  • RoundingMode.UNNECESSARY
    • 无需舍入,求精确的结果
public class Test2 {
    public static void main(String[] args) {
        BigDecimal a = new BigDecimal(Double.toString(-0.111));
        BigDecimal b = BigDecimal.valueOf(0.222);
        BigDecimal c = BigDecimal.valueOf(0.225);
        BigDecimal d = BigDecimal.valueOf(-0.225);
        BigDecimal e = BigDecimal.valueOf(-0.235);
        //八种舍入法
        
        //ROUND_UP
        //舍弃指定位数的小数部分,无论正负都向远离原点的方向舍入
        //即无论结果是正数还是负数都会对结果的最后一位的绝对值加1
        System.out.println(a.add(b).setScale(2, RoundingMode.UP));
        //-0.111 + 0.222 = 0.111    第二位1加上1等于2,所以结果为0.12
        System.out.println(a.subtract(b).setScale(2, RoundingMode.UP));
        //-0.111 - 0.222 = -0.333   第二位3加上1等于4,所以结果为-0.34


        //ROUND_DOWN
        // 舍弃指定位数的小数部分,无论正负都向接近原点的方向舍入
        // 即无论结果是正数还是负数都直接截断即可,无需进行额外的操作
        System.out.println(a.add(b).setScale(2, RoundingMode.DOWN));
        //-0.111 + 0.222 = 0.111    所以结果为0.11
        System.out.println(a.subtract(b).setScale(2, RoundingMode.DOWN));
        //-0.111 - 0.222 = -0.333   所以结果为-0.33

        //ROUND_CEILING
        // 向上取整,向正无穷方向舍入
        //即正数最后一位的绝对值加1,负数最后一位不变
        System.out.println(a.add(b).setScale(2, RoundingMode.CEILING));
        //-0.111 + 0.222 = 0.111    所以结果为0.12
        System.out.println(a.subtract(b).setScale(2, RoundingMode.CEILING));
        //-0.111 - 0.222 = -0.333   所以结果为-0.33

        //ROUND_FLOOR
        // 向下取整,向负无穷方向舍入
        //即正数最后一位不变,负数最后一位的绝对值加1
        System.out.println(a.add(b).setScale(2, RoundingMode.FLOOR));
        //-0.111 + 0.222 = 0.111    所以结果为0.11
        System.out.println(a.subtract(b).setScale(2, RoundingMode.FLOOR));
        //-0.111 - 0.222 = -0.333   所以结果为-0.34

        //ROUND_HALF_UP
        // 向“最接近”的整数方向舍入,四舍五入
        //即无论正数还是负数最后一位如果大于等于5则加1,小于5则不变
        System.out.println(c.setScale(2, RoundingMode.HALF_UP));
        //0.225   所以结果为0.23
        System.out.println(d.setScale(2, RoundingMode.HALF_UP));
        //-0.225  所以结果为-0.23

        //ROUND_HALF_DOWN
        // 向“最接近”的整数方向舍入,五舍六入
        //即无论正数还是负数最后一位如果大于6则加1,小于等于5则不变
        System.out.println(c.setScale(2, RoundingMode.HALF_DOWN));
        //0.225   所以结果为0.22
        System.out.println(d.setScale(2, RoundingMode.HALF_DOWN));
        //-0.225  所以结果为-0.22

        //ROUND_HALF_EVEN
        // 向“最接近”的整数方向舍入,如果与两个相邻的整数距离相等,则向相邻的偶数舍入
        //即无论正数还是负数最后一位如果大于6则加1,小于5则不变,
        //等于5时,如果5前是偶数则不变,是奇数则加1
        System.out.println(c.setScale(2, RoundingMode.HALF_EVEN));
        //0.225   所以结果为0.22
        System.out.println(e.setScale(2, RoundingMode.HALF_EVEN));;
        //-0.235  所以结果为-0.24

        //ROUND_UNNECESSARY
        //计算结果是精确的,不需要舍入模式
        //如果使用此模式对非精确结果进行舍入,则抛出ArithmeticException
        //System.out.println(c.setScale(2, RoundingMode.UNNECESSARY));
    }
}

JDK8之前的传统时间(不推荐使用)

1.Date

表示日期与时间,是可变对象

构造器
构造器说明
public Date( )创建一个Date对象,代表系统当前的时间
public Date(long time)将传入的时间毫秒值转为一个日期对象
方法
方法说明
public long getTime( )返回从1970年1月1日08:00:00到当前系统时间的总毫秒数
public void setTime(long Time)将日期对象的时间设置为传入的毫秒值对应的时间
public class Test {
    public static void main(String[] args) {
        // 创建日期对象,返回当前时间
        Date d = new Date();
        System.out.println(d);

        // 创建日期对象,把当前的毫秒值转成日期对象,从1970年1月1日 00:00:00开始经历的毫秒数
        Date d2 = new Date(0);
        //结果为1970年1月1日 08:00:00而不是00:00:00,
        //是因为我们在中国,所以按照中国时区会加上8个小时
        System.out.println(d2);

        // 获取当前时间对象对应的毫秒值
        long t = d.getTime();
        System.out.println(t);

        //将日期对象的时间通过输入的毫秒值改变
        d.setTime(0);
        System.out.println(d);
    }
}
/*
result:
        Mon Jul 31 21:38:22 CST 2023
        Thu Jan 01 08:00:00 CST 1970
        1690810702361
        Thu Jan 01 08:00:00 CST 1970
*/
时间单位转换
  • 一年 = 12月
  • 一月 = 30天
  • 一天 = 24小时
  • 一小时 = 60分钟
  • 一分钟 = 60秒
  • 一秒 = 1000毫秒
  • 一毫秒 = 1000微秒
  • 一微秒 = 1000纳秒

2.SimpleDateFormat

将Date对象的时间格式或毫秒值格式化成正常人能看懂的时间,即自定义时间格式

  • Mon Jul 31 21:38:22 CST 2023 转化成 2023-7-31 21:38:22
  • 是线程不安全的
构造器
构造器说明
public SimpleDateFormat(String pattern)创建对象,并根据输入定义时间的格式
方法
  • 将日期对象或者毫秒值转为自定义格式的字符串
    • public final String format(Date date/object time)
  • 将字符串解析为日期对象
    • public Date parse(String source)
public class Test2 {
    public static void main(String[] args) throws ParseException {
        Date d = new Date();

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE a ");

        // 将Date对象日期格式化
        String s1 = sdf.format(d);
        System.out.println(s1);
        // 将毫秒日期格式化
        String s2 = sdf.format(0);
        System.out.println(s2);

        //解析字符串为日期,必须要求字符串格式与SimpleDateFormat对象定义的格式一致
        Date p = sdf.parse(s1);
        System.out.println(p);
    }
}
/*
result:
        2023-08-01 09:20:20 星期二 上午 
        1970-01-01 08:00:00 星期四 上午 
        Tue Aug 01 09:20:20 CST 2023
*/
日期格式常见符号
符号意义
y
M
d
H
m
s
EEE星期几
a上午/下午

例:yyyy-MM-dd 2023-08-01

案例

在这里插入图片描述

public class Test3 {
    public static void main(String[] args) throws ParseException {
        String start = "2023年11月11日0:0:0";
        String end = "2023年11月11日0:10:0";
        String xj = "2023年11月11日0:01:18";
        String xp = "2023年11月11日0:10:57";

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
        // 将字符串转换为时间毫秒值
        long st = sdf.parse(start).getTime();
        long ed = sdf.parse(end).getTime();
        long xjt = sdf.parse(xj).getTime();
        long xpt = sdf.parse(xp).getTime();

        if (xjt >= st && xjt <= ed) {
            System.out.println("xj限价买入成功");
        } else {
            System.out.println("xj限价买入失败");
        }

        if (xpt >= st && xpt <= ed) {
            System.out.println("xp限价买入成功");
        } else {
            System.out.println("xp限价买入失败");
        }
    }
}

3.Calendar

  • 代表系统此刻的日历时间
  • 是可变对象,所有的操作都是直接修改该对象
  • 通过它可以单独获取、修改时间中的年、月、日、时、分、秒等
常用方法
方法说明
public static Calendar getInstance()获取当前日历对象
public int get(int field)根据参数获取日历中的某个信息
public final Date getTime()获取Date日期对象
public long getTimeInMillis()获取时间毫秒值
public void set(int field,int value)根据参数修改日历的某个信息
public void add(int field,int amount)根据参数对指定信息进行增加或减少
public class Test4 {
    public static void main(String[] args) {
        // Calendar是一个抽象类,不能直接new对象
        // 通过getInstance()方法获取实例
        Calendar cld = Calendar.getInstance();
        //System.out.println(cld);

        //根据传入的常量获取对象中的年
        int year = cld.get(Calendar.YEAR);
        System.out.println("修改前的年:" + year);
        //根据传入的常量获取对象中的月
        int month = cld.get(Calendar.MONTH);
        System.out.println(month + 1);//月份从0开始,所以要获取实际月份要加1

        //获取日期对象
        Date time = cld.getTime();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String format = sdf.format(time);
        System.out.println("当前日期:" + format);

        //获取当前日期的毫秒值
        long timeInMillis = cld.getTimeInMillis();

        //根据参数直接修改日历对象中的某个值
        cld.set(Calendar.YEAR, 1998);
        System.out.println("第一次修改后的年:" + cld.get(Calendar.YEAR));

        //根据参数的加或减修改日历对象中的某个值
        cld.add(Calendar.YEAR, 1);//1998+1=1999
        System.out.println("第二次修改后的年:" + cld.get(Calendar.YEAR));
        cld.add(Calendar.YEAR, -1);//1999-1=1998
        System.out.println("第三次修改后的年:" + cld.get(Calendar.YEAR));
    }
}
/*
result:
        修改前的年:2023
        8
        当前日期:2023-08-01 10:17:26
        第一次修改后的年:1998
        第二次修改后的年:1999
        第三次修改后的年:1998
*/

JDK8之前的时间对象存在的问题

  • 设计不合理,使用不方便,很多都被淘汰了。
  • 都是可变对象,修改后会丢失最开始的时间信息。
  • 线程不安全
  • 只能精确到毫秒。

JDK8之后的时间对象的优势

  • 设计更合理,功能丰富,使用更方便
  • 都是不可变对象,修改后会返回新的时间对象,不会丢失最开始的时间
  • 线程安全
  • 能精确到微秒,纳秒
时间单位转换
  • 一年 = 12月
  • 一月 = 30天
  • 一天 = 24小时
  • 一小时 = 60分钟
  • 一分钟 = 60秒
  • 一秒 = 1000毫秒
  • 一毫秒 = 1000微秒
  • 一微秒 = 1000纳秒

JDK8之后的时间对象(推荐使用)

代替Calendar的时间对象

1. LocalDate
  • 代表本地日期(年、月、日、星期)
  • 是不可变对象,与Calendar不同,Calendar是可变对象
常用方法
  • 获取LocalDate对象
方法说明
public static LocalDate now( )用于获取代表当前时间的LocalDate对象,相当于无参构造
public static LocalDate of(int year, int month, int dayOfMonth)用于获取指定时间的LocalDate对象,相当于有参构造,可以省略后面参数
public class LocalDateTest {
    public static void main(String[] args) {
        // 获取代表当前日期的LocalDate对象,是不可变对象
        //只能通过静态方法now()来创建LocalDate对象,不能使用构造器来创建
        //相当于无参构造器
        LocalDate now = LocalDate.now();
        System.out.println("当前时间:" + now);//当前时间:2023-08-01

        //获取指定日期的LocalDate对象
        //只能通过静态方法of()来创建LocalDate对象,不能使用构造器来创建
        //相当于有参构造器,可以省略后面的参数
        LocalDate date = LocalDate.of(1998, 4, 16);
        System.out.println("指定日期的时间:" + date);//指定日期的时间:1998-04-16
    }
}
  • 获取对象中的信息的方法
方法说明
public int getYear()获取年
public int getMonthValue()获取月
public int getDayOfMonth()获取日
public int getDayOfYear()获取一年中的第几天
public java.time.DayOfWeek getDayOfWeek() + public int getValue()获取星期几
public class LocalDateTest {
    public static void main(String[] args) {
        LocalDate now = LocalDate.now();
        System.out.println("当前时间:" + now);//包含年月日

        //获取年
        int year = now.getYear();
        System.out.println("year = " + year);//year = 2023
        
        //获取月
        //1-12,不是从0开始
        int month = now.getMonthValue();
        System.out.println("month = " + month);//month = 8
        
        //获取日
        int day = now.getDayOfMonth();
        System.out.println("day = " + day);//day = 1
        
        //获取一年中的第几天
        int dayOfYear = now.getDayOfYear();
        System.out.println("dayOfYear = " + dayOfYear);//dayOfYear = 213
        
        //获取星期几
        int dayOfWeek = now.getDayOfWeek().getValue();
        System.out.println("dayOfWeek = " + dayOfWeek);//dayOfWeek = 2
    }
}
  • 直接修改日期的方法
方法说明
public LocalDate withYear( int year )修改年,返回新对象
public LocalDate withMonth( int month )修改月,返回新对象
public LocalDate withDayOfMonth( int dayOfMonth )修改日,返回新对象
package com.LMH.NewDate;
import java.time.LocalDate;

public class LocalDateTest {
    public static void main(String[] args) {
        LocalDate now = LocalDate.now();
        System.out.println("当前时间:" + now);//包含年月日

        //直接修改日期
        //返回一个新的LocalDate对象,原来的对象不变,仍保存当前的时间
        //由此可见,LocalDate对象是不可变对象
        LocalDate now1 = now.withYear(1998).withMonth(8).withDayOfMonth(8);
        System.out.println("原来的时间" + now);//原来的时间2023-08-01
        System.out.println("修改后的时间:" + now1);//修改后的时间:1998-08-08
    }
}
  • 通过增加的方式修改日期的方法
方法说明
public LocalDate plusYears( long yearsToAdd )增加年
public LocalDate plusMonths( long monthsToAdd )增加月
public LocalDate plusDays( long daysToAdd )增加日
public class LocalDateTest {
    public static void main(String[] args) {
        LocalDate now = LocalDate.now();
        System.out.println("当前时间:" + now);//包含年月日
        
       //通过增加的方式修改日期
        //返回一个新的LocalDate对象,原来的对象不变,仍保存当前的时间
        LocalDate now2 = now.plusYears(1);
        LocalDate now3 = now.plusMonths(1).plusDays(2);
        System.out.println("原来的时间" + now);//原来的时间2023-08-01
        System.out.println("年+1后的时间:" + now2);//年+1后的时间:2024-08-01
        System.out.println("月+1,日+2后的时间 " + now3);//月+1,日+2后的时间 2023-09-03
    }
}
  • 通过减少的方式修改日期的方法
方法说明
public LocalDate minusYears( long yearsToSubtract)减少年
public LocalDate minusMonths( long monthsToSubtract )减少月
public LocalDate minusDays( long daysToSubtract )减少日
public class LocalDateTest {
    public static void main(String[] args) {
        LocalDate now = LocalDate.now();
        System.out.println("当前时间:" + now);//包含年月日

        //通过减少的方式修改日期
        //返回一个新的LocalDate对象,原来的对象不变,仍保存当前的时间
        LocalDate now4 = now.minusYears(1);
        LocalDate now5 = now.minusMonths(1).minusDays(2);
        System.out.println("原来的时间" + now);//原来的时间2023-08-01
        System.out.println("年-1后的时间:" + now4);//年-1后的时间:2022-08-01
        System.out.println("月-1,日-2后的时间 " + now5);//月-1,日-2后的时间 2023-06-29
    }
}
  • 比较两个LocalDate对象的方法
方法说明
public boolean equals( @Nullable Object obj )判断两个对象的时间是否相同
public boolean isBefore( @NotNull java.time.chrono.ChronoLocalDate other )判断当前对象的时间是否在传入的对象的时间之前
public boolean isAfter( @NotNull java.time.chrono.ChronoLocalDate other )判断当前对象的时间是否在传入的对象的时间之后
public class LocalDateTest {
    public static void main(String[] args) {
        LocalDate now = LocalDate.now();
        System.out.println("当前时间:" + now);
        LocalDate date = LocalDate.of(1998, 4, 16);
        System.out.println("指定日期的时间:" + date);

        //判断日期的先后
        System.out.println("now和date相同吗?" + now.equals(date));//false
        System.out.println("now在date之前吗?" + now.isBefore(date));//false
        System.out.println("now在date之后吗?" + now.isAfter(date));//true
    }
}
2. LocalTime
  • 代表本地时间(时、分、秒、纳秒)
  • 也是不可变对象
  • 用法与LocalDate大致相同
常用方法
  • 获取LocalDate对象
public class LocalTimeTest {
    public static void main(String[] args) {
        // 获取代表当前日期的LocalTime对象,是不可变对象
        //只能通过静态方法now()来创建LocalTime对象,不能使用构造器来创建
        //相当于无参构造器 
        //时,分,秒,纳秒
        LocalTime now = LocalTime.now();
        System.out.println(now);//15:30:29.580

        //获取指定日期的LocalTime对象,是不可变对象
        //只能通过静态方法of()来创建LocalTime对象,不能使用构造器来创建
        //相当于有参构造器
        //参数列表:时,分,秒,纳秒,可以省略最后的两个参数
//        LocalTime time = LocalTime.of(12);//参数不够,会报错
        LocalTime time1 = LocalTime.of(12, 15);
        System.out.println(time1);//12:15
        
        LocalTime time2 = LocalTime.of(12, 15, 13);
        System.out.println(time2);//12:15:13
        
        LocalTime time3 = LocalTime.of(12, 15, 13, 123456789);
        System.out.println(time3);//12:15:13.123456789
    }
}
  • 获取对象中的信息的方法
public class LocalTimeTest {
    public static void main(String[] args) {
        LocalTime now = LocalTime.now();
        System.out.println(now);//15:34:18.987

        //获取时
        int hour = now.getHour();
        System.out.println("hour = " + hour);//hour = 15
        //获取分
        int minute = now.getMinute();
        System.out.println("minute = " + minute);//minute = 34
        //获取秒
        int second = now.getSecond();
        System.out.println("second = " + second);//second = 18
        //获取纳秒
        int nano = now.getNano();
        System.out.println("nano = " + nano);//nano = 987000000
    }
}
  • 直接修改日期的方法
public class LocalTimeTest {
    public static void main(String[] args) {
        LocalTime now = LocalTime.now();
        System.out.println(now);//15:36:14.048

        //直接修改时间
        //返回一个新的LocalTime对象,原来的对象不变,仍保存当前的时间
        //由此可见,LocalTime对象是不可变对象
        LocalTime now1 = now.withHour(12).withMinute(12).withSecond(12);
        System.out.println("原来的时间" + now);//原来的时间15:36:14.048
        System.out.println("修改后的时间:" + now1);//修改后的时间:12:12:12
    }
}
  • 通过增加的方式修改日期的方法
public class LocalTimeTest {
    public static void main(String[] args) {
        LocalTime now = LocalTime.now();
        System.out.println(now);//15:39:08.347

        //增加时间
        //返回一个新的LocalTime对象,原来的对象不变,仍保存当前的时间
        LocalTime now2 = now.plusHours(1);
        LocalTime now3 = now.plusMinutes(1).plusSeconds(2).plusNanos(123456789);
        System.out.println("原来的时间" + now);
        System.out.println("增加1小时后的时间:" + now2);
        System.out.println("增加1分钟2秒123456789纳秒后的时间:" + now3);
    }
}
/*
result:
		原来的时间15:39:08.347
		增加1小时后的时间:16:39:08.347
		增加1分钟2秒123456789纳秒后的时间:15:40:10.470456789
*/
  • 通过减少的方式修改日期的方法
public class LocalTimeTest {
    public static void main(String[] args) {
        LocalTime now = LocalTime.now();
        System.out.println(now);//15:40:45.007

        //减少时间
        //返回一个新的LocalTime对象,原来的对象不变,仍保存当前的时间
        LocalTime now4 = now.minusHours(1);
        LocalTime now5 = now.minusSeconds(2);
        System.out.println("原来的时间" + now);//原来的时间15:40:45.007
        System.out.println("减少1小时后的时间:" + now4);//减少1小时后的时间:14:40:45.007
        System.out.println("减少2秒后的时间:" + now5);//减少2秒后的时间:15:40:43.007
    }
}
  • 比较两个LocalDate对象的方法
public class LocalTimeTest {
    public static void main(String[] args) {
        LocalTime time1 = LocalTime.of(12, 15);
        LocalTime time2 = LocalTime.of(12, 15, 13, 123456789);
        LocalTime time3 = LocalTime.of(12, 15, 13, 123456789);
        
        //比较时间
        System.out.println(time2.equals(time3));//true
        System.out.println(time1.isAfter(time2));//false
        System.out.println(time1.isBefore(time2));//true
    }
}
3. LocalDateTime(最常用)
  • 代表本地日期、时间(年、月、日、星期、时、分、秒、纳秒)
  • 也是不可变对象
  • 方法与LocalDate、LocalTime基本相同,新增LocalDateTime与LocalDate、LocalTime之间的相互转换
常用方法
  • 获取LocalDateTime对象以及LocalDateTime对象与LocalDate、LocalTime之间的相互转换
方法说明
public static LocalDateTime now()用于获取当前时间的LocalDateTime 对象,相当于无参构造
public static LocalDateTime of( int year, int month,int dayOfMonth, int hour,int minute, int second, nanoOfSecond )用于获取指定时间的LocalDateTime 对象,相当于有参构造,可以省略最后两个参数
public static LocalDateTime of(java.time.LocalDate date, java.time.LocalTime time )用于合并LocalDate和LocalTime 对象为LocalDateTime对象
public java.time.LocalDate toLocalDate()将LocalDateTime 对象转为LocalDate对象
public java.time.LocalTime toLocalTime()将LocalDateTime 对象转为LocalTime 对象
public class LocalDateTimeTest {
    public static void main(String[] args) {
        // 获取代表当前日期的LocalDateTime对象,是不可变对象
        //只能通过静态方法now()来创建LocalDateTime对象,不能使用构造器来创建
        //相当于无参构造器
        //年月日时分秒纳秒
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);//2023-08-01T16:00:22.456

        //获取指定日期的LocalDateTime对象
        //相当于有参构造器
        //传入参数:年月日时分秒纳秒,可以从后面开始省略,最少传入年月日时分
        LocalDateTime ldt1 = LocalDateTime.of(2019, 10, 1,12,12);
        System.out.println(ldt1);//2019-10-01T12:12
        LocalDateTime ldf2 = LocalDateTime.of(2019, 10, 1, 12, 12, 12, 12);
        System.out.println(ldf2);//2019-10-01T12:12:12.000000012

        //转换为LocalDate和LocalTime对象
        LocalDate ld = ldt.toLocalDate();
        System.out.println(ld);//2023-08-01
        LocalTime lt = ldt.toLocalTime();
        System.out.println(lt);//16:00:22.456

        //合并LocalDate和LocalTime对象为LocalDateTime对象
        LocalDateTime ldt3 = LocalDateTime.of(ld, lt);
        System.out.println(ldt3);//2023-08-01T16:00:22.456
    }
}
  • 获取对象中的信息的方法
方法说明
public int getYear()获取年
public int getMonthValue()获取月
public int getDayOfMonth()获取日
public int getDayOfYear()获取一年中的第几天
public java.time.DayOfWeek getDayOfWeek() + public int getValue()获取星期几
public int getHour()获取时
public int getMinute()获取分
public int getSecond()获取秒
public int getNano()获取纳秒
public class LocalDateTest1 {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);//2023-08-01T16:13:05.342

        //年
        int year = now.getYear();
        System.out.println("year = " + year);//year = 2023
        //月
        int month = now.getMonthValue();
        System.out.println("month = " + month);//month = 8
        //日
        int day = now.getDayOfMonth();
        System.out.println("day = " + day);//day = 1
        //星期几
        int dayOfWeek = now.getDayOfWeek().getValue();
        System.out.println("dayOfWeek = " + dayOfWeek);//dayOfWeek = 2
        //一年中的第几天
        int dayOfYear = now.getDayOfYear();
        System.out.println("dayOfYear = " + dayOfYear);//dayOfYear = 213
        //时
        int hour = now.getHour();
        System.out.println("hour = " + hour);//hour = 16
        //分
        int minute = now.getMinute();
        System.out.println("minute = " + minute);//minute = 13
        //秒
        int second = now.getSecond();
        System.out.println("second = " + second);//second = 5
        //纳秒
        int nano = now.getNano();
        System.out.println("nano = " + nano);//nano = 342000000
    }
}
  • 直接修改日期的方法
方法说明
public LocalDateTime withYear( int year )修改年,返回新对象
public LocalDateTime withMonth( int month )修改月,返回新对象
public LocalDateTime withDayOfMonth( int dayOfMonth )修改日,返回新对象
public LocalDateTime withHour( int hour )修改时,返回新对象
public LocalDateTime withMinute( int minute )修改分,返回新对象
public LocalDateTime withSecond( int second )修改秒,返回新对象
public LocalDateTime withNano( int nanoOfSecond )修改纳秒,返回新对象
public class LocalDateTimeTest2 {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);//2023-08-01T16:20:14.090

        //修改年份
        LocalDateTime ldt1 = now.withYear(2022);
        System.out.println(ldt1);//2022-08-01T16:20:14.090
        
        //修改月份
        LocalDateTime ldt2 = now.withMonth(10);
        System.out.println(ldt2);//2023-10-01T16:20:14.090
        
        //修改日
        LocalDateTime ldt3 = now.withDayOfMonth(10);
        System.out.println(ldt3);//2023-08-10T16:20:14.090
        
        //修改时、分
        LocalDateTime ldt4 = now.withHour(10).withMinute(10);
        System.out.println(ldt4);//2023-08-01T10:10:14.090
        
        //修改秒、纳秒
        LocalDateTime ldt5 = now.withSecond(10).withNano(10);
        System.out.println(ldt5);//2023-08-01T16:20:10.000000010
    }
}
  • 通过增加的方式修改日期的方法
方法说明
public LocalDateTime plusYears( long years )根据参数增加年
public LocalDateTime plusMonths( long months )根据参数增加月
public LocalDateTime plusDays( long days )根据参数增加日
public LocalDateTime plusHours( long hours )根据参数增加时
public LocalDateTime plusMinutes( long minutes )根据参数增加分
public LocalDateTime plusSeconds( long seconds )根据参数增加秒
public LocalDateTime plusNanos( long nanos )根据参数增加纳秒
public class LocalDateTimeTest3 {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        System.out.println("now = " + now);
        //now = 2023-08-01T16:27:33.039

        //年+1
        LocalDateTime ldt1 = now.plusYears(1);
        System.out.println("year + 1 = " + ldt1);
        //year + 1 = 2024-08-01T16:27:33.039
        
        //月+2
        LocalDateTime ldt2 = now.plusMonths(2);
        System.out.println("month + 2 = " + ldt2);
        //month + 2 = 2023-10-01T16:27:33.039
        
        //日+3
        LocalDateTime ldt3 = now.plusDays(3);
        System.out.println("day + 3 = " + ldt3);
        //day + 3 = 2023-08-04T16:27:33.039
        
        //时+4
        LocalDateTime ldt4 = now.plusHours(4);
        System.out.println("hour + 4 = " + ldt4);
        //hour + 4 = 2023-08-01T20:27:33.039
        
        //分+5
        LocalDateTime ldt5 = now.plusMinutes(5);
        System.out.println("minute + 5 = " + ldt5);
        //minute + 5 = 2023-08-01T16:32:33.039
        
        //秒+6
        LocalDateTime ldt6 = now.plusSeconds(6);
        System.out.println("second + 6 = " + ldt6);
        //second + 6 = 2023-08-01T16:27:39.039
        
        //纳秒+7
        LocalDateTime ldt7 = now.plusNanos(7);
        System.out.println("nano + 7 = " + ldt7);
        //nano + 7 = 2023-08-01T16:27:33.039000007
    }
}
  • 通过减少的方式修改日期的方法
方法说明
public LocalDateTime minusYears( long years )根据参数减少年
public LocalDateTime minusMonths( long months )根据参数减少月
public LocalDateTime minusDays( long days )根据参数减少日
public LocalDateTime minusHours( long hours )根据参数减少时
public LocalDateTime minusMinutes( long minutes )根据参数减少分
public LocalDateTime minusSeconds( long seconds )根据参数减少秒
public LocalDateTime minusNanos( long nanos )根据参数减少纳秒
public class LocalDateTest3 {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        System.out.println("now = " + now);
        //now = 2023-08-01T16:32:52.244

        //年-1
        LocalDateTime ldt1 = now.minusYears(1);
        System.out.println("year - 1 = " + ldt1);
        //year - 1 = 2022-08-01T16:32:52.244

        //月-2
        LocalDateTime ldt2 = now.minusMonths(2);
        System.out.println("month - 2 = " + ldt2);
        //month - 2 = 2023-06-01T16:32:52.244

        //日-3
        LocalDateTime ldt3 = now.minusDays(3);
        System.out.println("day - 3 = " + ldt3);
        //day - 3 = 2023-07-29T16:32:52.244

        //时-4
        LocalDateTime ldt4 = now.minusHours(4);
        System.out.println("hour - 4 = " + ldt4);
        //hour - 4 = 2023-08-01T12:32:52.244

        //分-5
        LocalDateTime ldt5 = now.minusMinutes(5);
        System.out.println("minute - 5 = " + ldt5);
        //minute - 5 = 2023-08-01T16:27:52.244

        //秒-6
        LocalDateTime ldt6 = now.minusSeconds(6);
        System.out.println("second - 6 = " + ldt6);
        //second - 6 = 2023-08-01T16:32:46.244

        //纳秒-7
        LocalDateTime ldt7 = now.minusNanos(7);
        System.out.println("nano - 7 = " + ldt7);
        //nano - 7 = 2023-08-01T16:32:52.243999993
    }
}
  • 比较两个LocalDate对象的方法
方法说明
public boolean equals( @Nullable Object obj )判断两个对象的时间是否相同
public boolean isBefore( @NotNull java.time.chrono.ChronoLocalDateTime<?> other )判断当前对象的时间是否在传入的对象的时间之前
public boolean isAfter( @NotNull java.time.chrono.ChronoLocalDateTime<?> other )判断当前对象的时间是否在传入的对象的时间之后
public class LocalDateTimeTest4 {
    public static void main(String[] args) {
        LocalDateTime ldf1 = LocalDateTime.of(2023, 1, 1, 2, 3);
        LocalDateTime ldf2 = LocalDateTime.of(2023, 1, 1, 2, 3);
        LocalDateTime ldf3 = LocalDateTime.of(2023, 1, 1, 2, 3, 4);

        System.out.println(ldf1.equals(ldf2));//true
        System.out.println(ldf1.isBefore(ldf3));//true
        System.out.println(ldf1.isAfter(ldf3));//false
    }
}
4. ZoneId

代表时区的对象

常用方法
方法说明
public static Set getAvailableZonelds()获取Java中支持的所有时区
public static Zoneld systemDefault()获取系统默认时区
public static Zoneld of(String zoneld)获取一个指定时区
public class ZoneIdTest {
    public static void main(String[] args) {

        //获取代表当前系统默认时区的ZoneId对象,相当于无参构造方法
        ZoneId zi1 = ZoneId.systemDefault();
        System.out.println("zi1 = " + zi1);
        //zi1 = Asia/Shanghai

        //获取代表指定时区的ZoneId对象,相当于有参构造方法
        ZoneId zi2 = ZoneId.of("America/New_York");
        System.out.println("zi2 = " + zi2);
        //zi2 = America/New_York

        //获取所有可用的时区ZoneId对象
        Set<String> allZi = ZoneId.getAvailableZoneIds();
        System.out.println("allZiLen = " + allZi.size());
        //allZiLen = 602,即有602个时区
    }
}
5. ZoneDateTime
  • 相当于带时区的LocalDateTime对象
  • 同样是不可变对象
  • 所有的方法同LocalDateTime对象基本一致
常用方法
方法说明
public static ZonedDateTime now()获取当前时区的ZonedDateTime对象
public static ZonedDateTime now(Zoneld zone)获取指定时区的ZonedDateTime对象
getYear、getMonthValue、getDayOfMonth、getDayOfYeargetDayofWeek、getHour、getMinute、getSecond、getNano获取年月日、时分秒、纳秒等,同LocalDateTime对象
public ZonedDateTime withXxx(时间)修改时间系列的方法,同LocalDateTime对象
public ZonedDateTime minusXxx(时间)减少时间系列的方法,同LocalDateTime对象
public ZonedDateTime plusXxx(时间)增加时间系列的方法,同LocalDateTime对象
equals、isBefore、isAfter两个对象的比较,同LocalDateTime对象
public class ZoneDateTimeTest {
    public static void main(String[] args) {
        ZoneId zi = ZoneId.of("America/New_York");
        
        // 获取当前时区的ZonedDateTime对象
        ZonedDateTime now1 = ZonedDateTime.now();
        System.out.println(now1);
        //2023-08-01T17:08:02.641+08:00[Asia/Shanghai]

        // 获取美国时区的ZonedDateTime对象
        ZonedDateTime now2 = ZonedDateTime.now(zi);
        System.out.println(now2);
        //2023-08-01T05:08:02.647-04:00[America/New_York]

        //获取世界标准时间的ZonedDateTime对象
        ZonedDateTime now3 = ZonedDateTime.now(Clock.systemUTC());
        System.out.println(now3);
        //2023-08-01T09:08:02.651Z
    }
}

代替Date的时间对象

1. Instant
概念
  • 通过获取Instant的对象可以拿到此刻的时间,该时间由两部分组成:从1970-01-01 00:00:00开始走到此刻的总秒数+不够1秒的纳秒数
  • 是不可变对象
  • 比Date更精确,因为还会记录纳秒
常用方法
方法说明
public static Instant now()获取当前时间的Instant对象(标准时间)
public long getEpochSecond()获取从1970-81-81T80:80:80开始记录的秒数。
public int getNano()从时间线开始,获取除秒数外的纳秒数
plusSeconds plusMillis plusNanos增加时间系列的方法(秒、毫秒、纳秒)
minusSeconds minusMillis minusNanos减少时间系列的方法(秒、毫秒、纳秒)
equals、isBefore、isAfter判断系列的方法
public class InstantTest {
    public static void main(String[] args) {
        // 获取当前时间戳
        Instant now = Instant.now();
        System.out.println(now);//2023-08-01T12:35:22.621Z

        //获取当前时间戳的秒数
        long second = now.getEpochSecond();
        System.out.println(second);//1690893322

        // 获取当前时间戳的毫秒数
        int nano = now.getNano();
        System.out.println(nano);//621000000

        //增加时间戳的秒数
        Instant now1 = now.plusSeconds(10);
        System.out.println("增加10秒后的时间戳:" + now1);
        //增加10秒后的时间戳:2023-08-01T12:35:32.621Z

        //增加时间戳的毫秒数
        Instant now2 = now.plusMillis(200);
        System.out.println("增加200毫秒后的时间戳:" + now2);
        //增加200毫秒后的时间戳:2023-08-01T12:35:22.821Z

        //增加时间戳的纳秒数
        Instant now3 = now.plusNanos(300);
        System.out.println("增加300纳秒后的时间戳:" + now3);
        //增加300纳秒后的时间戳:2023-08-01T12:35:22.621000300Z

        //减少时间戳的秒数
        Instant now4 = now.minusSeconds(10);
        System.out.println("减少10秒后的时间戳:" + now4);
        //减少10秒后的时间戳:2023-08-01T12:35:12.621Z

        //减少时间戳的毫秒数
        Instant now5 = now.minusMillis(200);
        System.out.println("减少200毫秒后的时间戳:" + now5);
        //减少200毫秒后的时间戳:2023-08-01T12:35:22.421Z

        //减少时间戳的纳秒数
        Instant now6 = now.minusNanos(300);
        System.out.println("减少300纳秒后的时间戳:" + now6);
        //减少300纳秒后的时间戳:2023-08-01T12:35:22.620999700Z

        //判断两个时间戳是否相等
        System.out.println(now.equals(now1));//false
        //判断两个时间戳的先后
        System.out.println(now.isBefore(now1));//true
        System.out.println(now.isAfter(now1));//false
    }
}
作用
  • 可以用来记录代码的执行时间,或用于记录用户操作某个事件的时间点。
public class InstantTest2 {
    public static void main(String[] args) {
        //评估程序的运行时间
        Instant start = Instant.now();
        run();
        Instant end = Instant.now();
        System.out.println("耗费时间为:" + 
                           (end.getEpochSecond() - start.getEpochSecond()) + "秒"+ 
                           (end.getNano() - start.getNano()) + "纳秒");
        //耗费时间为:0秒1000000纳秒
    }

    public static void run(){
        for (int i = 0; i < 10000; i++) {
            for (int j = 0; j < 100000; j++) {
                int k = i + j;
            }
        }
    }
}

代替SimpleDateFormat的时间对象

1. DateTimeFormatter

用于时间的格式化和解析

常用方法
  • 获取DateTimeFormatter对象的方法

    • public static DateTimeFormatter ofPattern(时间格式)
    	    //根据静态方法ofPattern()创建DateTimeFormatter对象
            //分别对应LocalDateTime、LocalDate、LocalTime的日期对象的格式
            DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
            DateTimeFormatter dtf3 = DateTimeFormatter.ofPattern("HH时mm分ss秒");
    
  • 将LocalDateTime、LocalDate、LocalTimed等时间对象格式化为自定义格式的字符串

    • 方法一:使用DateTimeFormatter对象的format()方法参数为时间对象
    • public String format(@NotNull java.time.temporal.TemporalAccessor temporal )
    public class DateTimeFormatterTest {
        public static void main(String[] args) {
            //根据静态方法ofPattern()创建DateTimeFormatter对象
            //分别对应LocalDateTime、LocalDate、LocalTime的日期对象的格式
            DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
            DateTimeFormatter dtf3 = DateTimeFormatter.ofPattern("HH时mm分ss秒");
            //创建日期时间对象
            LocalDateTime ldt = LocalDateTime.now();
            LocalDate ld = LocalDate.now();
            LocalTime lt = LocalTime.now();
    
            //将日期时间对象转换为字符串
            //方法一:正向格式化
            // 使用DateTimeFormatter的format()方法,参数为时间对象
            // 格式必须与时间对象匹配
            String str1 = dtf1.format(ldt);
            System.out.println(str1);//2023-08-01 21:28:16
            String str2 = dtf2.format(ld);
            System.out.println(str2);//2023年08月01日
            String str3 = dtf3.format(lt);
            System.out.println(str3);//21时28分16秒
        }
    }
    
    • 方法二:使用时间对象的format()方法参数为DateTimeFormatter对象
    • public String format(@NotNull java.time.format.DateTimeFormatter formatter )
    public class DateTimeFormatterTest {
        public static void main(String[] args) {
            //根据静态方法ofPattern()创建DateTimeFormatter对象
            //分别对应LocalDateTime、LocalDate、LocalTime的日期对象的格式
            DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
            DateTimeFormatter dtf3 = DateTimeFormatter.ofPattern("HH时mm分ss秒");
            //创建日期时间对象
            LocalDateTime ldt = LocalDateTime.now();
            LocalDate ld = LocalDate.now();
            LocalTime lt = LocalTime.now();
    
            //方法二:反向格式化
            // 使用时间对象的format()方法,参数为DateTimeFormatter对象
            // 格式必须与DateTimeFormatter对象的格式匹配
            String str4 = ldt.format(dtf1);
            System.out.println(str4);//2023-08-01 21:28:16
            String str5 = ld.format(dtf2);
            System.out.println(str5);//2023年08月01日
            String str6 = lt.format(dtf3);
            System.out.println(str6);//21时28分16秒
        }
    }
    
    
  • 解析字符串为时间对象

    • 只能使用日期对象的parse()方法参数为待解析字符串,DateTimeFormatter对象
    • public static LocalDateTime parse( @NotNull CharSequence text,
      @NotNull java.time.format.DateTimeFormatter formatter )
    public class DateTimeFormatterTest2 {
        public static void main(String[] args) {
            DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
            DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("HH时mm分ss秒");
    
            String str1 = "1998-09-09 09:09:09";
            String str2 = "1998年09月09日";
            String str3 = "09时09分09秒";
    
            //解析字符串为日期时间对象
            //使用日期对象的parse()方法,参数为字符串,DateTimeFormatter对象
            // 格式必须与DateTimeFormatter对象的格式匹配
            LocalDateTime localDateTime = LocalDateTime.parse(str1, formatter1);
            System.out.println(localDateTime);//1998-09-09T09:09:09
            LocalDate localDate = LocalDate.parse(str2, formatter2);
            System.out.println(localDate);//1998-09-09
            LocalTime localTime = LocalTime.parse(str3, formatter3);
            System.out.println(localTime);//09:09:09
        }
    }
    

补充的时间对象

1. Period
  • 可以用于计算两个LocalDate对象的时间间隔,即相差的年数、月数、天数。

  • 仅支持LocalDate对象

常用方法
方法说明
public static Period between(LocalDate start, LocalDate end)传入2个日期对象,得到Period对象
public int getYears()计算隔几年,只对两个年进行作差
public int getMonths()计算隔几个月,只对两个月进行作差
public int getDays()计算隔多少天,只对两个天进行作差

详细说明见下面的代码

public class PeriodTest {
    public static void main(String[] args) {
        LocalDate localDate1 = LocalDate.of(1998, 7, 9);
        LocalDate localDate2 = LocalDate.of(2023, 8, 1);

        //创建period对象
        Period period = Period.between(localDate1, localDate2);

        //获取相差的年
        int years = period.getYears();
        System.out.println(years);//25
        //计算年时,要综合考虑年月日
        //即1998年7月9日要达到2023年8月1号,需要25年

        //获取相差的月
        int months = period.getMonths();
        System.out.println(months);//0
        //计算月时,不计算年,只看月和日
        //7月9日要达到下一个月的8月1号,需要0个月

        //获取相差的日
        int days = period.getDays();
        System.out.println(days);//23
        //计算日时,不计算年和月,只看日
        //7月9日要达到下一个月的1号,需要23天
    }
}
2.Duration
  • 可以用于计算两个时间对象的时间间隔,即相差的天数、小时数、分数、秒数、纳秒数;

  • 支持LocalTime、LocalDateTime、Instants等时间对象

常用方法
方法说明
public static Duration between(开始时间对象1,截止时间对象2)传入2个时间对象,得到Duration对象
public long toDays()计算隔多少天,并返回
public long toHours()计算隔多少小时,并返回
public long toMinutes()计算隔多少分,并返回
public long toSeconds()计算隔多少秒,并返回
public long toMillis()计算隔多少毫秒,并返回
public long toNanos()计算隔多少纳秒,并返回
//以LocalDateTime为例
public class DurationTest {
    public static void main(String[] args) {
        LocalDateTime localDateTime1 = LocalDateTime.of(2020, 1, 1, 23, 7, 0);
        LocalDateTime localDateTime2 = LocalDateTime.of(2023, 5, 8, 5, 3, 1);

        //创建Duration对象
        Duration duration = Duration.between(localDateTime1, localDateTime2);

        //获取相差的天
        //计算的是两个日期相差的天数,与period对象的算法不同
        long days = duration.toDays();
        System.out.println(days);//1215
        //2023年5月8日5时3分1秒要达到2020年1月1日23时7分0秒,需要1215天

        //获取相差的小时
        long hours = duration.toHours();
        System.out.println(hours);//29166
        //2023年5月8日5时3分1秒要达到2020年1月1日23时7分0秒,需要29166小时
        
        //获取相差的分钟
        long minutes = duration.toMinutes();
        System.out.println(minutes);//1749961
        //2023年5月8日5时3分1秒要达到2020年1月1日23时7分0秒,需要1749961分钟
        
        //获取相差的秒
        long seconds = duration.getSeconds();
        System.out.println(seconds);//104997661
        //2023年5月8日5时3分1秒要达到2020年1月1日23时7分0秒,需要104997661秒
        
        //获取相差的毫秒
        long millis = duration.toMillis();
        System.out.println(millis);//104997661000
        //2023年5月8日5时3分1秒要达到2020年1月1日23时7分0秒,需要104997661000毫秒
        
        //获取相差的纳秒
        long nanos = duration.toNanos();
        System.out.println(nanos);//104997661000000000
        //2023年5月8日5时3分1秒要达到2020年1月1日23时7分0秒,需要104997661000000000纳秒
    }
}
3. Period与Duration的区别
Period计算时间间隔的方式
  • 计算年时,考虑年月日,看第二个日期对象经历多少年达到第一个日期对象
  • 计算月时,只考虑月和日,看第二个对象的月日经历多少月能达到第一个日期对象的月日
  • 计算日时,只考虑日,看第二个对象的日何时能达到第一个对象的日
Duration计算时间间隔的方式

无论计算月、日、分、秒、毫秒还是纳秒,都是看第二个日期对象经历多少月、日、分、秒、毫秒还是纳秒才能达到第一个日期对象代表的日期,都是考虑所有,可能存在负数

Arrays

操作数组的工具类

常用方法

方法说明
public static String toString( 类型[ ] a )返回数组的内容
public static 类型[ ] copyOf( 类型[ ] arr, int newLength )将arr的内容拷贝到长度为newLength的新数组
public static 类型[] copyOfRange( 类型[] original, int from, int to )将原数组[from, to - 1]位置的元素拷贝到新数组
public static void setAll( double[] array, @NotNull java.util.function.IntToDoubleFunction generator )将double类型的数组整体修改为新数据,直接修改原数组
public static void setAll( int[] array, @NotNull java.util.function.IntUnaryOperator generator )将int类型的数组整体修改为新数据,直接修改原数组
public static void setAll(long[] array, @NotNull java.util.function.IntToLongFunction generator )将long类型的数组整体修改为新数据,直接修改原数组
public static void sort(@NotNull 类型[] a )直接对原数组排序,默认升序
返回数组的内容
public class Test1 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        //通过Arrays.toString(arr)输出数组内容
        System.out.println(Arrays.toString(arr));//[1, 2, 3]
        //也可以是其他类型的数组
    }
}
将arr的内容拷贝到长度为newLength的新数组
public class Test1 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        
        //通过Arrays.copyOf(arr, int length)复制数组
        // length为新数组的长度
        // 如果length小于原数组长度,则只复制前length个元素
        //如果length大于原数组长度,则复制完原数组后,剩余元素用0填充
        int[] ar1 = Arrays.copyOf(arr, 2);
        System.out.println(Arrays.toString(ar1));//[1, 2]
        int[] ar2 = Arrays.copyOf(arr, 5);
        System.out.println(Arrays.toString(ar2));//[1, 2, 3, 0, 0]
    }
}
将原数组的元素按索引位置拷贝到新数组
public class Test1 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        //通过Arrays.copyOfRange(arr, int from, int to)复制数组复制arr[from, to)的元素
        int[] ar3 = Arrays.copyOfRange(arr, 1, 3);//复制arr[1, 2]的元素
        System.out.println(Arrays.toString(ar3));//[2, 3]
  
}
整体修改数组
1. 批量修改double类型数组
public class Test1 {
    public static void main(String[] args) {
        //批量修改double数组元素的值
        double[] ar = {1.1, 2.2, 3.3};
        //new IntToDoubleFunction(){}是一个匿名内部类
        //重写applyAsDouble方法,实现自定义批量修改double数组元素的值
        Arrays.setAll(ar, new IntToDoubleFunction(){
            @Override
            public double applyAsDouble(int value) {
                //value为数组的索引
                return ar[value] * 2;
            }
        });
        System.out.println(Arrays.toString(ar));//[2.2, 4.4, 6.6]
        
        /* 
        Arrays.setAll源码
        public static void setAll(double[] array, IntToDoubleFunction generator) {
            Objects.requireNonNull(generator);
            for (int i = 0; i < array.length; i++)
                array[i] = generator.applyAsDouble(i);
        }
        */
    }
}
2. 批量修改int类型数组
public class Test1 {
    public static void main(String[] args) {
        //批量修改int数组元素的值
        int[] ar4 = {1, 2, 3};
        Arrays.setAll(ar4, new IntUnaryOperator(){
            @Override
            public int applyAsInt(int operand) {
                //operand为数组的索引
                return ar4[operand] * 2;
            }
        });
        System.out.println(Arrays.toString(ar4));//[2, 4, 6]

        /*
         Arrays.setAll源码
         public static void setAll(int[] array, IntUnaryOperator generator) {
         	Objects.requireNonNull(generator);
            for (int i = 0; i < array.length; i++)
            	array[i] = generator.applyAsInt(i);
         }
        */
    }
}
3. 批量修改long类型数组
public class Test1 {
    public static void main(String[] args) {
        //批量修改long数组元素的值
        long[] ar5 = {1, 2, 3};
        Arrays.setAll(ar5, new IntToLongFunction(){
            @Override
            public long applyAsLong(int value) {
                //value为数组的索引
                return ar5[value] * 2;
            }
        });
        System.out.println(Arrays.toString(ar5));//[2, 4, 6]
        /*
         Arrays.setAll源码
         public static void setAll(long[] array, IntToLongFunction generator) {
            Objects.requireNonNull(generator);
            for (int i = 0; i < array.length; i++)
                array[i] = generator.applyAsLong(i);
        }
        */
    }
}
排序
自定义排序方法时两个对象比较的官方约定
  • 如果认为左边对象大于右边对象,应该返回正整数
  • 如果认为左边对象小于右边对象,应该返回负整数
  • 如果认为左边对象等于右边对象,应该返回0整数
1. 对基本数据类型数组的排序
//对基本数据类型的普通排序
public class Test1 {
    public static void main(String[] args) {
        int[] ar6 = {1, 3, 2};
        Arrays.sort(ar6);//默认升序
        System.out.println(Arrays.toString(ar6));//[1, 2, 3]
        
        //逆序
        Integer[] arr = {1, 2, 3, 4, 5};
        Arrays.sort(arr, (o1,o2) -> -Integer.compare(o1, o2));
    }
}
2. 对引用类型数组的排序
  • 方式一:让对象的类实现Comparable接口并重写compareTo方法自定义比较规则

    //未实现Comparable接口前的类
    public class Student {
        public String name;
        public int age;
        public double score;
    
        public Student(String name, int age, double score) {
            this.name = name;
            this.age = age;
            this.score = score;
        }
    
        @Override
        public String toString() {
            return "{name='" + name + '\'' +
                    ", age=" + age +
                    ", score=" + score +
                    '}';
        }
    }
    
    //实现接口并重写compareTo方法后的类
    public class Student implements Comparable<Student> {
        public String name;
        public int age;
        public double score;
    
        //重写compareTo方法
        @Override
        public int compareTo(Student o) {
            //根据年龄升序
            return Integer.compare(this.age, o.age);
            //根据年龄降序
    //        return -Integer.compare(this.age, o.age);
        }
    
        public Student(String name, int age, double score) {
            this.name = name;
            this.age = age;
            this.score = score;
        }
    
        @Override
        public String toString() {
            return "{name='" + name + '\'' +
                    ", age=" + age +
                    ", score=" + score +
                    '}';
        }
    }
    
    //测试类
    public class Test2 {
        public static void main(String[] args) {
            Student[] students = {
                    new Student("张三", 18, 80),
                    new Student("李四", 19, 90),
                    new Student("王五", 20, 100),
                    new Student("赵六", 20, 100)
            };
    
            Arrays.sort(students);//在实体类中定义规则
            System.out.println(Arrays.toString(students));
        }
    }
    /*
    升序结果:
    			   //[{name='张三', age=18, score=80.0}, 
                    // {name='李四', age=19, score=90.0},
                    // {name='王五', age=20, score=100.0}, 
                    // {name='赵六', age=20, score=100.0}]
    降序结果:
                    //[{name='王五', age=20, score=100.0}, 
                    // {name='赵六', age=20, score=100.0}, 
                    // {name='李四', age=19, score=90.0}, 
                    // {name='张三', age=18, score=80.0}]
    */
    
  • 方式二:使用Arrays类的sort方法,创建Comparator比较器接口的匿名内部类,自定义比较规则

  • public staticvoid sort(T[ ]arr, Comparators<? super T> c)

    //Student类,无需实现接口
    public class Student {
        public String name;
        public int age;
        public double score;
    
        public Student(String name, int age, double score) {
            this.name = name;
            this.age = age;
            this.score = score;
        }
    
        @Override
        public String toString() {
            return "{name='" + name + '\'' +
                    ", age=" + age +
                    ", score=" + score +
                    '}';
        }
    }
    
    public class Test2 {
        public static void main(String[] args) {
            Student[] students = {
                    new Student("张三", 18, 80),
                    new Student("李四", 19, 90),
                    new Student("王五", 20, 100),
                    new Student("赵六", 20, 100)
            };
    		
            //直接定义规则,不更改实体类,定义一个Comparator<T>接口的匿名内部类
            Arrays.sort(students, new Comparator<Student>() {
                @Override
                public int compare(Student o1, Student o2) {
    
                    //根据分数升序
                    return Double.compare(o1.score, o2.score);
                    //[{name='张三', age=18, score=80.0}, 
                    // {name='李四', age=19, score=90.0},
                    // {name='王五', age=20, score=100.0}, 
                    // {name='赵六', age=20, score=100.0}]
    
                    //根据分数降序
    //                return -Double.compare(o1.score, o2.score);
                    //[{name='王五', age=20, score=100.0}, 
                    // {name='赵六', age=20, score=100.0},
                    // {name='李四', age=19, score=90.0}, 
                    // {name='张三', age=18, score=80.0}]
                }
            });
            System.out.println(Arrays.toString(students));
        }
    }
    

Lambda表达式

作用

用于简化函数式接口的匿名内部类

注意:只能简化函数式接口的匿名内部类,抽象类的匿名内部类无法简化

函数式接口

  • 有且仅有一个抽象方法的接口
  • 大部分函数式接口,上面会存在@FunctionalInterface的注解。有该注解,一定是函数式接口。

简单写法

(被重写方法的形参列表) -> {
	被重写方法的方法体代码
}
public class Test4 {
    public static void main(String[] args) {
        // 常规的匿名内部类
        A a = new A() {
            @Override
            public void cry() {
                System.out.println("cry");
            }
        };

       //使用lambda表达式简化
        //
       A a1 = () ->{
              System.out.println("cry");
       };
    }
}

//函数式接口
interface A {
    void cry();
}

进一步简化Lambda表达式写法

  • 无论有几个参数,参数类型都可以省略不写
  • 如果只有一个参数,除参数类型外,括号也可以省略。
    • 注意:两个或多个参数不允许省略括号。
  • 如果Lambda表达式中的方法体代码只有一行代码
    • 可以省略大花括号不写,同时要省略分号!
    • 如果这行代码是return语句,也必须去掉return不写。
//根据规则进一步简化
public class Test5 {
    public static void main(String[] args) {
        double[] arr = {1.1, 2.2, 3.3};
        
        //未简化的匿名内部类
        Arrays.setAll(arr, new IntToDoubleFunction() {
            @Override
            public double applyAsDouble(int value) {
                return arr[value] * 2;
            }
        });

        // 使用lambda表达式简化
        Arrays.setAll(arr, (int value) -> {
            return arr[value] * 2;
        });

        //无论有几个参数,参数类型都可以省略不写
        Arrays.setAll(arr, (value) -> {
            return arr[value] * 2;
        });
        
        //如果只有一个参数,除参数类型外,括号也可以省略。
        Arrays.setAll(arr, value -> {
            return arr[value] * 2;
        });
        
        //如果Lambda表达式中的方法体代码只有一行代码,可以省略大花括号不写,同时要省略分号!
        //如果这行代码是return语句,也必须去掉return不写。
        Arrays.setAll(arr, value -> arr[value] * 2);//最终简化结果
    }
}

方法引用

进一步简化Lambda表达式 标志性符号“::"

1. 静态方法的引用

写法

类名::静态方法

使用场景

如果某个Lambda表达式里只是调用一个静态方法,并且前后参数的形式一致,(即重写的方法的输入的参数要与调用的静态方法的参数类型一致)就可以使用静态方法引用。

//调用的类的静态方法
public class CompareByData {
    public static int compareByScore(Student o1, Student o2) {
        return Double.compare(o1.score, o2.score);
    }
}//入参为两个Student类型,保证前后参数一致
public class Test6 {
    public static void main(String[] args) {
        Student[] students = {
                new Student("张三", 18, 80),
                new Student("李四", 19, 90),
                new Student("王五", 20, 100),
                new Student("赵六", 20, 100)
        };

        Arrays.sort(students, (o1,o2) -> Double.compare(o1.score, o2.score));

//错误示例:
	   Arrays.sort(students, Double::compare);
//会报错,因为前后参数必须一致,而上面的语句中前后参数不一致
//(o1,o2) -> Double.compare(o1.score, o2.score)重写方法的参数是(o1,o2),为Student类型
// 而该方法调用的Double.compare静态方法的入参(o1.score, o2.score)为double类型
//所以前后不一致

//正确示例:
        //CompareByData为重新定义的类,里面只有一个静态方法,见上文
        Arrays.sort(students, (o1,o2) -> CompareByData.compareByScore(o1,o2));
//入参一致
// 方法的入参为(o1,o2),为Student类型
// 而该方法调用的CompareByData.compareByAge静态方法的入参(o1,o2)也为Student类型
//最终简化为:
        Arrays.sort(students,CompareByData::compareByScore);
    }
}

2. 实例方法的引用

写法

对象名::方法

使用场景

如果某个Lambda表达式里只是调用一个实例方法,并且前后参数的形式一致,(即重写的方法的输入的参数要与对象调用的实例方法的参数类型一致)就可以使用实例方法引用。

public class CompareByData {
    public int compareByAge(Student o1, Student o2) {
        return Integer.compare(o1.age, o2.age);//比较两个Student对象的年龄
    }
}
public class Test7 {
    public static void main(String[] args) {
        Student[] students = {
                new Student("张三", 18, 80),
                new Student("李四", 19, 90),
                new Student("王五", 20, 100),
                new Student("赵六", 20, 100)
        };
        //初始的比较器
        Arrays.sort(students, (o1,o2) -> Integer.compare(o1.age, o2.age));
        //由于前后参数不一致,不能直接使用实例方法引用

        CompareByData cbd = new CompareByData();
        //调用对象中的cbd.compareByAge(o1, o2)方法,用来保证前后参数的一致性
        Arrays.sort(students, (o1,o2) -> cbd.compareByAge(o1, o2));
        //使用实例方法引用简化
        Arrays.sort(students, cbd::compareByAge);
    }
}

3. 特定类型方法的引用

写法

类型::方法

使用场景

如果某个Lambda表达式里只是调用一个实例方法,并且重写的方法的参数列表中的第一个参数是作为方法体中方法的主调(即使用第一个参数对象调用方法),后面的所有参数都是作为该实例方法的入参的(第一个参数对象调用的方法的入参是重写的方法的入参中除了第一个参数以外的所有方法),则此时就可以使用特定类型的方法引用。

public class Test8 {
    public static void main(String[] args) {
        String[] arr = {"tom", "Tzk", "mike"};
//public int compareToIgnoreCase(@NotNull String str)会忽略大小写比较两个字符串的ASCII码值
        
        //想将该数组排序,但是要忽略大小写
        Arrays.sort(arr,(a,b) -> a.compareToIgnoreCase(b));
        //重写方法的入参为a,b
        //使用入参a作为主调来调用compareToIgnoreCase方法,b作为参数传入
        //符合特定类型的方法引用
        //简化为:
        Arrays.sort(arr,String::compareToIgnoreCase);
    }
}

4. 构造器引用

写法

类型::new

使用场景

如果某个Lambda:表达式里只是在创建对象,并且前后参数情况一致,就可以使用构造器引用


public class Test9 {
    public static void main(String[] args) {
        //使用匿名内部类定义变量
        CreatPeople cp = (String name, int age, double score) -> new People(name, age, score);
        //使用构造器引用简化
        CreatPeople cp1 = People::new;
        //调用方法创建People对象
        People lmh = cp1.creatPeople("LMH", 20, 100);
        System.out.println(lmh);//People{name='LMH', age=20, score=100.0}
    }
}

@FunctionalInterface
interface CreatPeople{
    People creatPeople(String name, int age, double score);
}

class People{
    private String name;
    private int age;
    private double score;

    public People(String name, int age, double score){
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
写法

对象名::方法

使用场景

如果某个Lambda表达式里只是调用一个实例方法,并且前后参数的形式一致,(即重写的方法的输入的参数要与对象调用的实例方法的参数类型一致)就可以使用实例方法引用。

public class CompareByData {
    public int compareByAge(Student o1, Student o2) {
        return Integer.compare(o1.age, o2.age);//比较两个Student对象的年龄
    }
}
public class Test7 {
    public static void main(String[] args) {
        Student[] students = {
                new Student("张三", 18, 80),
                new Student("李四", 19, 90),
                new Student("王五", 20, 100),
                new Student("赵六", 20, 100)
        };
        //初始的比较器
        Arrays.sort(students, (o1,o2) -> Integer.compare(o1.age, o2.age));
        //由于前后参数不一致,不能直接使用实例方法引用

        CompareByData cbd = new CompareByData();
        //调用对象中的cbd.compareByAge(o1, o2)方法,用来保证前后参数的一致性
        Arrays.sort(students, (o1,o2) -> cbd.compareByAge(o1, o2));
        //使用实例方法引用简化
        Arrays.sort(students, cbd::compareByAge);
    }
}

3. 特定类型方法的引用

写法

类型::方法

使用场景

如果某个Lambda表达式里只是调用一个实例方法,并且重写的方法的参数列表中的第一个参数是作为方法体中方法的主调(即使用第一个参数对象调用方法),后面的所有参数都是作为该实例方法的入参的(第一个参数对象调用的方法的入参是重写的方法的入参中除了第一个参数以外的所有方法),则此时就可以使用特定类型的方法引用。

public class Test8 {
    public static void main(String[] args) {
        String[] arr = {"tom", "Tzk", "mike"};
//public int compareToIgnoreCase(@NotNull String str)会忽略大小写比较两个字符串的ASCII码值
        
        //想将该数组排序,但是要忽略大小写
        Arrays.sort(arr,(a,b) -> a.compareToIgnoreCase(b));
        //重写方法的入参为a,b
        //使用入参a作为主调来调用compareToIgnoreCase方法,b作为参数传入
        //符合特定类型的方法引用
        //简化为:
        Arrays.sort(arr,String::compareToIgnoreCase);
    }
}

4. 构造器引用

写法

类型::new

使用场景

如果某个Lambda:表达式里只是在创建对象,并且前后参数情况一致,就可以使用构造器引用


public class Test9 {
    public static void main(String[] args) {
        //使用匿名内部类定义变量
        CreatPeople cp = (String name, int age, double score) -> new People(name, age, score);
        //使用构造器引用简化
        CreatPeople cp1 = People::new;
        //调用方法创建People对象
        People lmh = cp1.creatPeople("LMH", 20, 100);
        System.out.println(lmh);//People{name='LMH', age=20, score=100.0}
    }
}

@FunctionalInterface
interface CreatPeople{
    People creatPeople(String name, int age, double score);
}

class People{
    private String name;
    private int age;
    private double score;

    public People(String name, int age, double score){
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值