在程序开发中经常会用到字符串,所谓字符串就是指一连串的字符,它是由许多单个字符连接而成的,如多个英文字母所组成的一个英文单词。字符串中可以包含任意字符,这些字符必须包含在一对双引号“”之内,例如“abc”。Java中定义了三个封装字符串的类,分别是String、StringBuffer和StringBuilder,它们位于java.lang包中,并提供了一系列操作字符串的方法,这些方法不需要导包就可以直接使用。
1.使用字符串常量直接初始化一个String对象,具体代码如下:
String str1 = "abc";
由于String类比较常用,所以提供了这种简化的语法,用于创建并初始化String对象,其中“abc”表示一个字符串常量。
2.使用String类的构造方法初始化字符串对象,String类的常见构造方法如下表。
方法声明 | 功能描述 |
---|---|
String() | 创建一个内容为空的字符串 |
String(String value) | 根据指定的字符串内容创建对象 |
String(char[] value) | 根据指定的字符数组创建对象 |
String(byte[] bytes) | 根据指定的字节数组创建对象 |
接下来通过一个案例学习String类的使用。
1 public class Example01 {
2 public static void main(String[] args) throws Exception {
3 // 创建一个空的字符串
4 String str1 = new String();
5 // 创建一个内容为abcd的字符串
6 String str2 = new String("abcd");
7 // 创建一个内容为字符数组的字符串
8 char[] charArray = new char[] { 'D', 'E', 'F' };
9 String str3 = new String(charArray);
10 // 创建一个内容为字节数组的字符串
11 byte[] arr = {97,98,99};
12 String str4 = new String(arr);
13 System.out.println("a" + str1 + "b");
14 System.out.println(str2);
15 System.out.println(str3);
16 System.out.println(str4);
17 }
18}
上述代码中,第4行代码创建了名称为str1的空字符串;第6行代码创建名称为str2的字符串,其内容为“abcd”;第8~9行代码创建了名称为charArray的char类型字符数组,并将charArray赋值给名称为str3的字符串,第11~12行代码创建了名称为arr的byte类型的字节数组,并将arr赋值给名称为str4的字符串;最后在第13~16行代码打印了str1、str2、str3和str4的值。
String类常用方法如下表。
方法声明 | 功能描述 |
---|---|
int indexOf(int ch) | 返回指定字符ch在字符串中第一次出现位置的索引 |
int lastIndexOf(int ch) | 返回指定字符ch在字符串中最后一次出现位置的索引 |
int indexOf(String str) | 返回指定子字符串str在字符串第一次出现位置的索引 |
int lastIndexOf(String str) | 返回指定子字符串str在此字符串中最后一次出现位置的索引 |
char charAt(int index) | 返回字符串中index位置上的字符,其中index的取值范围是0~(字符串长度-1) |
Boolean endsWith(String suffix) | 判断此字符串是否以指定的字符串结尾 |
int length() | 返回此字符串的长度 |
boolean equals(Object anObject) | 将此字符串与指定的字符串比较 |
boolean isEmpty() | 判断字符串长度是否为0,如果为0则返回true,反之则返回flase |
---|---|
boolean startsWith(String prefix) | 判断此字符串是否以指定的字符串开始 |
boolean contains(CharSequence cs) | 判断此字符串中是否包含指定的字符序列 |
String toLowerCase() | 使用默认语言环境的规则将String中的所有字符都转换为小写 |
String toUpperCase() | 使用默认语言环境的规则将String中的所有字符都转换为大写 |
static String valueOf(int i) | 将int变量i转换成字符串 |
char[] toCharArray() | 将此字符串转换为一个字符数组 |
String replace(CharSequence oldstr, CharSequence newstr) | 返回一个新的字符串,它是通过用 newstr替换此字符串中出现的所有 oldstr得到的 |
String[] split(String regex) | 根据参数regex将原来的字符串分割为若干个子字符串 |
String substring(int beginIndex) | 返回一个新字符串,它包含从指定的beginIndex处开始,直到此字符串末尾的所有字符 |
String substring(int beginIndex, int endIndex) | 返回一个新字符串,它包含从指定的beginIndex处开始,直到索引endIndex-1处的所有字符 |
String trim() | 返回一个新字符串,它去除了原字符串首尾的空格 |
1.字符串的获取功能
在Java程序中,需要对字符串进行一些获取的操作,如获得字符串长度、获得指定位置的字符等。
1 public class Example02 {
2 public static void main(String[] args) {
3 String s = "ababcdedcba"; // 定义字符串s
4 // 获取字符串长度,即字符个数
5 System.out.println("字符串的长度为:" + s.length());
6 System.out.println("字符串中第一个字符:" + s.charAt(0));
7 System.out.println("字符c第一次出现的位置:" + s.indexOf('c'));
8 System.out.println("字符c最后一次出现的位置:" + s.lastIndexOf('c'));
9 System.out.println("子字符串ab第一次出现的位置:" +
10 s.indexOf("ab"));
11 System.out.println("子字符串ab字符串最后一次出现的位置:" +
12 s.lastIndexOf("ab"));
13 }
14 }
上述代码中,第3行代码创建一个名称为s的String字符串,并赋值为“ababcdedcba”,第5~11行代码依次打印了字符串长度、字符串中第一个字符内容、字符c第一次出现的位置、子字符串ab第一次出现的位置和子字符串ab最后一次出现的位置。从图5-2中可以看出,String类提供的方法可以很方便地获取字符串的长度,获取指定位置的字符以及指定字符和字符串的位置。
2.字符串的转换操作
程序开发中,经常需要对字符串进行转换操作。例如,将字符串转换成数组的形式,将字符串中的字符进行大小写转换等。
1 public class Example03 {
2 public static void main(String[] args) {
3 String str = "abcd";
4 System.out.print("将字符串转为字符数组后的结果:");
5 char[] charArray = str.toCharArray(); // 字符串转换为字符数组
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 System.out.println("将字符串转换成小写之后的结果:" +
20 str.toLowerCase());
21 }
22 }
上述代码中,第5行代码使用String类的toCharArray()方法将一个字符串转为一个字符数组,第16行代码使用静态方法valueOf()将一个int类型的整数转为字符串,第18行代码使用toUpperCase()方法将字符串中的字符都转为大写,第20行代码使用toLowerCase()方法将字符串中的字符都转换为小写。其中valueOf()方法有多种重载的形式,float、double、char等其他基本类型的数据都可以通过valueOf()方法转为String字符串类型。
3.字符串的替换和去除空格操作 程序开发中,用户输入数据时经常会有一些错误和空格,这时可以使用String类的replace()和trim()方法,进行字符串的替换和去除空格操作。
1 public class Example04 {
2 public static void main(String[] args) {
3 String s = "itcast";
4 // 字符串替换操作
5 System.out.println("将it替换成cn.it的结果:" + s.replace("it",
6 "cn.it"));
7 // 字符串去除空格操作
8 String s1 = " i t c a s t ";
9 System.out.println("去除字符串两端空格后的结果:" + s1.trim());
10 System.out.println("去除字符串中所有空格后的结果:" + s1.replace(" ",
11 ""));
12 }
13 }
上述代码中,第3行代码定义了名称为s的字符串,第5~6行代码使用replace()方法将字符串s的“it”替换为“cn.it”;在第8行代码中定义了名称为s1的字符串,并在第9行代码使用trim()去除字符串两端的空格;在第10行代码使用replace()方法将字符串s1的“ ”替换为“”,这样做是为了去除字符串中所有的空格。
trim()方法只能去除两端的空格,不能去除中间的空格。若想去除字符串中间的空格,需要调用String类的replace()方法。
4.字符串的判断操作 操作字符串时,经常需要对字符串进行一些判断,如判断字符串是否以指定的字符串开始、结束,是否包含指定的字符串,字符串是否为空等。
1 public class Example05 {
2 public static void main(String[] args) {
3 String s1 = "String"; // 声明一个字符串
4 String s2 = "Str";
5 System.out.println("判断是否以字符串Str开头:" +
6 s1.startsWith("Str"));
7 System.out.println("判断是否以字符串ng结尾:" + s1.endsWith("ng"));
8 System.out.println("判断是否包含字符串tri:" + s1.contains("tri"));
9 System.out.println("判断字符串是否为空:" + s1.isEmpty());
10 System.out.println("判断两个字符串是否相等" + s1.equals(s2));
11 }
12 }
在程序中可以通过“==”和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()方法用于将字符串按照某个字符进行分割。
1 public class Example06 {
2 public static void main(String[] args) {
3 String str = "石家庄-武汉-哈尔滨";
4 // 下面是字符串截取操作
5 System.out.println("从第5个字符截取到末尾的结果:" +
6 str.substring(4));
7 System.out.println("从第5个字符截取到第6个字符的结果:" +
8 str.substring(4, 6));
9 // 下面是字符串分割操作
10 System.out.print("分割后的字符串数组中的元素依次为:");
11 String[] strArray = str.split("-"); // 将字符串转换为字符串数组
12 for (int i = 0; i < strArray.length; i++) {
13 if (i != strArray.length - 1) {
14 // 如果不是数组的最后一个元素,在元素后面加逗号
15 System.out.print(strArray[i] + ",");
16 } else {
17 // 数组的最后一个元素后面不加逗号
18 System.out.println(strArray[i]);
19 }
20 }
21 }
22 }
上述代码中,第3行代码定义了名称为str的字符串;第6行代码在substring()方法中传入参数4,表示截取字符串中第5个之后的所有字符;第8行代码在substring()方法中传入参数4和6,表示从第5个字符截取到第6个字符;第10~19行代码先使用split()方法将字符串以 “-”进行分割,并将分割后的字符串数组命名为strArray,最后在for循环中使用if条件语句判断元素是否为最后一个元素,若不是最后一个元素则在该元素末尾添加“,”。
脚下留心:字符串角标越界异常
String字符串在获取某个字符时,会用到字符的索引,当访问字符串中的字符时,如果字符的索引不存在,则会发生StringIndexOutOfBoundsException(字符串角标越界异常)。
1 public class Example07 {
2 public static void main(String[] args) {
3 String s = "itcast";
4 System.out.println(s.charAt(8));
5 }
6 }
脚下留心:字符串角标越界异常
通过运行结果可以看出,访问字符串中的字符时,不能超出字符的索引范围,否则会出现异常,这与数组中的角标越界异常非常相似。
由于字符串是常量,因此一旦创建,其内容和长度是不可改变的。如果需要对一个字符串进行修改,则只能创建新的字符串。为了对字符串进行修改,Java提供了一个StringBuffer类(也称字符串缓冲区)。StringBuffer类和String类最大的区别在于它的内容和长度都是可以改变的。StringBuffer类似一个字符容器,当在其中添加或删除字符时,并不会产生新的StringBuffer对象。
StringBuffer类的常见操作如下表。
方法声明 | 功能描述 |
---|---|
StringBuffer append(char c) | 添加参数到StringBuffer对象中 |
StringBuffer insert(int offset,String str) | 将字符串中的offset位置插入字符串str |
StringBuffer deleteCharAt(int index) | 移除此序列指定位置的字符 |
StringBuffer delete(int start,int end) | 删除StringBuffer对象中指定范围的字符或字符串序列 |
StringBuffer replace(int start,int end,String s) | 在StringBuffer对象中替换指定的字符或字符串序列 |
void setCharAt(int index, char ch) | 修改指定位置index处的字符序列 |
String toString() | 返回StringBuffer缓冲区中的字符串 |
StringBuffer reverse() | 将此字符序列用其反转形式取代 |
接下来通过一个案例学习StringBuffer类的常用方法。
1public class Example08 {
2 public static void main(String[] args) {
3 System.out.println("1、添加------------------------");
4 add();
5 System.out.println("2、删除------------------------");
6 remove();
7 System.out.println("3、修改------------------------");
8 alter();
9 }
10 public static void add() {
11 StringBuffer sb = new StringBuffer(); // 定义一个字符串缓冲区
12 sb.append("abcdefg"); // 在末尾添加字符串
13 System.out.println("append添加结果:" + sb);
14 sb.insert(2, "123"); // 在指定位置插入字符串
15 System.out.println("insert添加结果:" + sb);
16 }
17 public static void remove() {
18 StringBuffer sb = new StringBuffer("abcdefg");
19 sb.delete(1, 5); // 指定范围删除
20 System.out.println("删除指定位置结果:" + sb);
21 sb.deleteCharAt(2); // 指定位置删除
22 System.out.println("删除指定位置结果:" + sb);
23 sb.delete(0, sb.length()); // 清空缓冲区
24 System.out.println("清空缓冲区结果:" + sb);
25 }
26 public static void alter() {
27 StringBuffer sb = new StringBuffer("abcdef");
28 sb.setCharAt(1, 'p'); // 修改指定位置字符
29 System.out.println("修改指定位置字符结果:" + sb);
30 sb.replace(1, 3, "qq"); // 替换指定位置字符串或字符
31 System.out.println("替换指定位置字符(串)结果:" + sb);
32 System.out.println("字符串翻转结果:" + sb.reverse());
33 }
34 }
第10~16行代码创建了add()方法,用于演示StringBuffer的添加操作,在第11行代码中创建了一个StringBuffer类型的字符串sb,第12行代码使用append()方法在字符串sb的末尾添加了新的字符串,第14行代码使用insert()方法在字符串sb索引为2的位置插入字符串“123”。不同的是,使用append()方法插入的新字符串始终位于字符串sb的末尾,而insert()方法则可以将新字符串插入指定位置。
第17~25行代码创建了remove()方法,用于演示StringBuffer的字符串删除操作,在第18行代码中创建了一个StringBuffer类型的字符串sb,在第19行代码使用delete()方法删除字符串下标从1到5的字符,第21行代码使用deleteCharAt()方法删除字符串下标2之后的所有字符,第23的delete()方法用于清空缓冲区结果。
第26~33行代码创建了alter()方法,用于演示StringBuffer的字符串替换和反转的操作,在第27行代码中创建了一个StringBuffer类型的字符串sb,在第28行代码中使用setCharAt()方法修改下标为1的字符为“p”,第30行代码中使用replace()方法替换下标从1到3的字符为“qq”,第32行代码中使用reverse()方法将字符串反转。
StringBuilder类也可以对字符串进行修改,StringBuffer类和StringBuilder类的对象都可以被多次修改,并不产生新的未使用对象,StringBuilder类是JDK5中新加的类,它与StringBuffer之间最大不同在于StringBuilder的方法是线程安全的,也就是说StringBuffer不能被同步访问,而StringBuilder可以。
下面通过一个案例对比StringBuilder和StringBuffer的运行效率。
1 public class Example09{
2 private static final int TIMES = 100000;
3 public static void main(String[] args) {
4 Example09.testString();
5 Example09.testStringBuffer();
6 Example09.testStringBuilder();
7 }
8 //String时间效率测试
9 public static void testString() {
10 long startTime = System.currentTimeMillis();
11 String str = "";
12 for (int i = 0; i < TIMES; i++) {
13 str += "test";
14 }
15 long endTime = System.currentTimeMillis();
16 System.out.println("String test usedtime: "
17 + (endTime - startTime));
18 }
19 //StringBuffer时间效率测试(线程安全)
20 public static void testStringBuffer() {
21 long startTime = System.currentTimeMillis();
22 StringBuffer str = new StringBuffer();
23 for (int i = 0; i < TIMES; i++) {
24 str.append("test");
25 }
26 long endTime = System.currentTimeMillis();
27 System.out.println("StringBuffer test usedtime: " + (endTime –
28 startTime));
29 }
30 //StringBuffer时间效率测试(非线程安全)
31 public static void testStringBuilder() {
32 long startTime = System.currentTimeMillis();
33 StringBuilder str = new StringBuilder();
34 for (int i = 0; i < TIMES; i++) {
35 str.append("test");
36 }
37 long endTime = System.currentTimeMillis();
38 System.out.println("StringBuilder test usedtime: " + (endTime –
39 startTime));
40 }
41 }
上述代码中,第9~18行代码定义了一个testString()方法,并在testString()方法中利用for循环测试String对于字符串修改的时间;第20~29行代码定义了一个testStringBuffer()方法,并在testStringBuffer()方法中利用for循环测试StringBuffer对于字符串修改的时间;第31~40行代码定义了一个testStringBuilder()方法,并在testStringBuilder()方法中利用for循环测试StringBuilder对于字符串修改的时间;最后在4~6行代码中调用testString()方法、testStringBuffer()方法和testStringBuilder()方法,目的主要是想通过一个简单的方法去测试String、StringBuffer和StringBuilder三个操作字符串的效率。从图5-11的运行结果可以看出三者的工作效率为StringBuilder>StringBuffer>String。
StringBuilder类和StringBuffer类、String类有很多相似之处,初学者在使用时很容易混淆。接下来针对这三个类进行对比,简单归纳一下三者的不同。
(1)String类表示的字符串是常量,一旦创建后,内容和长度都是无法改变的。而StringBuilder和StringBuffer表示字符容器,其内容和长度可以随时修改。在操作字符串时,如果该字符串仅用于表示数据类型,则使用String类即可,但是如果需要对字符串中的字符进行增删操作,则使用StringBuffer与StringBuilder类。如果有大量字符串拼接操作,不要求线程安全的情况下,采用StringBuilder更高效。相反如果需要线程安全则需要使用StringBuffer。
(2)对于equals()方法的使用我们已经有所了解,但是在StringBuffer类与StringBuilder类中并没有被Object类的equals()方法覆盖,也就是说,equals()方法对于StringBuffer类与StringBuilder类来言并不起作用。
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
StringBuilder sbr1=new StringBuilder("abc");
StringBuilder sbr2=new StringBuilder("abc");
System.out.println(sbr1.equals(sbr2));
(3)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; // 编译出错
在超市购物时,小票上都会有一个订单号,而且每个订单号都是唯一的。本例要求编写一个程序,模拟订单系统中订单号的生成。在生成订单号时,使用年月日和毫秒值组合生成唯一订单号。例如,给定一个包括年月日以及毫秒值的数组arr={2020,0504,1101},将其拼接成字符串s:[202005041101],作为一个订单号。
(1)分析任务描述可知,做此任务需要先定义一个数组,数组中包括年月日以及毫秒值。
(2)要实现数组转成一个字符串,首先定义一个方法实现数组拼接成字符串。参数类型为数组,返回值类型为String,可先使用String定义一个空串,然后使用字符串的操作方法在开始最后加上“[]”字符。之后在加“[]”字符中间循环遍历数组用开始定义的字符串接收。自此,我们拼接的方法完成。在方法中将数组遍历,然后把每一个得到的字符拼接成一个字符串并且返回。
(3) 在主函数入口调用刚才的方法。并定义一个字符串变量接收结果
(4) 输出结果,观察控制台的效果。
1package com.itheima;
2public class example1 {
3 public static void main(String[] args) {
4 // 订单的年份月份毫秒值,定义一个数组即可
5 int[] arr = {2019,0504,1101};
6 //调用方法,用一个变量接受结果
7 String s = arrayToString(arr);
8 //输出结果
9 System.out.println("s:" + s);
10 }
11 // 定义一个方法实现数组拼接成字符串。参数类型为数组 返回值类型为String
12 public static String arrayToString(int[] arr) {
13 String s = "";
14 s += "[";
15 for (int i = 0; i < arr.length; i++) {
16 if(i == arr.length-1) {
17 s+=arr[i];
18 }else {
19 s+= arr[i];
20 }
21 }
22 s += "]";
23 //在方法中将数组遍历,然后把每一个得到的字符拼接成一个字符串并且返回
24 return s;
25 }
26}
本例要求编写一个程序,模拟默认密码的自动生成策略,手动输入用户名,根据用户名自动生成默认密码。在生成密码时,将用户名反转即为默认的密码。
(1) 分析任务描述可知,做此任务首先需要用Scanner类相关方法实现键盘手动输入一个字符串代表用户名。
(2) 要实现默认密码自动生成,根据任务描述可知默认密码就是手动输入用户名的反转及字符串的反转。因此需要将字符串用循环倒着遍历,用charAt()方法接收遍历的字符并赋值给空串。
(3) 在主函数入口调用刚才的方法。并定义一个字符串变量接收结果
(4) 输出结果,观察控制台的效果。
1package com.itheima;
2import java.util.Scanner;
3public class example2 {
4 public static void main(String[] args) {
5 //键盘输入一个字符串,用Scanner实现
6 Scanner sc = new Scanner(System.in);
7 System.out.println("请您输入用户名:");
8 String line = sc.nextLine();
9 //调用方法,用一个变量接收
10 String reverse = reverse(line);
11 //输出结果
12 System.out.println("恭喜"+line+"用户注册成功,您的初始密码
13 为:"+reverse);
14 }
15 //定义一个方法实现字符串反转
16 public static String reverse(String s) {
17 String ss = "";
18 //在方法中将字符串倒着遍历,然后把每一个得到的字符拼接成一个字符串并且返
19 回
20 for (int i = s.length()-1; i >=0; i--) {
21 ss+=s.charAt(i);
22 }
23 return ss;
24 }
25}
在使用一些APP时,通常都需要填写用户名和密码。用户名和密码输入都正确才会登录成功,否则会提示用户名或密码错误。
本例要求编写一个程序,模拟用户登录。程序要求如下:
(1) 用户名和密码正确,提示登录成功。
(2) 用户名或密码不正确,提示“用户名或密码错误”。
(3) 总共有3次登录机会,在3次内(包含三次)输入正确的用户名和密码后给出登录成功的相应提示。超过3次用户名或密码输入有误,则提示登录失败,无法再继续登录。。
在登录时,需要比较用户输入的用户名密码与已知的用户名密码是否相同,本案例可以使用Scanner类以及String类的相关方法实现比较操作。
(1) 分析任务描述可知,已知用户名密码,定义两个字符串表示即可。
(2) 键盘录入要登录的用户名密码。用Scanner实现。
(3) 拿键盘录入的用户名密码和已知的用户名密码进行比较,给出相应的提示,字符串内容比较用equles实现。
(4) 循环实现多次机会。这里次数明确,用for循环实现。并在登录成功的时候。用break结束循环。
1package com.itheima;
2import java.util.Scanner;
3public class example3 {
4 public static void main(String[] args) {
5 //已知用户名密码,定义两个字符串表示即可。
6 String username = "itheima";
7 String password = "czbk";
8 for (int i = 0; i < 3; i++) {
9 // 键盘录入要登录的用户名密码。用Scanner实现
10 Scanner sc = new Scanner(System.in);
11 System.out.println("请输入用户名:");
12 String uname = sc.nextLine();
13 System.out.println("请输入用户密码:");
14 String pwd = sc.nextLine();
15 //拿键盘录入的用户名密码和已知的用户名密码进行比较,给出相应的提示,字符
16 串内容比较用equles实现。
17 if(uname.equals(username)&&pwd.equals(password)) {
18 System.out.println("登录成功!");
19 break;
20 }else {
21 if(2-i == 0) {
22 System.out.println("你的账户被锁定了,请联系管理员!!");
23 }else {
24 //2,1,0
25 System.out.println("登录失败,还有"+(2-i)+"次机会");
26 }
27 }
28 }
29 }
30}
System类对读者来说并不陌生,因为在之前所学知识中,需要打印结果时,使用的都是“System.out.println();”语句,这句代码中就使用了System类。System类定义了一些与系统相关的属性和方法,它所提供的属性和方法都是静态的,因此,想要引用这些属性和方法,直接使用System类调用即可。
System类的常用方法如下表。
方法名称 | 功能描述 |
---|---|
static void exit(int status) | 该方法用于终止当前正在运行的Java虚拟机,其中参数status表示状态码,若状态码非0 ,则表示异常终止 |
static void gc() | 运行垃圾回收器,并对垃圾进行回收 |
static void 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.arraycopy()方法 arraycopy()方法用于将数组从源数组复制到目标数组,声明格式如下:
static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
关于声明格式中参数的相关介绍如下: ● src:表示源数组。 ● dest:表示目标数组。 ● srcPos:表示源数组中拷贝元素的起始位置。 ● destPos:表示拷贝到目标数组的起始位置。 ● length:表示拷贝元素的个数。
在进行数组复制时,目标数组必须有足够的空间来存放拷贝的元素,否则会发生角标越界异常。
接下来通过一个案例演示数组元素的拷贝。
1 public class Example10 {
2 public static void main(String[] args) {
3 int[] fromArray = { 10, 11, 12, 13, 14, 15 }; // 源数组
4 int[] toArray = { 20, 21, 22, 23, 24, 25, 26 }; // 目标数组
5 System.arraycopy(fromArray, 2, toArray, 3, 4); // 拷贝数组元素
6 // 打印拷贝后数组的元素
7 System.out.println("拷贝后的数组元素为:");
8 for (int i = 0; i < toArray.length; i++) {
9 System.out.println(i + ": " + toArray[i]);
10 }
11 }
12 }
上述代码中,第3~4行代码创建了两个数组fromArray和toArray,分别代表源数组和目标数组,第5行代码中当调用arraycopy()方法进行元素拷贝时,由于指定了从源数组中索引为2的元素开始拷贝,并且拷贝4个元素存放在目标数组中索引为3的位置,因此,在打印目标数组的元素时,程序首先打印的是数组toArray的前三个元素20、21、22,然后打印的是从fromArray中拷贝的四个元素12、13、14、15。
2.currentTimeMillis()方法 currentTimeMillis()方法用于获取当前系统的时间,返回值是long类型的值,该值表示当前时间与1970年1月1日0点0分0秒之间的时间差,单位是毫秒,通常也将该值称作时间戳。
接下来通过一个for循环的求和的案例计算程序运行时所消耗的时间。
1 public class Example11 {
2 public static void main(String[] args) {
3 long startTime = System.currentTimeMillis();// 循环开始时的当前时间
4 int sum = 0;
5 for (int i = 0; i < 1000000000; i++) {
6 sum += i;
7 }
8 long endTime = System.currentTimeMillis();// 循环结束后的当前时间
9 System.out.println("程序运行的时间为:"+(endTime - startTime)+"毫
10 秒");
11 }
12 }
上述代码中,第4~7行代码演示了数字的求和操作,程序在求和开始和结束时,分别调用了currentTimeMillis()方法获得了两个时间戳(系统当前时间),两个时间戳之间的差值便是求和操作所耗费的时间。
3.getProperties()和getProperty()方法 System类的getProperties()方法用于获取当前系统的全部属性,该方法会返回一个Properties对象,其中封装了系统的所有属性,这些属性是以键值对形式存在的。getProperty() 方法用于根据系统的属性名获取对应的属性值。
1 import java.util.*;
2 public class Example12 {
3 public static void main(String[] args) {
4 // 获取当前系统属性
5 Properties properties = System.getProperties();
6 // 获得所有系统属性的key,返回Enumeration对象
7 Enumeration propertyNames = properties.propertyNames();
8 while (propertyNames.hasMoreElements()) {
9 // 获取系统属性的键key
10 String key = (String) propertyNames.nextElement();
11 // 获得当前键key对应的值value
12 String value = System.getProperty(key);
13 System.out.println(key + "--->" + value);
14 }
15 }
16 }
上述代码中,第5行代码通过System的getProperties()方法获取了系统的所有属性,第7行代码通过Properties的propertyNames()方法获取所有的系统属性的key,并使用名称为propertyNames的Enumeration对象接受获取到的key值,第8~14行代码对Enumeration对象进行迭代循环,通过Enumeration的nextElement()方法获取系统属性的key,再通过System的getProperty(key)方法获取当前key对应的value,最后将所有系统属性的键以及对应的值打印出来。
从运行结果中可以看出,这些系统属性包括虚拟机版本号,用户的国家、操作系统的版本等。关于集合将在下一章进行讲解,在这里读者只需知道通过System.getProperties()方法可以获得系统属性和通过System.getProperty()方法可以根据系统属性名获得系统属性值即可。
4.gc()方法 在Java中,当一个对象成为垃圾后仍会占用内存空间,时间一长,就会导致内存空间的不足。针对这种情况,Java中引入了垃圾回收机制。有了这种机制,程序员不需要过多关心垃圾对象回收的问题,Java虚拟机会自动回收垃圾对象所占用的内存空间。
一个对象在成为垃圾后会暂时地保留在内存中,当这样的垃圾堆积到一定程度时,Java虚拟机就会启动垃圾回收器将这些垃圾对象从内存中释放,从而使程序获得更多可用的内存空间。除了等待Java虚拟机进行自动垃圾回收外,还可以通过调用System.gc()方法通知Java虚拟机立即进行垃圾回收。当一个对象在内存中被释放时,它的finalize()方法会被自动调用,因此可以在类中通过定义finalize()方法观察对象何时被释放。
接下来通过一个案例演示Java虚拟机进行垃圾回收的过程。
1 class Person {
2 // 下面定义的finalize方法会在垃圾回收前被调用
3 public void finalize() {
4 System.out.println("对象将被作为垃圾回收...");
5 }
6 }
7 public class Example13{
8 public static void main(String[] args) {
9 // 下面是创建了两个Person对象
10 Person p1 = new Person();
11 Person p2 = new Person();
12 // 下面将变量置为null,让对象成为垃圾
13 p1 = null;
14 p2 = null;
15 // 调用方法进行垃圾回收
16 System.gc();
17 for (int i = 0; i < 1000000; i++) {
18 // 为了延长程序运行的时间
19 }
20 }
21 }
上述代码中,第3~5行代码定义了一个finalize()方法,该方法的返回值必须为void。第10~11行代码创建了两个对象p1和p2,然后将两个对象设置为null,这意味着新创建的两个对象成为垃圾了,第16行代码通过“System.gc()”语句通知虚拟机进行垃圾回收。需要注意的是,Java虚拟机的垃圾回收操作是在后台完成的,程序结束后,垃圾回收的操作也将终止。因此,程序的第17~19行代码使用了一个for循环,延长程序运行的时间,从而能够更好地看到垃圾对象被回收的过程。
从运行结果可以看出,虚拟机针对两个垃圾对象进行了回收,并在回收之前分别调用两个对象的finalize()方法。 除了以上案例涉及到的方法,System类还有一个常见的方法exit(int status),该方法用于终止当前正在运行的Java虚拟机,其中参数status用于表示当前发生的异常状态,通常指定为0,表示正常退出,否则表示异常终止。
Runtime类用于表示虚拟机运行时的状态,它用于封装JVM虚拟机进程。每次使用java命令启动虚拟机都对应一个Runtime实例,并且只有一个实例,因此在Runtime类定义的时候,它的构造方法已经被私有化了(单例设计模式的应用),对象不可以直接实例化。若想在程序中获得一个Runtime实例,只能通过以下方式: Runtime run = Runtime.getRuntime(); 由于Runtime类封装了虚拟机进程,因此,在程序中通常会通过该类的实例对象来获取当前虚拟机的相关信息。
Runtime类常用方法如下表。
方法声明 | 功能描述 |
---|---|
getRuntime() | 该方法用于返回当前应用程序的运行环境对象。 |
exec(String command) | 该方法用于根据指定的路径执行对应的可执行文件 |
freeMemory**()** | 该方法用于返回Java虚拟机中的空闲内存量,以字节为单位。 |
maxMemory() | 该方法用于返回Java虚拟机的最大可用内存量。 |
availableProcessors() | 该方法用于返回当前虚拟机的处理器个数 |
totalMemory() | 该方法用于返回Java虚拟机中的内存总量 |
1.获取当前虚拟机信息 Runtime类可以获取当前Java虚拟机的处理器的个数、空闲内存量、最大可用内存量和内存总量的信息。
1 public class Example14 {
2 public static void main(String[] args) {
3 Runtime rt = Runtime.getRuntime(); // 获取
4 System.out.println("处理器的个数: " + rt.availableProcessors()+"个
5 ");
6 System.out.println("空闲内存数量: " + rt.freeMemory() / 1024 / 1024
7 + "M");
8 System.out.println("最大可用内存数量: " + rt.maxMemory() / 1024 /
9 1024 + "M");
10 System.out.println("虚拟机中内存总量: " + rt.totalMemory() / 1024 /
11 1024 + "M");
12 }
13 }
在上述代码中,第3行代码中通过Runtime的getRuntime()方法创建一个名称为rt的Runtime实例对象,在第4~5行代码通过Runtime的availableProcessors()方法获取了Java虚拟机的处理器个数;第6~7行代码通过Runtime的freeMemory()方法获取了Java虚拟机的空闲内存数;第8~9行代码通过Runtime的maxMemory()方法获取了Java虚拟机的最大可用内存数量;第10~11行代码通过Runtime的totalMemory()方法获取了Java虚拟机中内存总量。
由于每个人的机器配置不同,该文件的打印结果可能不同,另外空闲内存数、可用最大内存数和内存总量都是以字节为单位计算的,上述运行结果已经将字节换算成了兆(M)。
2.操作系统进程 Runtime类中提供了一个exec()方法,该方法用于执行一个dos命令,从而实现和在命令行窗口中输入dos命令同样的效果。例如,通过运行“notepad.exe”命令打开一个Windows自带的记事本程序。
1 import java.io.IOException;
2 public class Example15{
3 public static void main(String[] args) throws IOException {
4 Runtime rt = Runtime.getRuntime(); // 创建Runtime实例对象
5 rt.exec("notepad.exe"); // 调用exec()方法
6 }
7 }
上述代码中,第4行代码中通过Runtime的getRuntime()方法创建一个名称为rt的Runtime实例对象,在第5行代码中调用Runtime的exec()方法,并将 “otepad.exe”作为参数传递给exec()方法。运行程序会在桌面上打开一个记事本。
程序运行后,会在Windows系统中产生一个新的进程notepad.exe,可以通过任务管理器进行观察。
查阅API文档会发现,Runtime类的exec()方法返回一个Process对象,该对象就是exec()所生成的新进程,通过该对象可以对产生的新进程进行管理,如关闭此进程只需调用destroy()方法即可。具有代码如下所示:
public class Example {
public static void main(String[] args) throws Exception {
Runtime rt = Runtime.getRuntime(); // 创建一个Runtime实例对象
Process process = rt.exec("notepad.exe");//得到表示进程的Process对象
Thread.sleep(3000); // 程序休眠3秒
process.destroy(); // 杀掉进程
}
}
上述代码中,通过调用Process对象的destroy()方法关闭了打开的记事本。为了突出演示的效果,使用了Thread类的静态方法sleep(long millis)使程序休眠了3秒,因此,程序运行后,会看到打开的记事本在3秒后自动关闭了。
Math类提供了大量的静态方法来便于我们实现数学计算,如求绝对值、取最大或最小值等。
方法声明 | 功能描述 |
---|---|
abs() | 该方法用于计算绝对值 |
sqrt() | 该方法用于计算方根 |
ceil(a,b) | 该方法用于计算大于参数的最小整数 |
floor() | 该方法用于计算小于参数的最小整数 |
round() | 该方法用于计算小数进行四舍五入后的结果 |
max() | 该方法用于计算两个数的较大值 |
min() | 该方法用于计算两个数的较小值 |
random() | 该方法用于生成一个大于0.0小于1.0的随机值 |
sqrt() | 该方法用于计算开平方的结果 |
pow() | 该方法用于计算指数函数的值 |
1public class Example16 {
2 public static void main(String[] args) {
3 System.out.println("计算绝对值的结果: " + Math.abs(-10));
4 System.out.println("求大于参数的最小整数: " + Math.ceil(5.6));
5 System.out.println("求小于参数的最大整数: " + Math.floor(-4.2));
6 System.out.println("对小数进行四舍五入后的结果: " + Math.round(-4.6));
7 System.out.println("求两个数的较大值: " + Math.max(2.1, -2.1));
8 System.out.println("求两个数的较小值: " + Math.min(2.1, -2.1));
9 System.out.println("生成一个大于等于0.0小于1.0随机值: " +
10 Math.random());
11 System.out.println("开平方的结果: "+Math.sqrt(4));
12 System.out.println("指数函数的值: "+Math.pow(2, 3));
13 }
14 }
上述代码对Math类的常用方法进行了演示,在第3行代码中,使用Math的abs()方法计算-10的绝对值;在第4行代码中,使用Math的ceil()方法计算5.6的最小整数;在第5行代码中,使用Math的floor()方法计算-4.2的最大整数;在第6行代码中,使用Math的rount()方法计算-4.6的四舍五入结果;在第7行代码中,使用Math的max()方法求2.1与-2.1的较大值;在第8行代码中,使用Math的min()方法求2.1与-2.1的较小值,第9~10行代码中,使用random()方法生成一个大于等于0小于1.0的随机值;第11行代码中,使用sqrt()方法求4的开平方结果;第12行代码使用pow()方法求2与3的指数函数值,此方法前面的数是底数,后边的数是指数。
Java的java.util包中有一个Random类,它可以在指定的取值范围内随机产生数字。Random类中提供了两个构造方法如下表。
方法声明 | 功能描述 |
---|---|
Random() | 构造方法,用于创建一个伪随机数生成器 |
Random(long seed) | 构造方法,使用一个long型的seed种子创建伪随机数生成器 |
Random类的两个构造方法,其中第一个构造方法是无参的,通过它创建的Random实例对象每次使用的种子是随机的,因此每个对象所产生的随机数不同。如果希望创建的多个Random实例对象产生相同的随机数,则可以在创建对象时调用第二个构造方法,传入相同的参数即可。
接下来先采用第一种构造方法来产生随机数。
1 import java.util.Random;
2 public class Example17 {
3 public static void main(String args[]) {
4 Random r = new Random(); // 不传入种子
5 // 随机产生10个[0,100)之间的整数
6 for (int x = 0; x < 10; x++) {
7 System.out.println(r.nextInt(100));
8 }
9 }
10 }
从两次运行结果可知,两次产生的随机数序列是不一样的。这是因为当创建Random的实例对象时,没有指定种子,系统会以当前时间戳作为种子,产生随机数。
接下来采用第二种构造方法产生随机数。
1 import java.util.Random;
2 public class Example18 {
3 public static void main(String args[]) {
4 Random r = new Random(13); // 创建对象时传入种子
5 // 随机产生10个[0,100)之间的整数
6 for (int x = 0; x < 10; x++) {
7 System.out.println(r.nextInt(100));
8 }
9 }
10 }
相对于Math的random()方法而言,Random类提供了更多的方法来生成各种伪随机数,不仅可以生成整数类型的随机数,还可以生成浮点类型的随机数。Random类常用的方法如下表。
方法声明 | 功能描述 |
---|---|
double nextDouble() | 随机生成double类型的随机数 |
float nextFloat() | 随机生成float类型的随机数 |
int nextInt() | 随机生成int类型的随机数 |
int nextInt(int n) | 随机生成0~n之间int类型的随机数 |
Random类的nextDouble()方法返回的是0.0和1.0之间double类型的值,nextFloat()方法返回的是0.0和1.0之间float类型的值,nextInt(int n)返回的是0(包括)和指定值n(不包括)之间的值。
接下来通过一个案例学习这些方法的使用
1 import java.util.Random;
2 public class Example19 {
3 public static void main(String[] args) {
4 Random r1 = new Random(); // 创建Random实例对象
5 System.out.println("产生float类型随机数: " + r1.nextFloat());
6 System.out.println("产生double类型的随机数:" + r1.nextDouble());
7 System.out.println("产生int类型的随机数:" + r1.nextInt());
8 System.out.println("产生0~100之间int类型的随机数:" +
9 r1.nextInt(100));
10 }
11 }
本例要求编写一个程序,从键盘录入一个字符串,将字符串转换为二进制数。在转换时,将字符串中的每个字符单独转换为一个二进制数,将所有二进制数连接起来进行输出。
案例在实现时,要求使用Math类、String类以及Scanner等常见Java API的常用方法实现。
(1) 分析任务描述可知,键盘录入要转化的字符串。用Scanner实现。
(2)定义一个二维数组。其中4代表每一行长度。ss.length()根据键盘录入的字符串表示有多少行。
(3)利用for循环遍历字符串,遍历后用String的charAt()方法获取每个字符并转化成int。char与int进行运算,char的数值要减去 48,因为ASCII码中0的值是48,1就是49。
(4)转化成int后再用for循环获取每一个int类型的数进行转化成二进制赋值给数组。其中Math.pow()方法是Math类中求幂的方法。
(5)最后用双重for循环遍历二维数组。将结果输出到控制台。
1 package com.itheima;
2 import java.util.Scanner;
3 public class example4 {
4 public static void main(String[] args) {
5 //键盘录入要转化的字符串。用Scanner实现。
6 Scanner sc = new Scanner(System.in);
7 System.out.println("请输入要转换的字符串:");
8 String ss = sc.nextLine();
9 //定义一个二维数组。其中4代表每一行长度。ss.length()根据键盘录入的字
10 符串表示有多少行。
11 int [][] arr = new int[ss.length()][4];
12 //利用for循环遍历字符串,遍历后用String的charAt()方法获取每个字符并
13 转化成int。char与int进行运算,char的数值要减去 48,因为ASCII码中0
14 的值是48,1就是49。
15 for (int i = 0; i < ss.length(); i++) {
16 int charss = (int) ss.charAt(i)-48;
17 for (int j = 0; j < 4; j++) {
18 //转化成int后再用for循环获取每一个int类型的数进行转化成二进
19 制赋值给数组。其中Math.pow()方法是Math类中求幂的方法。
20 arr[i][j] = (int)((charss/Math.pow(2, 3-j))%2);
21 }
22 }
23 // 最后用双重for循环遍历二维数组。将结果输出到控制台。
24 System.out.println("二进制的数是:");
25 // 最后用双重for循环遍历二维数组。将结果输出到控制台。
26 for (int i = 0; i < ss.length(); i++) {
27 for (int j = 0; j < 4; j++) {
28 System.out.print(arr[i][j]);
29 }
30 }
31 }
}
在开发中经常需要处理日期和时间,Java提供了一套专门用于处理日期时间的API,在日期时间类中了包含LocalDate类、LocalTime类、Instant类、Duration类以及Period类等,这些类都包含在java.time包中。
类的名称 | 功能描述 |
---|---|
Instant | 表示时刻,代表的是时间戳 |
LocalDate | 不包含具体时间的日期 |
LocalTime | 不含日期的时间 |
LocalDateTime | 包含了日期及时间 |
Duration | 基于时间的值测量时间量 |
Period | 计算日期时间差异,只能精确到年月日 |
Clock | 时钟系统,用于查找当前时刻 |
Instant 类代表的是某个时间。其内部是由两个Long字段组成,第一部分保存的是标准Java计算时代(就是1970年1月1日开始)到现在的秒数,第二部分保存的是纳秒数。
方法声明 | 功能描述 |
---|---|
now() | 从系统时钟获取当前瞬时 |
now(Clock clock) | 从指定时钟获取当前瞬时 |
ofEpochSecond(long epochSecond): | 使用从自标准Java计算时代开始的秒数获得一个Instant的实例。 |
ofEpochMilli**(long** epochMilli**):** | 使用从自标准Java计算时代开始的秒数获得一个Instant的实例。 |
getEpochSecond(): | 从1970-01-01T00:00:00Z的Java时代获取秒数。 |
getNano(): | 从第二个开始就从时间线获得纳秒的数量。 |
parse(CharSequence text) | 从一个文本字符串(如2007-12-03T10:15:30.00Z获取一个Instant的实例。 |
from(TemporalAccessor tenporal) | 从时间对象获取一个Instant的实例。 |
接下来通过一个案例演示Instant类的使用。
1 import java.time.Instant;
2 public class Example20 {
3 public static void main(String[] args) {
4 // Instant 时间戳类从1970 -01 - 01 00:00:00 截止到当前时间的毫秒值
5 Instant now = Instant.now();
6 System.out.println("从系统获取的当前时刻为:"+now);
7 Instant instant = Instant.ofEpochMilli(1000 * 60 * 60 * 24);
8 System.out.println("计算机元年增加毫秒数后为:"+instant);
9 Instant instant1 = Instant.ofEpochSecond(60 * 60 * 24);
10 System.out.println("计算机元年增加秒数后为:"+instant1);
11 System.out.println("获取的秒值为:"+Instant.parse
12 ("2007-12-03T10:15:30.44Z").getEpochSecond());
13 System.out.println("获取的纳秒值为:"+Instant.parse
14 ("2007-12-03T10:15:30.44Z").getNano());
15 System.out.println("从时间对象获取的Instant实例为:"+
16 Instant.from(now));
17 }
18 }
上述代码中,第5行代码使用Instant的now()方法获取了系统中的当前时刻;第7行代码使用Instant的ofEpochMilli()方法获取了计算机元年增加毫秒数后的结果;第9行代码使用Instant的ofEpochSecond()方法获取了计算机元年增加秒数后的结果;第11~12行代码使用Instant的getEpochSecond()方法获取了从“007-12-03T10:15:30.44Z”到现在的秒值;第13~14行代码使用getNano()方法获取了从“007-12-03T10:15:30.44Z”到现在的纳秒值;第15~16行代码使用from()方法从时间对象获取了Instant的实例。从运行结果中可以看出每个方法所输出结果的格式。需要注意的是,now()方法默认获取的是西六区时间;parse()是从文本字符串获取的Instant实例。
LocalData类仅用来表示日期。通常表示的是年份和月份,该类不能代表时间线上的即时信息,只是日期的描述。在LocalData类中提供了两个获取日期对象的方法now()和of(int year, int month, int dayOfMonth), 具体代码如下所示:
//从一年,一个月和一天获得一个 LocalDate的实例
LocalDate date = LocalDate.of(2020, 12, 12);
//从默认时区的系统时钟获取当前日期
LocalDate now1 = LocalDate.now();
LocalData还提供了日期格式化、增减年月日等一系列的常用方法。
方法声明 | 功能描述 |
---|---|
getYear() | 获取年份字段 |
getMonth() | 使用Month枚举获取月份字段 |
getMonthValue() | 将月份字段从1到12 |
getDayOfMonth() | 获取当月第几天字段 |
format(DateTimeFormatter formatter) | 使用指定的格式化程序格式化此日期。 |
isBefore(ChronoLocalDate other) | 检查此日期是否在指定日期之前。 |
isAfter(ChronoLocalDate other) | 检查此日期是否在指定日期之后。 |
isEqual(ChronoLocalDate other) | 检查此日期是否等于指定的日期。 |
isLeapYear() | 根据ISO培训日历系统规则,检查年份是否是闰年。 |
方法声明 | 功能描述 |
---|---|
parse(CharSequence text) | 从一个文本字获取一个 LocalDate的实例。 |
parse(CharSequence text, DateTimeFormatter formatter) | 使用特定格式化 LocalDate从文本字符串获取 LocalDate的实例。 |
plusYears(long yearsToAdd) | 增加指定年份 |
plusMonths(long monthsToAdd) | 增加指定月份 |
plusDays(long daysToAdd) | 增加指定日数 |
minusYears(long yearsToSubtract) | 减少指定年份 |
minusMonths(long monthsToSubtract) | 减少指定月份 |
minusDays(long daysToSubtract) | 减少指定日数 |
withYear(int year) | 指定年 |
withMonth(int month) | 指定月 |
withDayOfYear(int dayOfYear) | 指定日 |
接下来通过一个案例来学习这些方法的使用。
1 import java.time.LocalDate;
2 import java.time.format.DateTimeFormatter;
3 public class Example21 {
4 public static void main(String[] args) {
5 //获取日期时分秒
6 LocalDate now = LocalDate.now();
7 LocalDate of = LocalDate.of(2015, 12, 12);
8 System.out.println("1. LocalData的获取及格式化的相关方法--------");
9 System.out.println("从LocalData实例获取的年份为:"+now.getYear());
10 System.out.println("从LocalData实例获取的月份:"
11 +now.getMonthValue());
12 System.out.println("从LocalData实例获取当天在本月的第几天:"+
13 now.getDayOfMonth());
14 System.out.println("将获取到的Loacaldata实例格式化为:"+
15 now.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
16 System.out.println("2. LocalData判断的相关方法----------------");
17 System.out.println("判断日期of是否在now之前:"+of.isBefore(now));
18 System.out.println("判断日期of是否在now之后:"+of.isAfter(now));
19 System.out.println("判断日期of和now是否相等:"+now.equals(of));
20 System.out.println("判断日期of是否时闰年:"+ of.isLeapYear());
21 //给出一个符合默认格式要求的日期字符串
22 System.out.println("3. LocalData解析以及加减操作的相关方法---------");
23 String dateStr="2020-02-01";
24 System.out.println("把日期字符串解析成日期对象后为"+
25 LocalDate.parse(dateStr));
26 System.out.println("将LocalData实例年份加1为:"+now.plusYears(1));
27 System.out.println("将LocalData实例天数减10为:"
28 +now.minusDays(10));
29 System.out.println("将LocalData实例指定年份为2014:"+
30 now.withYear(2014));
31 }
32 }
第6行代码定义了一个名称为now的LocalDate无参实例,第7行代码定义了一个名称为of的LocalDate有参实例,参数值为“2015,12,12”;第9~15行代码使用了LocalDate的获取及格式化的相关方法,其中第9行代码使用LocalDate的getYerar()方法获取了当前的年份,第10~11行代码使用LocalDate的getMonthValue()方法获取了当前的月份,第12~13行代码使用LocalDate的getDayOfMonth()方法获取了当前在本月的第几天,第14~15行代码使用LocalDate的farmot()方法将日期格式设置为(yyyy年MM月dd日)。
第17~21行代码使用了LocalDate判断的相关方法,其中第17行代码使用LocalDate的isBefore()判断日期of是否在当前时间前,第18行代码使用LocalDate的isAfter()方法判断日期of是否在当前时间后,第19行代码使用LocalDate的equals()方法判断日期of是否和now相等,第20行代码使用LocalDate的isLeapYear()方法判断日期of是否为闰年。
第23~30行代码使用了LocalDate解析以及加减操作的相关方法,其中第23行代码定义了一个名称为dateStr的字符串,dateStr的值为“2020—02—01”,第24~25行代码使用LocalDate的parse()方法将dateStr解析为日期对象,第26行代码使用LocalDate的plusYears()方法将now实例年份加1,第27~28行代码使用LocalDate的minusDays()方法将now实例天数减10,第29~30行代码使用LocalDate的withYear()方法将now实例年份指定为2014。
LocalTime类用来表示时间,通常表示的是小时分钟秒。与LocalData类一样,该类不能代表时间线上的即时信息,只是时间的描述。在LocalTime类中提供了获取时间对象的方法,与LocalData用法类似。 同时LocalTime类也提供了与日期类相对应的时间格式化、增减时分秒等常用方法,这些方法与日期类相对应,这里我们不再详细列举。
接下来我们通过一个案例来学习一下LocalTime类的方法。
1 import java.time.LocalTime;
2 import java.time.format.DateTimeFormatter;
3 public class Example22 {
4 public static void main(String[] args) {
5 // 1. 获取当前时间,包含毫秒数
6 LocalTime time = LocalTime.now();
7 LocalTime of = LocalTime.of(9,23,23);
8 System.out.println("从LocalTime获取的小时为:"+time.getHour());
9 System.out.println("将获取到的LoacalTime实例格式化为:"+
10 time.format(DateTimeFormatter.ofPattern(" HH:mm:ss")));
11 System.out.println("判断时间of是否在now之前:"+of.isBefore(time));
12 System.out.println("将时间字符串解析为时间对象后为:"+
13 LocalTime.parse("12:15:30"));
14 System.out.println("从LocalTime获取当前时间,不包含毫秒数:"+
15 time.withNano(0));
16 }
17 }
LocalDataTime类是LocalData类与LocalTime类的综合,它即包含日期也包含时间,通过查看API可以知道,LocalDataTime类中的方法包含了LocalData类与LocalTime类的方法。 需要注意的是,LocalDateTime默认的格式是 2020-02-29T21:23:26.774,这可能与我们经常使用的格式不太符合,所以它经常和DateTimeFormatter一起使用指定格式,除了LocalData与LocalTime类中的方法,额外提供了转换的方法。
下面我们通过一个案例来学习LocalDataTime类中特有的方法。
1 import java.time.LocalDateTime;
2 import java.time.format.DateTimeFormatter;
3 public class Example23 {
4 public static void main(String[] args) {
5 //获取当前年月日,时分秒
6 LocalDateTime now = LocalDateTime.now();
7 System.out.println("获取的当前日期时间为:"+now);
8 System.out.println("将目标LocalDateTime转换为相应的LocalDate实例:"+
9 now.toLocalDate());
10 System.out.println("将目标LocalDateTime转换为相应的LocalTime实例:"+
11 now.toLocalTime());
12 //指定格式
13 DateTimeFormatter ofPattern = DateTimeFormatter.ofPattern
14 ("yyyy年MM月dd日 HH时mm分ss秒");
15 System.out.println("格式化后的日期时间为:"+now.format(ofPattern));
16 }
17 }
第6行代码定义了一个名称为now的LocalDateTime实例,在7行代码直接打印当前日期now,第8~9行代码使用LocalDateTime的toLocalDate()方法将now转换为相应的LocalDate实例,第10~11行代码使用toLocalTime()方法将now转换为相应的LocalTime实例,第13~14行代码使用DateTimeFormatter的ofPattern()方法将时间格式指定为“yyyy年MM月DD日 HH时mm分ss秒”,第15行代码使用LocalDateTime的format()方法将now的时间按指定格式打印。
Duration类 Duration类基于时间值,其作用范围是天、时、分、秒、毫秒和纳秒,Duration类的常用方法如下表。
方法声明 | 功能描述 |
---|---|
between(Temporal startInclusive, Temporal endExclusive) | 获取一个Duration表示两个时间对象之间的持续时间。 |
toDays(): | 将时间转换为以天为单位的 |
toHours(): | 将时间转换为以时为单位的 |
toMinutes(): | 将时间转换为以分钟为单位的 |
toMillis(): | 将时间转换为以毫秒为单位的 |
toNanos(): | 将时间转换为以纳秒为单位的 |
接下来通过一个案例讲解Duration类中常用方法的使用。
1 import java.time.Duration;
2 import java.time.LocalTime;
3 public class Example24{
4 public static void main(String[] args) {
5 LocalTime start = LocalTime.now();
6 LocalTime end = LocalTime.of(20,13,23);
7 Duration duration = Duration.between(start, end);
8 //间隔的时间
9 System.out.println("时间间隔为:"+duration.toNanos()+"纳秒");
10 System.out.println("时间间隔为:"+duration.toMillis()+"毫秒");
11 System.out.println("时间间隔为:"+duration.toHours()+"小时");
12 }
13 }
上述代码中,第7行代码通过between()方法计算出start与end的时间间隔,第9行代码通过toNanos()方法将这个时间间隔转化为纳秒为单位的,第10行代码通过toMillis()方法将这个时间间隔转化为毫秒为单位的,第11行代码通过toHours()方法将这个时间间隔转化为小时为单位的。
Period类
Period主要用于计算两个日期的间隔,与Duration相同,也是通过between计算日期间隔,并提供了获取年月日的三个常用方法,分别是 getYears()、getMonths()和getDays()。
1 import java.time.LocalDate;
2 import java.time.Period;
3 public class Example25 {
4 public static void main(String[] args) {
5 LocalDate birthday = LocalDate.of(2018, 12, 12);
6 LocalDate now = LocalDate.now();
7 //计算两个日期的间隔
8 Period between = Period.between(birthday, now);
9 System.out.println("时间间隔为"+between.getYears()+"年");
10 System.out.println("时间间隔为"+between.getMonths()+"月");
11 System.out.println("时间间隔为"+between.getDays()+"天");
12 }
13 }
在上述代码中,第8行代码通过between()方法计算出birthday与now的时间间隔,第9行代码通过getYears()方法获取时间间隔的年份,第10行代码通过getMonths()方法获取时间间隔的月份,第11行代码通过getDays()方法获取时间间隔的天数。
二月是一个有趣的月份,平年的二月有28天,闰年的二月有29天。闰年每四年一次,在判断闰年时,可以使用年份除以4,如果能够整除,则该年是闰年。
本例要求编写一个程序,从键盘输入年份,根据输入的年份计算这一年的2月有多少天。在计算二月份天数时,可以使用日期时间类的相关方法实现。
(1)分析任务描述可知,要实现此功能,首先程序要用键盘录入一个年份。可以使用Scanner类实现
(2)设置日历对象的年、月、日。年:来自于键盘的输入,月:设置为2月,因为我们就是计算2月的。所以直接设置为2月。日:设置为1天,
(3)获取这一天输出。将结果打印到控制台。
1 package com.itheima;
2 import java.util.Scanner;
3 public class example4 {
4 public static void main(String[] args) {
5 //键盘录入要转化的字符串。用Scanner实现。
6 Scanner sc = new Scanner(System.in);
7 System.out.println("请输入要转换的字符串:");
8 String ss = sc.nextLine();
9 //定义一个二维数组。其中4代表每一行长度。ss.length()根据键盘录入的字
10 符串表示有多少行。
11 int [][] arr = new int[ss.length()][4];
12 //利用for循环遍历字符串,遍历后用String的charAt()方法获取每个字符并
13 转化成int。char与int进行运算,char的数值要减去 48,因为ASCII码中0
14 的值是48,1就是49。
15 for (int i = 0; i < ss.length(); i++) {
16 int charss = (int) ss.charAt(i)-48;
17 for (int j = 0; j < 4; j++) {
18 //转化成int后再用for循环获取每一个int类型的数进行转化成二进
19 制赋值给数组。其中Math.pow()方法是Math类中求幂的方法。
20 arr[i][j] = (int)((charss/Math.pow(2, 3-j))%2);
21 }
22 }
23 // 最后用双重for循环遍历二维数组。将结果输出到控制台。
24 System.out.println("二进制的数是:");
25 // 最后用双重for循环遍历二维数组。将结果输出到控制台。
26 for (int i = 0; i < ss.length(); i++) {
27 for (int j = 0; j < 4; j++) {
28 System.out.print(arr[i][j]);
29 }
30 }
31 }
32 }
Java是一种面向对象的语言,Java中的类可以把方法与数据连接在一起,但是Java语言中却不能把基本的数据类型作为对象来处理。而某些场合下可能需要把基本数据类型的数据作为对象来使用,为了解决这样的问题,JDK中提供了一系列的包装类,可以把基本数据类型的值包装为引用数据类型的对象,在Java中,每种基本类型都有对应的包装类。
各种基本类型都有对应的包装类。
基本数据类型 | 对应的包装类 |
---|---|
byte | Byte |
char | Character |
int | Integer |
short | Short |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
包装类和基本数据类型在进行转换时,引入了装箱和拆箱的概念,其中装箱是指将基本数据类型的值转为引用数据类型,反之,拆箱是指将引用数据类型的对象转为基本数据类型。
接下来以int类型的包装类Integer为例,通过一个案例演示装箱与拆箱的过程。
1 public class Example26 {
2 public static void main(String args[]) {
3 int a = 20;
4 Integer in = a;//自动装箱
5 System.out.println(in);
6 int l =in;//自动拆箱
7 System.out.println(l);
8 }
9 }
上述案例演示了包装类Integer的装箱过程和int的拆箱过程,在创建Integer对象时,将int类型的变量a作为参数传入,从而转为Integer类型。在创建基本数据类型int时,将Integer类型的值in直接赋值给l,从而转为int类型。
Integer类除了具有Object类的所有方法外,还有一些特有的方法。
方法声明 | 功能描述 |
---|---|
Integer valueOf(int i) | 返回一个表示指定的int值的 Integer 实例 |
Integer valueOf(String s) | 返回保存指定的String的值的 Integer 对象 |
int parseInt(String s) | 将字符串参数作为有符号的十进制整数进行解析 |
intValue() | 将 Integer 类型的值以int类型返回 |
上面表格列举了Integer的常用方法,其中的intValue()方法可以将Integer类型的值转为int类型,这个方法可以用来进行手动拆箱操作。parseInt(String s)方法可以将一个字符串形式的数值转成int类型,valueOf(int i)可以返回指定的int值为Integer实例。
接下来通过一个案例演示这些方法的使用。
1 public class Example27 {
2 public static void main(String args[]) {
3 Integer num = new Integer(20);//手动装箱
4 int sum = num.intValue() + 10;//手动拆箱
5 System.out.println("将Integer类值转化为int类型后与10求和为:"+ sum);
6 System.out.println("返回表示10的Integer实例为:" +
7 Integer.valueOf(10));
8 int w = Integer.parseInt("20")+32;
9 System.out.println("将字符串转化为整数位:" + w);
10 }
11 }
上述代码演示了手动拆箱的过程,Integer对象通过调用intValue()方法,将Integer对象转为int类型,从而可以与int类型的变量a进行加法运算,最终将运算结果正确打印。valueOf()方法将int类型的值转为Integer的实例。Integer对象通过调用包装类Integer的parseInt()方法将字符串转为整数,将字符串转为int类型,从而可以与int类型的常量10进行加法运算。
使用包装类时,需要注意以下几点: (1)包装类都重写了Object类中的toString()方法,以字符串的形式返回被包装的基本数据类型的值。
(2)除了Character外,包装类都有valueOf(String s)方法,可以根据String类型的参数创建包装类对象,但参数字符串s不能为null,而且字符串必须是可以解析为相应基本类型的数据,否则虽然编译通过,但运行时会报错。具体示例如下:
Integer i = Integer.valueOf("123"); // 合法
Integer i = Integer.valueOf("12a"); // 不合法
(3)除了Character外,包装类都有parseXxx(String s)的静态方法,将字符串转换为对应的基本类型的数据。参数s不能为null,而且同样字符串必须可以解析为相应基本类型的数据,否则虽然编译通过,但运行时会报错。具体示例如下:
int i = Integer.parseInt("123"); // 合法
Integer in = Integer.parseInt("itcast"); // 不合法
在程序开发过程中,我们会对一些字符串做各种限制,比如我们常见的注册邮箱、手机号等操作,一般都会做限制长度、格式等。而这些做限制的操作就是用正则表达式做到的。所谓的正则表达式就是指一个用来描述或者匹配一系列符合某个语法规则的字符串的单个字符串。其实就是一种规则。
正则表达式由普通字符(例如字符a-z)和特殊字符(元字符)组成的文字模式,所谓元字符就是指那些在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。
正则表达式常见的元字符如下表。
元字符 | 功能描述 |
---|---|
** | 转义字符,例如"\n"匹配"\n" |
^ | 正则表达式的开头标志 |
$ | 正则表达式的结尾标志 |
* | 匹配零次或多次 |
+ | 匹配一次或多次 |
? | 匹配一次或零次 |
. | 匹配任意字符 |
{n} | 匹配n次 |
{n,} | 至少匹配n次 |
{n,m} | n<=m,最少匹配n次,最多匹配m次 |
x|y| | 匹配x或y |
[xyz] | 字符集合。匹配所包含的任意一个字符 |
[a-z] | 字符范围。匹配指定范围内的任意字符 |
元字符 | 功能描述 |
---|---|
[a-zA-Z] | 匹配a到z或Z |
[a-z] | 字符范围。匹配指定范围内的任意字符 |
\d | 数字:[0-9] |
\D | 非数字: 0-9 |
\s | 空白字符:[ \t\n\x0B\f\r] |
\S | 非空白字符:\s |
\w | 单词字符:[a-zA-Z_0-9] |
\b | 单词边界 |
\B | 非单词边界 |
\A | 输入的开头 |
\G | 上一个匹配的结尾 |
\Z | 输入的结尾,仅用于最后的结束符(如果有的话) |
\z | 输入的结尾 |
\b | 单词边界 |
Pattern类 Pattern类用于创建一个正则表达式,也可以说创建一个匹配模式,它的构造方法是私有的,不可以直接创建,但可以通过Pattern.complie(String regex)简单工厂方法创建一个正则表达式,具体代码如下所示:
Pattern p=Pattern.compile("\\w+");
Pattern类常用方法如下表。
方法声明 | 功能描述 |
---|---|
split(CharSequence input) | 将给定的输入序列分成这个模式的匹配 |
Matcher matcher(CharSequence input) | 创建一个匹配器,匹配给定的输入与此模式 |
Static boolean matches(String regex, CharSequence input) | 编译给定的正则表达式,并尝试匹配给定的输入 |
接下来我们通过一个案例来学习Pattern类常用方法的使用。
1 import java.util.regex.Matcher;
2 import java.util.regex.Pattern;
3 public class Example28 {
4 public static void main(String[] args) {
5 Pattern p=Pattern.compile("\\d+");
6 String[] str=p.split("我的QQ是:456456我的电话是:0532214我的邮箱
7 是:[email protected]");
8 System.out.println("是否匹配Pattern的输入模式");
10 Pattern.matches("\\d+","2223aa"));
11 Matcher m=p.matcher("22bb23");
12 System.out.println("返回该Matcher对象是由哪个Pattern对象创建的,即p
13 为:"+ m.pattern());
14 System.out.print("将给定的字符串分割成Pattern模式匹配为:");
15 for (int i=0;i
上述代码中,第5行代码通过compile()方法创建一个正则表达式,第6~7行代码通过split()方法将字符串按照给定的模式进行分割,并返回名称为str的数组,第8~11行代码通过matcher()方法判断是否匹配pattern的输入模式,第12~14行代码通过pattern()方法判断Matcher对象是由哪个Pattern对象创建的,第15~18行代码通过for循环输出str数组。需要注意的是matches(String regex,CharSequence input)方法用于快速匹配字符串,该方法适合用于只匹配一次,且匹配全部字符串。
Matcher类 Matcher类用于在给定的Pattern实例的模式控制下进行字符串的匹配工作,同理Matcher的构造方法也是私有的,不能直接创建,只能通过Pattern. matcher(CharSequence input)方法得到该类的实例。
Matcher类的常用方法如下表。
方法声明 | 功能描述 |
---|---|
boolean matches() | 对整个字符串进行匹配,只有整个字符串都匹配了才返回true |
boolean lookingAt() | 对前面的字符串进行匹配,只有匹配到的字符串在最前面才返回true |
boolean find() | 对字符串进行匹配,匹配到的字符串可以在任何位置 |
int end() | 返回最后一个字符匹配后的偏移量 |
string group() | 返回匹配到的子字符串 |
int start() | 返回匹配到的子字符串在字符串中的索引位置 |
接下来通过一个案例来学习Matcher类常用方法的使用。
1 import java.util.regex.Matcher;
2 import java.util.regex.Pattern;
3 public class Example29 {
4 public static void main(String[] args) {
5 Pattern p=Pattern.compile("\\d+");
6 Matcher m=p.matcher("22bb23");
7 System.out.println("字符串是否匹配:"+ m.matches());
8 Matcher m2=p.matcher("2223");
9 System.out.println("字符串是否匹配:"+ m2.matches());
10 System.out.println("对前面的字符串匹配结果为"+ m.lookingAt());
11 Matcher m3=p.matcher("aa2223");
12 System.out.println("对前面的字符串匹配结果为:"+m3.lookingAt());
13 m.find();//返回true
14 System.out.println("字符串任何位置是否匹配:"+ m.find());
15 m3.find();//返回true
16 System.out.println("字符串任何位置是否匹配:"+ m3.find());
17 Matcher m4=p.matcher("aabb");
18 System.out.println("字符串任何位置是否匹配:"+ m4.find());
19 Matcher m1=p.matcher("aaa2223bb");
20 m1.find();//匹配2223
21 System.out.println("上一个匹配的起始索引::"+ m1.start());
22 System.out.println("最后一个字符匹配后的偏移量"+ m1.end());
23 System.out.println("匹配到的子字符串:"+ m1.group());
24 }
25 }
上述代码中,第6~9行代码通过matches()方法判断字符串是否匹配,第10~12行代码通过lookingAt()方法对前面的字符串进行匹配,第13~20行代码通过find()方法对字符串进行匹配,匹配到的字符串可以在任何位置,第21行代码通过start()方法得出上一个字符匹配的起始索引,第22行代码通过end()方法得出最后一个字符匹配后的偏移量,第23行代码通过group()方法得出匹配到的字符串。
String类提供了3个方法支持正则操作,如下表。
方法声明 | 功能描述 |
---|---|
boolean matches(String regex) | 匹配字符串 |
String replaceAll(String regex, String replacement) | 字符串替换 |
String[] split(String regex) | 字符串拆分 |
接下来通过一个案例学习这些方法的使用。
1 public class Example30{
2 public static void main(String[] args) {
3 String str = "A1B22DDS34DSJ9D".replaceAll("\\d+","_");
4 System.out.println("字符替换后为:"+str);
5 boolean te = "321123as1".matches("\\d+");
6 System.out.println("字符串是否匹配:"+te);
7 String s [] ="SDS45d4DD4dDS88D".split("\\d+");
8 System.out.print("字符串拆分后为:");
9 for(int i=0;i
通过运行结果可以看出,String类提供的方法可以很方便对字符串进行操作。需要注意的是,String类matches(String regex)方法的使用同Pattern类和Matcher类中该方法使用一样,必须匹配所有的字符串才返回true,否则返回false。
本章详细介绍了Java API的基础知识。首先从String类、StringBuffer类和StringBuilder类三个类的使用上介绍了字符串类;其次介绍了System类和Runtime类的使用;接着介绍了Math类与Random类的使用;然后详细介绍了日期时间类中的Instant类、LocalData类、LocalTime类、Period类和Dueation类;又介绍了基本类型所对应的包装类;最后从元字符、Pattern类、Matcher类和String类对正则表达式的支持详解介绍了正则表达式的使用。深入理解Java API,对以后的实际开发也是大有裨益的。