Java中的常用类

每天学习新知识,每天进步一点点。

Java提供了丰富的基础类库,通过这些类库可以提高开发效率,降低开发难度。在基础中,提供了处理字符串、数字运算、以及日期和时间等功能的类,本文将对基础类库中的常用类进行介绍方便学习。

1. String类与StringBuffer类

字符串中可以包含任意字符,这些字符必须包含在一对英文双引号("")之内。Java中定义了String和StringBuffer两个类来封装字符串,并提供了一系列操作字符串的方法。

1.1 String类的初始化

在操作String类之前,首先需要对String类进行初始化,Java中可以通过一下两种方式对String类进行初始化,如下:

  1. 使用字符串常量直接初始化一个String对象,其语法格式如下:
String 变量名 = 字符串;

在初始化字符串对象时,既可以将字符串对象的初始化值设为空,也可以初始化为一个具体的字符串,如下:

String str = null; // 空
String str1 = ""; // 空字符串
String str2 = "Hello World"; // 字符串常量
  1. 使用String的构造方法初始化一个String对象,其语法格式如下:
String 变量名 = new String(字符串);

String类中包含多个构造方法,常用的构造方法如表1-1所示:

表1-1 String类常用构造方法

方法声明功能描述
String()创建一个空字符串
String(char[] value)根据指定的字符数组创建对象
String(String value)根据指定的字符串内容创建对象

接下来通过一个案例学习String类是如果通过构造方法来初始化字符串对象的,如例:

例1-1:Demo1.java

public class Demo1 {
    public static void main(String[] args) {
        //创建一个空的字符串
        String str1 = new String();
        //创建一个内容为abc的字符串
        String str2 = new String("abc");
        //创建一个内容为字符数组的字符串
        char[] chars ={'a','b','c','d'};
        String str3 = new String(chars);
        //输出字符串内容
        System.out.println("a"+str1+"b");
        System.out.println(str2);
        System.out.println(str3);
    }
}

输出

ab
abc
abcd

例1-1中,分别使用了三个构造方法创建了字符串对象,第一种使用无参构造方法创建的是一个空字符串,所以第一个输出语句中的str1为空(""),当使用字符+连接a和b后,输出的结果为ab。第二种使用字符串常量"abc"创建了一个字符串对象,所以第二个输出语句中的str2为abc。第三种使用字符数组chars创建了一个字符串对象,所以第三个输出语句中的str3为abcd。可以看出,它们最后的输出结果就是存储在字符串对象中的内容。

tip:注意,String类是不可变的,即字符串内容一旦创建,就不能修改。如果需要修改字符串内容,可以创建新的String对象。每次更改赋值,都会产生一个新的String对象,即便没有显式的new String(),编译器也会自动创建String对象。

1.2 String类的常用方法

String类提供了一系列常用方法来操作字符串,如表1-2所示:

表1-2 String类常用方法

方法声明功能描述
int indexOf(int ch)返回指定字符在此字符串中第一次出现处的索引
int indexOf(String str)返回指定字符串在此字符串中第一次出现处的索引
int lastIndexOf(int ch)返回指定字符在此字符串中最后一次出现处的索引
int lastIndexOf(String str)返回指定字符串在此字符串中最后一次出现处的索引
char charAt(int index)返回指定索引处的字符,index的取值范围为0~(字符串长度-1)
boolean startsWith(String prefix)判断字符串是否以指定前缀开始
boolean endsWith(String suffix)判断字符串是否以指定后缀结束
int length()返回字符串的长度
boolean equals(Object anObject)将此字符串与指定的字符串进行比较
boolean isEmpty()判断字符串是否为空,当且仅当字符串长度为0时返回true
boolean contains(String str)判断当前字符串是否包含指定字符串
String toLowerCase()将字符串转换为小写
String toUpperCase()将字符串转换为大写
static String valueOf(int i)返回int参数的字符串表示形式
char[] toCharArray()将字符串转换为字符数组
String replace(CharSequence oldChar, CharSequence newChar)返回一个新的字符串,它是通过用newstr替换字符串中出现的所有oldstr得到的
String[] split(String regex)根据参数regex(正则表达式,用来限定分割规格)将字符串分割为若干个子字符串
String substring(int beginIndex)返回一个新字符串,包含从指定索引处开始直到此字符串末尾的所有字符
String substring(int beginIndex, int endIndex)返回一个新字符串,包含从指定索引处开始直到索引endIndex-1处的所有字符
String trim()返回一个新字符串,去除了字符串两端的空格
String concat(String str)返回一个新的字符串,它是将指定字符串连接到此字符串的末尾

1.2.1 字符串的基本操作

在程序中,需要对字符串进行一些基本操作,如获取字符串长度,获取指定位置的字符等。String类针对每一个操作都提供了对应的方法。

接下来通过一个案例来学习这些基本方法的使用,如例:

例1-2:Demo2.java

public class Demo2 {
    public static void main(String[] args) {
        String str = "abcdcbdaabc"; //初始化字符串
		//获取字符串的长度
        System.out.println("字符串长度为:"+str.length());
        //获取指定位置的字符
        System.out.println("字符串第一个字符为:"+str.charAt(0));
        //字符c第一次出现的位置
		System.out.println("字符c第一次出现的位置:" + str.indexOf('c'));
        //字符c最后一次出现的位置
		System.out.println("字符c最后一次出现的位置:" + str.lastIndexOf('c'));
        //子字符串abc第一次出现的位置
		System.out.println("子字符串abc第一次出现的位置:" + str.indexOf("abc"));
        //子字符串abc最后一次出现的位置
		System.out.println("子字符串abc最后一次出现的位置:" + str.lastIndexOf("abc"));
    }
}

输出

字符串长度为:11
字符串第一个字符为:a
字符c第一次出现的位置:2
字符c最后一次出现的位置:10
子字符串abc第一次出现的位置:0
子字符串abc最后一次出现的位置:8

tip:注意两个indexOf()方法的区别,一个是获取字符的位置,所以需要使用单引号(''),另一个是获取字符串的位置,所以需要使用双引号("")

1.2.2 字符串的转换操作

通过一个案例来演示字符串的转换操作,如例:

例1-3:Demo3.java

public class Demo3 {
    public static void main(String[] args) {
        String str = "Hello World"; //初始化字符串
        char[] chars = str.toCharArray();  //将字符串转换为字符数组
		for(int i =0;i<chars.length;i++){
			//如果不是数组的最后一个元素,就在元素后面加,
			if(i != chars.length-1){
				System.out.print(chars[i] + ",");
			}else{
				//最后一个元素了,不加,并且换行
				System.out.println(chars[i]);
			}
		}
		
		
		//将int类型转换为String类型
		System.out.println("将int类型转为String类型:" + String.valueOf(100));
		//将字符串转换为小写
        System.out.println("将字符串转为小写:" + str.toLowerCase());
        //将字符串转换为大写
        System.out.println("将字符串转为大写:" + str.toUpperCase());
    }
}

输出

H,e,l,l,o, ,W,o,r,l,d
将int类型转为String类型:100
将字符串转为小写:hello world
将字符串转为大写:HELLO WORLD

1.2.3 字符串的替换和去除空格操作

通过一个案例来演示字符串的替换和去除空格操作,如例:

例1-4:Demo4.java

public class Demo4 {
    public static void main(String[] args) {
        String str = "  http:// localhost : 8080    "; //初始化字符串

        //去除字符串两端的空格
        System.out.println("去除两端空格后的字符串:" + str.trim());

        //替换字符串中所有的空格
        System.out.println("替换空格后的字符串:" + str.replace(" ", ""));
    }
}

输出

去除两端空格后的字符串:http:// localhost : 8080
替换空格后的字符串:http://localhost:8080

1.2.4 字符串的判断操作

通过一个案例来演示字符串的判断操作,如例:

例1-5:Demo5.java

