24.Java 字符串相关类(String/StringBuilder/StringBuffer/字符串效率测试)/时间处理相关类(Date/DateFormat/SimpleDateFormat/Calendar 日历类)
字符串相关类
简述
字符串相关类分为 String 类、StringBuilder 类、StringBuffer 类。
String 类:不可变字符序列。
StringBuffer 类:可变字符序列,线程安全,效率低(做线程同步检查)。
StringBuilder 类:可变字符序列,线程不安全,效率高(不做线程同步检查)。
String 类
简述
String 类对象代表不可变的 Unicode 字符序列,因此称 String 对象为 "不可变对象"。
"不可变对象" 即指对象内部的成员变量不可改变。
String 类常用方法
String 类的简单使用
代码示例:String 类的简单使用
package com.jungle.ClassTest;
// 测试 String 类的简单使用
public class StringTest01 {
public static void main(String[] args) {
/*
subString(int,int):返回字符串的子字符串。
hashCode() :返回字符串的哈希码。
toHexString(int): 以十六进制的无符号整数形式返回一个整数参数的字符串表示形式
*/
String s1 = new String("abcndf");
String s2 = s1.substring(2,5); //
// 打印结果:ab19bdce
System.out.println(Integer.toHexString(s1.hashCode()));
// 打印结果:18159 ,s1 和 s2 不是同一个对象。
System.out.println(Integer.toHexString(s2.hashCode()));
}
}
字符串常量拼接优化
遇到字符串常量之间的拼接时,编译器会在编译期间完成字符串的拼接,直接进行优化。
代码示例:字符串常量拼接时的优化
package com.jungle.ClassTest;
// 测试 String 类中字符串常量拼接时的优化
public class StringTest02 {
public static void main(String[] args) {
// 当编译器发现拼接的两个操作对象都是常量时,会进行优化,直接在编译时将字符串进行拼接
String str1 = "hello" + " 阿jun"; // 相当于 String str1 = "hello 阿jun"
String str2 = "hello 阿jun";
System.out.println(str1 == str2); // true
String str3 = "hello";
String str4 = " 阿jun";
// 下方代码在编译时编译器不知道变量的存储内容是什么,所以没办法在编译时进行优化
String str5 = str3 + str4;
// 优化比较
System.out.println(str2 == str5); // false
System.out.println(str2); // "hello 阿jun"
System.out.println(str5); // "hello 阿jun"
}
}
StringBuffer/StringBuilder 常用类
StringBuffer:可变字符序列,线程安全,效率低(做线程同步检查)。
StringBuilder:可变字符序列,线程不安全,效率高(不做线程同步检查),建议使用。
StringBuffer/StringBuilder 常用方法
代码示例:StringBuilder/StringBuffer 常用方法
package com.jungle.ClassTest;
// 测试 StringBuilder/StringBuffer 的常用方法
public class StringTest03 {
public static void main(String[] args) {
String sb1 = null; // 不可变字符序列
StringBuffer sb2 = null; // 可变字符序列,线程安全,效率低
StringBuilder sb3 = null; // 可变字符序列,线程不安全,效率高,推荐使用
/* StringBuilder 代码 */
// 创建一个新的 StringBuilder 对象
sb3 = new StringBuilder("阿jun");
// 调用 append() 方法,返回的是 this,自身
sb3.append("修炼");
sb3.append("手册");
System.out.println(sb3); // 阿jun修炼手册
sb3.append("Zzz").append("zzZ");
System.out.println(sb3); // 阿jun修炼手册ZzzzzZ
for (int i=0;i<=10;i++){
sb3.append(i); // append() 只修改里面的数组内容,不创建新对象
}
System.out.println(sb3); // 阿jun修炼手册ZzzzzZ012345678910
/* StringBuffer 代码,下方代码同样适用于 StringBuilder */
StringBuffer sb4 = new StringBuffer("阿jun");
// 插入字符串
sb4.insert(0,"Zzz").insert(0,"zzZ");
System.out.println(sb4); // zzZZzz阿jun
// 删除子字符串,索引包头不包尾
sb4.delete(0,3);
System.out.println(sb4); // Zzz阿jun
// 删除某一个字符,操作后返回的是自身对象
sb4.deleteCharAt(0).deleteCharAt(0).deleteCharAt(0);
System.out.println(sb4); // 阿jun
// 获取某个字符,下方是获取索引位置为 0 的字符
System.out.println(sb4.charAt(0)); // 阿
// 字符串逆序
System.out.println(sb4.reverse()); // nuj阿
}
}
可变和不可变字符序列使用陷阱
简述
String 一经初始化以后,就不会改变其内容。
对于 String 字符串的操作实际是对其副本(原始拷贝)的操作,不影响原本字符串。
代码测试:String 和 StringBuilder 在字符串频繁修改时的效率测试
package com.jungle.ClassTest;
// 测试看可变和不可变字符序列在频繁修改时的效率问题
public class StringTest04 {
public static void main(String[] args) {
/* 使用 String 进行字符串的拼接 */
String str1 = "";
// 本质上是使用的 StringBuilder 拼接,但每次循环都会产生一个新的 StringBuilder 对象
long num1 = Runtime.getRuntime().freeMemory(); // 获取系统剩余的内存空间
long time1 = System.currentTimeMillis(); // 获取系统的当前时间
// 通过 for 循环进行 5000 次 String 对象的拼接
for (int i = 0;i<5000;i++){
str1 = str1 + i; // 相当于拼接 5000 次
}
long num2 = Runtime.getRuntime().freeMemory(); // 获取系统甚于的内存空间
long time2 = System.currentTimeMillis(); // 获取系统的当前时间
System.out.println("String 操作以后消耗内存为:" + (num1 - num2));
System.out.println("String 操作以后消耗时间为:" + (time2 - time1));
/* StringBuilder 进行字符串的拼接 */
StringBuilder str2 = new StringBuilder(""); // 创建一个 StringBuilder 类的空字符串
long num3 = Runtime.getRuntime().freeMemory(); // 获取系统剩余的内存空间
long time3 = System.currentTimeMillis(); // 获取系统的当前时间
// 通过 for 循环进行 5000 次 StringBuilder 类的字符串的拼接
for (int i=0;i < 5000;i++){
str2 = str2.append(i);
}
long num4 = Runtime.getRuntime().freeMemory(); // 获取系统剩余的内存空间
long time4 = System.currentTimeMillis(); // 获取系统的当前时间
System.out.println("StringBuilder 操作以后消耗内存为:" + (num3 - num4));
System.out.println("StringBuilder 操作以后消耗时间为:" + (time4 - time3));
}
}
时间处理相关类
Date 类用法
简述
计算机中,将 1970 年 1 月 1 日 00:00:00 定位基准时间(零刻度),单位是毫秒(1秒的千分之一)
使用 long 类型的变量表示时间。
获取当前时刻:
long now = System.currentTimeMills();
时间相关类如下:
核心类是 java.util.Date 类,其余都是围绕它转
Date 时间类(java.util.Date)
代码示例:
package com.test;
import java.util.Date;
// 测试时间类
public class TestTime01 {
public static void main(String[] args) {
// 计算 Integer 最大值表示多少年
long a = Long.MAX_VALUE/(1000L * 3600 * 24 * 365); // 除以一年的毫秒数
System.out.println(a); // 292471208 年,大约是 2.9 亿年
long nowNum = System.currentTimeMillis(); // 代表当前时刻的毫秒数
System.out.println(nowNum); // 1640132064067
/* 测试 Date */
Date d1 = new Date(); // 创建时间类 Date 的对象时没有传参,则代表的是当前时刻
System.out.println(d1); // Wed Dec 22 08:24:38 CST 2021
System.out.println(d1.getTime()); // 1640132678369:gettime() 返回当前时间的毫秒数
// 给时间类对象传参
Date d2 = new Date(1000L * 3600 * 24 * 365 * 200); // 从 1970 年开始,再加上 200 年的时间
System.out.println(d2); // Mon Nov 13 08:00:00 CST 2169 (闰年会少那么几天,所以就会是 2169,而不是 2170)
}
}
时间格式化类
DateFormat 类
作用是把时间对象与指定格式的字符串进行互相转化。
DateFormat 是一个抽象类,一般用它的子类 SimpleDateFormat 类来实现。
代码示例:
package com.test;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 测试时间对象和字符串的互相转化
* 使用 DateFormat、SimpleDateFormat
* */
public class TestTime02 {
public static void main(String[] args) throws ParseException {
SimpleDateFormat df1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd");
// 将字符串转成 Date 对象
Date d1 = df1.parse("1992-10-22 10:56:50");
System.out.println(d1.getTime()); // 719722610000
// 将 Date 对象转为字符串
Date d2 = new Date(); // 如果不传参,默认就是现在时刻
String str = df1.format(d2);
System.out.println(str); // 1970-01-02 02:00:00
// 利用不同的格式化字符,输出不同的时间元素值,比如某年、某月、第多少天、第多少周,详见格式化字符图表
DateFormat df2 = new SimpleDateFormat("这周是今年第w周,今年的第D天,今天是星期中的E。"); // 字母即代表详细的格式化字符串含义
System.out.println(d2);
System.out.println(df2.format(d2));
}
}
格式化字符的含义表:
Calendar 日历类
Calendar 类是一个抽象类,提供了关于日期计算的相关功能,比如年、月、日、时、分、秒的展示和计算。
GregorianCalendar 是 Calendar 的一个具体子类,提供了标准的日历系统
注意点:
月份用 0-11 表示,即 11 代表 12 月
星期用 1-7 表示,即 1 是周日,2 是周一
代码示例:Calendar 日历类
package com.test;
import java.util.*;
/* 测试 Calendar 类和 GregorianCalendar 类的使用 */
public class TestCalendar {
public static void main(String[] args) {
// 创建 GregorianCalendar 子类对象,传入一个日期参数
GregorianCalendar calendar1 = new GregorianCalendar(2021,11,23,05,27,35);
int year = calendar1.get(Calendar.YEAR); // 打印年份:2021
// 月份用 0-11 表示,即 11 代表 12 月
int month = calendar1.get(Calendar.MONTH); // 打印月份:11
int day = calendar1.get(Calendar.DAY_OF_MONTH); // 打印当月的第多少号: 23
int day2 = calendar1.get(Calendar.DATE); // 打印当月的第多少号:23
// 日:Calendar.DATE 与 Calendar.DAY_OF_MONTH 同等意义
int date = calendar1.get(Calendar.DAY_OF_WEEK); // 打印今天是周几:5
// 星期用 1-7 表示,即 1 是周日,2 是周一,5 是周四
System.out.println(year); // 2021
System.out.println(month); // 11
System.out.println(day); // 23
System.out.println(day2); // 23
System.out.println(date); // 5
printCalendar(calendar1); // 2021年12月23日,星期4 5:27:35
// 自定义日期:set()
// 创建 GregorianCalendar 子类对象,传入一个日期参数
GregorianCalendar calendar2 = new GregorianCalendar();
calendar2.set(Calendar.YEAR,2021); // 设置年号
calendar2.set(Calendar.MONTH,Calendar.DECEMBER); // 设置月份:12月
calendar2.set(Calendar.DAY_OF_MONTH,23); // 设置当月的第多少号
calendar2.set(Calendar.DATE,23); // 设置当月的第多少号
calendar2.set(Calendar.DAY_OF_WEEK,5); // 设置周几
printCalendar(calendar2); // 2021年12月23日,星期4 6:17:27
// 日期计算:add()
GregorianCalendar calendar3 = new GregorianCalendar(2021,11,23,05,27,35);
calendar3.add(Calendar.YEAR,-2); // 年份减 2 年
calendar3.add(Calendar.DATE,7); // 增加 7 天
printCalendar(calendar3); // 2019年12月30日,星期1 5:27:35
// 日历对象和时间对象转化
Date d = calendar3.getTime();
GregorianCalendar calendar4 = new GregorianCalendar();
calendar4.setTime(new Date());
}
static void printCalendar(Calendar calendar){
int year = calendar.get(Calendar.YEAR); // 设置年份
// 月份用 0-11 表示,即 11 代表 12 月
int month = calendar.get(Calendar.MONTH) + 1; // 设置月份
int day = calendar.get(Calendar.DAY_OF_MONTH); // 设置当月的第多少号
int day2 = calendar.get(Calendar.DATE); // 设置当月的第多少号
// 日:Calendar.DATE 与 Calendar.DAY_OF_MONTH 同等意义
int date = calendar.get(Calendar.DAY_OF_WEEK) - 1; // 设置今天是周几
String week = "" + ((date==0)?"日":date);
// 星期用 1-7 表示,即 1 是周日,2 是周一,5 是周四
int hour = calendar.get(Calendar.HOUR); // 设置多少时
int minute = calendar.get(Calendar.MINUTE); // 设置多少分
int second = calendar.get(Calendar.SECOND); // 设置多少秒
// 打印多个拼接变量,用 printf
System.out.printf("%d年%d月%d日,星期%s %d:%d:%d\n",year,month,day,date,hour,minute,second);
}
}