Java 基础学习(十二)文本I/O、日期与时间API

1 文本 I/O

1.1 字符流

1.1.1 什么是字符流

在Java中,字符流是指提供了基于字符的I/O能力的API。

Java 1.0中提供的基于字节的I/O流API只能支持8位字节流,无法妥善地处理16位Unicode字符。由于需要支持Unicode处理国际化字符,因此Java 1.1 对基础流式I/O库进行了重大的修改,核心是增加了字符流相关的API,处理国际化Unicode字符的编码和解码。

字符流是以字符(char)为单位读写数据的:一次处理一个 Unicode。字符流的底层仍然是基本的字节流,它封装了字符的编码解码算法。

字符流以Reader和Writer为核心抽象类,所有的字符输入流类均继承Reader,所有的字符输出流均继承Writer。如下图所示:

 

字符流相关子类可以分为节点流和处理流:上图中带阴影的是节点流,不带阴影的是处理流。

1.1.2 【案例】FileWriter示例

编写代码,使用FileWriter将字符写入文件。代码示意如下:

package api_04;
import java.io.FileWriter;
import java.io.Writer;
public class WriterDemo1 {
    public static void main(String[] args){
        Writer writer = null;
        try{
            writer = new FileWriter("./src/api_04/demo");
            writer.write("Hello World!\n");
            writer.write("Hello FileWriter!\n");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (writer != null){
                try{
                    writer.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}

1.1.3 【案例】FileReader示例

编写代码,使用FileReader从文件中读取字符。代码示意如下:

package api_04;
import java.io.FileReader;
import java.io.Reader;
public class ReaderDemo1 {
    public static void main(String[] args) {
        Reader reader = null;
        try{
            reader = new FileReader("./src/api_04/demo");
            char[] data = new char[5];
            int len = 0;
            while (len !=-1){
                System.out.print(new String(data,0,len));
                len = reader.read(data);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (reader != null){
                try{
                    reader.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}

1.1.4 try-with-resources语法

在前面的案例中,为了妥善的处理I/O操作中可能出现的异常,并保证在程序结束后关流,我们使用了try-catch-finally语句块。但在实际编码中遇到很多不方便的情况,例如:

  • finally语句块不能访问try语句块中声明的变量
  • finally语句块中需要再次添加try-catch语句块

幸好Java 7 引入了try-with-resources语法,可以很好地简化上述代码。

 

这种称为try-with-resources语法。其要求为:

1、try后面的括号称为资源说明头,用于创建语句块中使用的资源对象

2、资源说明头中创建的对象必须实现java.lang.AutoCloseable接口,该接口只有一个方法-close()

3、资源说明头中可以包含多个创建对象的语句,用分号隔开,最后的分号可以省略

4、不论如何退出try语句块,都会自动调用所有资源对象的close方法

5、退出try语句块时,会以与声明资源对象相反的顺序去调用资源对象的close方法

1.1.5 【案例】try-with-resources示例

编写代码,测试try-with-resources的用法。代码示意如下:

package api_04;
import java.io.Closeable;
import java.io.IOException;
public class TWRDemo {
    public static void main(String[] args) {
        try(
            MyStream1 stream1 = new MyStream1();
            MyStream2 stream2 = new MyStream2();
        ){
            System.out.println("try...");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
class MyStream1 implements Closeable{
    @Override
    public void close() throws IOException {
        System.out.println("MyStream1 close...");
    }
}
class MyStream2 implements Closeable{
    @Override
    public void close() throws IOException {
        System.out.println("MyStream2 close...");
    }
}

1.2 字符缓冲流

1.2.1 字符缓冲流概述

BufferedReader和BufferedWriter分别是字符输入流和字符输出流对应的缓冲流,内置了缓冲区,通过缓冲区来减少实际的物理读写操作,进而提高读写效率。这两个类默认的缓冲区大小均为8192个字符,并支持通过构造器来设置缓冲区大小。

BufferedReader中还提供了一些增强的I/O操作方法,例如:

  • readLine:读取文本中的一行内容,以'\n','\r'来识别,返回的内容不包含换行符
  • skip:跳过指定数量的字符

BufferedWriter中没有提供相似的writeLine方法,但是提供了newLine方法,调用时可以向流中输出一个换行符,以达到换行的效果。

1.2.2 【案例】BufferedWriter示例

编写代码,测试BufferedWriter的用法。代码示意如下:

package api_04;
import java.io.*;
public class BufferedWriterDemo {
    public static void main(String[] args) {
        try(
            Writer writer = new FileWriter("./src/api_04/demo2");
            BufferedWriter bufferedWriter = new BufferedWriter(writer);
        ){
            bufferedWriter.write("Hello world!");
            bufferedWriter.newLine();
            bufferedWriter.write("Hello BufferedWriter!");
            bufferedWriter.newLine();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

1.2.3 【案例】BufferedReader示例

编写代码,测试BufferedReader的用法。代码示意如下:

package api_04;
import java.io.*;
public class BufferedReaderDemo {
    public static void main(String[] args) {
        try(
            Reader reader = new FileReader("./src/api_04/demo2");
            BufferedReader bufferedReader = new BufferedReader(reader);
        ){
            String line = bufferedReader.readLine();
            while (line!=null) {
                System.out.println(line);
                line = bufferedReader.readLine();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

1.3 转换流

1.3.1 转换流概述

转换流用于实现字节流和字符流之间的转换。

InputStreamReader是字节输入流到字符输入流的桥梁,用于将InputStream转换为Reader,可以读取字节数据并根据指定的字符集转换为字符数据。

OutputStreamWriter是字符输出流到字节输出流的桥梁,用于将OutputStream转换为Writer,可以根据指定的字符集将写出的字符数据转换为字节数据。

1.3.2 指定字符编码

InputStreamReader 的构造方法允许设置字符集

  • InputStreamReader(InputStream in,String charsetName):基于给定的字节输入流以及字符编码创建
  • InputStreamReader(InputStream in):该构造方法会根据系统默认字符集创建

OutputStreamWriter 的构造方法:

  • OutputStreamWriter(OutputStream out,String charsetName):基于给定的字节输出流以及字符编码创建
  • OutputStreamWriter(OutputStream out):该构造方法会根据系统默认字符集创建

1.3.3 【案例】OutputStreamWriter示例

编写代码,测试OutputStreamWriter的用法,并指定字符编码。代码示意如下:

package api_04;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
public class OutputStreamWriterDemo {
    public static void main(String[] args) {
        try(
            FileOutputStream fos
                        = new FileOutputStream("./src/api_04/demo3");
            OutputStreamWriter osw
                        = new OutputStreamWriter(fos,"utf-8");
            BufferedWriter bw = new BufferedWriter(osw)
        ){
            bw.write("世界你好!");
            bw.newLine();
            bw.write("转换流你好!");
            bw.newLine();
            System.out.println("写出完毕!");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

1.3.4 【案例】InputStreamReader示例

编写代码,测试InputStreamReader的用法,并指定字符编码。代码示意如下:

package api_04;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
public class InputStreamReaderDemo {
    public static void main(String[] args) {
        try(
            FileInputStream fis
                        = new FileInputStream("./src/api_04/demo3");
            InputStreamReader isr
                        = new InputStreamReader(fis,"utf-8");
            BufferedReader br = new BufferedReader(isr);
        ){
            String line =br.readLine();
            while(line!=null){
                System.out.println(line);
                line = br.readLine();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

1.3.5 【案例】数据统计示例

掌握了I/O相关API后,可以结合之前所学的Java基础知识,实现基础的数据统计操作。

weather_data_ny_201906.csv文件是某气象网站提供的纽约市2019年6月的天气数据。文件内容如下图所示:

 现有如下需求:

1、数据预处理:提取weather_data_ny_201906.csv文件中的"STATION","DATE","MAX"三列的值,写入新的文件data1.csv,新文件中需要表头行,的数据继续使用英文逗号分隔,但是数据前后不再包含双引号。data1.csv文件如下图所示:

 2、基于data1.csv,统计每个站点的6月平均温度,结果四舍五入保留小数点后2位,写入data2.csv,data2.csv文件的表头为"STATION", "AVG"。data2.csv文件如下图所示:

 代码示意如下:

package api_04;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
public class DataPreProcessDemo {
    public static void main(String[] args) {
        try(
                FileReader fr =new FileReader("./src/api_04/weather_data_ny_201906.csv");
                BufferedReader br =new BufferedReader(fr);
                FileWriter fw =new FileWriter("./src/api_04/data1.csv");
                BufferedWriter bw =new BufferedWriter(fw)
        ){
            // 写出第一行表头行
            bw.write("STATION,DATE,MAX");
            // 读取数据
            String line =br.readLine(); // 表头行,需跳过
            line = br.readLine(); // 第一行数据
            while(line!=null){
                StringBuilder builder = new StringBuilder();
                // 处理数据
                String[] dataArray = line.replaceFirst("\"","") // 去掉首个字符串
                                            .split("\",\""); // 使用","切分为字符串数组
                builder.append(dataArray[0].trim()).append(",") // 拼接STATION列
                        .append(dataArray[5].trim()).append(",") // 拼接DATE列
                        .append(dataArray[9].trim());
                // 写出一行新数据行
                bw.newLine();
                bw.write(builder.toString());
                // 读取一行新的数据
                line = br.readLine();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
package api_04;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class StatisticsDemo {
    public static void main(String[] args) {
        try(
                FileReader fr =new FileReader("./src/api_04/data1.csv");
                BufferedReader br =new BufferedReader(fr);
                FileWriter fw =new FileWriter("./src/api_04/data2.csv");
                BufferedWriter bw =new BufferedWriter(fw)
        ){
            // 写出第一行表头行
            bw.write("STATION,AVG");
            /*
             * 处理数据思路:
             * 分组求均值,按STATION分组,求组内均值
             * 共5个站点,分5组
             * 需要过滤掉非6月的数据
             */
            // 存储各个站点的温度和,站点id和数组下标对应关系
            // 99727199999-0, 72505394728-1, 72055399999-2,
            // 99774399999-3, 72503014732-4
            BigDecimal[] tempDataArray = new BigDecimal[5];
            for(int i =0; i < tempDataArray.length; i++){
                tempDataArray[i]=new BigDecimal(0);
            }
            int[] dataCount = new int[5]; // 统计数据条数
            String[] stationArray = new String[]
                    {"99727199999", "72505394728", "72055399999"
                            ,"99774399999","72503014732"};
            // 读取数据
            String line =br.readLine(); // 表头行,需跳过
            line = br.readLine(); // 第一行数据
            while(line!=null){
                // 处理数据
                String[] dataArray = line.split(",");
                // 过滤非6月数据
                if (!dataArray[1].startsWith("2019-06")){
                    line=br.readLine();
                    continue; // 不处理该行数据
                }
                BigDecimal temp = new BigDecimal(dataArray[2]);
                switch (dataArray[0]){
                    case "99727199999":
                        tempDataArray[0] = tempDataArray[0].add(temp);
                        dataCount[0] = dataCount[0] + 1;
                        break;
                    case "72505394728":
                        tempDataArray[1] = tempDataArray[1].add(temp);
                        dataCount[1] = dataCount[1] + 1;
                        break;
                    case "72055399999":
                        tempDataArray[2] = tempDataArray[2].add(temp);
                        dataCount[2] = dataCount[2] + 1;
                        break;
                    case "99774399999":
                        tempDataArray[3] = tempDataArray[3].add(temp);
                        dataCount[3] = dataCount[3] + 1;
                        break;
                    case "72503014732":
                        tempDataArray[4] = tempDataArray[4].add(temp);
                        dataCount[4] = dataCount[4] + 1;
                        break;
                    default:
                        System.out.println("站点名称未匹配:"+dataArray[0]);
                        break;
                }
                // 读取一行新的数据
                line = br.readLine();
            }
            // 求均值
            for(int i=0;i < tempDataArray.length;i++) {
                BigDecimal avg = tempDataArray[i]
                        .divide(new BigDecimal(dataCount[i]),2, RoundingMode.HALF_UP);
                StringBuilder builder = new StringBuilder();
                builder.append(stationArray[i]).append(",").append(avg);
                // 写出一行新数据行
                bw.newLine();
                bw.write(builder.toString());
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

2 日期与时间API

2.1 Date类

2.1.1 Java中的日期时间

日期和时间在应用程序中具有非常广泛和多样的应用,包括日期时间数据的获取和封装、日期时间数据的计算,以及日期时间数据的时区转换和格式转换等多个方面。

  • 大部分的应用程序都有日志模块,记录应用运行过程中的程序的执行情况和用户的各类操作
  • 一些特定的功能会用到计时器功能,如订单的支付限时为15分钟
  • 通过用户的生日和当前日期计算用户的年龄
  • 通过用户的登录时间和当前时间计算用户在线时长等
  • 一些面向国际化的应用程序也存在日期时间数据的时区转换和格式转换的需求

Java中提供了多种日期时间相关的工具类和丰富的API,使开发者可以简化日期时间操作,以提高应用程序的开发效率。

2.1.2 纪元(Epoch)

在计算机领域,“纪元”是计算机测量系统时间的日期和时间。大多数计算机系统将时间确定为一个数字,表示从特定的任意日期和时间以来经过的秒数。 例如,Unix和POSIX将时间测量为自1970年1月1日00:00:00 GMT 以来经过的秒数,该时间点称为 Unix“纪元”。

由于我国处于东八区,因此基准时间为1970年1月1日8时0分0秒。北京时间2023年1月1日凌晨可以表示为1672502400。

这种用于表示日期和时间的数字就是在开发中广泛使用的时间戳(Timestamp),常有精确到秒和精确到毫秒2种表示形式。

Java中使用的时间戳支持精确到秒、毫秒和纳秒,之前在统计程序运行效率中使用的System.currentTimeMillis()方法,就是返回当前的毫秒级时间戳。

2.1.3 Date类

java.util.Date类封装日期和时间信息,表示一个特定瞬间的类,时间可以精确到毫秒。Date的每一个实例用于表示一个时间,内部维护一个long值,该值保存的是自标准基准时间以来的毫秒数。

Date类是Java中使用最为广泛的一个日期时间类之一。

由于设计问题,该类中的很多方法已被标记过时,使用时应注意避免。查看下图:

 

这种已经被标记过时的方法,应尽量避免使用。

2.1.4【案例】Date类示例

编写代码,测试Date类的用法。代码示意如下:

package api_04;
import java.util.Date;
public class DateDemo {
    public static void main(String[] args) {
        Date now = new Date();
        System.out.println(now);
        // Date大部分方法都过时了
        // now.getYear();
        /*
         * 获取Date内部维护的毫秒值
         */
        long time = now.getTime();
        System.out.println(time);
        /*
         * 设置一个毫秒使当前Date表示该时间
         */
        now.setTime(0);
        System.out.println(now);
    }
} 

2.2 SimpleDateFormat类

2.2.1 DateFormat 类概述

在输出Date对象代表的时间时,会自动调用Date类中的toString()方法。Date类对Object类中的toString()方法进行了重写,按照“dow mon dd hh:mm:ss zzz yyyy”(星期 月份 日期 小时:分钟:秒 时区 年份)的格式输出该Date对象代表的时间。在很多应用中,开发者需要指定自定义的时间输出格式。Java中提供的java.text.DateFormat用来满足开发者对日期时间格式化的需求。

DateFormat是日期/时间格式化子类的抽象类,通过这个类可以完成日期和文本之间的转换。DateFormat类支持日期的格式化(日期→文本)、日期的解析(文本→日期)和日期的规范化。

DateFormat为抽象类,不能直接使用。在实际开发中,比较常用的是DateFormat类的子类——SimpleDateFormat。

2.2.2 SimpleDateFormat 类

SimpleDateFormat类的构造方法:

  • SimpleDateFormat( )
  • SimpleDateFormat(String pattern):用给定的日期时间格式pattern构造SimpleDateFormat对象

参数pattern是一个字符串,代表日期时间的自定义格式,常用的格式为“yyyy-MM-dd HH:mm:ss”,其中,yyyy表示年份,MM表示月份,dd表示日期,HH表示小时,mm表示分钟,ss表示秒。

SimpleDateFormat的常用方法:

  • String format(Date date):将 Date 对象代表的时间格式化为字符串
  • Date parse(String source):将字符串解析为 Date 对象

2.2.3 日期模式匹配字符

日期模式匹配字符如下表所示:

 

2.2.4【案例】日期格式化示例

编写代码,测试日期格式化输出。代码示意如下:

package api_04;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatDemo1 {
    public static void main(String[] args) {
        Date now = new Date();
        System.out.println(now);
        /*
         * yyyy-MM-dd HH:mm:ss
         */
        SimpleDateFormat sdf
                = new SimpleDateFormat(
                "yyyy-MM-dd HH:mm:ss a E");
        /*
         * String format(Date date)
         * 将给定的Date按照当前SDF指定的日期
         * 格式转换为字符串
         */
        String line = sdf.format(now);
        System.out.println(line);
    }
}

2.3 Java 8 新日期时间类

2.3.1新日期时间类概述

从Java 8开始,java.time包提供了新的日期和时间API,主要涉及的类型有:

  • 本地日期和时间:LocalDateTime,LocalDate,LocalTime
  • 带时区的日期和时间:ZonedDateTime
  • 时刻:Instant
  • 时区:ZoneId,ZoneOffset
  • 时间间隔:Duration

以及一套新的用于取代SimpleDateFormat的格式化类型DateTimeFormatter。

和旧的API相比,新API严格区分了时刻、本地日期、本地时间和带时区的日期时间,并且,对日期和时间进行运算更加方便。

此外,新API修正了旧API不合理的常量设计:

  • Month的范围用1~12表示1月到12月
  • Week的范围用1~7表示周一到周日

2.3.2本地化日期时间 API

在很多本地化的应用中,考虑时区是没有必要的。Java 8在java.time包中提供了简化版的日期时间操作类,包括LocalDate、LocalTime和LocalDateTime。

  • LocalDate表示本地日期
  • LocalTime表示本地时间
  • LocalDateTime可以看成前两者的组合,表示本地日期和时间

2.3.3【案例】LocalDateTime示例

编写代码,测试LocalDateTime。代码示意如下:

package api_04;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
public class LocalDateTimeDemo {
    public static void main(String[] args) {
        System.out.println("-------------构建LocalDateTime对象-------------");
        // 获取当前时间的ldt对象
        LocalDateTime ldt1 = LocalDateTime.now();
        System.out.println("ldt1:\t"+ldt1);
        // 获取表示指定时间的ldt对象
        LocalDateTime ldt2 = LocalDateTime.of(2022,9,18,22,0,0,0);
        System.out.println("ldt2:\t"+ldt2);
        System.out.println("-------------获取日期时间信息方法-------------");
        System.out.println(ldt1.getYear()+":"+ldt1.getMonthValue()
                +":"+ldt1.getDayOfMonth());
        System.out.println(ldt1.getHour()+":"+ldt1.getMinute()
                +":"+ldt1.getSecond());
        System.out.println("-------------日期时间计算方法-------------");
        LocalDateTime ldt3 = ldt1.plus(6, ChronoUnit.HOURS)
                .plusMinutes(10);
        System.out.println("ldt3:\t"+ldt3);
        LocalDateTime ldt4 = ldt1.minus(1, ChronoUnit.HOURS)
                .minusMonths(1);
        System.out.println("ldt4:\t"+ldt4);
    }
}

2.3.4【案例】ZonedDateTime示例

编写代码,测试ZonedDateTime。代码示意如下:

package api_04;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class ZonedDateTimeDemo {
    public static void main(String[] args) {
        // 直接创建ZonedDateTime对象
        ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
        // 用指定时区获取当前时间
        ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
        System.out.println(zbj);
        System.out.println(zny);
        // 通过LocalDateTime转换
        LocalDateTime ldt = LocalDateTime.of(2023, 1, 2, 8, 15, 33);
        ZonedDateTime zdt1 = ldt.atZone(ZoneId.systemDefault());
        ZonedDateTime zdt2 = ldt.atZone(ZoneId.of("America/New_York"));
        System.out.println(zdt1);
        System.out.println(zdt2);
        // 时区转换
        // 以中国时区获取当前时间:
        ZonedDateTime zdt3 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
        // 转换为巴黎时间:
        ZonedDateTime zdt4 = zbj.withZoneSameInstant(ZoneId.of("Europe/Paris"));
        System.out.println(zdt3);
        System.out.println(zdt4);
    }
}

2.3.5【案例】DateTimeFormatter示例

编写代码,测试DateTimeFormatter。代码示意如下:

package api_04;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class DateTimeFormatterDemo {
    public static void main(String[] args) {
        ZonedDateTime zdt = ZonedDateTime.now();
        // 日期格式化
        DateTimeFormatter formatter1 =
                DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm ZZZZ");
        System.out.println(formatter1.format(zdt));
        DateTimeFormatter formatter2 =
                DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm", Locale.CHINA);
        System.out.println(formatter2.format(zdt));
        DateTimeFormatter formatter3 =
                DateTimeFormatter.ofPattern("E, MMMM/dd/yyyy HH:mm", Locale.US);
        System.out.println(formatter3.format(zdt));
        // 字符串转时间
        DateTimeFormatter formatter4 =
                DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String time = "2020-05-12 20:13:15";
        LocalDateTime ldt = LocalDateTime.parse(time,formatter4);
        System.out.println(ldt);
    }
} 

2.3.6【案例】Instant示例

编写代码,测试Instant。代码示意如下:

public class InstantDemo {
    public static void main(String[] args) {
        // 获取当前时间对象
        Instant i1 = Instant.now();
        // 输出当前时间,默认使用零时区
        System.out.println("defalut:\t"+i1);
        // 获取时间戳
        System.out.println("timestamp:\t"+i1.getEpochSecond());
        System.out.println("timestamp:\t"+i1.toEpochMilli());
        // 通过Clock的API获取东八区对应的时钟
        Clock offsetClock = Clock.offset(Clock.systemUTC(),
                Duration.ofHours(8));
        // 获取当前时间对象,设置使用东八区时钟
        Instant i2 = Instant.now(offsetClock);
        // 输出当前时间
        System.out.println("UTC+8:\t \t "+i2);
        // 基于指定时间获取Instant对象
        Instant i3 = Instant.ofEpochSecond(i1.getEpochSecond()
                -3600);
        System.out.println("one hour ago:\t "+i3);
        // 基于字符串获取Instant对象
        Instant i4 = Instant.parse("2022-09-18T06:00:00Z");
        System.out.println("from String:\t "+i4);
        // Instant相关操作
        Instant i5 = Instant.parse("2022-09-18T06:00:00Z");
        System.out.println("i1:\t\t"+i1);
        // 时间的加法运算
        Instant i6 = i1.plus(3L, ChronoUnit.HOURS);
        System.out.println("i1+3 hour:\t"+i2);
        // 时间的减法运算
        Instant i7 = i1.minus(5L,ChronoUnit.HOURS);
        System.out.println("i1-5 hour:\t"+i3);
        // 两个时间的差值
        System.out.println("i1-i2 in seconds:"
                +i1.until(i2,ChronoUnit.SECONDS));
        // 判断时间先后
        System.out.println("i1 is before i2:"+i1.isBefore(i2));
        System.out.println("i1 is after i2:"+i1.isAfter(i2));
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/250905.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TCP/IP详解——HTTPS 协议

文章目录 1. HTTPS 协议1.1 HTTPS 原理1.2 HTTPS 过程1.3 从数据包角度看 HTTPS 交互过程1.4 常见的 HTTPS 数据包解码1.4.1 ClientHello 数据包1.4.2 ServerHello 数据包 1.5 思考 1. HTTPS 协议 1.1 HTTPS 原理 HTTPS概念 HTTPS 是以安全为目标的HTTP通道&#xff0c;并不…

小 cookie,大作用:探索网站中的隐私追踪器(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

持续集成交付CICD:Jenkins使用GitLab共享库实现基于Ansible的CD流水线部署前端应用的蓝绿发布

目录 一、实验 1.蓝绿发布准备 2.Jenkins使用GitLab共享库实现基于Ansible的CD流水线部署前端应用的蓝绿发布 二、问题 1.手动构建Jenkins前端项目CI流水线报错 2.如何优化手动构建流水线选项参数 一、实验 1.蓝绿发布准备 &#xff08;1&#xff09;环境 表1 蓝绿发布…

flume:Ncat: Connection refused.

一&#xff1a;nc -lk 44444 和 nc localhost 44444区别 nc -lk 44444 和 nc localhost 44444 是使用 nc 命令进行网络通信时的两种不同方式。 1. nc -lk 44444&#xff1a; - 这个命令表示在本地监听指定端口&#xff08;44444&#xff09;并接受传入的连接。 - -l 选项…

前端视角看 Docker : 基础命令全面指南

引言 Docker是一种开源的容器化平台&#xff0c;它允许开发者将应用程序和其依赖打包在一个轻量级的、可移植的容器中。这使得应用程序在不同的环境中部署变得简单且高效。本文将介绍Docker的一些基础命令和概念&#xff0c;帮助初学者快速上手。 1. Docker简介 Docker使用…

054:vue工具 --- BASE64加密解密互相转换

第054个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

云原生之深入解析使用Telepresence轻松在本地调试和开发Kubernetes应用程序

一、 准备 telepresence 下载&#xff1a;https://www.telepresence.io/docs/latest/install/kubectl 下载&#xff1a;https://kubernetes.io/docs/tasks/tools/ 二、版本检测 $telepresence version Client: v2.5.3 (api v3) Root Daemon: not running User Daemon: not r…

css文本样式的使用

在CSS中&#xff0c;可以通过以下属性来设置文本的样式&#xff1a; color&#xff1a;设置文本的颜色。 p {color: red; }效果图&#xff1a; font-size&#xff1a;设置文本的字体大小。 p {font-size: 16px; }效果图&#xff1a; font-family&#xff1a;设置文本的字…

uniGUI学习之UniHTMLMemo1富文本编辑器

1]系统自带的富文本编辑器 2]jQueryBootstarp富文本编辑器插件summernote.js 1]系统自带的富文本编辑器 1、末尾增加<p> 2、增加字体 3、解决滚屏问题 4、输入长度限制问题 5、显示 并 编辑 HTML源代码(主要是图片处理) 1、末尾增加<p> UniHTMLMemo1.Lines…

【星环云课堂大数据实验】kafka消息发布与订阅

文章目录 一、Kafka概述二、实验环境三、实验准备四、实验目的五、实验步骤5.1、创建Kafka Topic5.2、Kafka消息发布5.3、Kafka消息订阅 六、实验感悟 一、Kafka概述 Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;由Scala和Java编写。该项目的目标是为处理实…

持续集成交付CICD:Jenkins使用GitLab共享库实现基于Ansible的CD流水线部署前后端应用

目录 一、实验 1.部署Ansible自动化运维工具 2.K8S 节点安装nginx 3.Jenkins使用GitLab共享库实现基于Ansible的CD流水线部署前后端应用 二、问题 1.ansible安装报错 2.ansible远程ping失败 3. Jenkins流水线通过ansible命令直接ping多台机器的网络状态报错 一、实验 …

Hadoop分布式配置小白篇(附加各阶段问题解决方式)

看的黑马的课&#xff0c;记录一下配置步骤 目录 1.VMware安装&#xff1a; 方法1&#xff1a; 方法2&#xff1a; 2.创建虚拟机 1.ISO镜像文件获取&#xff08;CentOS&#xff09;&#xff1a; 2.创建&#xff08;简略步骤&#xff09; 3.克隆虚拟机&#xff08;克隆伪…

idea第一次提交到git(码云)

1.先创建一个仓库 2.将idea和仓库地址绑定 2.将idea和仓库地址绑定

《Kotlin核心编程》笔记:集合、序列与内联函数

集合的高阶函数API map 操作 val list listOf(1, 2, 3, 4, 5, 6) val newList list.map { it * 2 }当然&#xff0c;在 Java 8 中&#xff0c;现在也能像Kotlin那样去操作集合了。 上面的方法实际上就是一个高阶函数&#xff0c;它接收的参数实际上就是一个函数&#xff0…

使用Pytorch从零开始构建LoRA

引言 在这篇博文中&#xff0c;我将向大家展示如何使用Pytorch从头开始构建 LoRA。LoRA 是Low-Rank Adaptation或Low-Rank Adapters的缩写&#xff0c;它提供了一种高效且轻量级的方法来微调预先存在的语言模型。这包括BERT和RoBERTa等掩码语言模型&#xff0c;以及GPT、Llama…

微服务实战系列之ZooKeeper(中)

前言 昨日博主的第一篇ZooKeeper&#xff0c;对它自身具备的能力做了初步介绍。书接上文&#xff0c;马不停蹄&#xff0c;我们继续挖掘它内在的美&#xff0c;充分把握它的核心与脉络。 揭秘ZooKeeper Q&#xff1a;集群一致性协同是如何进行的 我们讲到分布式&#xff0c;…

Linux--LAMP 平台部署及应用

5.1 LAMP平台概述 LAMP架构是目前成熟的企业网站应用模式之一&#xff0c;指的是协同工作的一整套系统和相关软件&#xff0c;能够提供动态Web站点服务及其应用开发环境。LAMP是一个缩写词&#xff0c;具体包括Linux操作系统&#xff0c;Apache 网站服务器、MySQL数据库服务器&…

modbus 通信协议介绍与我的测试经验分享

1、简介 Modbus 协议是一种通信协议&#xff0c;用于工业自动化系统中的设备间通信。该协议最初由 Modicon 公司开发&#xff0c;并于 1979 年发布。 Modbus 协议通过串行通信格式进行通信&#xff0c;在物理层上支持 RS-232、RS-422 和 RS-485 等多种通信方式。在协议层面&am…

python3GUI--仿win风格天气By:PyQt5

文章目录 一&#xff0e;前言二&#xff0e;展示1.首页-白色1.首页-白色22.首页-黑色3.天气预报视频4.天气资讯-白色5.天气资讯-黑色6.收藏夹-白色7.收藏夹-黑色8.搜索9.mini-白色10.mini-黑色11.光遇天气 三&#xff0e;心得四&#xff0e;总结五&#xff0e;参考 一&#xff…

软件设计师——计算机网络(三)

&#x1f4d1;前言 本文主要是【计算机网络】——软件设计师——计算机网络的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1…