public class Demo5 {
    public static void main(String[] args) {
        String str1 = "Hello World"; //初始化字符串
        String str2 = "Hello"; //初始化字符串
        //判断是否以字符串开头
		System.out.println("判断是否以字符串He开头:" + str1.startsWith("He"));
        //判断是否以字符串结尾
		System.out.println("判断是否以字符串ld结尾:" + str1.endsWith("ld"));
        //判断是否包含字符串
		System.out.println("判断是否包含字符串:" + str1.contains(str2));
        //判断是否为空
		System.out.println("判断是否为空:" + str1.isEmpty());
        //判断两个字符串是否相等
		System.out.println("判断两个字符串是否相等:" + str1.equals(str2));
    }
}

输出

判断是否以字符串He开头:true
判断是否以字符串ld结尾:true
判断是否包含字符串:true
判断是否为空:false
判断两个字符串是否相等:false

tip: 在程序中,可以通过==equals()两种方式对字符串进行比较。但这两种方式有明显的区别,equals()方法是用来比较两个字符串的内容是否相等,而==是用来比较两个字符串对象的内存地址是否相等,是否指向同一个对象。对于两个字符串对象
,当它们的字符值完全相同时,使用equals()方法比较它们的结果为true,但是使用==比较的结果一定为false,因为它们指向不同的对象。

示例:

String str1 = "Hello World";
String str2 = "Hello World";
System.out.println(str1.equals(str2)); //true
System.out.println(str1 == str2); //false

1.2.5 字符串的截取、分割与拼接

通过一个案例来演示字符串的截取、分割与拼接,如例:

例1-6:Demo6.java

public class Demo6 {
    public static void main(String[] args) {
		String str = "Hello Java"; //初始化字符串
		//字符串进行分割操作
		System.out.println("对字符串进行分割后,分割后的数组元素为:");
		String[] strArray = str.split(" ");
		for(int i =0;i <strArray.length;i++){
			if(i != strArray.length -1){
				System.out.print(strArray[i] + ",");
			}else{
				System.out.println(strArray[i]);
			}
		}
		
		//字符串进行截取操作   索引从0开始   第三个字符索引值为2
		System.out.println("从第3个字符截取到末尾:" + str.substring(2));
		//截取,含头不含尾索引从0开始   第三个字符索引值为2,第九个字符索引为8,但是这样截取不包含第九个字符,所以需要索引+1,为9
		System.out.println("从第3个字符截取到第9个字符:" + str.substring(2,9));
		
		//字符串进行拼接操作
		System.out.println("字符串进行拼接操作:" + str.concat("我是后面拼接的字符串"));
    }
}

输出

对字符串进行分割后,分割后的数组元素为:
Hello,Java
从第3个字符截取到末尾:llo Java
从第3个字符截取到第9个字符:llo Jav
字符串进行拼接操作:Hello Java我是后面拼接的字符串

字符串截取图:
字符串截取.png

1.3 StringBuffer类

在Java中,由于String类是final类型的,所以String定义的字符串是一个常量。因此它一旦创建,其内容和长度是不可改变的。如果需要对一个字符串进行修改则只能创建新的字符串。为了便于对字符串进行修改,在JDK中提供了一个StringBuffer类来操作字符串。StringBuffer类和String类类似,但StringBuffer类是可变的,可以对字符串进行修改。StringBuffer类似一个字符容器,在其中添加或删除字符时,所操作的都是这个字符容器,因此不会产生新的StringBuffer对象。

表1-3 StringBuffer类常用方法

方法声明功能描述
StringBuffer append(char c)指定的字符追加到此 StringBuffer 的末尾
StringBUffer insert(int offset, String str)将指定的字符串插入此 StringBuffer 中的指定位置
StringBuffer deleteCharAt(int index)删除此 StringBuffer 中指定位置的字符
StringBuffer delete(int start, int end)删除此 StringBuffer 中从指定位置开始到指定位置结束的字符
StringBuffer replace(int start, int end, String str)用指定的字符串替换此 StringBuffer 中从指定位置开始到指定位置结束的字符串
void setCharAt(int index, char ch)用指定的字符替换此 StringBuffer 中指定位置的字符
String toString()返回此 StringBuffer 的字符串表示形式
StringBuffer reverse()反转此 StringBuffer

接下来通过一个案例来学习一下表中方法的具体使用,如例:

例1-7:Demo7.java

public class Demo7 {
    // 添加字符串的方法
    static void add() {
        // 定义一个字符串缓冲区,初始内容为 "Hello "
        StringBuffer sb = new StringBuffer("Hello ");
        
        // 使用 append 方法在字符串末尾添加内容
        sb.append("World"); 
        System.out.println("append添加结果:" + sb); // 输出添加后的结果
        
        // 使用 insert 方法在指定位置插入内容
        sb.insert(6, "你好"); // 在索引6的位置插入 "你好"  现在sb的内容式Hello World,索引6的位置是W,所以 插入的你好,会出现在W的前面 从而变成 Hello 你好World
        System.out.println("insert插入结果:" + sb); // 输出插入后的结果
    }
    
    // 修改字符串的方法
    static void update() {
        // 定义一个新的字符串缓冲区,初始内容为 "ABAAA"
        StringBuffer sb = new StringBuffer("ABAAA");

        // 使用 setCharAt 方法替换指定位置的字符
        sb.setCharAt(1, 'A'); // 将索引1位置的字符 'B' 替换为 'A'
        System.out.println("setCharAt指定替换结果:" + sb); // 输出替换后的结果
        
        // 使用 replace 方法替换指定位置开始到结束的字符串
        sb.replace(1, 4, "CDU"); // 替换从索引1到索引4的内容为 "CDU"(包含索引1,不包含索引4)
        System.out.println("replace指定位置替换结果:" + sb); // 输出替换后的结果
        
        // 使用 reverse 方法反转字符串
        System.out.println("reverse反转字符串:" + sb.reverse()); // 输出反转后的结果
    }
    
    // 删除字符串的方法
    static void delete() {
        // 定义一个新的字符串缓冲区,初始内容为 "ABCDEFG"
        StringBuffer sb = new StringBuffer("ABCDEFG");

        // 使用 deleteCharAt 方法删除指定位置的字符
        sb.deleteCharAt(3); // 删除索引3位置的字符 'D'
        System.out.println("删除指定位置字符结果:" + sb); // 输出删除后的结果
        
        // 使用 delete 方法删除指定起始和结束位置的字符串
        sb.delete(2, 4); // 删除从索引2到索引4的字符(包含索引2,不包含索引4)'CD'
        System.out.println("删除指定起始和结尾位置的字符串结果:" + sb); // 输出删除后的结果
        
        // 使用 delete 方法清空缓冲区
        sb.delete(0, sb.length()); // 删除整个字符串
        System.out.println("清空缓冲区结果:" + sb); // 输出清空后的结果,应该是空字符串
    }

    public static void main(String[] args) {
        // 调用添加字符串的方法
        System.out.println("1.添加-----------------------");
        add();
        
        // 调用修改字符串的方法
        System.out.println("2.修改-----------------------");
        update();
        
        // 调用删除字符串的方法
        System.out.println("3.删除-----------------------");
        delete();
    }
}

输出

1.添加-----------------------
append添加结果:Hello World
insert插入结果:Hello 你好World
2.修改-----------------------
setCharAt指定替换结果:AAAAA
replace指定位置替换结果:ACDUA
reverse反转字符串:AUDCA
3.删除-----------------------
删除指定位置字符结果:ABCEFG
删除指定起始和结尾位置的字符串结果:ABFG
清空缓冲区结果:

StringBuffer类和String类有很多相似之处,使用时很容易混淆。所以接下来对两个类进行对比,归纳不同。具体如下:

  1. String类定义的字符串是常量,一旦创建后,内容和长度是无法改变的。StringBuffer表示字符容器,其内容和长度随时可变。在操作字符串时,如果该字符串仅用于表示数据类型,则使用String类即可,如果需要对字符串中的字符进行增删操作,则需要使用StringBuffer类。
  2. String重写了Object类的equals()方法,而StringBuffer类没有重写。示例如下:
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1.equals(str2)); // true

