一、字符串相关类
(一)String类常用方法
1. String类
- String代表字符串常量,用 “ ” 表示,是一个final类,不可被继承
- 实现Serializable接口表示字符串支持序列化,实现Comparable接口表示String可以比较大小
- 内部定义了final char[] value 用于存储字符串数据
- String代表不可变的字符序列
- 对字符串重新赋值时,需要重新指定内存区域赋值,不能对原有value进行修改赋值
- 对字符串进行连接操作时,需要重新指定内存区域赋值,不能对原有value进行修改赋值
- 当调用replace()修改字符/字符串时,需要重新指定内存区域赋值
- 通过字面量方式(区别于new)给字符串赋值,此时字符串值声明在字符串常量池中
- 字符串常量池中不会存储相同内容 (返使用equals()返回true)的字符串
public static void main(String[] args) {
String str = "abc";
stringTest(str);
System.out.println(str); // abc
String str1 = new String("abc");
stringTest(str1);
System.out.println(str1); // abc
}
public static void stringTest(String str) {
str = "stringTest";
}
public void test1() {
String s1 = "abc"; // 字面量的定义方式
String s2 = "abc";
System.out.println(s1 == s2); // 地址值 ture
s1 = "hello";
System.out.println(s1 == s2); // 地址值 false
String s3 = "abc";
s3 += "def";
System.out.println(s3); // abcdef
System.out.println(s3 == s2); // false
String s4 = "abcdef";
System.out.println(s3 == s4); // false
String s5 = "abc";
String s6 = s5.replace('a', 'm');
System.out.println(s5 == s6); // false
}
2. String对象的创建
- 通过字面量方式
- 通过new对象的方式
- String s = new String("abc"); 方式创建对象,在内存中创建了几个对象?
- 2个:①堆空间new结构 ②char[]对应常量池中的数据 “abc”
- String s = new String("abc"); 方式创建对象,在内存中创建了几个对象?
public void test2() {
// 此时s1,s2中的数据声明在方法区中的常量池中
String s1 = "JavaEE";
String s2 = "JavaEE";
// 此时s3,s4保存的地址值,是数据在堆空间中的地址值
String s3 = new String("JavaEE");
String s4 = new String("JavaEE");
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s3 == s4); // false
}
Person p1 = new Person(new String("Tom"), 12);
Person p2 = new Person(new String("Tom"), 12);
System.out.println(p1.name.equals(p2.name)); // true
System.out.println(p1.name == p2.name); // false
3. 字符串的拼接
- 常量与常量的拼接结果在常量池,常量池中不会存在相同内容的常量
- 只要拼接内容中有一个是变量,结果就在堆中
- 如果拼接结果调用intern(),返回值就在常量池中
public void test3() {
String s1 = "JavaEE";
String s2 = "Hadoop";
String s3 = "JavaEEHadoop";
String s4 = "JavaEE" + "Hadoop";
String s5 = s1 + "Hadoop";
String s6 = "JavaEE" + s2;
String s7 = s1 + s2;
System.out.println(s3 == s4); // true
System.out.println(s3 == s5); // false
System.out.println(s5 == s6); // false
System.out.println(s4 == s7); // false
String s8 = s5.intern();
System.out.println(s3 == s8); // true
}
- 面试小题:final修饰的为常量
String s1 = "JavaEEHadoop";
String s2 = "JavaEE";
String s3 = s2 + "Hadoop";
System.out.println(s1 == s3); // false
final String s4 = "JavaEE"; // s4是常量
String s5 = s4 + "Hadoop";
System.out.println(s1 == s5); // true
4. 字符串的值传递问题 —— String不可变性
String str = new String("good");
char[] ch = {'t', 'e', 's', 't'};
public void change(String str, char ch[]){
str = "test ok";
ch[0] = 'b';
}
public static void main(String[] args) {
StringTest1 test = new StringTest1();
test.change(test.str, test.ch);
System.out.print(test.str + " and "); // good and
System.out.println(test.ch); // best
}
5. JVM中涉及字符串的内存管理
- 三种JVM: ① Sun公司的HotSpot (默认) ② BEA公司的JRockit ③ IBM公司的J9VM
- 堆内存
- 一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的
- 类加载器读取类文件后把类、方法、常变量放到堆内存中,保存引用变量的真实信息
- 堆内存的三部分
- Young Generation Space 新生区:类产生、应用、被GC回收的区域
- Eden Space 伊甸园区:所有类都在该区被new出来
- Survivor Space 幸存者区
- 1区 (Survivor 1 Space)
- 0区 (Survivor 0 Space)
- Tenure Generation Space 养老区
- Permanent Space(Non-Heap)永久区、元空间 (方法区)
- 存放JDK自身携带的 Claerfass Intce元数据,存储运行环境必须加载的类信息
- 常驻内存区域,不会被GC回收,关闭JVM才会释放该区占用的内存
- 别名 Non-Heap 将其区分于堆,永久区是方法区的一个实现
- Young Generation Space 新生区:类产生、应用、被GC回收的区域
- 涉及JDK的内存区域
- JDK1.6:字符串常量池存储在方法区(永久区)
- JDK1.7:字符串常量池存储在堆空间
- JDK1.8:字符串常量池存储在方法区(元空间)
- OOM (OutOfMemoryError) 异常
- 产生java.lang.OutofMemoryError: Java heap space 的原因
- ES区满需创建对象执行Minor GC, 剩余对象移动到0区 >> 1区 >> 养老区
- 养老区满执行Major GC(Full GC),依然无法创建对象就会产生OOM
- 代码中创建了大量对象并且长时间不能被GC收集 —— 内存溢出、内存泄漏
- JVM堆内存设置不够,可通过参数-Xms、-Xmx调整
- 产生java.lang.OutofMemoryError: PermGen space 的原因
- eg. Tomcat下部署了太多应用、大量动态反射生成的类不断被加载等
- Java虚拟机堆永久代Perm内存设置不够,程序启动需加载大量第三方jar包
- 产生java.lang.OutofMemoryError: Java heap space 的原因
6. String类常用方法
- int length()
- char charAt(int index)
String s1 = "HelloWorld!";
System.out.println(s1.length()); // 11
// System.out.println(s1.charAt(11)); // StringIndexOutOfBoundsException
System.out.println(s1.charAt(10)); //!
- boolean isEmpty():String是否为空,判断标准为lenth = 0
String s2 = "";
System.out.println(s2.isEmpty()); // true
- String toLowerCase()
- String toUpperCase()
String s1 = "HelloWorld!";
String s3 = s1.toLowerCase();
String s4 = s1.toUpperCase();
System.out.println(s1); // HelloWorld! s1不可变
System.out.println(s3); // helloworld!
System.out.println(s4); // HELLOWORLD!
- String trim(): 忽略前空格和后空格,字符串中的空格不会删除
String s5 = " Hello World! ";
String s6 = s5.trim();
System.out.println(s6); //Hello World! 只删除前后空格,中间空格不会删除
- boolean equals(Object obj): 比较字符串内容
- boolean equalsIgnareCase(String s): 忽略大小写比较内容
String s1 = "HelloWorld!";
String s3 = s1.toLowerCase();
System.out.println(s1.equals(s3)); // false
System.out.println(s1.equalsIgnoreCase(s3)); // true
- String concat(String str):字符串连接
String s7 = s1.concat(" Java");
System.out.println(s7); // HelloWorld! Java
- int compareTo(String anotherString): 比较两个字符串大小; 负数当前对象小, 正数当前对象大
String s1 = "abc";
String s2 = "abe";
System.out.println(s1.compareTo(s2)); // -2 >> (99-101 = -2)
String s3 = "ac"; // a-97
String s4 = "Ae"; // A-65
System.out.println(s3.compareTo(s4)); // 32 (97 - 65 = 32)
- String substring(int beginIndex): 返回一个新字符串,从beginIndex开始到最后一个字符
- String substring(int biginIndex, int endIndex):返回新字符串,从begin到end,左闭右开
String s8 = "abcdefg";
String s9 = s8.substring(3);
String s10 = s8.substring(3, 6);
System.out.println(s9);// defg
System.out.println(s10); // def >> 左闭右开
- boolean endsWith(String suffix):判断字符串是否以suffix结束
- boolean startsWith(String prefix):判断字符串是否以prefix开始
- boolean startsWith(String prefix, int toffset): 判断字符串是否在toffset角标以字符串prefix开始
String str = "HelloWorld!";
boolean b1 = str.endsWith("world"); // false
boolean b2 = str.startsWith("Hello"); // true
boolean b3 = str.startsWith("hello"); // false
boolean b4 = str.startsWith("loW", 3); // true
- boolean contains(CharSequence s):判断字符串是否包含字符串s
String str1 = "HelloWorld!";
String str2 = "World";
boolean b5 = str1.contains(str2); // true
- int indexOf(String str):从前往后找返回首次出现str的首字母的角标,未找到返回 -1
- int indexOf(String str, int fromIndex):从指定索引开始从前往后找
- int lastIndexOf(String str) / lastIndexOf(String str, int fromIndex):从后向前找
- indexOf(str) 与 lastIndexOf(str) 返回值相同:① str只出现一次 ② str不存在
String str = "HelloWorld!";
System.out.println(str.indexOf("lo")); // 3
System.out.println(str.indexOf("lol")); // -1
System.out.println(str.indexOf("lo", 5)); // -1
System.out.println(str.lastIndexOf("ll")); // 2
System.out.println(str.lastIndexOf("l", 7)); // 3
- String replace(char oldChar, char newChar): 替换所有字符
- String replace(CharSequence target, CharSequence replacement):替换所有字符串
String str1 = "HelloJava!";
String str2 = str1.replace('a','o');
String str3 = str1.replace("Java", "World");
System.out.println(str1); // HelloJava!
System.out.println(str2); // HelloJovo!
System.out.println(str3); // HelloWorld!
- String replaceAll(String regex, String replacement): 替换匹配正则表达式regex的所有字符串
- String replaceFirst(String regex, String replacement): 替换匹配正则表达式的第一个字符串
String str = "12hello34world567java8910";
String str4 = str.replaceAll("\\d+", "!").replaceAll("^!|!$", ""); // 将数字(\d+)替换为!, 把开头(^)和结尾($)的!删除
String str5 = str.replaceFirst("\\d+", "!"); // 将数字替换为!
System.out.println(str4); // hello!world!java
System.out.println(str5); // !hello34world567java8910
- boolean matches(String regex):匹配正则表达式
String str = "12345";
boolean matches = str.matches("\\d+"); // true 字符串是否匹配数字
String tel = "0571-1234567";
boolean result = tel.matches("0571-\\d{7,8}"); // true 由0571-开头,后匹配长度为7-8位的数字
- String[] split(String regex)
- String[] split(String regex, int limit) 切割字符串最多不超过limit个, 剩下放在最后一个字符串中
String str = "hello|world|java";
String[] strs1 = str.split("\\|");
for (int i = 0; i < strs1.length; i++) {
System.out.print(strs1[i] + "\t"); //hello world java
}
String[] strs2 = str.split("\\|", 2);
for (int i = 0; i < strs2.length; i++) {
System.out.print(strs2[i] + "\t"); //hello world|java
}
String text = "a:b:c::";
String[] resultWithoutLimit = text.split(":");
String[] resultWithZeroLimit = text.split(":", 0);
String[] resultWithNegativeLimit = text.split(":", -1);
System.out.println("Without limit: " + java.util.Arrays.toString(resultWithoutLimit));
// 输出:[a, b, c]
System.out.println("With zero limit: " + java.util.Arrays.toString(resultWithZeroLimit));
// 输出:[a, b, c]
System.out.println("With negative limit: " + java.util.Arrays.toString(resultWithNegativeLimit));
// 输出:[a, b, c, , ]
7. String的类型转换
1)String、基本数据类型、包装类之间转换
- String >> 基本数据类型:调用包装类的静态方法 parseXxx()
- 基本数据类型 >> String:① 调用String重载的方法valueOf(xxx) ② 基本数据类型 + “”
String string = "123";
int num = Integer.parseInt(string);
String str1 = String.valueOf(num);
String str2 = num + "";
System.out.println(string == str2); // false
2)String与char[]之间转换
- String >> char[]:调用String的 toCharArray()
- char[] >> String:调用String的构造器
String string = "abc123";
char[] charArray = string.toCharArray();
String str = new String(charArray);
3)String与byte[]之间转换
- 编码:字符串 >> 字节:String >> byte[]:调用String的 toByteArray()
- 解码:字节 >> 字符串:byte[] >> String:调用String的构造器
- 解码时使用的字符集必须与编码时使用的字符集相同,否则会出现乱码
String string = "abc123中国"; // UTF-8编码1个汉字占3个字节, gbk中占2个字节
byte[] byteArray = string.getBytes(); // 使用默认字符集编码,输出字符的对应ASkII码
byte[] gbks = string.getBytes("gbk"); // 使用gbk进行编码
String str = new String(byteArray); // 使用默认字符集解码
String str1 = new String(gbks); // 出现乱码,原因:编码集和解码集不一致
String str2 = new String(gbks, "gbk"); // 定义解码方式,编解码方式一致不会乱码
8. 几道算法题
1)模拟一个trim方法,去除字符串两端的空格
public String trim(String str) {
if (str != null) {
int startIndex = 0, endIndex = str.length() - 1;
while (startIndex < endIndex && str.charAt(startIndex) == ' ') {
startIndex++;
}
while (endIndex > startIndex && str.charAt(endIndex) == ' ') {
endIndex--;
}
if (str.charAt(startIndex) == ' ') return "";
return str.substring(startIndex, endIndex + 1);
}
return null;
}
2)将一个字符串进行反转。将字符串中指定部分进行反转
// 方式1
public String reverse1(String str, int startIndex, int endIndex) {
if (str != null && str.length() != 0) {
char[] arr = str.toCharArray();
for (int x = startIndex, y = endIndex; x < y; x++, y--) {
char temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
return new String(arr);
}
return str;
}
// 方式2 使用String拼接操作
public String reverse2(String str, int startIndex, int endIndex) {
if (str != null && str.length() != 0) {
String reverseStr = str.substring(0, startIndex);
for (int i = endIndex; i >= startIndex; i--) {
reverseStr += str.charAt(i);
}
reverseStr += str.substring(endIndex + 1);
return reverseStr;
}
return str;
}
// 方式3 使用StringBuffer/StringBuilder替换String
public String reverse3(String str, int startIndex, int endIndex) {
if (str != null && str.length() != 0) {
StringBuilder sBuilder = new StringBuilder();
sBuilder.append(str.substring(0, startIndex));
for (int i = endIndex; i > startIndex; i--) {
sBuilder.append(str.charAt(i));
}
sBuilder.append(str.substring(endIndex - 1));
return sBuilder.toString();
}
return str;
}
3)获取一个字符串在另一个字符串中出现的次数
public int count(String mainStr, String subStr) {
int mainLength = mainStr.length();
int subLength = subStr.length();
int count = 0;
int index = 0;
if (mainLength >= subLength) {
// 方式1
while ((index = mainStr.indexOf(subStr)) != -1) {
count++;
mainStr = mainStr.substring(index + subLength);
}
// 方式2
while ((index = mainStr.indexOf(subStr, index)) != -1) {
count++;
index += subLength;
}
}
return count;
}
4)获取两个字符串中最大的相同子串
public String[] subString2(String str1, String str2) {
if (str1 != null && str2 != null) {
StringBuffer sBuffer = new StringBuffer();
String longStr = str1.length() > str2.length() ? str1 : str2;
String shortStr = str1.length() >= str2.length() ? str2 : str1; // 长度相同情况
for (int length = shortStr.length(); length > 0; length--) {
for (int startIndex = 0, endIndex = startIndex + length - 1;
endIndex < shortStr.length(); startIndex++, endIndex++) {
String subStr = shortStr.substring(startIndex, endIndex + 1);
if (longStr.contains(subStr)) {
sBuffer.append(subStr + ",");
}
}
if (sBuffer.length() != 0) {
return sBuffer.toString().replaceAll(",$", "").split("\\,");
}
}
}
return null;
}
5)对字符串中的字符进行自然顺序排序
public String sort(String str) {
char[] chars = str.toCharArray();
for (int i = 0; i < str.length() - 1; i++) {
for (int j = 0; j < str.length() - i - 1; j++) {
if (chars[i] < chars[j]) {
char temp = chars[i];
chars[i] = chars[j];
chars[j] = temp;
}
}
}
return new String(chars);
}
(二)StringBuffer、StringBuilder
1. String、StringBuffer、StringBuilder三者异同
- 相同点:都是字符串相关类,底层使用char[]存储
- 不同点
- String:不可变字符序列
- StringBuffer:可变字符序列;线程安全,效率低;
- StringBuilder:可变字符序列;jdk5.0新增,线程不安全,效率高;
- 底层存储:char[]数组
- 数组长度: 输出字符串内容长度
- 扩容问题: 底层数组长度不够扩容, 默认情况原长度2倍+2, 同时将原数组元素赋值到新数组中
String str = new String(); // char[] value = new char[0];
String str1 = new String("abc"); // char value = new char[]{'a', 'b', 'c'};
StringBuffer sb1 = new StringBuffer(); // char value = new char[16]; 相当与底层创建了长度为16的char[]\
System.out.println(sb1.length()); // 0
sb1.append('a'); // value[0] = 'a';
sb1.append('b'); // value[1] = 'b';
StringBuffer sb2 = new StringBuffer("abc"); // char value = new char["abc".length + 16];
System.out.println(sb2.length()); // 3
2. 常用方法
- StringBuffer append(xxx) 字符串拼接。可扩容,支持方法链
StringBuffer sb = new StringBuffer();
sb.append('a');
sb.append("bcdef");
- StringBuffer delete(int start, int end) 删除指定位置的内容 左闭右开
StringBuffer sb = new StringBuffer("abcd");
sb.delete(2, 4);
System.out.println(sb); // ab
- StringBuffer replace(int start, int end, String str) 把(start,end)位置内容换为str
StringBuffer sb = new StringBuffer("abcdef");
sb.replace(2, 4, "hello");
System.out.println(sb); // abhelloef
- StringBuffer insert(int offset, xxx) 在指定位置插入xxx。可扩容,支持方法链
StringBuffer sb = new StringBuffer("abCdef");
sb.insert(2, false); // abfalseCedf
- StringBuffer reverse() 字符串序列逆转
StringBuffer sb = new StringBuffer("abc");
sb.reverse(); // cba
- int indexOf(String str)
StringBuffer sb = new StringBuffer("hello");
int index = sb.indexOf("l"); // 2
- String substring(int start, int end) 返回从索引start到end 左闭右开区间内的新子字符串
StringBuffer sb = new StringBuffer("hello");
String string = sb.substring(1, 4);
System.out.println(string); // ell
- int length()
- char charAt(int n)
- void setCharAt(int n, char ch) 将指定索引位置字符改为新字符
3. 三者效率对比:StringBuffer > StringBuilder > String
@Test
public void test() {
// 初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer sBuffer = new StringBuffer("");
StringBuilder sBuilder = new StringBuilder("");
// 开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
sBuffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime)); // 5
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
sBuilder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime)); // 2
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime)); // 427
}
4. String、StringBuffer、StringBuilder之间转换
- String >> StringBuffer、StringBuilder
- 调用StringBuffer、StringBuilder的构造器
- StringBuffer、StringBuilder >> String
- 调用String的构造器
- 调用StringBuffer、StringBuilder的toString()
String str = "abc";
StringBuffer sBuffer = new StringBuffer(str);
StringBuilder sBuilder = new StringBuilder(str);
String str1 = new String(sBuffer);
String str2 = new String(sBuilder);
String str3 = sBuffer.toString();
String str4 = sBuilder.toString();
5. 一道面试题
String str = null;
StringBuffer sBuffer = new StringBuffer();
sBuffer.append(str);
System.out.println(sBuffer.length()); // 4 // appendNull(): value[] = {'n', 'u', 'l', 'l'}
System.out.println(sBuffer); // "null"
StringBuffer sBuffer1 = new StringBuffer(str);
System.out.println(sBuffer1); // NullPointerException
二、日期时间API
- 第一代:JDK1.0 Date类
- 第二代:JDK1.1 Calander类
- 第三代:JDK1.8 提出一套新API
(一)JDK8前
1. System类静态方法
- public static long currentTimeMillis()
- 返回当前时间与1/1/1970 00:00:00 GMT的时间差,也称为时间戳,单位为毫秒
- 国际标准时间
- UTC (Coordinated Universal Time)
- GMT (Greenwich Mean Time)
- CST (Central Standard Time)
long time = System.currentTimeMillis();
System.out.println(time); // 单位毫秒,代表1970年1月1日到现在的时间差,称为时间戳
2. Date类
1)java.util.Date类
- 两个构造器的使用
- Date(): 创建一个当前时间的Date对象
- Date(long型时间): 创建一个指定时间戳的Date对象
- 两个方法的使用
- toString(): 显示当前的年、月、日、时、分、秒
- getTime(): 获取当前Date对象对应的时间戳
Date date1 = new Date();
System.out.println(date1.toString()); // Mon Oct 03 15:50:06 CDT 2022
System.out.println(date1.getTime()); // 1664830206571
// @Deprecated: 过时了,也能用
Date date2 = new Date(1664830206571L);
System.out.println(date2.toString()); // Mon Oct 03 15:50:06 CDT 2022
2)java.sql.Date类
- 实例化问题
- java.util.Date对象 >> java.sql.Date对象
java.sql.Date date3 = new java.sql.Date(1664830206571L);
System.out.println(date3); // 2022-10-03
Date date4 = new java.sql.Date(1664830206571L);
java.sql.Date date5 = (java.sql.Date) date4;
Date date6 = new Date();
// java.sql.Date date7 = (java.sql.Date) date6; // java.lang.ClassCastException
java.sql.Date date7 = new java.sql.Date(date6.getTime());
3. SimpleDateFormat类
- 对日期Date类的格式化和解析
1)格式化:日期 >> 字符串
SimpleDateFormat sdf = new SimpleDateFormat(); // 默认空参构造器
Date date = new Date(); // Thu Oct 06 19:23:53 CDT 2022
String format = sdf.format(date); // 2022/10/6 下午7:23
2)解析:字符串 >> 日期
SimpleDateFormat sdf = new SimpleDateFormat();
String str = "2022/10/6 下午7:23";
Date date = sdf.parse(str); // Thu Oct 06 19:23:00 CDT 2022
String str1 = "2022/10/6 7:23";
Date date4 = sdf.parse(str1); // 报错 Unparseable date: "2022/10/6 7:23"
3) 带有日期格式的实例化
- 字符串必须符合SDF识别的格式(构造器参数格式),否则会报错Unparseable date
// SimpleDateFormat实例化(指定日期格式参数)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
//格式化日期
String format = sdf.format(new Date()); // 2022-10-07 01:45:06
// 解析日期
String str = "2022-10-07 01:45:06";
Date date = sdf.parse(str);// Fri Oct 07 01:45:06 CDT 2022
- 练习一:将字符串”2022-10-07“转换为java.sql.Date类型
String str = "2022-10-07";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date parse = sdf.parse(str);
java.sql.Date date = new java.sql.Date(parse.getTime());
- 练习二:三天打鱼两天晒网
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date1 = sdf.parse("1990-01-01");
Date date2 = sdf.parse("2020-10-10");
long time = date2.getTime() - date1.getTime();
long day = time / (1000 * 60 * 60 * 24) + 1;
String result = (day % 5 >= 1 && day % 5 <= 3) ? "打鱼" : "晒网";
4. Calender类
- Calendar是一个抽象类,完成日期字段之间相互操作的功能
1)实例化
- 方式1:创建子类GregorianCalendar对象
- 方式2:调用静态方法getInstance()
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getClass()); // GregorianCalendar
2)常用方法
- Calender是可变的
- 注意
- 获取月份时(MONTH):一月是0,二月是1 ... 十二月是11
- 获取星期时(DAY_OF_WEEK):周日是1,周一是2 ... 周六是7
// get()
int day = calendar.get(Calendar.DAY_OF_MONTH); // 7 返回当前日期是当月的第几天
// set()
calendar.set(Calendar.DAY_OF_MONTH, 22);
day = calendar.get(Calendar.DAY_OF_MONTH); // 22
// add()
calendar.add(Calendar.DAY_OF_MONTH, 3); // 25
calendar.add(Calendar.DAY_OF_MONTH, -3); // 22
// getTime()
Date date = calendar.getTime(); // 修改后的calendar日期 Sat Oct 22 19:45:58 CDT 2022
// setTime()
calendar.setTime(date); // 修改calendar为date日期
(二)JDK8
- Calendar与Date类存在的问题
- 可变性:日期和时间这样得类应该是不可变的
- 偏移性:Date中的年份是从1900年开始的,月份是从0开始的
- 格式化:格式化只对Date有用,Calendar则不行
- 线程不安全,不能处理闰秒
Date date = new Date(2020 - 1900, 1 -1, 1); // 偏移性
- 新日期时间API
- java.time: 值对象的基础包
- java.time.chrono: 提供对不同的日历系统的访问
- java.time.format: 格式化和解析时间和日期
- java.time.temporal: 包括底层框架和延展特性
- java.time.zone: 包含时区支持的类
1. LocalDate、LocalTime、LocalDateTime类
- 实例是不可变的对象
- 表示使用ISO-8601日历系统的日期、时间、日期和时间, 不包含当前时间信息、时区相关信息
1)实例化相关
- now(): 获取当前日期/时间/日期时间
- of(): 设置指定年月日时分秒,无偏移量
LocalDate localDate = LocalDate.now(); // 2022-10-08
LocalTime localTime = LocalTime.now(); // 13:22:12.199293300
LocalDateTime localDateTime = LocalDateTime.now(); // 2022-10-08T13:22:12.200296100
LocalDateTime ofLocalDateTime = LocalDateTime.of(2022, 10, 9, 13, 33, 33);
2)getXxx()获取相关属性:起始为1
int dayOfMonth = localDateTime.getDayOfMonth(); // 8 获取该月第几天
int monthValue = localDateTime.getMonthValue(); // 10
3)withXxx()设置相关属性:体现不可变性
LocalDate localDate1 = localDate.withDayOfMonth(22);
System.out.println(localDate1); // 2022-10-22
System.out.println(localDate); // 2022-10-08
4)plus/ minus 操作:体现不可变性
LocalDate localDate2 = localDate.plusMonths(3);
LocalDate localDate3 = localDate.minusMonths(10);
System.out.println(localDate2); // 2023-01-08
System.out.println(localDate3); // 2021-12-08
System.out.println(localDate); // 2022-10-08
2. Instant类
- 本初子午线时间的瞬时点,起始点1970年1月1日,单位为毫秒,其他时区需加偏移量
// now(): 获取本初子午线时间
Instant instant = Instant.now(); // 本初子午线时间
// atOffSet(): 添加时间偏移量
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8)); //获取东八区时间
// toEpochMilli(): 获取举例1970年1月1日0时0分0秒到现在的瞬时毫秒数
long milli = instant.toEpochMilli();
// ofEpochMilli(Long long): 通过给定毫秒数创建instant实例
Instant instant1 = Instant.ofEpochMilli(1665251928772L);
3. DateTimeFormatter类
- 格式化或解析日期/时间,类似于SimpleDateFormat
1)三种实例化方式
- 预定义的标准模式: (ISO预定义) ISO_LOCAL_DATE, ISO_OFFSET_DATE, ISO_DATE...
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
// 格式化: 日期 >> 字符串
String strFormat = formatter.format(LocalDateTime.now()); // 2022-10-08T14:19:51.2200785
// 解析: 字符串 >> 日期
TemporalAccessor parse = formatter.parse("2022-10-08T14:18:43.2536188"); // {},ISO resolved to 2022-10-08T14:18:43.253618800
- 本地化相关的格式
// 1. ofLocalizedDateTime(): FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT
// 注:FormatStyle.LONG: JDK 8.0可运行,8.0以上需要添加.withZone(ZoneId.systemDefault()
DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
String format1 = formatter1.format(LocalDateTime.now()); // 2022/10/8 下午3:24
DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG).withZone(ZoneId.systemDefault());
String format2 = formatter2.format(LocalDateTime.now()); // 2022年10月8日 CDT 下午3:24:16
// 2. ofLocalizedDate(): FormatStyle.FULL / FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT
DateTimeFormatter formatter3 = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);
String format3 = formatter3.format(LocalDateTime.now()); // 2022年10月8日星期六
- 自定义格式: ofPattern("自定义日期时间pattern")
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
// 格式化
String format = dateTimeFormatter.format(LocalDateTime.now()); // 2022-10-08 03:37:43
// 解析
TemporalAccessor parse1 = dateTimeFormatter.parse("2022-10-08 03:37:43");
2)常用方法总结
- ofParttern(String pattern): 自定义格式,返回一个指定字符串格式的DateTimeFormatter
- format(TemporalAccessor t): 格式化,日期 >> 字符串
- parse(charSequence text0): 解析,字符串 >> 日期
4. 其他API
1)时区相关
- ZoneId.of(): 获取指定时区的时间
LocalDateTime localDateTime = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
- ZonedDateTime: 含时区的日期时间
- now(): 获取本时区
- now(zoneId id): 获取指定时区的ZonedDateTime对象
ZonedDateTime zonedDateTime = ZonedDateTime.now();
ZonedDateTime zonedDateTime1 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
2)间隔相关
- Duration: 计算两个”时间“间隔,以秒和纳秒为基准
- between(): 静态方法,返回Duration对象,表示两个时间的间隔
- end时间在start时间前,结果为负数
LocalTime localTime1 = LocalTime.now();
LocalTime localTime2 = LocalTime.of(15, 23, 23);
Duration duration = Duration.between(localTime1, localTime2);
System.out.println(duration.getSeconds());
System.out.println(duration.getNano());
LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 10, 22, 22, 22);
LocalDateTime localDateTime2 = LocalDateTime.of(2022, 10, 10, 22, 22, 22);
Duration duration1 = Duration.between(localDateTime1, localDateTime2);
System.out.println(duration1.toDays());
- Period: 用于计算两个”日期“间隔,以年、月、日衡量
LocalDate date1 = LocalDate.now();
LocalDate date2 = LocalDate.of(2020, 10, 10);
Period period = Period.between(date2, date1);
System.out.println(period); // P1Y11M28D
System.out.println(period.getYears()); // 1
System.out.println(period.getMonths()); // 11
System.out.println(period.getDays()); // 28
Period period1 = period.withYears(2); // 不可变性
System.out.println(period1); // P2Y11M28D
3)时间矫正
- TemporalAdjuster: 时间矫正量
// 获取当前日期的下一个周日
TemporalAdjuster temporalAdjuster = TemporalAdjusters.next(DayOfWeek.SUNDAY);
LocalDateTime nextSunday = LocalDateTime.now().with(temporalAdjuster);
System.out.println(nextSunday); // 2022-10-09T17:55:53.506655300
// 获取下一个工作日
LocalDate nextWorkDay = LocalDate.now().with(new TemporalAdjuster() {
@Override
public Temporal adjustInto(Temporal temporal) {
LocalDate date = (LocalDate) temporal;
if (date.getDayOfWeek().equals(DayOfWeek.FRIDAY)) {
return date.plusDays(3);
} else if (date.getDayOfWeek().equals(DayOfWeek.SATURDAY)) {
return date.plusDays(2);
} else return date.plusDays(1);
}
});
System.out.println(nextWorkDay);// 2022-10-10
三、Java比较器
- Java中的对象正常只能进行比较:== , != (判断引用指向对象实体是否相同), 不能使用 >, <
1. Comparable接口 (自然排序)
- String/包装类等实现了Comparable接口, 重写compareTo()方法提供比较两个对象大小的方式
- 重写compareTo()规则
- 当前对象this与形参对象obj相比,大于返回正整数,小于返回负整数,相等返回0
- 结果默认从小到大排列
- 自定义类需要排序,需实现Comparable接口,重写CompareTo()指明如何排序
String[] str = new String[]{"FF", "AA", "DD", "CC", "BB"};
Arrays.sort(str);
@Test public void test() {
Goods[] arr = new Goods[5];
arr[0] = new Goods("lianxiang", 34);
arr[1] = new Goods("Huawei", 50);
arr[2] = new Goods("dell", 45);
arr[3] = new Goods("yiaomi", 12);
arr[4] = new Goods("xiaomi", 12);
Arrays.sort(arr); // 如果没有实现Comparable接口会报ClassCastException
System.out.println(Arrays.toString(arr));
}
class Goods implements Comparable {
private String name;
private double price;
// 构造器, getter, setter, toString省略
@Override
public int compareTo(Object o) { // 指明商品比较大小的方式
if (o instanceof Goods) {
Goods goods = (Goods) o;
// 方式1
if (this.price > goods.price) {
return 1;
} else if (this.price < goods.price) {
return -1;
} else {
// 价格相同 按名称从大到小排
return -this.name.compareTo(goods.name);
}
// 方式2
return Double.compare(this.price, goods.price);
}
throw new RuntimeException("传入数据类型不一致");
}
}
2. Comparator接口 (定制排序)
- 当元素类型没有实现Comparable接口又不方便修改代码,或接口规则无法满足需求
- 重写Compare(Object o1, Object o2)规则
- o1与o2相比,大于返回正整数,小于返回负整数,相等返回0
- Comparable接口与Comparator接口对比
- Comparable接口方式一旦指定,可保证实现类的对象在任何位置都可以比较大小
- Comparator接口属于临时性的比较
@Test public void test2() {
Goods[] arr = new Goods[5];
arr[0] = new Goods("lianxiang", 34);
arr[1] = new Goods("Huawei", 50);
arr[2] = new Goods("dell", 45);
arr[3] = new Goods("xiaomi", 20);
arr[4] = new Goods("xiaomi", 12);
Arrays.sort(arr, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
// 产品名称从低到高,价格从高到底
if (o1 instanceof Goods && o2 instanceof Goods) {
Goods g1 = (Goods) o1;
Goods g2 = (Goods) o2;
if (g1.getName().equals(g2.getName())) {
return -Double.compare(g1.getPrice(), g2.getPrice());
} else {
return g1.getName().compareTo(g2.getName());
}
}
throw new RuntimeException("输入数据类型不一致");
}
});
System.out.println(Arrays.toString(arr));
}
四、System类
- System类包含系统级的多种属性和控制方法,位于java.lang包
- 构造器是private的,无法创建该类的对象
- 内部的成员变量和成员方法是static的,方便调用
- 成员变量
- PrintStream err:标准错误输出流
- InputStream in:标准输入流
- PrintStream out:标准输出流
- 成员方法
- native long currenTimeMillis():
- void exit(int status): 退出程序,status 为0代表正常退出,非零代表异常退出
- void gc(): 请求系统进行垃圾回收,系统是否回收取决于回收算法实现以及系统执行情况
- String getProperty(String key): 获得系统中属性名为key的属性对应的值
属性名 | 属性说明 |
java.version | Java执行时环境版本 |
java.home | Java安装目录 |
os.name | 操作系统的名称 |
os.version | 操作系统的版本 |
user.name | 用户的账户名称 |
user.home | 用户的主目录 |
user.dir | 用户的当前工作目录 |
五、Math类
- 提供一些列静态方法用于科学计算,参数和返回值一般为double类型
- 常用方法
- abs() 绝对值
- acos, asin, atan, cos, sin, tan: 三角函数
- sqrt: 平方根
- pow(double a, double b): a的b次幂
- exp: e为底指数
- max(double a, double b)
- min(double a, double b)
- random(): 返回0.0 ~ 1.0 的随机数
- long round(double a): double型数据a转换为long型(四舍五入)
- toDegrees(double angrad): 弧度 >> 角度
- toRadians(double angdeg): 角度 >> 弧度
六、BigInteger与BigDecimal
1. BigInteger
- Integer存储最大整型值,Long存储最大值为
- BigInteger表示不可变的任意精度的整数
2. BigDecimal
- BigDecimal支持不可变的、任意精度的有符号十进制定点数
BigInteger b1 = new BigInteger("12433241123");
BigDecimal bd = new BigDecimal("12435.531");
BigDecimal bd2 = new BigDecimal("11");
System.out.println(b1);
System.out.println(bd.divide(bd2)); // 除尽不会报错,除不尽会报错
System.out.println(bd.divide(bd2, BigDecimal.ROUND_HALF_UP)); // 四舍五入
System.out.println(bd.divide(bd2, 15, BigDecimal.ROUND_HALF_UP)); // 四舍五入保留15位小数