在操作String类之前,首先需要对String类进行初始化。在Java中,可以通过以下两种方式对String类进行初始化,具体如下:
1.使用字符串常量直接初始化一个String对象,其语法格式如下:
String 变量名= 字符串;
在初始化字符串对象时,既可以将字符串对象的初始化值设为空,也可以初始化为一个具体的字符串,其示例如下:
String str1 = null; // 初始化为空
String str2 = ""; // 初始化为空字符串
String str3 = "abc"; // 初始化为abc,其中abc为字符串常量
2.使用String的构造方法初始化字符串对象,其语法格式如下:
String 变量名 = new String(字符串);
在上述语法中,字符串同样可以为空或是一个具体的字符串。当为具体字符串时,会使用String类的不同参数类型的构造方法来初始化字符串对象。
String类中包含多个构造方法,常用的构造方法如表1所示。
表1 String类的常用构造方法
方法声明 | 功能描述 |
---|---|
String() | 创建一个内容为空的字符串 |
String(String value) | 根据指定的字符串内容创建对象 |
String(char[] value) | 根据指定的字符数组创建对象 |
表1中,列出了String类的三种构造方法,通过调用不同参数的构造方法便可完成String类的初始化。接下来通过一个案例来学习String类是如何通过构造方法来初始化字符串对象的,如文件1所示。
文件1 Example01.java
1 public class Example01 {
2 public static void main(String[] args) {
3 // 创建一个空的字符串
4 String str1 = new String();
5 // 创建一个内容为abc的字符串
6 String str2 = new String("abc");
7 // 创建一个内容为字符数组的字符串
8 char[] charArray = new char[] { 'A', 'B', 'C' };
9 String str3 = new String(charArray);
10 // 输出结果
11 System.out.println("a" + str1 + "b");
12 System.out.println(str2);
13 System.out.println(str3);
14 }
15 }
运行结果如图1所示。
图1 运行结果
文件1中,分别使用表1中的3个构造方法创建了字符串对象。其中第4行代码使用无参构造方法创建的是一个空字符串,所以第一个输出语句中的str1为空(””),当使用连字符(+)连接a和b后,输出的结果为ab。第6行代码使用参数类型为String的构造方法创建了一个内容为abc的字符串,第8~9行代码使用参数类型为字符数组的构造方法创建了一个内容为字符数组的字符串。从图1可以看出,它们最后的输出结果就是存储在字符串对象中的内容。
小提示:
连接字符串可以通过运算符“+”来实现,例如上面案例代码("a" + str1 + "b")中的“+”的作用就是将两个字符串合并到一起并生成新的字符串。在Java中,如果“+”的两边操作数有一个为String类型,那么“+”就表示字符串连接运算符。
String类在实际开发中的应用非常广泛,因此灵活地使用String类是非常重要的,接下来讲解一下String类常用的一些方法,如表1所示。
表1 String类的常用方法
方法声明 | 功能描述 |
---|---|
int indexOf(int ch) | 返回指定字符在此字符串中第一次出现处的索引 |
int lastIndexOf(int ch) | 返回指定字符在此字符串中最后一次出现处的索引 |
int indexOf(String str) | 返回指定子字符串在此字符串中第一次出现处的索引 |
int lastIndexOf(String str) | 返回指定子字符串在此字符串中最后一次出现处的索引 |
char charAt(int index) | 返回字符串中index位置上的字符,其中index的取值范围是:0~(字符串长度-1) |
boolean endsWith(String suffix) | 判断此字符串是否以指定的字符串结尾 |
int length() | 返回此字符串的长度 |
boolean equals(Object anObject) | 将此字符串与指定的字符串比较 |
boolean isEmpty() | 当且仅当字符串长度为0时返回true |
boolean startsWith(String prefix) | 判断此字符串是否以指定的字符串开始 |
boolean contains(CharSequence cs) | 判断此字符串中是否包含指定的字符序列 |
String toLowerCase() | 使用默认语言环境的规则将String中的所有字符都转换为小写 |
String toUpperCase() | 使用默认语言环境的规则将String中的所有字符都转换为大写 |
static String valueOf(int i) | 返回 int 参数的字符串表示形式 |
char[] toCharArray() | 将此字符串转换为一个字符数组 |
String replace(CharSequence oldstr, CharSequence newstr) | 返回一个新的字符串,它是通过用newstr替换此字符串中出现的所有oldstr得到的 |
String[] split(String regex) | 根据参数regex(regex是一个正则表达式,用来限定分隔规则)将字符串分割为若干个子字符串 |
String substring(int beginIndex) | 返回一个新字符串,它包含从指定的beginIndex起始角标处开始,直到此字符串末尾的所有字符 |
String substring(int beginIndex, int endIndex) | 返回一个新字符串,它包含从指定的beginIndex起始角标处开始,直到索引endIndex-1角标处的所有字符 |
String trim() | 返回一个新字符串,它去除了原字符串首尾的空格 |
在表1中,列出了String类常用的方法,为了让读者更加熟悉这些方法的作用,接下来通过几个案例来具体学习一下String类中常用方法的使用。
1.字符串的基本操作
在程序中,需要对字符串进行一些基本操作,如获得字符串长度、获得指定位置的字符等。String类针对每一个操作都提供了对应的方法,接下来通过一个案例来学习这些方法的使用,如文件1所示。
文件1 Example02.java
1 public class Example02 {
2 public static void main(String[] args) {
3 String s = "abcabcbacdba"; // 初始化字符串
4 System.out.println("字符串的长度为:" + s.length());
5 System.out.println("字符串中第一个字符:" + s.charAt(0));
6 System.out.println("字符c第一次出现的位置:" + s.indexOf('c'));
7 System.out.println("字符c最后一次出现的位置:" + s.lastIndexOf('c'));
8 System.out.println("子字符串第一次出现的位置:" + s.indexOf("ab"));
9 System.out.println("子字符串最后一次出现的位置:"
10 + s.lastIndexOf("ab"));
11 }
12 }
运行结果如图1所示。
图1 运行结果
从图1可以看出,String类提供的方法可以很方便地获取字符串的长度,获取指定位置的字符以及指定字符和字符串的位置。
2.字符串的转换操作
程序开发中,经常需要对字符串进行转换操作,例如将字符串转换成字符数组、将字符串中的字符进行大小写转换等。接下来通过一个案例来演示字符串的转换操作,如文件2所示。
文件2 Example03.java
1 public class Example03 {
2 public static void main(String[] args) {
3 String str = "java";
4 char[] charArray = str.toCharArray(); // 字符串转换为字符数组
5 System.out.print("将字符串转为字符数组后的遍历结果:");
6 for (int i = 0; i < charArray.length; i++) {
7 if (i != charArray.length - 1) {
8 // 如果不是数组的最后一个元素,在元素后面加逗号
9 System.out.print(charArray[i] + ",");
10 } else {
11 // 数组的最后一个元素后面不加逗号
12 System.out.println(charArray[i]);
13 }
14 }
15 System.out.println("将int值转换为String类型之后的结果:"
16 + String.valueOf(12));
17 System.out.println("将字符串转换成大写之后的结果:"
18 + str.toUpperCase());
19 }
20 }
运行结果如图2所示。
图2 运行结果
在文件2中,使用String类的toCharArray()方法将一个字符串转为一个字符数组,静态方法valueOf()将一个int类型的整数转为字符串,toUpperCase()方法将字符串中的字符都转为大写。其中valueOf()方法有很多重载的形式,float、double、char等其他基本类型的数据都可以通过该方法转为String字符串类型。
3.字符串的替换和去除空格操作
在开发程序的过程中,需要考虑到用户输入数据时会有一些错误和空格的情况,这时可以使用String类的replace()和trim()方法,进行字符串的替换和去除空格操作。接下来通过一个案例来学习这两个方法的使用,如文件3所示。
文件3 Example04.java
1 public class Example04 {
2 public static void main(String[] args) {
3 String s = " http :// localhost : 8080 ";
4 // 字符串去除空格操作
5 System.out.println("去除字符串两端空格后的结果:" + s.trim());
6 // 字符串替换操作
7 System.out.println("去除字符串中所有空格后的结果:"
8 + s.replace(" ", ""));
9 }
10 }
运行结果如图3所示。
图3 运行结果
在文件3中,调用了String类的两个方法,其中trim()方法用于去除字符串中首尾的空格,replace()方法用于将字符串中所有与指定字符串匹配的子串替换成另一个字符串。
需要注意的是,trim()方法只能去除两端的空格,不能去除中间的空格。若想去除字符串中间的空格,则可以通过String类的replace()方法来实现。
4.字符串的判断操作
操作字符串时,经常需要对字符串进行一些判断,如判断字符串是否以指定的字符串开始、结束,是否包含指定的字符串,字符串是否为空等。在String类中针对字符串的判断操作提供了很多方法,接下来通过一个案例来学习这些判断方法的使用,如文件4所示。
文件4 Example05.java
1 public class Example05 {
2 public static void main(String[] args) {
3 String s1 = " Starter"; // 声明一个字符串
4 String s2 = "St";
5 System.out.println("判断是否以字符串St开头:" + s1.startsWith("St"));
6 System.out.println("判断是否以字符串er结尾:" + s1.endsWith("er"));
7 System.out.println("判断是否包含字符串ar:" + s1.contains("ar"));
8 System.out.println("判断字符串是否为空:" + s1.isEmpty());
9 System.out.println("判断两个字符串是否相等" + s1.equals(s2));
10 }
11 }
运行结果如图4所示。
图4 运行结果
在文件4中涉及到的方法都是用于判断字符串的,并且返回值均为boolean类型。在所使用的方法中,equals()方法是比较重要的,在String类中重写了父类Object中的equals()方法。
在程序中可以通过“==”和equals()两种方式对字符串进行比较,但这两种方式有显著的区别。equals()方法用于比较两个字符串中的字符值是否相等,“==”方式用于比较两个字符串对象的内存地址是否相同。对于两个字符串对象,当它们的字符值完全相同时,使用equals判断结果会为true,但使用“==”判断时,结果一定为false。为了便于理解,下面给出示例代码:
String str1 = new String("abc");
String str2 = new String("abc");
// 结果为false,因为str1和str2是两个对象
System.out.println(str1 == str2);
// 结果为true,因为str1和str2字符内容相同
System.out.println(str1.equals(str2));
5.字符串的截取和分割
在String类中针对字符串的截取和分割操作提供了两个方法,其中,substring()方法用于截取字符串的一部分,split()方法可以将字符串按照某个字符进行分割。接下来通过一个案例来学习这两个方法的使用,如文件5所示。
文件5 Example06.java
1 public class Example06 {
2 public static void main(String[] args) {
3 String str = "2018-01-24";
4 // 下面是字符串截取操作
5 System.out.println("从第6个字符截取到末尾的结果:"
6 + str.substring(5));
7 System.out.println("从第6个字符截取到第7个字符的结果:"
8 +str.substring(5, 7));
9 // 下面是字符串分割操作
10 System.out.print("分割后的字符串数组中的元素依次为:");
11 // 通过横线连接符“—”将字符串转换为字符串数组
12 String[] strArray = str.split("-");
13 // 循环输出数组中的元素
14 for (int i = 0; i < strArray.length; i++) {
15 if (i != strArray.length - 1) {
16 // 如果不是数组的最后一个元素,在元素后面加顿号
17 System.out.print(strArray[i] + "、");
18 } else {
19 // 数组的最后一个元素后面不加顿号
20 System.out.println(strArray[i]);
21 }
22 }
23 }
24 }
运行结果如图5所示。
图5 运行结果
在文件5中,调用了String类中重载的两个substring()方法,在第6行代码调用substring(5)方法时,因为字符串中的字符索引是从0开始的,所以会截取字符串中第6个字符以及之后的所有字符;第8行代码调用substring(5,7)方法时,会截取第6个和第7个字符。文件中的第12~22行代码演示了split()方法的用法,该方法会根据指定的符号“-”将字符串分割成了三部分,并存放到一个String类型的数组当中。使用for循环遍历数组即可按照要求输出所需内容,这里将各个日期之间使用顿号分隔。
String字符串在获取某个字符时,会用到字符的索引,当访问字符串中的字符时,如果字符的索引不存在,则会发生StringIndexOutOfBoundsException(字符串角标越界异常)。
接下来通过一个案例来演示这种异常,如文件6所示。
文件6 Example07.java
1 public class Example07 {
2 public static void main(String[] args) {
3 String s = "abcde";
4 System.out.println(s.charAt(10));
5 }
6 }
运行结果如图6所示。
图6 运行结果
从图6可以看出,访问字符串中的字符时,不能超出字符的索引范围,否则会出现异常,这与数组中的角标越界异常相似。
在Java中,由于String类是final类型的,所以使用String定义的字符串是一个常量,因此它一旦创建,其内容和长度是不可改变的。如果需要对一个字符串进行修改,则只能创建新的字符串。为了便于对字符串进行修改,在JDK中提供了一个StringBuffer类(也称字符串缓冲区)来操作字符串。StringBuffer类和String类最大的区别在于它的内容和长度都是可以改变的。StringBuffer类似一个字符容器,当在其中添加或删除字符时,所操作的都是这个字符容器,因此并不会产生新的StringBuffer对象。
针对添加和删除字符的操作,StringBuffer类提供了一系列的方法,如表1所示。
表1 StringBuffer类常用方法
方法声明 | 功能描述 |
---|---|
StringBuffer append(char c) | 添加字符到StringBuffer对象中末尾 |
StringBuffer insert(int offset,String str) | 在StringBuffer对象中的offset位置插入字符串str |
StringBuffer deleteCharAt(int index) | 移除StringBuffer对象中指定位置的字符 |
StringBuffer delete(int start,int end) | 删除StringBuffer对象中指定范围的字符或字符串 |
StringBuffer replace(int start,int end,String s) | 将StringBuffer对象中指定范围的字符或字符串用新的字符串s进行替换 |
void setCharAt(int index, char ch) | 修改指定位置index处的字符 |
String toString() | 返回StringBuffer缓冲区中的字符串对象 |
StringBuffer reverse() | 将此StringBuffer对象用其反转形式取代 |
在表1中,列出了StringBuffer类的一系列常用方法,对于初学者来说比较难以理解。接下来通过一个案例来学习一下表中方法的具体使用,如文件1所示。
文件1 Example08.java
1 public class Example08 {
2 public static void main(String[] args) {
3 System.out.println("1、添加------------------------");
4 add();
5 System.out.println("2、修改------------------------");
6 update();
7 System.out.println("3、删除------------------------");
8 delete();
9 }
10 // 添加
11 public static void add() {
12 StringBuffer sb = new StringBuffer(); // 定义一个字符串缓冲区
13 sb.append("ABC"); // 添加字符串
14 System.out.println("append添加结果:" + sb);
15 sb.insert(3, "DE"); // 在指定位置插入字符串
16 System.out.println("insert添加结果:" + sb);
17 }
18 // 修改
19 public static void update() {
20 StringBuffer sb = new StringBuffer("ABAAA");
21 sb.setCharAt(2, 'C'); // 修改指定位置字符
22 System.out.println("修改指定位置字符结果:" + sb);
23 sb.replace(3, 5, "DE"); // 替换指定位置字符串或字符
24 System.out.println("替换指定位置字符(串)结果:" + sb);
25 System.out.println("字符串翻转结果:" + sb.reverse());
26 }
27 // 删除
28 public static void delete() {
29 StringBuffer sb = new StringBuffer("ABCDEFG");
30 sb.delete(3, 7); // 指定范围删除
31 System.out.println("删除指定位置结果:" + sb);
32 sb.deleteCharAt(2); // 指定位置删除
33 System.out.println("删除指定位置结果:" + sb);
34 sb.delete(0, sb.length()); // 清空缓冲区
35 System.out.println("清空缓冲区结果:" + sb);
36 }
37 }
运行结果如图1所示。
图1 运行结果
在文件1中涉及到StringBuffer类的很多方法,其中append()和insert()方法是最常用的,并且这两个方法有很多重载形式,它们都用于添加字符。不同的是,append()方法始终将这些字符添加到缓冲区的末尾,而insert()方法则可以在指定的位置添加字符。另外, StringBuffer对象的delete()方法用于删除指定位置的字符,包含起始索引,不包含结束索引,setCharAt()和replace()方法用于替换指定位置的字符。
在文件1中涉及到StringBuffer类的很多方法,其中append()和insert()方法是最常用的,并且这两个方法有很多重载形式,它们都用于添加字符。不同的是,append()方法始终将这些字符添加到缓冲区的末尾,而insert()方法则可以在指定的位置添加字符。另外, StringBuffer对象的delete()方法用于删除指定位置的字符,包含起始索引,不包含结束索引,setCharAt()和replace()方法用于替换指定位置的字符。
StringBuffer类和String类有很多相似之处,初学者在使用时很容易混淆。接下来针对这两个类进行对比,简单归纳一下两者的不同,具体如下:
①String类定义的字符串是常量,一旦创建后,内容和长度都是无法改变的。StringBuffer表示字符容器,其内容和长度可以随时修改。在操作字符串时,如果该字符串仅用于表示数据类型,则使用String类即可,但是如果需要对字符串中的字符进行增删操作,则使用StringBuffer类。
②String类重写了Object类的equals()方法,而StringBuffer类没有重写Object类的equals()方法。具体示例如下:
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1.equals(s2)); // 打印结果为true
StringBuffer sb1 = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");
System.out.println(sb1.equals(sb2)); // 打印结果为false
③String类对象可以用操作符“+”进行连接,而StringBuffer对象之间不能,具体示例如下:
String s1 = "a";
String s2 = "b";
String s3 = s1+s2; // 合法
System.out.println(s3); // 打印输出 ab
StringBuffer sb1 = new StringBuffer("a");
StringBuffer sb2 = new StringBuffer("b");
StringBuffer sb3 = sb1 + sb2; // 编译出错
除了使用StringBuffer外,JDK 1.5之后提供了一个StringBuilder类同样可以操作字符串。StringBuilder与StringBuffer的功能相似,且两个类中所提供的方法也基本相同。二者所不同的是StringBuffer是线程安全的,而StringBuilder没有实现线程安全功能,所以性能略高。通常情况下,如果创建一个内容可变的字符串对象,应该优先考虑StringBuilder类。
StringBuilder类同样提供了一系列的追加(append)、插入(insert)、替换(replace)和删除(delete)的方法。如果将文件1中的StringBuffer换成StringBuilder,程序同样可以正确执行。
System类对于读者来说并不陌生,因为在之前所学知识中,需要打印结果时,使用的都是“System.out.println();”语句,这句代码中就使用了System类。System类定义了一些与系统相关的属性和方法,它所提供的属性和方法都是静态的,因此,想要引用这些属性和方法,直接使用System类调用即可。
System类的常用方法,如表1所示。
表1 System类的常用方法
方法声明 | 功能描述 |
---|---|
static void exit(int status) | 该方法用于终止当前正在运行的Java虚拟机,其中参数status表示状态码,若状态码非0 ,则表示异常终止 |
static 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) | 获取指定键描述的系统属性 |
在表1中,列出了System类的常用方法,接下来通过一些案例针对表中的方法进行逐一讲解。
1.getProperties()方法
System类的getProperties()方法用于获取当前系统的全部属性,该方法会返回一个Properties对象,其中封装了系统的所有属性,这些属性是以键值对形式存在的。接下来通过一个案例来演示getProperties()方法的使用,如文件1所示。
文件1 Example09.java
1 import java.util.*;
2 public class Example09 {
3 public static void main(String[] args) {
4 // 获取当前系统属性
5 Properties properties = System.getProperties();
6 System.out.println(properties);
7 // 获取所有系统属性的key(属性名),返回Set对象
8 Set propertyNames = properties.stringPropertyNames();
9 for (String key : propertyNames ) {
10 //获取当前键key(属性名)所对应的值(属性值)
11 String value = System.getProperty(key);
12 System.out.println(key +"--->"+ value);
13 }
14 }
15 }
运行结果如图1所示。
图1 运行结果
文件1实现了获取当前系统属性的功能。首先通过System的getProperties()方法获取封装了系统属性的Properties集合,然后对Properties集合进行迭代,将所有系统属性的键以及对应的值打印出来。关于集合将在下一章中进行讲解,在这里读者只需知道通过System.getProperties()方法可以获得系统属性即可。从图1中可以看出,这些系统属性包括虚拟机版本号、用户的国家、操作系统的版本等。
2.currentTimeMillis()
currentTimeMillis()方法返回一个long类型的值,该值表示当前时间与1970年1月1日0点0分0秒之间的时间差,单位是毫秒,通常也将该值称作时间戳。为了便于读者理解该方法的使用,接下来通过一个案例来计算程序在进行求和操作时所消耗的时间,如文件2所示。
文件2 Example10.java
1 public class Example10 {
2 public static void main(String[] args) {
3 long startTime = System.currentTimeMillis();// 循环开始时的当前时间
4 int sum = 0;
5 for (int i = 0; i < 100000000; i++) {
6 sum += i;
7 }
8 long endTime = System.currentTimeMillis();// 循环结束后的当前时间
9 System.out.println("程序运行时间为:"+(endTime - startTime)+ "毫秒");
10 }
11 }
运行结果如图2所示。
图2 运行结果
文件2中,演示了数字的求和操作,程序在求和开始和结束时,分别调用了currentTimeMillis()方法获得了两个时间戳,两个时间戳之间的差值便是求和操作耗费的时间。需要注意的是,由于处理器性能等原因,程序运行的时间也会有所不同。
3.arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
arraycopy()方法用于将一个数组中的元素快速拷贝到另一个数组,其中参数列表中的参数具体说明如下:
● src:表示源数组;
● dest:表示目标数组;
● srcPos:表示源数组中拷贝元素的起始位置;
● destPos:表示拷贝到目标数组的起始位置;
● length:表示拷贝元素的个数。
在进行数组复制时,目标数组必须有足够的空间来存放拷贝的元素,否则会发生角标越界异常。接下来通过一个案例来演示数组元素的拷贝,如文件3所示。
文件3 Example11.java
1 public class Example11 {
2 public static void main(String[] args) {
3 int[] srcArray = { 101, 102, 103, 104, 105, 106 }; // 源数组
4 int[] destArray = { 201, 202, 203, 204, 205}; // 目标数组
5 System.arraycopy(srcArray, 2, destArray, 0, 4); // 拷贝数组元素
6 // 打印目标数组中的元素
7 for (int i = 0; i < destArray.length; i++) {
8 System.out.println(i + ": " + destArray[i]);
9 }
10 }
11 }
运行结果如图3所示。
图3 运行结果
文件3中,创建了两个数组srcArray和destArray,分别代表源数组和目标数组,当调用arraycopy()方法进行元素拷贝时,由于指定了从源数组中索引为2的元素开始拷贝,并且拷贝4个元素存放在目标数组中索引为0的位置,因此,在打印目标数组的元素时,程序首先打印输出的是从源数组srcArray中拷贝的的后四个元素,然后打印输出的destArray中的最后一个元素。
需要注意的是,使用arraycopy()方法进行数组元素拷贝时,一定要保证源数组元素类型和目标数组元素类型一样,否则程序运行会出现ArrayStoreException异常。另外,使用arraycopy()方法时,最后一个复制的元素长度参数length既不能超过截取的源数组从指定位置srcPos开始剩余元素个数,也不能超过目标数组从指定位置destPos开始可容纳的元素个数,否则程序运行会出现ArrayIndexOutOfBoundsException异常。
小提示:
除了以上案例涉及到的方法外,System类还有两个常见的方法,分别是gc()和exit(int status)方法。其中gc()方法用来启动Java的垃圾回收器,并且对内存中的垃圾对象进行回收。exit(int status)方法用来终止当前正在运行的Java虚拟机,其中的参数status用于表示当前发生的异常状态,通常指定为0,表示正常退出,否则表示异常终止。
Runtime类用于表示Java虚拟机运行时的状态,它用于封装Java虚拟机进程。每次使用“java”命令启动Java虚拟机时都会对应一个Runtime实例,并且只有一个实例,应用程序会通过该实例与其运行时的环境相连。应用程序不能创建自己的Runtime实例,若想在程序中获得一个Runtime实例,可以通过getRuntime()方法获取与之相关的Runtime对象,具体方式如下:
Runtime run = Runtime.getRuntime();
由于Runtime类封装了Java虚拟机进程,因此,可以通过该类的实例对象来获取当前虚拟机的相关信息。接下来通过一个案例来演示Runtime类的使用,如文件1所示。
文件1 Example12.java
1 public class Example12 {
2 public static void main(String[] args) {
3 Runtime rt = Runtime.getRuntime(); // 获取Java程序关联的运行时对象
4 System.out.println("处理器的个数: "
5 + rt.availableProcessors() + "个");
6 System.out.println("空闲内存大小: "
7 + rt.freeMemory() / 1024 / 1024 + "M");
8 System.out.println("最大可用内存大小: "
9 + rt.maxMemory() / 1024 / 1024 + "M");
10 }
11 }
运行结果如图1所示。
图1 运行结果
文件1中,通过“Runtime.getRuntime();”方法创建了一个Runtime的实例对象,并分别调用该对象的availableProcessors()方法、freeMemory()方法和maxMemory()方法,将当前虚拟机的处理器个数、空闲内存大小和可用最大内存大小的信息打印出来。
需要注意的是,由于每台计算机的配置和性能不同,该文件的打印结果也会有所不同。另外,空闲内存大小和可用最大内存大小都是以字节为单位计算的,文件1中程序的运行结果已经换算成了以兆(M)为单位的值。
Runtime类中提供了一个exec()方法,该方法用于执行一个DOS命令,从而实现和在命令行窗口中输入DOS命令同样的效果。例如,可以通过运行“notepad.exe”命令打开一个Windows自带的记事本,程序代码如文件2所示。
文件2 Example13.java
1 import java.io.IOException;
2 public class Example13 {
3 public static void main(String[] args) throws IOException {
4 Runtime rt = Runtime.getRuntime(); // 创建Runtime实例对象
5 rt.exec("notepad.exe"); // 调用exec()方法
6 }
7 }
文件2中,调用了Runtime对象的exec()方法,并将系统命令“notepad.exe”作为参数传递给方法。运行程序后会在桌面上打开一个记事本,如图2所示。
图2 记事本
此时,会在Windows系统中产生一个新的进程notepad.exe,可以通过任务管理器进行观察,如图3所示。
图3 任务管理器
Runtime类的exec()方法会返回一个Process对象,该对象表示操作系统的一个进程,此处为notepad.exe进程,通过Process对象可以对产生的新进程进行管理,如关闭此进程只需调用destroy()方法即可。
接下来通过一个案例来实现打开的记事本并在3秒后自动关闭的功能,如文件3所示。
文件3 Example14.java
1 public class Example14 {
1 public static void main(String[] args) throws Exception {
2 // 创建一个Runtime实例对象
3 Runtime rt = Runtime.getRuntime();
4 // 得到表示进程的Process对象
5 Process process = rt.exec("notepad.exe");
6 // 程序休眠3秒
7 Thread.sleep(3000);
8 // 关闭进程
9 process.destroy();
10 }
11 }
在文件3中,通过调用Process对象的destroy()方法,将打开的记事本关闭了。为了突出演示的效果,使用了Thread类的静态方法sleep(long millis)使程序休眠了3秒,因此,程序运行后,会看到打开的记事本在3秒后自动关闭。
Math类是一个工具类,主要用于完成复杂的数学运算,如求绝对值、三角函数、指数运算等。由于其构造方法被定义成private,因此无法创建Math类的对象。Math类中的所有方法都是静态方法,可以直接通过类名来调用它们。除静态方法外,Math类中还有两个静态常量PI和E,分别代表数学中的π和e。
由于Math类比较简单,因此初学者可以通过查看API文档来学习Math类的具体用法。接下来通过一个案例对Math类中比较常用的方法进行演示,如文件1所示。
文件1 Example15.java
1 public class Example15 {
2 public static void main(String[] args) {
3 System.out.println("计算绝对值的结果: " + Math.abs(-1));
4 System.out.println("计算正弦的结果: " + Math.sin(1.57));
5 System.out.println("计算余弦的结果: " + Math.cos(2.0));
6 System.out.println("计算正切的结果: " + Math.tan(0.8));
7 System.out.println("计算平方根的结果: " + Math.sqrt(4));
8 System.out.println("计算立方根的结果: " + Math.cbrt(9));
9 System.out.println("计算乘方的结果: " + Math.pow(2,2));
10 System.out.println("求大于参数的最小整数: " + Math.ceil(4.6));
11 System.out.println("求小于参数的最大整数: " + Math.floor(-5.2));
12 System.out.println("对小数进行四舍五入后的结果: " + Math.round(-8.6));
13 System.out.println("求两个数的较大值: " + Math.max(5.1, 5.5));
14 System.out.println("求两个数的较小值: " + Math.min(5.1, 5.5));
15 System.out.println("生成一个大于等于0小于1的随机值: "+
16 Math.random());
17 }
18 }
运行结果如图1所示。
图1 运行结果
在文件1中,对Math类的常用方法进行了演示。从图1的运行结果中可以看出每个方法的作用。
需要注意的是,round()方法用于对某个小数进行四舍五入,此方法会将小数点后面的数字全部忽略,返回一个int类型的数,而ceil()方法和floor()方法返回的都是double类型的数,这个数在数值上等于一个整数。
在JDK的java.util包中,有一个Random类,它可以在指定的取值范围内随机产生数字。在Random类中提供了两个构造方法,如表1所示。
表1 Random的构造方法
方法声明 | 功能描述 |
---|---|
Random() | 构造方法,用于创建一个随机数生成器,每次实例化Random对象会生成不同的随机数 |
Random(long seed) | 构造方法,使用一个long型的seed(种子)创建伪随机数生成器,当seed相同时,每次实例化Random对象会生成相同的随机数 |
表1中列举了Random类的两个构造方法,其中第一个构造方法是无参的,通过它创建的Random实例对象每次使用的种子是随机的,因此每个对象所产生的随机数不同。如果希望创建的多个Random实例对象产生相同序列的随机数,则可以在创建对象时调用第二个构造方法,传入相同的种子即可。
相对于Math的random()方法而言,Random类提供了更多的方法来生成各种伪随机数。它不仅可以生成整数类型的随机数,还可以生成浮点类型的随机数,Random类中的常用方法,如表2所示。
表2 Random类的常用方法
方法声明 | 功能描述 |
---|---|
boolean nextBoolean() | 随机生成boolean类型的随机数 |
double nextDouble() | 随机生成double类型的随机数 |
float nextFloat() | 随机生成float类型的随机数 |
int nextInt() | 随机生成int类型的随机数 |
int nextInt(int n) | 随机生成[0,n)之间int类型的随机数 |
long nextLong() | 随机生成long类型的随机数 |
表2中,列出了Random类常用的方法,其中,Random类的nextDouble()方法返回的是0.0和1.0之间double类型的值,nextFloat()方法返回的是0.0和1.0之间float类型的值,nextInt(int n)返回的是0(包括)和指定值n(不包括)之间的值。接下来通过一个案例来学习这些方法的使用,如文件1所示。
文件1 Example16.java
1 import java.util.Random;
2 public class Example16 {
3 public static void main(String[] args) {
4 Random r = new Random();
5 System.out.println("生成boolean类型的随机数:"
6 + r.nextBoolean());
7 System.out.println("生成double类型的随机数:"
8 + r.nextDouble());
9 System.out.println("生成float类型的随机数:"
10 + r.nextFloat());
11 System.out.println("生成int类型的随机数:"
12 + r.nextInt());
13 System.out.println("生成0到10之间int类型的随机数:"
14 + r.nextInt(10));
15 System.out.println("生成long类型的随机数:"
16 + r.nextLong());
17 }
18 }
运行结果如图1所示。
图1 运行结果
从图1可以看出,通过调用Random类不同的方法分别产生了不同类型的随机数。
虽然Java是面向对象的编程语言,但它所包含的8种基本数据类型却不支持面向对象的编程机制(没有属性和方法)。Java之所以提供这8种基本数据类型,是为了方便常规数据的处理。在Java中,很多类的方法都需要接收引用类型的对象,此时就无法将一个基本数据类型的值传入。为了解决这样的问题,JDK中提供了一系列的包装类,通过这些包装类可以将基本数据类型的值包装为引用数据类型的对象。
在Java中,每种基本类型都有对应的包装类,具体如表1所示。
表1 基本类型对应的包装类
基本数据类型 | 对应的包装类 |
---|---|
byte | Byte |
char | Character |
int | Integer |
short | Short |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
在表1中,列举了8种基本数据类型及其对应的包装类。除了Integer和Character类外,其他对应的包装类的名称都与其基本数据类型一样,只不过首字母需要大写。
包装类和基本数据类型在进行转换时,引入了自动装箱(Autoboxing)和自动拆箱(AutoUnboxing)的概念,其中自动装箱是指将基本数据类型的变量赋给对应的包装类变量;反之,拆箱是指将包装类对象类型直接赋给一个对应的基本数据类型变量。
接下来以int基本类型与对应的包装类Integer为例,学习一下装箱拆箱的过程,如文件1所示。
文件1 Example17.java
1 public class Example17 {
2 public static void main(String args[]) {
3 // 定义一个基本类型的变量a,并赋值为20
4 int a = 20;
5 // 自动装箱:将基本类型的变量a赋给Integer类型的变量b
6 Integer b = a;
7 System.out.println(b);
8 // 自动拆箱:将Integer类型的变量b赋给基本类型的变量a
9 int c = b;
10 System.out.println(c);
11 }
12 }
运行结果如图1所示。
图1 运行结果
文件1中,通过包装类和自动拆箱、装箱功能,开发人员可以把基本数据类型的变量转换成对象来使用,也可以把包装类的实例转换成基本类型的变量来使用。
Java中,除了支持基本数据类型与对应包装类之间进行转换外,还提供了其他方法来支持基本数据类型、基本数据包装类以及字符串之间的相互转换。具体如下:
(1)通过引用数据类型字符串String类的valueOf()方法可以将8种基本数据类型转换为对应的字符串类型;
(2)通过8种包装类的静态方法valueOf()既可以将对应的基本数据类型转换为包装类,也可以将变量内容匹配的字符串转换为对应的包装类(Character包装类除外);
(3)通过8种包装类的有参构造方法同样既可以将对应的基本数据类型转换为包装类,也可以将变量内容匹配的字符串转换为对应的包装类(Character包装类除外);
(4)通过8种包装类的静态方法parseXxx()可以将变量内容匹配的字符串转换为对应的基本数据类型;
(5)包装类都重写了Object类中的toString()方法,以字符串的形式返回被包装的基本数据类型的值。
下面通过上面的方法来实现基本数据类型、包装类以及字符串之间的相互转换,如文件2所示。
文件2 Example18
1 public class Example18 {
2 public static void main(String args[]) {
3 int num = 123;
4 // 1、通过String.valueOf()方法将基本类型转换为字符串
5 String string = String.valueOf(num);
6 System.out.println("将int变量转换为字符串的结果:"+string);
7 // 2、通过包装类的valueOf()静态方法将基本类型和字符串转换为包装类
8 String str = "998";
9 Integer integer = Integer.valueOf(num);
10 Integer integer2 = Integer.valueOf(str);
11 System.out.println("将int变量转换为包装类的结果:"+integer);
12 System.out.println("将字符串变量转换为包装类的结果:"+integer2);
13 // 3、通过包装类的有参构造方法将基本类型和字符串转换为包装类
14 Integer integer3 = new Integer(num);
15 Integer integer4 = new Integer(str);
16 System.out.println("通过构造器将int变量转换为包装类的结果:"
17 +integer3);
18 System.out.println("通过构造器将字符串变量转换为包装类的结果:"
19 +integer4);
20 // 4、通过包装类的parseXxx()静态方法将字符串转换为基本数据类型
21 int parseInt = Integer.parseInt(str);
22 System.out.println("将字符串转换为基本类型的结果:"+parseInt);
23 // 5、通过包装类的toString()方法将包装类转换为字符串
24 String string2 = integer.toString();
25 System.out.println("将包装类转换为字符串的结果:"+string2);
26 }
27 }
运行结果如图2所示。
图2 运行结果
从图2可以看出,介绍的几种方法可以实现基本数据类型、包装类以及字符串之间的相互转换,但在使用valueOf(String s)和parseXxx(String s)方法时,还需要注意以下几点:
①除了Character外,包装类都有valueOf(String s)方法,可以根据String类型的参数创建包装类对象,但参数字符串s不能为null,而且字符串必须是可以解析为相应基本类型的数据,否则虽然编译通过,但运行时会报错。具体示例如下:
Integer i = Integer.valueOf("123"); // 合法
Integer i = Integer.valueOf("12a"); // 不合法
②除了Character外,包装类都有parseXxx(String s)的静态方法,将字符串转换为对应的基本类型的数据。参数s不能为null,而且同样必须是可以解析为相应基本类型的数据,否则虽然编译通过,但运行时会报错。具体示例如下:
int i = Integer.parseInt("123"); // 合法
Integer in = Integer.parseInt("itcast");// 不合法
在JDK的java.util包中,提供了一个Date类用于表示日期和时间,该类在JDK 1.0时就已经开始使用。随着JDK版本的不断升级和发展,Date类中大部分的构造方法和普通方法都已经不再推荐使用。目前JDK 8中,Date类只有两个构造方法是可以使用的,具体如下:
● Date():用来创建当前日期时间的Date对象。
● Date(long date):用于创建指定时间的Date对象,其中date参数表示1970年1月1日0时0分0(称为历元)以来的毫秒数,即时间戳。
接下来通过一个案例来说明如何使用这两个构造函数创建Date对象,如文件1所示。
文件1 Example19.java
1 import java.util.*;
2 public class Example19 {
3 public static void main(String[] args) {
4 // 创建表示当前时间的Date对象
5 Date date1 = new Date();
6 // 获取当前时间后1秒的时间
7 Date date2 = new Date(System.currentTimeMillis() + 1000);
8 System.out.println(date1);
9 System.out.println(date2);
10 }
11 }
运行结果如图1所示。
图1 运行结果
从图1可以看出,第一条输出语句输出的是当前计算机的日期和时间,第二条输出语句输出的是当前计算机的日期和时间加1秒后的时间。
对于Date类,只需要了解如何通过创建对象封装时间值即可。由于Date类在设计之初,没有考虑国际化的问题,因此从 JDK 1.1 开始,Date类相应的功能也被Calendar类中的方法取代。
Calendar类用于完成日期和时间字段的操作,它可以通过特定的方法设置和读取日期的特定部分,比如年、月、日、时、分和秒等。Calendar类是一个抽象类,不可以被实例化,在程序中需要调用其静态方法getInstance()来得到一个Calendar对象,然后才能调用其相应的方法,具体示例如下:
Calendar calendar = Calendar.getInstance();
Calendar类为操作日期和时间提供了大量的方法,下面列举一些常用的方法,如表1所示。
表1 Calendar的常用方法
方法声明 | 功能描述 |
---|---|
int get(int field) | 返回指定日历字段的值 |
void add(int field,int amount) | 根据日历规则,为指定的日历字段增加或减去指定的时间量 |
void set(int field,int value) | 为指定日历字段设置指定值 |
void set(int year,int month,int date) | 设置Calendar对象的年、月、日三个字段的值 |
void set(int year.int month,int date,int hourOfDay,int minute,int second) | 设置Calendar对象的年、月、日、时、分、秒六个字段的值 |
表1中,大多数方法都用到了int类型的参数field,该参数需要接收Calendar类中定义的常量值,这些常量值分别表示不同的字段,如Calendar.YEAR用于表示年份,Calendar.MONTH用于表示月份,Calendar.SECOND用于表示秒等。尤其要注意的是,在使用Calendar.MONTH字段时,月份的起始值是从0开始的,而不是从1开始,因此要获取当前的月份需要在Calendar.MONTH的基础上加1。
接下来通过一个案例来学习下Calender类如何获取当前计算机的日期和时间,如文件1所示。
文件1 Example20.java
1 import java.util.*;
2 public class Example20 {
3 public static void main(String[] args) {
4 // 获取表示当前时间的Calendar对象
5 Calendar calendar = Calendar.getInstance();
6 int year = calendar.get(Calendar.YEAR); // 获取当前年份
7 int month = calendar.get(Calendar.MONTH) + 1; // 获取当前月份
8 int date = calendar.get(Calendar.DATE); // 获取当前日
9 int hour = calendar.get(Calendar.HOUR); // 获取时
10 int minute = calendar.get(Calendar.MINUTE); // 获取分
11 int second = calendar.get(Calendar.SECOND); // 获取秒
12 System.out.println("当前时间为:" + year + "年 " + month + "月 "
13 + date + "日 "+ hour + "时 " + minute + "分 " + second + "秒");
14 }
15 }
运行结果如图1所示。
图1 运行结果
在文件1中,调用Calendar的getInstance()方法创建了一个代表默认时区内当前时间的Calendar对象,然后调用该对象的get(int field)方法,通过传入不同的常量字段值来分别得到日期、时间各个字段的值。
在程序中除了要获得当前计算机的时间外,还会经常设置或修改某个时间,比如一项工程的开始时间为2018年的1月1日,假设要100天后竣工,此时要想知道竣工日期是哪天就需要先将日期设定在开始的那天,然后对日期的天数进行增加,如果没有按照预期完成,可能还需要对日期进行修改。其中添加和修改时间的功能就可以通过Calendar类中的add()和set()方法来实现。
接下来就通过案例来实现上述例子,如文件2所示。
文件2 Example21.java
1 import java.util.*;
2 public class Example21 {
3 public static void main(String[] args) {
4 // 获取表示当前时间的Calendar对象
5 Calendar calendar = Calendar.getInstance();
6 // 设置指定日期
7 calendar.set(2018, 1, 1);
8 // 为指定日期增加时间
9 calendar.add(Calendar.DATE, 100);
10 // 返回指定日期的年
11 int year = calendar.get(Calendar.YEAR);
12 // 返回指定日期的月
13 int month = calendar.get(Calendar.MONTH) + 1;
14 // 返回指定日期的日
15 int date = calendar.get(Calendar.DATE);
16 System.out.println("计划竣工日期为:" + year + "年"
17 + month + "月" + date + "日");
18 }
19 }
运行结果如图2所示。
图2 运行结果
文件2的代码中调用Calendar的set()方法将日期设置为2018年1月1号,然后调用add()方法在Calendar.Date字段上增加100,从图5-21可以看出,增加100天的日期为2018年5月12日。
需要注意的是,Calendar.Date表示的是天数,当天数累加到当月的最大值时,如果继续再累加一次,就会从1开始计数,同时月份值会加1,这和算术运算中的进位有点类似。
Calendar有两种解释日历字段的模式——lenient模式(容错模式)和non-lenient模式(非容错模式)。当Calendar处于lenient模式时,它的字段可以接收超过允许范围的值,当调用get(int field)方法获取某个字段值时,Calendar会重新计算所有字段的值,将字段的值标准化。换句话说,就是在lenient模式下,允许出现一些数值上的错误,例如月份只有12个月,取值为0到11,但在这种模式下,月份值指定为13也是可以的。当Calendar处于non-lenient模式时,如果某个字段的值超出了它允许的范围,程序将会抛出异常。接下来通过一个案例来演示这种异常情况,如文件3所示。
文件3 Example22.java
1 import java.util.*;
2 public class Example22 {
3 public static void main(String[] args) {
4 // 获取表示当前时间的Calendar对象
5 Calendar calendar = Calendar.getInstance();
6 // 设置指定日期,将MONTH设为13
7 calendar.set(Calendar.MONTH, 13);
8 System.out.println(calendar.getTime());
9 // 开启non-lenient模式
10 calendar.setLenient(false);
11 calendar.set(Calendar.MONTH, 13);
12 System.out.println(calendar.getTime());
13 }
14 }
运行结果如图3所示。
图3 运行结果
从图3可以看出,文件3中的第一个输出语句可以正常地输出时间值,而第二个输出语句在输出时间值时报错。出现这样现象的原因在于,Calendar类默认使用lenient模式,当调用Calendar的set()方法将MONTH字段设置为13时,会发生进位,YEAR字段加1,然后MONTH字段变为1,第一个输出语句打印出的结果是“Feb 27”(2月27日)。当代码调用Calendar的setLenient(false)方法开启non-lenient模式后,同样地设置MONTH字段为13,会因为超出了MONTH字段0~11的范围而抛出异常。
本例中用到了Calendar的getTime()方法,getTime()方法会返回一个表示Calendar时间值的Date对象,同时Calendar有一个setTime(Date date)方法,setTime()方法接收一个Date对象,将Date对象表示的时间值设置给Calendar对象,通过这两个方法就可以完成Date和Calendar对象之间的转换。
为了满足更多的需求,JDK 8中新增了一个java.time包,在该包下包含了更多的日期和时间操作类,其常用类如表1所示。
表1 JDK 8新增日期、时间常用类
类名 | 功能描述 |
---|---|
Clock | 用于获取指定时区的当前日期、时间。 |
DayOfWeek | 枚举类,定义了一周七天周一到周日的枚举值 |
Duration | 表示持续时间。该类提供的ofXxx()方法用于获取指定的时间的小时、分钟、秒数等。 |
Instant | 表示一个具体时刻,可以精确到纳秒。该类提供了静态的now()方法来获取当前时刻,提供了静态的now(Clock clock)方法来获取clock对应的时刻。同时还提供了一系列的plusXxx()方法来获取当前时刻基础上加上一段时间,以及一系列的minusXxx()方法在当前时刻基础上减去一段时间。 |
LocalDate | 表示不带时区的日期,如2018-01-27。该类提供了静态的now()方法来获取当前日期,提供了静态的now(Clock clock)方法来获取clock对应的日期。同时还提供了一系列的plusXxx()方法在当前年份基础上加上几年、几月、几日等,以及一系列的minusXxx()方法在当前年份基础上减去几年、几月、几日等。 |
LocalTime | 表示不带时区的时间,如14:49:20。该类提供了静态的now()方法来获取当前时间,提供了静态的now(Clock clock)方法来获取clock对应的时间。同时还提供了一系列的plusXxx()方法在当前年份基础上加上几小时、几分、几秒等,以及一系列的minusXxx()方法在当前年份基础上减去几小时、几分、几秒等。 |
LocalDateTime | 表示不带时区的日期、时间。该类提供了静态的now()方法来获取当前日期、时间,提供了静态的now(Clock clock)方法来获取clock对应的日期、时间。同时还提供了一系列的plusXxx()方法在当前年份基础上加上几年、几月、几日、几小时、几分、几秒等,以及一系列的minusXxx()方法在当前年份基础上减去几年、几月、几日、几小时、几分、几秒等。 |
Month | 枚举类,定义了一月到十二月的枚举值 |
MonthDay | 表示月日,如--01-27。该类提供了静态的now()方法来获取当前月日,提供了静态的now(Clock clock)方法来获取clock对应的月日。 |
Year | 表示年,如2018。该类提供了静态的now()方法来获取当前年份,提供了静态的now(Clock clock)方法来获取clock对应的年份。同时还提供了plusYears()方法在当前年份基础上增加几年,以及minusYears()方法在当前年份基础上减去几年。 |
YearMonth | 表示年月,如2018-01。该类提供了静态的now()方法来获取当前年月,提供了静态的now(Clock clock)方法来获取clock对应的年月。同时还提供了plusXxx()方法在当前年月基础上增加几年、几月,以及minusXxx()方法在当前年月基础上减去几年、几月。 |
ZoneId | 表示一个时区 |
ZonedDateTime | 表示一个时区化的日期、时间 |
了解了上述各个类的作用后,接下来通过一个具体的案例来演示这些类的用法,如文件1所示。
文件1 Example23
1 import java.time.*;
2 public class Example23 {
3 public static void main(String[] args) {
4 // 1、Clock的使用
5 Clock clock = Clock.systemUTC();
6 System.out.println("获取UTC时区转换的当前时间:" + clock.instant());
7 System.out.println("获取UTC时区转换的的毫秒数:" + clock.millis());
8 // 2、Duration的使用
9 Duration d = Duration.ofDays(1);
10 System.out.println("一天等于" + d.toHours() +"小时");
11 System.out.println("一天等于" + d.toMinutes() +"分钟");
12 System.out.println("一天等于" + d.toMillis() +"秒");
13 // 3、Instant的使用
14 Instant instant = Instant.now();
15 System.out.println("获取UTC时区的当前时间为:" + instant);
16 System.out.println("当前时间一小时后的时间为:"
17 + instant.plusSeconds(3600));
18 System.out.println("当前时间一小时前的时间为:"
19 + instant.minusSeconds(3600));
20 // 4、LocalDate的使用
21 LocalDate localDate = LocalDate.now();
22 System.out.println("从默认时区的系统时钟获得当前日期:" + localDate);
23 // 5、LocalTime的使用
24 LocalTime localTime = LocalTime.now();
25 System.out.println("从默认时区的系统时钟获取当前时间:" + localTime);
26 // 6、LocalDateTime的使用
27 LocalDateTime localDateTime = LocalDateTime.now();
28 System.out.println("从默认时区的系统时钟获取日期、时间:"
29 + localDateTime);
30 LocalDateTime times = localDateTime.plusDays(1)
31 .plusHours(3).plusMinutes(30);
32 System.out.println("当前的日期、时间加上1天3小时30分之后:" + times);
33 // 7、Year、YearMonth、MonthDay的使用
34 Year year = Year.now();
35 System.out.println("当前年份为:" + year);
36 YearMonth yearMonth = YearMonth.now();
37 System.out.println("当前年月为:" + yearMonth);
38 MonthDay monthDay = MonthDay.now();
39 System.out.println("当前月日为:" + monthDay);
40 // 8、 获取系统默认时区
41 ZoneId zoneId = ZoneId.systemDefault();
42 System.out.println("当前系统默认时区为:" + zoneId);
43 }
44 }
运行结果如图1所示。
图1 运行结果
文件1中,演示了JDK 8新增日期时间、包中类的一些用法。需要注意的是,从图5-23可以看出,通过clock.instant()和Instant.now()获取的当前时间与本地系统显示时间有8个小时的时差,这是因为Instant默认使用的是UTC(Universal Time Coordinated)世界协调时间,又称世界标准时间,UTC 提供了一种与时区无关的时间,与CST(China Standard Time)中国标准时间(北京时间)有8个小时的时差。
使用Date类时,在程序中打印Date对象所输出的当前时间都是以默认的英文格式输出的,如果要将Date对象表示的日期以指定的格式输出,例如输出中文格式的时间,就需要用到DateFormat类。
DateFormat类专门用于将日期格式化为字符串或者将用特定格式显示的日期字符串转换成一个Date对象。DateFormat是一个抽象类,不能被直接实例化,但它提供了一系列的静态方法来获取DateFormat类的实例对象,并能调用其他相应的方法进行操作。
DateFormat类中提供的常用方法,如表1所示。
表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) | 将给定字符串解析成一个日期 |
表1中,列出了DateFormat类的四个静态方法,这四个静态方法能用于获得DateFormat类的实例对象,每种方法返回的对象都具有不同的作用,它们可以分别对日期或者时间部分进行格式化。
在DateFormat类中还定义了许多常量,其中有四个常量值是用于作为参数传递给方法的,包括FULL、LONG、MEDIUM和SHORT。FULL常量用于表示完整格式,LONG常量用于表示长格式,MEDIUM常量用于表示普通格式,SHORT常量用于表示短格式。
接下来通过一个案例针对DateFormat类的使用进行演示,如文件1所示。
文件1 Example24.java
1 import java.text.*;
2 import java.util.*;
3 public class Example24 {
4 public static void main(String[] args) {
5 // 创建Date对象
6 Date date = new Date();
7 // Full格式的日期格式器对象
8 DateFormat fullFormat =
9 DateFormat.getDateInstance(DateFormat.FULL);
10 // Long格式的日期格式器对象
11 DateFormat longFormat =
12 DateFormat.getDateInstance(DateFormat.LONG);
13 // MEDIUM格式的日期/时间 格式器对象
14 DateFormat mediumFormat = DateFormat.getDateTimeInstance(
15 DateFormat.MEDIUM, DateFormat.MEDIUM);
16 // SHORT格式的日期/时间格式器对象
17 DateFormat shortFormat = DateFormat.getDateTimeInstance(
18 DateFormat.SHORT, DateFormat.SHORT);
19 // 下面打印格式化后的日期或者日期/时间
20 System.out.println("当前日期的完整格式为:"
21 + fullFormat.format(date));
22 System.out.println("当前日期的长格式为:"
23 + longFormat.format(date));
24 System.out.println("当前日期的普通格式为:"
25 + mediumFormat.format(date));
26 System.out.println("当前日期的短格式为:"
27 + shortFormat.format(date));
28 }
29 }
运行结果如图1所示。
图1 运行结果
文件1中,演示了四种格式下时间和日期格式化输出的效果,其中调用getDateInstance()方法获得的实例对象用于对日期部分进行格式化,getDateTimeInstance()方法获得的实例对象可以对日期和时间部分进行格式化。
DateFormat中还提供了一个parse(String source)方法,能够将一个字符串解析成Date对象,但是它要求字符串必须符合日期/时间的格式要求,否则会抛出异常。
接下来通过一个案例来演示parse()方法的使用,如文件2所示。
文件2 Example25.java
1 import java.text.*;
2 public class Example25 {
3 public static void main(String[] args) throws ParseException {
4 // 创建DateFormat对象
5 DateFormat dt1 = DateFormat.getDateInstance();
6 // 创建Long格式的DateFormat对象
7 DateFormat dt2 = DateFormat.getDateInstance(DateFormat.LONG);
8 // 定义两个日期格式的字符串
9 String str1 = "2018-01-27";
10 String str2 = "2018年01月27日";
11 // 输出对应格式的字符串解析成Date对象后的结果
12 System.out.println(dt1.parse(str1));
13 System.out.println(dt2.parse(str2));
14 }
15 }
运行结果如2所示。
图2 运行结果
文件2中,首先创建了一个普通的DateFormat对象,以及LONG格式的DateFormat对象,然后定义了两种日期格式的字符串,最后在输出语句中调用parse()方法将对应格式的字符串解析成Date对象。
在使用DateFormat对象的parse()方法将字符串解析为日期时,需要输入固定格式的字符串,这显然不够灵活。为了能够更好地格式化日期、解析字符串,Java中提供了一个SimpleDateFormat类。
SimpleDateFormat类是DateFormat类的子类,它可以使用new关键字创建实例对象。在创建实例对象时,它的构造方法需要接收一个表示日期格式模板的字符串参数。
接下来通过一个案例演示,如何使用SimpleDateFormat类将日期对象以特定的格式转为字符串形式,如文件1所示。
文件1 Example26.java
1 import java.text.*;
2 import java.util.*;
3 public class Example26 {
4 public static void main(String[] args) throws Exception {
5 // 创建一个SimpleDateFormat对象
6 SimpleDateFormat sdf = new SimpleDateFormat(
7 "Gyyyy年MM月dd日:今天是yyyy年的第D天,E");
8 // 按SimpleDateFormat对象的日期模板格式化Date对象
9 System.out.println(sdf.format(new Date()));
10 }
11 }
运行结果如图1所示。
图1 运行结果
在文件1中,在创建SimpleDateFormat对象时传入日期格式模板“Gyyyy年MM月dd日:今天是yyyy年的第D天,E”,在调用SimpleDateFormat的format()方法时,会将Date对象格式化成如模板格式的时间形式,即“公元2018年01月27日:今天是2018年的第27天,星期六”。
上面通过SimpleDateFormat类将一个Date时间对象转换为指定格式字符串形式,接下来通过一个案例来演示,如何使用SimpleDateFormat类将一个指定日期格式的字符串解析为Date对象,如文件2所示。
文件2 Example27.java
1 import java.text.*;
2 public class Example27 {
3 public static void main(String[] args) throws ParseException{
4 // 创建一个SimpleDateFormat对象,并指定日期格式
5 SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
6 // 定义一个日期格式的字符串
7 String str = "2018/01/27";
8 // 将字符串解析成Date对象
9 System.out.println(sdf.parse(str));
10 }
11 }
运行结果如图2所示。
图2 运行结果
在文件2中,首先在创建SimpleDateFormat对象时传入日期格式模板“yyyy/MM/dd”,然后定义了一个指定日期格式的字符串“2018/01/27”,最后调用SimpleDateFormat的parse()方法将符合日期模板格式的字符串解析成Date对象。
SimpleDateFormat的功能非常强大,在创建SimpleDateFormat对象时,只要传入合适的格式字符串参数,就能解析各种形式的日期字符串或者将Date日期格式化成任何形式的字符串。其中,格式字符串参数是一个使用日期/时间字段占位符的日期模板。
除了DataFormat和SimpleDateFormat类外,JDK 8在java.time.format包下还提供了一个DateTimeFormatter类,该类也是一个格式化类,其功能相当于DataFormat和SimpleDateFormat的合体,它不仅可以将日期、时间对象格式化成字符串,还能将特定格式的字符串解析成日期、时间对象。
要使用DateTimeFormatter进行格式化或者解析,就必须先获得DateTimeFormatter对象。获取DateTimeFormatter对象有三种方式,具体如下:
● 使用静态常量创建DateTimeFormatter格式器。在DateTimeFormatter类中包含大量的静态常量,如BASIC_ISO_DATE、ISO_LOCAL_DATE、ISO_LOCAL_TIME等,通过这些静态常量都可以获取DateTimeFormatter实例。
● 使用不同风格的枚举值来创建DateTimeFormatter格式器。在FormatStyle类中定义了FULL、LONG、MEDIUM和SHORT四个枚举值,它们表示日期和时间的不同风格。
● 根据模式字符串创建DateTimeFormatter格式器。
了解了DateTimeFormatter的作用及其对象获取方式后,下面分别讲解下如何使用DateTimeFormatter来格式化和解析日期、时间。
1.完成日期、时间格式化
使用DateTimeFormatter将日期、时间格式化为字符串,可以通过以下两种方式:
● 调用DateTimeFormatter的format(TemporalAccessor temporal)方法执行格式化,其中参数temporal是一个TemporalAccessor类型接口,其主要实现类有LocalDate、LocalDateTime。
● 调用LocalDate、LocalDateTime等日期、时间对象的format(DateTimeFormatter formatter)方法执行格式化。
接下来通过一个案例来演示,如何使用DateTimeFormatter来格式化日期、时间,如文件1所示。
文件1 Example28.java
1 import java.time.*;
2 import java.time.format.*;
3 public class Example28 {
4 public static void main(String[] args) {
5 LocalDateTime date = LocalDateTime.now();
6 // 1、使用常量创建DateTimeFormatter
7 System.out.print("使用常量创建DateTimeFormatter:");
8 System.out.println(DateTimeFormatter
9 .ISO_LOCAL_DATE.format(date));
10 // 2、使用Long类型风格的DateTimeFormatter
11 System.out.print("使用Long类型风格的DateTimeFormatter:");
12 DateTimeFormatter dtf = DateTimeFormatter
13 .ofLocalizedDateTime(FormatStyle.LONG);
14 System.out.println(dtf.format(date));
15 // 3、根据模式字符串来创建DateTimeFormatter格式器
16 System.out.print("根据模式字符串来创建DateTimeFormatter:");
17 DateTimeFormatter formatter = DateTimeFormatter
18 .ofPattern("yyyy MM dd HH:mm:ss");
19 // 使用LocalDateTime的format()方法格式化
20 String text = date.format(formatter);
21 // 使用格式化程序解析文本,返回日期时间
22 LocalDateTime parsedDate = LocalDateTime.parse(text, formatter);
23 System.out.println(parsedDate);
24 }
25 }
运行结果如图1所示。
图1 运行结果
文件1中,分别使用三种方式创建了DateTimeFormatter格式器,并使用不同方式创建的格式器对LocalDateTime进行格式化。
2.解析字符串
要使用DateTimeFormatter将指定格式的字符串解析成日期、时间对象,可以通过日期时间对象所提供的parse(CharSequence text, DateTimeFormatter formatter)方法来实现。
下面通过一个具体的案例来演示,如何使用DateTimeFormatter解析日期、时间,如文件2所示。
文件2 Example29.java
1 import java.time.*;
2 import java.time.format.*;
3 public class Example29 {
4 public static void main(String[] args) {
5 // 定义两种日期格式的字符串
6 String str1 = "2018-01-27 12:38:36";
7 String str2 = "2018年01月29日 15时01分20秒";
8 // 定义解析所用的格式器
9 DateTimeFormatter formatter1 = DateTimeFormatter
10 .ofPattern("yyyy-MM-dd HH:mm:ss");
11 DateTimeFormatter formatter2 = DateTimeFormatter
12 .ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
13 // 使用LocalDateTime的parse()方法执行解析
14 LocalDateTime localDateTime1 = LocalDateTime
15 .parse(str1, formatter1);
16 LocalDateTime localDateTime2 = LocalDateTime
17 .parse(str2, formatter2);
18 // 输出结果
19 System.out.println(localDateTime1);
20 System.out.println(localDateTime2);
21 }
22 }
运行结果如图2所示。
图2 运行结果
在文件2中,首先定义了两种日期格式的字符串,然后通过DateTimeFormatter对象定义了解析不同格式字符串的格式器,接下来通过LocalDateTime的parse()方法对字符串进行解析,最后使用输出语句输出解析后的结果。
未完,持续更新中。。。
自学java教程视频+资料+笔记,企鹅49.89.138.68