StringBuffer sb1 = new StringBuffer("Hello");
StringBuffer sb2 = new StringBuffer("Hello");
System.out.println(sb1.equals(sb2)); // false
  1. String类可以使用操作符+进行连接,而StringBuffer对象之间不能。示例如下:
String s1 = "a";
String s2 = "b";
String s3 = s1 + s2; // 连接字符串

StringBuffer sb1 = new StringBuffer("a");
StringBuffer sb2 = new StringBuffer("b");
// 连接字符串,编译会报错,因为sb1和sb2是不同对象
StringBuffer sb3 = sb1+sb2;

tip:除了使用StringBuffer类外,JDK1.5之后提供了一个StringBuilder类,和StringBuffer类类似。通常情况下,如果创建一个内容可变的字符串对象,应该优先考虑StringBuilder类。StringBuilder类和StringBuffer类在性能上没有明显差异,但StringBuilder类是非线程安全的,因此在单线程环境下使用StringBuilder类更加合适。

2. System类和Runtime类

2.1 System类

System类定义了一些与系统相关的属性和方法,他所提供的属性和方法都是静态的。因此想要引用这些方法,直接使用System类调用即可。

表2-1 System类常用方法

方法声明功能描述
static void exit(int status)退出当前正在运行的Java虚拟机 ,其中参数status表示状态码,若状态码非0,则表示异常终止
stati void gc()运行垃圾回收器
static native long currentTimeMillis()返回当前时间(以毫秒为单位)
static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)src引用的指定源数组拷贝到dest引用的数组,拷贝从指定位置开始,可到指定位置结束。(复制数组内容)
static Properties getProperties()获取系统属性
static String getProperty(String key)获取指定键描述的系统属性

2.1.1 getProperties()方法

getProperties()方法用于获取系统属性,返回一个Properties对象,其中包含了系统的所有属性。这些属性是以键值对形式存在的,可以通过getProperty(String key)方法获取指定键描述的系统属性。接下来通过一个案例来演示getProperties()方法的使用,如例:

例2-1:Demo8.java

import java.util.Properties;

public class Demo8 {
    public static void main(String[] args) {    
        // 获取系统属性
        Properties props = System.getProperties();
        System.out.println(props); // 输出系统属性
        // 获取指定键描述的系统属性
        String javaVersion = System.getProperty("java.version");
        System.out.println("Java版本:" + javaVersion); // 输出Java版本
        String username = System.getProperty("user.name");
        System.out.println("用户名" + username); // 输出Java版本
        String osVersion = System.getProperty("os.version");
        System.out.println("操作系统版本:" + osVersion); // 输出Java版本
    }
}

输出

{java.specification.version=14, sun.cpu.isalist=amd64, sun.jnu.encoding=GBK, java.class.path=., java.vm.vendor=Oracle Corporation, sun.arch.data.model=64, user.variant=, java.vendor.url=https://java.oracle.com/, user.country.format=HK, java.vm.specification.version=14, os.name=Windows 10, sun.java.launcher=SUN_STANDARD, user.country=CN, sun.boot.library.path=D:\Program Files\Java\jdk-14.0.2\bin, sun.java.command=Demo8, jdk.debug=release, sun.cpu.endian=little, user.home=C:\Users\17634, user.language=zh, sun.stderr.encoding=ms936, java.specification.vendor=Oracle Corporation, java.version.date=2020-07-14, java.home=D:\Program Files\Java\jdk-14.0.2, file.separator=\, java.vm.compressedOopsMode=Zero based, line.separator=
, sun.stdout.encoding=ms936, java.vm.specification.vendor=Oracle Corporation, java.specification.name=Java Platform API Specification, user.script=, user.script.format=Hans, sun.management.compiler=HotSpot 64-Bit Tiered Compilers, java.runtime.version=14.0.2+12-46, user.name=旺旺, path.separator=;, os.version=10.0, java.runtime.name=Java(TM) SE Runtime Environment, file.encoding=GBK, java.vm.name=Java HotSpot(TM) 64-Bit Server VM, java.vendor.url.bug=https://bugreport.java.com/bugreport/, java.io.tmpdir=C:\Users\17634\AppData\Local\Temp\, java.version=14.0.2, user.dir=D:\demo\4. Java中的常用类, os.arch=amd64, java.vm.specification.name=Java Virtual Machine Specification, sun.os.patch.level=, java.library.path=D:\Program Files\Java\jdk-14.0.2\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;D:\Program Files\Python311\Scripts\;D:\Program Files\Python311\;C:\Python38\Scripts\;C:\Python38\;D:\Program Files (x86)\VMware\VMware Workstation\bin\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;D:\Program Files\;L:\快速访问\;D:\;rogram Files\OpenSSH\;D:\NetSarang\Xshell 7\;C:\Program Files (x86)\Common Files\Thunder Network\KanKan\Codecs;D:\Android\SDK\tools;D:\Android\SDK\platform-tools;D:\Program Files\OpenSSL-Win64\bin;D:\MinGW\bin;C:\Program Files\dotnet\;D:\Program Files (x86)\IDM Computer Solutions\UltraEdit;D:\soft\maven\apache-maven-3.6.0\bin;D:\soft\mysql-5.7.36-winx64\mysql-5.7.36-winx64\bin;C:\Program Files\Microsoft SQL Server\120\Tools\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\110\Tools\Binn\;C:\Program Files (x86)\Microsoft SQL Server\120\Tools\Binn\;C:\Program Files\Microsoft SQL Server\120\DTS\Binn\;C:\Program Files (x86)\Microsoft SQL Server\120\Tools\Binn\ManagementStudio\;C:\Program Files (x86)\Microsoft SQL Server\120\DTS\Binn\;D:\Program Files\Java\jdk-14.0.2\bin;L:\Git\cmd;L:\nvm;L:\nodejs;L:\Program Files\nodejs\;C:\Users\17634\AppData\Local\Microsoft\WindowsApps;D:\Program Files (x86)\GTK2-Runtime\bin;D:\Program Files\JetBrains\IntelliJ IDEA 2020.1.4\bin;;D:\Program Files\JetBrains\WebStorm 2020.1.4\bin;;D:\Program Files\JetBrains\PyCharm 2020.3\bin;;D:\soft\ADB;D:\Android\SDK\tools;D:\Android\SDK\platform-tools;D:\Programs\Microsoft VS Code\bin;C:\Users\17634\.dotnet\tools;D:\Program Files\JetBrains\GoLand\bin;;L:\nvm;L:\nodejs;L:\Program Files\nodejs\;C:\Users\17634\AppData\Roaming\npm;;., java.vm.info=mixed mode, sharing, java.vendor=Oracle Corporation, java.vm.version=14.0.2+12-46, sun.io.unicode.encoding=UnicodeLittle, java.class.version=58.0}
Java版本:14.0.2
用户名旺旺
操作系统版本:10.0

可以看出输出了系统的属性信息,包括os.name=Windows 10,操作系统的名称。os.arch=amd64,操作系统架构。user.country=CN,用户国家。java.vm.version=14.0.2+12-46,JVM的版本号等等。包括我们也使用getProperty()获取了java.versionuser.nameos.version三个系统属性,并输出了它们的值。

2.1.2 currentTimeMillis()方法

currentTimeMillis()方法用于获取当前时间,返回一个long类型的值,该值表示当前时间与1970年1月1日0分0秒之间的时间差,以毫秒为单位,通常也将该值成为时间戳。

接下来通过一个计算求和操作时消耗时间的案例来演示currentTimeMillis()方法的使用,如例:

例2-2:Demo9.java

public class Demo9{
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis(); // 获取开始时间
        int sum = 0;
        for(int i=0;i<100000000;i++){
            sum += i;
        }
        long endTime = System.currentTimeMillis(); // 获取结束时间
        long time = endTime - startTime; // 计算消耗时间
        System.out.println("计算消耗时间:" + time + "毫秒"); // 输出计算消耗时间
    }
}

输出

计算消耗时间:28毫秒

tip: 由于处理器性能等原因,同样程序运行的时间也会不同。

2.1.3 arraycopy()方法

arraycopy()方法用于复制数组内容,可以将一个数组的内容复制到另一个数组中。其中参数列表的参数具体说明如下:

  1. src:源数组,即要复制的数组。
  2. srcPos:源数组的起始位置,即要复制的数组的起始位置。
  3. dest:目标数组,即复制到的数组。
  4. destPos:目标数组的起始位置,即复制到的数组的起始位置。
  5. length:要复制的元素个数。

在进行数组拷贝时,目标数组必须要有足够的空间来存放拷贝的元素,否则会抛出IndexOutOfBoundsException(数组下标越界)异常。接下来通过一个案例来演示arraycopy()方法的使用,如例:
例2-3:Demo10.java

public class Demo10{
	public static void main(String[] args){
		int[] arr1 = {101,102,103,104,105,106};   //源数组
		int[] arr2 = {201,202,203,204};				    //目标数组
		//拷贝数组元素
		System.arraycopy(arr1,2,arr2,0,4);	           
		//打印目标数组中的元素
		for(int i = 0; i< arr2.length; i++){
			System.out.println(i + ":" + arr2[i]);
		}
	}
}

输出

0:103
1:104
2:105
3:106

上面的案例中,使用arraycopy()方法将源数组arr1的第2个元素开始的4个元素复制到目标数组arr2的第0个元素开始的位置。 arr1的第2个元素为103,所以从103开始的四个元素103104105106被复制到了arr2的第0个元素开始的位置。需要注意的是,arr2只有四个元素长度,所以如果我们修改一下,将源数组arr1的第1个元素开始的5个元素复制到目标数组arr2的第0个元素开始的位置。这个时候会发生什么?结果当然是编译报错,如下:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: arraycopy: last destination index 5 out of bounds for int[4]
        at java.base/java.lang.System.arraycopy(Native Method)
        at Demo10.main(Demo10.java:6)

所以一定要注意目标数组必须要有足够的空间来存放拷贝的元素

tip:除了注意足够存放拷贝元素的空间外,还要注意源数组与目标数组元素类型一致,否则程序运行会出现ArrayStoreException(数组元素类型不一致)异常。

2.2 Runtime类

Runtime类用于表示Java虚拟机运行时的状态,用于封装Java虚拟机进程。每次使用java命令启动java虚拟机时都会对应一个Runtime实例,并且只有一个实例,应用程序会通过该实例与其运行时的环境相连。

应用程序不能创建自己的Runtime实例,若想在程序中获得一个Runtime实例,则只能通过getRuntime()方法。getRuntime()方法返回一个Runtime实例,具体方式如下:

Runtime runtime = Runtime.getRuntime();

由于Runtime类封装了Java虚拟机进程,因此,可以通过该类的实例对象来获取虚拟机的相关信息。接下来通过一个案例来演示Runtime类的使用,如例:

例2-4:Demo11.java

public class Demo11{
    public static void main(String[] args){
        // 获取运行时对象
        Runtime runtime = Runtime.getRuntime();
        // 输出Java虚拟机的最大内存
        long maxMemory = runtime.maxMemory();
        System.out.println("Java虚拟机的最大内存:" + maxMemory / 1024 / 1024 + "MB");
        // 输出Java虚拟机的可用内存
        long freeMemory = runtime.freeMemory();
        System.out.println("Java虚拟机的可用内存:" + freeMemory / 1024 / 1024 + "MB");
        // 输出Java虚拟机的总内存
        long totalMemory = runtime.totalMemory();
        System.out.println("Java虚拟机的总内存:" + totalMemory / 1024 / 1024 + "MB");
        // 输出Java虚拟机的处理器数量
        int availableProcessors = runtime.availableProcessors();
        System.out.println("Java虚拟机的处理器数量:" + availableProcessors);
    }
}

输出

Java虚拟机的最大内存:3944MB
Java虚拟机的可用内存:246MB
Java虚拟机的总内存:248MB
Java虚拟机的处理器数量:8

在上面的案例中,通过Runtime类的实例对象runtime调用了maxMemory()freeMemory()totalMemory()availableProcessors()方法,分别获取Java虚拟机的最大内存、可用内存、总内存和处理器数量。这些方法返回的结果都是以字节为单位的,所以需要除以10241024得到以MB为单位的结果。

Runtime类中提供了一个exec()方法,可以用来执行一个DOS命令,从而实现在和命令行窗口输入DOS命令同样的效果。

例如,可以通过运行"notepad.exe"命令来打开记事本,代码如下:

public class Demo12{
    public static void main(String[] args){
        Runtime runtime = Runtime.getRuntime();
        try {
            //使用 exec() 方法启动一个外部进程,这里是打开记事本(Notepad)。exec() 方法返回一个 Process 对象,表示这个外部进程。
            Process process = runtime.exec("notepad.exe");
            process.waitFor();   //使当前线程等待,直到 notepad.exe 进程结束。换句话说,代码会在这个位置停下来,直到用户关闭记事本应用程序。

            //也可以等待程序自动关闭记事本进程
            /*
            Thread.sleep(3000);   //等待三秒钟,以便记事本进程自动关闭。
            process.destroy();   //强制关闭 notepad.exe 进程。
            */
        } catch (Exception e) { //本来是IOException,因为不想导入包,所以改成了IOException
            e.printStackTrace();    
        }
    }
}

tipRuntime类中还有很多其他的方法,可以用来获取Java虚拟机的相关信息,具体请参考API文档。

3. Math类与Random类

3.1 Math类

Math类是一个工具类,主要完成复杂的数学运算。如求绝对值、三角函数、指数运算等。Math类的所有方法都是静态方法,所以可以直接通过类名调用。除了静态方法外,Math类还有两个静态常量PIE,分别代表数学中的Πe

由于Math类比较简单,可以查看API文档来学习,所以接下来会通过一个案例来演示Math类中的一些常用方法,如例:
例3-1:Demo13.java

public class Demo13{
    public static void main(String[] args){
        System.out.println("计算绝对值:" + Math.abs(-1));
        System.out.println("计算正弦:" + Math.sin(1.57));
        System.out.println("计算余弦:" + Math.cos(2.0));
        System.out.println("计算正切:" + Math.tan(0.8));
        System.out.println("计算平方根:" + Math.sqrt(4));
        System.out.println("计算立方根:" + Math.cbrt(27));
        System.out.println("计算乘方:" + Math.pow(4,2));
        System.out.println("计算大于参数的最小整数(向上取整):" + Math.ceil(4.6));
        System.out.println("计算小于参数的最大整数(向下取整):" + Math.floor(-5.2));
        System.out.println("计算小数四舍五入:" + Math.round(-8.6));
        System.out.println("计算两个数的较大值:" + Math.max(5.1,5.5));
        System.out.println("计算两个数的较小值:" + Math.min(5.1,5.5));
        System.out.println("生成一个大于等于0.0小于1.0的随机值:" + Math.random());
    }
}

输出

计算绝对值:1
计算正弦:0.9999996829318346
计算余弦:-0.4161468365471424
计算正切:1.0296385570503641
计算平方根:2.0
计算立方根:3.0
计算乘方:16.0
计算大于参数的最小整数(向上取整):5.0
计算小于参数的最大整数(向下取整):-6.0
计算小数四舍五入:-9
计算两个数的较大值:5.5
计算两个数的较小值:5.1
生成一个大于等于0.0小于1.0的随机值:0.1960915745629419

tip:详细的可以查看API开发文档

3.2 Random类

在JDK的java.util包中,有一个Random类,可以在指定的取值范围内随机产生数字。

Random类中提供了两个构造方法,如表所示:

表3-2-1 Random的构造方法

方法声明功能描述
Random()用于创建一个随机数生成器,每次实例化Random对象会生成不同的随机数
Random(long seed)使用一个long型的seed(种子)创建伪随机数生成器,当seed相同时,每次实例化Random对象会生成相同的随机数

表3-2-2 Random类的常用方法

方法声明功能描述
boolean nextBoolean()返回一个随机的boolean值,true或false
double nextDouble()返回一个随机的double值,范围在0.0到1.0之间
float nextFloat()返回一个随机的float值,范围在0.0f到1.0f之间
int nextInt()返回一个随机的int值,范围在0到2^31-1之间
int nextInt(int n)返回一个随机的int值,范围在0到n-1之间
long nextLong()返回一个随机的long值,范围在0到2^63-1之间

接下来通过一个案例来学习这些方法,如例:

例3-2:Demo14.java

import java.util.Random;

public class Demo14{
    public static void main(String[] args){
        // 创建一个Random对象
        Random random = new Random();
        System.out.println("生成0到100之间的随机数: " + random.nextInt(100));
        System.out.println("生成double类型的随机数: " + random.nextDouble());
        System.out.println("生成float类型的随机数: " + random.nextFloat());
        System.out.println("生成int类型的随机数: " + random.nextInt());
        System.out.println("生成boolean类型的随机数: " + random.nextBoolean());
    }
}

输出

生成0到100之间的随机数: 15
生成double类型的随机数: 0.9626853754564346
生成float类型的随机数: 0.90120316
生成int类型的随机数: 661990809
生成boolean类型的随机数: false

4. 包装类

虽然Java时面向对象的编程语言,但它所包含的8中基本类型却不支持面向对象的编程机制。在Java中,很多类的方法都需要接收引用类型的对象,为了解决这样的问题,JDK提供了一系列的包装类,通过这些包装类,可以将基本数据类型的值包装为引用数据类型的对象。

在Java中,每种基本类型都有对应的包装类,如表4-1所示:

表4-1 Java基本类型对应的包装类

基本数据类型对应的包装类基本数据类型对应的包装类
byteBytelongLong
charCharacterfloatFloat
intIntegerdoubleDouble
shortShortbooleanBoolean

包装类和基本数据类型再进行转换时,引入了自动装箱自动拆箱的概念。其中自动装箱是指将基本数据类型的变量赋值给对应的包装类变量。反之,自动拆箱是指将包装类变量赋值给对应的基本数据类型变量。

接下来以int类型与对应的包装类Integer为例学习装箱、拆箱的过程。如例:
例4-1:Demo15.java

public class Demo15{
    public static void main(String[] args){
        int a = 10;
        Integer b = a;   // 自动装箱
        System.out.println("b的值:" + b);
        System.out.println("b的类型:" + b.getClass());
        System.out.println("b的值为:" + b.intValue());

        int c = b;   // 自动拆箱
        System.out.println("c的值:" + c);
    }
}

输出

b的值:10
b的类型:class java.lang.Integer
b的值为:10
c的值:10

例4-1中,首先定义了一个int类型的变量a,然后将其赋值给Integer类型的变量b。由于a是基本数据类型,而b是包装类,因此b是自动装箱。b的值为10,类型为Integer,可以通过intValue()方法获取其基本数据类型的值。然后,又定义了一个int类型的变量c,将b赋值给c。由于b是包装类,而c是基本数据类型,因此c是自动拆箱。c的值为10

Java中,除了支持基本数据类型与对应包装类之间进行转换外,还提供了其他方法来支持基本数据类型、基本数据包装类以及字符串之间的相互转换。具体如下:

  1. 通过引用数据类型字符串String类的valueOf()方法可以将8种基本数据类型转换为字符串,如Integer.toString(10)可以将10转换为字符串"10"
  2. 通过8种包装类的静态方法valueOf()既可以将对应的基本数据类型转换为包装类,也可以将变量内容匹配的字符串转换为对应的包装类(Character包装类除外)。
  3. 通过8种包装类的有参构造方法同样既可以将对应的基本数据类型转换为包装类,也可以将变量内容匹配的字符串转换为对应的包装类(Character包装类除外)。
  4. 通过8种包装类的静态方法parseXxx()可以将变量内容匹配的字符串转换为对应的基本数据类型。
  5. 包装类都重写了Object类中的toString()方法,以字符串的形式返回被包装的基本数据类型的值。

下面通过上面的方法实现基本数据类型、包装类以及字符串之间的相互转换。如例:
例4-2:Demo16.java

public class Demo16{
    public static void main(String[] args){
        int num = 12;
        //1. 通过String.valueOf()转为字符串
        String str = String.valueOf(num);
        System.out.println("通过String.valueOf()转为字符串str的值:" + str);
        //2. 通过包装类的valueOf()将基本类型和字符串转为包装类
        Integer integer = Integer.valueOf(str);
        System.out.println("通过包装类的valueOf()将字符串转为包装类integer的值:" + integer);
        Integer integer2 = Integer.valueOf(num);
        System.out.println("通过包装类的valueOf()将基本类型转为包装类integer2的值:" + integer2);
        //3. 通过包装类的有参构造方法将基本类型和字符串转为包装类
		Integer integer3 = new Integer(str);
		Integer integer4 = new Integer(num);
	    System.out.println("通过包装类的有参构造方法将基本类型转为包装类integer3的值:" + integer);
        System.out.println("通过包装类的有参构造方法将字符串转为包装类integer4的值:" + integer);
        //4. 通过包装类的parseXxx()方法将字符串转为基本类型
		int a = Integer.parseInt(str);
		System.out.println("通过包装类的parseXxx()方法将字符串str转为int类型a的值:" + a);
        //5. 通过包装类的toString()方法将包装类转为字符串
		String str1 = integer.toString();
		System.out.println("将包装类转为字符串:"+ str1);
    }
}

输出

通过String.valueOf()转为字符串str的值:12
通过包装类的valueOf()将字符串转为包装类integer的值:12
通过包装类的valueOf()将基本类型转为包装类integer2的值:12
通过包装类的有参构造方法将基本类型转为包装类integer3的值:12
通过包装类的有参构造方法将字符串转为包装类integer4的值:12
通过包装类的parseXxx()方法将字符串str转为int类型a的值:12
将包装类转为字符串:12

由运行结果可以看出,几种方法可以实现基本数据类型、包装类以及字符串之间的相互转换,但在使用valueOf(String s)parseXxx(String s)方法时,需要注意以下几点:

  1. 除了Character外,包装类都有valueOf(String s)方法,可以根据String类型的参数创建包装类对象,但参数字符串s不能为null,而且字符串必须时可以解析为相应基本类型的数据,否则编译通过,但是运行会报错,如例:
Integer integer = Integer.valueOf("123"); // 合法
Integer integer2 = Integer.valueOf("abc"); // 不合法
  1. 除了Character外,包装类都有parseXxx(String s)方法,可以将字符串解析为相应基本类型的数据,但参数字符串s不能为null,而且同样字符串必须时可以解析为相应基本类型的数据,否则编译通过,但是运行会报错,如例:
int a = Integer.parseInt("123"); // 合法
int b = Integer.parseInt("abc"); // 不合法

5. 日期与时间类

5.1 Date类

在JDK的java.util包种,Date类是表示日期和时间的类,它提供了对日期和时间的各种操作方法。

在JDK8种,Date类只有两个构造方法是可以使用的,具体如下:

  • Date():默认构造方法,用来创建当前日期时间的Date对象。
  • Date(long date):用于创建指定时间的Date对象,其中date参数表示1970年1月1日0时0分0秒以来的毫秒数,即时间戳。

接下来通过一个案例来学习这些方法,如例:

例5-1:Demo17.java

import java.util.Date;

public class Demo17{
	public static void main(String[] args){
		Date date = new Date();
		System.out.println(date);
		Date date1 = new Date(System.currentTimeMillis() + 1000);
		System.out.println(date1);
	}
}

输出

Thu Oct 17 17:11:45 HKT 2024
Thu Oct 17 17:11:46 HKT 2024

例5-1中,首先创建了一个Date对象date,然后打印出当前日期时间。接着,创建了一个Date对象date1,并传入当前时间的1秒钟之后的毫秒数作为参数,然后打印出date1

tip:对于Date类,只需要了解如何通过创建对象封装时间值即可。由于Date类在设计之初没有考虑国际化问题,因此从JDK1.1开始,Date类相应的功能也被Calendar类的方法取代。

5.2 Calendar类

Calendar类用于完成日期和时间字段的操作,它可以通过特定的方法设置和读取日期的特定部分,如年、月、日、时、分、秒等。Calendar是一个抽象类,不可以被实例化,程序中需要调用其静态方法getInstance()来获取一个Calendar对象,然后才能调用相应的方法。

Calendar类几个常用方法:

  • getInstance(): 获取一个Calendar对象,根据系统默认时区和语言环境获取相应的日历信息。
  • int get(int field): 获取指定日历字段的值。
  • void set(int field, int value): 设置指定日历字段的值。
  • void add(int field, int value): 根据日历规则,为指定日历字段增加或减少指定值。
  • void set(int year, int month, int date): 设置年、月、日。
  • void set(int year, int month, int date, int hour, int minute, int second): 设置年、月、日、时、分、秒。

在上面大多数方法中都用到了int类型的参数field,该参数需要接收Calendar类中的常量,如Calendar.YEAR年份、Calendar.MONTH月、Calendar.DATE日等。要注意的是,在使用Calendar.MONTH时,需要注意月份的范围是从0到11,即0表示一月,1表示二月,以此类推。所以获取当前月份需要在get(Calendar.MONTH)方法调用后加1。

接下来通过一个案例来学习这些方法,如例:

例5-2:Demo18.java

import java.util.Calendar;

public class Demo18{
	public static void main(String[] args){
		Calendar cal = Calendar.getInstance();		//获取表示当前时间的Calendar对象
		int year = cal.get(Calendar.YEAR);      	    //获取当前年份
		int month = cal.get(Calendar.MONTH) + 1;  //获取当前月份
		int date = cal.get(Calendar.DATE);    //获取当前日
		int hour = cal.get(Calendar.HOUR);   //时
		int minute = cal.get(Calendar.MINUTE);   //分
		int second = cal.get(Calendar.SECOND); //秒
		System.out.println("当前时间为:" + year + "年" + month + "月" + date + "日" + hour + "时" + minute + "分" + second + "秒");
	}
}

输出

当前时间为:2024年10月17日5时27分23秒

例5-2中,首先使用静态方法getInstance()获取表示当前时间的Calendar对象cal,然后通过get(int field)方法传入不同的常量参数获取当前年份、月份、日、时、分、秒,并打印出当前时间。

在程序中,除了要获取当前时间外,还会经常设置或修改某个时间,比如一个工程的开始时间为2024年10月20日,假设要100天后竣工,此时想要知道竣工日期是哪天,就需要先将日期设定在开始的那天,然后对日期的天数进行增加,如果没有按照预期完成,可能还需要对日期进行修改。其中添加和修改时间的功能就可以通过Calendar类种的add()set()方法来实现。

接下来就通过案例实现上述例子,如例:

例5-3:Demo19.java

import java.util.Calendar;

public class Demo19{
	public static void main(String[] args){
		//获取表示当前日期的Calendar对象
		Calendar car = Calendar.getInstance();
		//设置指定日期,
		car.set(2024,10,20);
		//增加时间,100天后
		car.add(Calendar.DATE,100);
		
		//返回指定日期的年、月、日
		int year = car.get(Calendar.YEAR);
		int month = car.get(Calendar.MONTH) + 1;
		int date = car.get(Calendar.DATE);
		
		System.out.println("计划竣工日期为:" + year + "年" + month + "月" + date + "日");
		
	}
}

输出

计划竣工日期为:2025年2月28日

例5-3中,首先使用Calendar.getInstance()获取表示当前日期的Calendar对象car,然后使用set(int year, int month, int date)方法设置指定日期为2024年10月20日。接着,使用add(int field, int value)方法增加时间,增加100天,即2024年10月20日之后的100天。最后,使用get(int field)方法获取指定日期的年、月、日,并打印出计划竣工日期。

5.3 Calendar扩展

Calendar类有两种解释日历字段的模式-lenient模式和non-lenient模式。当Calendar处于lenient模式时,它的字段允许接收超过允许范围的值,当调用get(int field)方法获取某个字段值,Calendar会重新计算所有字段的值,将字段的值进行标准化。例如:月份只有12个月,允许输入的值为0~11,但是在这种模式下,我们输入13也是可以的。当Calendar处于non-lenient模式时,如果某个字段的值超出了它所允许的范围,程序就会抛出异常。

接下来通过一个案例来演示这种异常情况,如例:
例5-4:Demo20.java

import java.util.Calendar;

public class Demo20{
    public static void main(String[] args){
        //获取当前时间的Calendar对象
		Calendar cal = Calendar.getInstance();
		//获取当前年月日
		int year = cal.get(Calendar.YEAR);		
		int month = cal.get(Calendar.MONTH) + 1;
		int date = cal.get(Calendar.DATE);
		System.out.println("修改后的日期为:" + year + "年" + month + "月" + date+"日");
		//设置指定日期,将MONTH设置为13
		cal.set(Calendar.MONTH,13);
		//重新获取年月日
		year = cal.get(Calendar.YEAR);		
		month = cal.get(Calendar.MONTH) + 1;
		date = cal.get(Calendar.DATE);
		System.out.println("修改后的日期为:" + year + "年" + month + "月" + date+"日");
		
		
		//设置模式为 non-lenient模式
		cal.setLenient(false);
		cal.set(Calendar.MONTH,13);
		System.out.println(cal.getTime());
    }
}

输出

修改后的日期为:2024年10月17日
修改后的日期为:2025年2月17日
Exception in thread "main" java.lang.IllegalArgumentException: MONTH
        at java.base/java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2646)
        at java.base/java.util.Calendar.updateTime(Calendar.java:3429)
        at java.base/java.util.Calendar.getTimeInMillis(Calendar.java:1813)
        at java.base/java.util.Calendar.getTime(Calendar.java:1786)
        at Demo20.main(Demo20.java:24)

可以从上述例子中看出,当Calendar默认为lenient模式时,我们将MONTH设置为13,程序并没有报错,并且对字段重新进行了计算,正常输出了日期。这是因为Calendar的MONTH限制为0~11,当调用set(int field,int value)时,会自动进位,YEAR字段加1,MONTH字段加1。当更改模式为non-lenient时,设置MONTH为13,则会超出0~11的范围限制,程序会抛出IllegalArgumentException异常。在例子中还使用到了getTime()方法,该方法返回Date对象,同时还有一个setTime(Date date)方法,可以实现Calendar对象与Date对象之间的相互转换。

5.4 JDK 8 的日期与时间类

为了满足更多的需求,JDK 8比之前的版本增加了一个java.time包,其中包含了新的日期与时间类。其常用类如表5-1所示:

类名功能描述
Clock用于获取指定时区当前日期、时间
DayOfWeek枚举类,定义了一周七天周一到周日的枚举值
Duration表示持续时间。该类提供的ofXxx()方法用于获取指定的时间的小时、分钟、秒数
Instant表示一个具体时刻,可以精确到纳秒。该类提供了静态的now()方法来获取当前时刻,提供了静态的now(Clock clock)方法来获取clock对应的时刻。同时还提供了一系列的plusXxxx()、minusXxxx()方法来对日期进行加减操作
LocalDate表示不带时区的时间,如2024-10-17。该类提供了静态的now()方法来获取当前时刻,提供了静态的now(Clock clock)方法来获取clock对应的时刻。同时还提供了一系列的plusXxxx()、minusXxxx()方法来在当前时间基础上对年、月、日进行加减操作
LocalTime表示不带时区的时间,如19:30:19。该类提供了静态的now()方法来获取当前时刻,提供了静态的now(Clock clock)方法来获取clock对应的时刻。同时还提供了一系列的plusXxxx()、minusXxxx()方法来在当前时间基础上对时、分、秒进行加减操作
LocalDateTime表示不带时区的时间。该类提供了静态的now()方法来获取当前时刻,提供了静态的now(Clock clock)方法来获取clock对应的时刻。同时还提供了一系列的plusXxxx()、minusXxxx()方法来在当前时间基础上对年、月、日、时、分、秒进行加减操作
Month枚举类,定义了一月到十二月的枚举值
MonthDay表示月日,如--10-17。该类提供了静态的now()方法来获取当前月日,提供了静态的now(Clock clock)用来获取clock对应的月日
Year表示,如2024。该类提供了静态的now()方法来获取当前年,提供了静态的now(Clock clock)方法来获取clock对应的年。同时还提供了plusYears()、minusYears()方法来对年进行加减操作
YearMonth表示年月,如2024-10。该类提供了静态的now()方法来获取当前年月,提供了静态的now(Clock clock)用来获取clock对应的年月。同时提供了plusXxxx()、minusXxxx()方法来对年、月进行加减操作
ZoneId表示一个时区
ZonedDateTime表示一个时区化的日期、时间

在了解了上述各个类的作用后,通过一个案例来演示这些类的用法,如例:
例5-5:Demo21.java

import java.time.*;

public class Demo21{
	public static void main(String[] args){
		//1.Clock的使用
		Clock clock = Clock.systemUTC();
		System.out.println("获取UTC时区转换的当前时间:" + clock.instant());
		System.out.println("获取UTC时区转换的毫秒数:" + clock.millis());
		//2.Duration的使用
		Duration d = Duration.ofDays(1);  //获取一天
		System.out.println("一天等于" + d.toHours() + "小时");
		System.out.println("一天等于" + d.toMinutes() + "分钟");
		System.out.println("一天等于" + d.toMillis() + "秒");
		//3.Instant的使用
		Instant instant = Instant.now();
		System.out.println("获取UTC时区转换的当前时间:" + instant);
		System.out.println("获取当前时间一小时后的时间为:" + instant.plusSeconds(3600));
		System.out.println("获取当前时间一小时前的时间为:" + instant.minusSeconds(3600));
		//4.LocalDate的使用
		LocalDate localDate = LocalDate.now();
		System.out.println("从默认时区的系统时钟获得当前日期:" + localDate);
		//5.LocalTime的使用
		LocalTime localTime = LocalTime.now();
		System.out.println("从默认时区的系统时钟获得当前时间:" + localTime);
		//6.LocalDateTime的使用
		LocalDateTime localDateTime = LocalDateTime.now();
		System.out.println("从默认时区的系统时钟获得当前日期时间:" + localDateTime);
		LocalDateTime localDateTime1 = localDateTime.plusDays(1).plusHours(3).plusMinutes(30);
		System.out.println("从默认时区的系统时钟获得当前日期时间加上一天三小时三十分:" + localDateTime1);
		//7.Year、YearMonth、MonthDay的使用
		Year year = Year.now();
		YearMonth yearMonth = YearMonth.now();
		MonthDay monthDay = MonthDay.now();
		System.out.println("当前年份为:" + year);
		System.out.println("当前年月为:" + yearMonth);
		System.out.println("当前月日为:" + monthDay);
		//8.获取系统默认时区
		ZoneId zone = ZoneId.systemDefault();
		System.out.println("当前系统默认的时区为:"+zone);
		
	}
}

输出

获取UTC时区转换的当前时间:2024-10-17T11:57:03.619666800Z
获取UTC时区转换的毫秒数:1729166223652
一天等于24小时
一天等于1440分钟
一天等于86400000秒
获取UTC时区转换的当前时间:2024-10-17T11:57:03.669727500Z
获取当前时间一小时后的时间为:2024-10-17T12:57:03.669727500Z
获取当前时间一小时前的时间为:2024-10-17T10:57:03.669727500Z
从默认时区的系统时钟获得当前日期:2024-10-17
从默认时区的系统时钟获得当前时间:19:57:03.720347700
从默认时区的系统时钟获得当前日期时间:2024-10-17T19:57:03.723459200
从默认时区的系统时钟获得当前日期时间加上一天三小时三十分:2024-10-18T23:27:03.723459200
当前年份为:2024
当前年月为:2024-10
当前月日为:--10-17
当前系统默认的时区为:Asia/Hong_Kong

上述例子中演示了JDK8中日期时间类的一些用法。需要注意的是,通过clock.instant()Instant.now()获取的当前时间与本地系统显示时间有8小时的误差,这是因为Instant类表示的是UTC时间(世界协调时间,又称为世界标准时间),而本地系统显示的时间是CST时间,两者相差八个小时。

6. 格式化类

6.1 DateFormat类

使用Date类时,打印的Date对象所输出的当前时间都是默认的英文格式。如果想要将Date对象格式化为其他语言的格式,可以使用DateFormat类。DateFormat类提供了多种格式化方法,如format()方法,可以将Date对象格式化为指定格式的字符串。

表6-1 常用DateFormat类的方法

方法声明功能描述
static DateFormat getDateInstance()用于创建默认语言环境和格式化风格的日期格式器
static DateFormat getDateInstance(int style)用于创建默认语言环境的指定格式化风格的日期格式器
static DateFormat getDateTimeInstance()用于创建默认语言环境和格式化风格的日期时间格式器
static DateFormat getDateTimeInstance(int dateStyle,int timeStyle)用于创建默认语言环境的指定日期格式和时间格式的日期时间格式器
String format(Date date)将Date对象格式化为默认语言环境和格式化风格的字符串
Date parse(String source)将字符串解析为Date对象

表中列出了DateFormat类的四个静态方法,这四个静态方法能用于获取DateFormat类的实例对象,在DateFormat类中还定义了许多常量,其中有四个常量值时用作参数传递给方法的,包括FULL、LONG、MEDIUM、SHORT四个常量值,分别表示完整格式、长格式、普通格式、短格式的日期格式。

  • SHORT:完全为数字,如 12.5.10 或 5:30pm。
  • MEDIUM:较长,如 May 10,2016。
  • LONG:更长,如 May 12,2016 或 11:15:32am。
  • FULL:是完全指定,如 Tuesday、May 10、2012 AD 或 11:l5:42am CST。

接下来通过一个案例来演示DateFormat类的用法,如例:
例6-1:Demo22.java

import java.text.DateFormat;
import java.util.Date;

public class Demo22{
	public static void main(String[] args){
		//创建date对象
		Date date = new Date(); 
		//FULL格式的日期格式器对象
		DateFormat fullFormat = DateFormat.getDateInstance(DateFormat.FULL);
		//LONG格式的日期格式器对象
		DateFormat longFormat = DateFormat.getDateInstance(DateFormat.LONG);
		//MEDIUM格式的日期格式器对象
		DateFormat mediumFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.MEDIUM);
		//SHORT格式的日期格式器对象
		DateFormat shortFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.SHORT);
		//下面打印格式化后的日期或者日期/时间
		System.out.println("当前日期的完整格式为:" + fullFormat.format(date));
		System.out.println("当前日期的长格式为:" + longFormat.format(date));
		System.out.println("当前日期的普通格式为:" + mediumFormat.format(date));
		System.out.println("当前日期的短格式为:" + shortFormat.format(date));
	}
}

输出

当前日期的完整格式为:2024年10月17日星期四
当前日期的长格式为:2024年10月17日
当前日期的普通格式为:2024年10月17日 下午8:25:44
当前日期的短格式为:17/10/24 下午8:25

上述例子中创建了Date对象,并创建了四种DateFormat对象,其中调用getDateTimeInstance()方法创建的mediumFormat对象用于格式化日期和时间,调用getDateInstance()方法创建的shortFormat对象用于格式化日期。然后调用format()方法将Date对象格式化为指定格式的字符串,并打印输出。

DateFormat类中还提供了一个parse()方法,该方法可以将字符串解析为Date对象。但是字符串必要符合日期/时间的格式要求,否则会抛出异常。

接下来通过一个案例来演示parse()方法的用法,如例:
例6-2:Demo23.java

import java.text.DateFormat;
import java.util.Date;
import java.text.ParseException;

public class Demo23{
    public static void main(String[] args){
        //创建DateFormat对象
        DateFormat format = DateFormat.getDateInstance();
        //创建LONG格式的DateFormat对象
        DateFormat longFormat = DateFormat.getDateInstance(DateFormat.LONG);
        //定义日期格式的字符串
        String dateStr = "2024年10月17日";
        //解析日期字符串为Date对象
        try {
            Date date = longFormat.parse(dateStr);
            //打印解析后的Date对象
            System.out.println("解析后的Date对象:" + date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

输出

解析后的Date对象:Thu Oct 17 00:00:00 HKT 2024

上述例子中创建了DateFormat对象,并定义了日期格式的字符串。然后调用parse()方法将字符串解析为Date对象,并打印输出。如果字符串格式不正确,则会抛出ParseException异常。

6.2 SimpleDateFormat类

SimpleDateFormat类是DateFormat类的子类,它提供了更多的格式化选项,可以指定日期/时间的格式。可以使用new关键字创造实例对象。在创建实例对象时,它的构造方法需要接收一个表示日期格式模板的字符串参数。

接下来通过一个案例来演示SimpleDateFormat类的用法,如例:
例6-3:Demo24.java

import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo24{
    public static void main(String[] args){
        //创建SimpleDateFormat对象
        SimpleDateFormat format = new SimpleDateFormat("Gyyyy年MM月dd日:今天是yyyy年的第D天,E");
        //创建Date对象
        Date date = new Date();
        //格式化Date对象为字符串
        String str = format.format(date);
        //打印格式化后的字符串
        System.out.println("格式化后的字符串:" + str);
    }
}

输出

格式化后的字符串:公元2024年10月17日:今天是2024年的第291天,周四

上述例子中创建了SimpleDateFormat对象,并传入日期格式模板字符串。然后创建Date对象,并调用format()方法将Date对象格式化为字符串。最后打印输出。

SimpleDateFormat类提供了很多格式化选项,如:

  • yyyy:年份
  • MM:月份
  • dd:日期
  • HH:24小时制的小时
  • hh:12小时制的小时
  • mm:分钟
  • ss:秒
  • SSS:毫秒
  • E:星期几
  • D:一年中的第几天
  • F:一月中的第几周
  • w:一年中的第几周
  • W:一月中的第几周
  • a:AM/PM
  • K:一天中的第几刻钟
  • z:时区
  • Z:时区缩写
  • X:时区偏移量

上面通过SimpleDateFormat类将一个Date时间对象转换为了指定格式字符串形式,接下来通过一个案例演示SimpleDateFormat类将一个指定日期格式的字符串解析为Date对象,如例:
例6-4:Demo25.java

import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo25{
    public static void main(String[] args){
        //创建SimpleDateFormat对象
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //定义日期字符串
        String dateStr = "2024-10-17 19:57:03";
        //解析日期字符串为Date对象
        try {
            Date date = format.parse(dateStr);
            //打印解析后的Date对象
            System.out.println("解析后的Date对象:" + date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

输出

解析后的Date对象:Thu Oct 17 19:57:03 HKT 2024

上述例子中创建了SimpleDateFormat对象,并传入日期格式模板字符串。然后定义日期字符串,并调用parse()方法将字符串解析为Date对象。最后打印输出。

tipSimpleDateFormat的功能非常强大,在创建SimpleDateFormat对象时,只要传入合适的格式字符串参数,就能解析各种形式的日期字符串或者将Date日期格式化成任何形式的字符串。

6.3 DateTimeFormatter类

除了DateFormatSimpleDateFormat类,Java 8在java.time.format包中提供了DateTimeFormatter类,它是java.time包中的日期/时间格式化类。DateTimeFormatter类提供了更丰富的格式化选项,可以指定日期/时间的格式。不仅可以将日期、时间对象格式化成字符串,还能将特定格式的字符串解析成日期、时间对象。

要使用DateTimeFormatter进行格式化或者解析,就必须先获得DateTimeFormatter类的实例对象。获取DateTimeFormatter类的实例对象有三种方式:

  1. 使用静态常量创建DateTimeFormatter格式器。在DateTimeFormatter类中定义了大量的静态常量,如BASIC_ISO_DATEISO_DATE_TIMEISO_LOCAL_DATE_TIME等,可以直接使用。通过这些常量都可以获取DateTimeFormatter类的实例对象。
  2. 使用不同风格枚举值来创建DateTimeFormatter格式器。DateTimeFormatter类中定义了FormatStyle枚举类,它提供了多种风格的格式化选项,如FULLLONGMEDIUMSHORT等。通过枚举值来创建DateTimeFormatter类的实例对象。
  3. 根据模式字符串来创建DateTimeFormatter格式器。

了解了DateTimeFormatter的作用及其对象获取方式后,下面分别介绍如何使用DateTimeFormatter进行格式化和解析日期、时间。

6.3.1 完成日期、时间格式化

使用DateTimeFormatter将日期、时间格式化为字符串,可以通过以下两种方式:

  1. 调用DateTimeFormatter实例对象的format(TemporalAccessor temporal)方法进行格式化。其中参数temporal是一个TemporalAccessor类型接口,主要实现类有LocalDateLocalDateTime
  2. 调用LocalDateLocalDateTime等日期、时间对象的format(DateTimeFormatter formatter)方法进行格式化。

接下来通过一个案例来演示DateTimeFormatter类的用法,如例:
例6-5:Demo26.java

import java.time.*;
import java.time.format.*;

public class Demo26{
	public static void main(String[] args){
		LocalDateTime date =LocalDateTime.now();
		//1. 使用常量创建DateTimeFormatter对象
		System.out.println("使用常量创建DateTimeFormatter对象:");
		DateTimeFormatter dtf1 = DateTimeFormatter.ISO_DATE_TIME;
		System.out.println(dtf1.format(date));
		
		//2.使用MEDIUM类型风格的DateTimeFormatter对象
		System.out.println("使用MEDIUM类型风格的DateTimeFormatter对象:");
		DateTimeFormatter dtf2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
		System.out.println(dtf2.format(date));
		
		//3.根据模式字符串来创建DateTimeFormatter对象
		System.out.println("根据模式字符串来创建DateTimeFormatter对象:");
		DateTimeFormatter dtf3 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
		System.out.println(dtf3.format(date));
	}
}

输出

使用常量创建DateTimeFormatter对象:
2024-10-17T21:22:56.9333734
使用MEDIUM类型风格的DateTimeFormatter对象:
2024年10月17日 下午9:22:56
根据模式字符串来创建DateTimeFormatter对象:
2024-10-17 21:22:56

上述例子中,首先获取当前日期时间,然后使用三种方式创建DateTimeFormatter对象,并调用format()方法将日期时间格式化为字符串。

6.3.2 解析字符串

要使用DateTimeFormatter将指定格式的字符串解析成日期、事件对象,可以通过日期、事件对象所提供的parse(CharSequence text,DateTimeFormatter formatter)方法来实现。

接下来通过一个案例来演示DateTimeFormatter类的用法,如例:
例6-6:Demo27.java

import java.time.*;
import java.time.format.*;

public class Demo27{
    public static void main(String[] args){
		//定义两种日期格式的字符串
		String str1 = "2024-10-17 21:37:27";
		String str2 = "2024年10月17日21时30分30秒";
		//定义解析所用的格式器
		DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
		DateTimeFormatter format1 = DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒");
		//使用LocalDateTime的parse()方法进行解析
		LocalDateTime ldt1 = LocalDateTime.parse(str1,format);
		LocalDateTime ldt2 = LocalDateTime.parse(str2,format1);
		
		//输出结果
		System.out.println(ldt1);
		System.out.println(ldt2);
		
	}
}

输出

2024-10-17T21:37:27
2024-10-17T21:30:30

上述例子中,首先定义了两种日期格式的字符串,然后定义解析所用的格式器。然后使用LocalDateTimeparse()方法将字符串解析为LocalDateTime对象。最后输出结果。

tip:本文主要讲解了Java中常用一些类的使用。首先介绍了处理字符串的String类和StringBuffer类的使用,然后是与系统相关的System类和Runtime类的使用,以及Math类、Random类和Java中包装类的相关知识。最后介绍了Java中日期、时间类以及格式化器类的使用。最后,没必要把所有类和方法全部学习,现在是仅作学习了解,只需要记住最常用的方法即可,遇到相关需求再去查阅相关API文档就能掌握这些类和方法的使用。