String类
基础知识
String类: 代表字符串。Java程序中的所有字符串字面值(如"abc")都作为此类的实例实现。
String是一个final类,代表不可变的字符序列。
字符串是常量,用双引号引起来表示。它们的值在创建之后不能更改。
String对象的字符内容是存储在一个字符数组value[]中的。
字符串常量池:
三种JVM:
- Sun公司的HotSpot(使用的较多)
- BEA公司的JRockit
- IBM公司的J9 VM
Heap 堆
—个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三部分︰
1
2
3
|
Young Generation Space新生区 Young
Tenure generation space养老区 old
Permanent Space永久存储区 Perm
|
事实上永久区是划分在方法区的:
永久区:
永久存储区是一个常驻内存区域,用于存放JDK自身所携带的Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM才会释放此区域所占用的内存。
如果出现java.lang.OutOfMemoryError: PermGen space
,说明是Java虚拟机对永久代Perm内存设置不够。一般出现这种情况,都是程序启动需要加载大量的第三方jar包。例如:在一个Tomcat下部署了太多的应用。或者大量动态反射生成的类不断被加载,最终导致Perm区被占满。
Jdk1.6及之前:常量池分配在永久代, Jdk1.6在方法区
Jdk1.7:有,但已经逐步“去永久代”,1.7在堆
Jdk1.8及之后:无,1.8在元空间
String对象的创建:
1
2
3
4
5
6
7
8
9
10
11
12
|
string str = "hello";
//本质上this.value = new char[0];
String s1 = new String();
//this.value = original.value;
String s2 = new string(String origina1);
//this.value = Arrays.copyof(value,value.length);
string s3 = new String(char[] a);
String s4 = new String(char[]a,int startIndex,int count);
|
两种创建方式的区别:
字符串的特性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
package cn.xpshuai.java1;
import org.junit.Test;
/**
* @author: 剑胆琴心
* @create: 2021-01-19 10:44
* @功能:String的使用
*
*/
public class StringTest {
/**
* 1.String声明为final,不可被继承
* 2.String实现了Serializable接口:表示字符串是支持序列化的
* 实现了Comparable接口:表示可以比较大小
* 3.String内部定义了final char[] value用于存储字符数据
* 4.String代表不可变的字符序列
* 体现:
* 1.当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
2.当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的地址的value进行赋值
* 3.当调用replace()修改字符串或字符时,也需要重新指定内存区域赋值,不能使用原有的地址的value进行赋值
*5.通过字面量方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中,字符串常量池中是不会存储相同内容的字符串的
*/
@Test
public void test1(){
String s1 = "asd"; //字面量
String s2 = "asd";
System.out.println(s1 == s2); //不是基本数据类型,比较s1和s2的地址池 true
s1 = "hello";
System.out.println(s1 == s2); //比较s1和s2的地址池 false
System.out.println(s1);
System.out.println(s2);
String s3 = "asd";
s3 += "fgh";
System.out.println(s3); // 拼接了,还得新造一个,不能直接在原来地址上添加
System.out.println(s2);
String s4 = "asd";
String s5 = s4.replace('a', '6'); //也是新造,不能在原地址区进行任何修改
System.out.println(s4);
System.out.println(s5);
}
/**
* String的实例化方式:
方式1:通过字面量定义的方式
方式2:通过 new + 构造器的方式
面试题: string s = new String("abc");方式创建对象,在内存中创建了几个对象?
答:两个,一个是堆空间中new的,另一个是char[]对象的常量池中的数据"abc"
*/
@Test
public void test2(){
//此时的s1和s2的数据是声明在方法区中的字符串常量池中
String s1 = "java";
String s2 = "java";
//通过 new + 构造器的方式::此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值。
String s3 = new String("java");
String s4 = new String("java");
System.out.println(s1 == s2); //true
System.out.println(s1 == s3); //false
System.out.println(s3 == s4); //false
// String中equals()比较的是值,==比较的是地址
// 通过字面量定义的str会存储在字符串常量池中
}
/**
* 结论:1.常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量
* 2.只要其中有一个是变量,结果就在堆中
* 3.如果拼接的结果调用intern()方法,返回值就在常量池中
*/
@Test
public void test3() {
//此时的s1和s2的数据是声明在方法区中的字符串常量池中
String s1 = "java";
String s2 = "EE";
String s3 = "javaEE"; //字面量
String s4 = "java" + "EE"; //字面量的连接,在常量池中
String s5 = s1 = "EE"; //只要有变量名参与,就不在常量池而在堆空间中了
String s6 = "java" + s2;
String s7 = s1 + s2;
System.out.println(s3 == s4); // true
System.out.println(s3 == s5); // false
System.out.println(s3 == s6);// false
System.out.println(s3 == s7);// false
System.out.println(s5 == s6);// false
System.out.println(s5 == s7);// false
System.out.println(s6 == s7);// false
// intern()
String s8 = s5.intern(); //返回值得到的s8使用的常量池中已经存在的"javaEE"
System.out.println(s3 == s8); // true
}
}
|
练习
练习1:
面试题2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package cn.xpshuai.java1;
/**
* @author: 剑胆琴心
* @create: 2021-01-19 14:38
* @功能:面试题一道, 涉及String与值传递机制
*/
public class CaseTest {
String str = new String("good");
char[] cs = {'t', 'e', 's', 't'};
public void change(String str, char ch[]){
// 这里形参是String,是引用型数据,但是String是不可变的,这里形参相当于造了一个变量
// 但是是无法改变String的,所以还是good
str = "test ok";
ch[0] = 'b';
// ch是数组,是引用型数据,是在堆里的,可以修改,t-->b
}
public static void main(String[] args) {
CaseTest ex = new CaseTest();
ex.change(ex.str, ex.cs);
System.out.println(ex.str); // good
System.out.println(ex.cs); // best
}
}
|
String常用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
package cn.xpshuai.java1;
import org.junit.Test;
/**
* @author: 剑胆琴心
* @create: 2021-01-19 14:51
* @功能:String常用方法
*/
public class StringMethodTest {
@Test
public void tes1(){
String s0 = "我是大帅锅";
String s1 = " Hello World ";
String s2 = "I am A boy.";
System.out.println(s1.length()); //长度
System.out.println(s1.charAt(0)); //取索引上的一个值
System.out.println(s1.isEmpty()); //是否为空
String s11 = s1.toLowerCase();
System.out.println(s11); //小写的字符串
System.out.println(s1); //s1还不变,仍然为原来的
System.out.println(s1.toUpperCase()); //转换大写
String s3 = s1.trim();
System.out.println(s3); //去除首尾空格(注册用户写输入框的时候)
System.out.println(s1.equalsIgnoreCase(s11)); //忽略大小写的情况下比较值是否相等
System.out.println(s1.equals(s2));
System.out.println(s1.concat("EFG")); //拼接
//涉及到字符串排序
System.out.println(s1.compareTo(s1)); //比较字符串大小(逐个比较ASCII码),相等为0,负数表示前面的数小
System.out.println(s0.substring(1)); // 从索引位置开始截取
System.out.println(s0.substring(1, 3)); // 从索引位置开始截取到end索引位置(左闭右开)
}
@Test
public void tes2() {
String s0 = "我是大帅锅";
String s1 = "Hellor World";
String s2 = "I am A boy.";
String s3 = "am A boy";
boolean b1 = s1.endsWith("ld");
System.out.println(b1);
boolean b2 = s1.startsWith("H"); //是否以指定字符开始
System.out.println(b2);
boolean b3 = s1.startsWith("ll", 2); //从索引位置开始,是否以指定字符开始
System.out.println(b3);
System.out.println(s2.contains(s3)); //是否包含s3, true
//返回指定子串在字符串中第一次出现位置的索引(如果没找到,返回-1)
System.out.println(s1.indexOf("ll"));
System.out.println(s1.indexOf("ll", 2)); //从指定索引开始找子串(想找子串出现了几次,找到一次之后从上一次找到的索引+子串长度继续往后找)
//从后往前找
System.out.println(s1.lastIndexOf("or"));
System.out.println(s1.lastIndexOf("or", 6)); //从指定索引反向搜索
// 什么时候indexOf和lastIndexOf返回索引相同?
// 1.存在唯一的str
// 2.不存在这个str
}
@Test
public void tes3() {
String s0 = "我1是大帅锅";
String s1 = "Hellor World";
String s2 = "I am A boy.";
String s3 = "am A boy";
//替换
String s11 = s1.replace("or", "XX"); //替换字符串(所有出现的都要替换)
String s111 = s1.replace('W', '时'); //替换单个字符
System.out.println(s11);
System.out.println(s111);
String str = "12hello34world5java798mysql465";
String strr = s1.replaceAll("\\d+", ","); //根据正则替换
System.out.println(strr);
// s0.replaceFirst() //只替换第一次
//匹配正则表达式 boolean
System.out.println(str.matches("\\d+"));
//切片 根据正则
String[] s00 = s0.split("\\d+");
for (int i = 0; i <s00.length ; i++) {
System.out.println(s00[i]);
}
}
}
|
String与其他类型转换
1.String与基本数据类型、包装类之间的转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* 1.String与基本数据类型、包装类之间的转换
* String -->基本数据类型、包装类:调用包装类的静态方法:parseXXX(str)
*
*基本数据类型、包装类 -->String:调用String重载的valueOf(xxx)
*
*
*/
@Test
public void test1(){
String s1 = "123"; //在常量池里
int n = Integer.parseInt(s1);
String s2 = String.valueOf(n);
String s3 = n + ""; //在堆里
}
|
2.String与char[]之间的转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* 2.String与char[]之间转换
*String -->char[]:调用String的toCharArray()
*char[] --> String: 调用String的构造器
*
*/
@Test
public void test2(){
String s1 = "abc123"; //在常量池里
char[] c1 = s1.toCharArray(); //
for (int i = 0; i < c1.length; i++) {
System.out.println(c1[i]);
}
char[] arr = new char[]{'h','e','l','l','o'};
String s2 = new String(arr);
System.out.println(s2);
|
3.String与字节数组byte[]之间的转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
/**
* 3.String与字节数组byte[]之间的转换
* 编码:String --> byte[]:调用String的getBytes()
*
* 解码:byte[] --> String: 调用String的构造器
*
*
*/
@Test
public void test3() throws UnsupportedEncodingException {
String s1 = "qwe123中国";
byte[] bytes = s1.getBytes(); //转换为字节,中文采用的就是默认的字符编码集(汉字在utf-8占3个字节)
System.out.println(Arrays.toString(bytes)); //这么遍历输出也可以
byte[] gbks = s1.getBytes("gbk"); //使用指定的字符集进行编码(汉字在gnk占2个字节)
System.out.println(Arrays.toString(gbks)); //这么遍历输出也可以
String s2 = new String(bytes);
// String s2 = new String(gbks); //如果不用前面相同的字符集解码,会乱码
System.out.println(s2);
}
|
一个加了final的拼接问题:
1
2
3
4
5
6
7
8
9
10
11
|
@Test
public void test4(){
String s1 = "javaEE";
String s2 = "java";
String s3 = s2 + "EE";
System.out.println(s1 == s3); //false
final String s4 = "java"; //加了final,就是 s4:常量
String s5 = s4 + "EE";
System.out.println(s1 == s5); // true
}
|
常见算法题
StringBuffer与StringBuilder
三者区别:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
package cn.xpshuai.java1;
import org.junit.Test;
/**
* @author: 剑胆琴心
* @create: 2021-01-19 16:22
* @功能:StringBuffer与StringBuilder
*/
public class StringBufferTest {
/**
*
* String、StringBuffer、StringBuilder有何异同?
* String: 不可变的字符序列:底层使用char[]存储
* StringBuffer:可变的字符序列:线程安全的,效率偏低:底层使用char[]存储
* StringBuilder:可变的字符序列:线程不安全,效率高一些,jdk5.0新增:底层使用char[]存储
*
*
*
* 为啥第一个不可变,后俩可变呢?
*
*
* 【源码分析:】
* String s = new String(); //new char[0];
* String s1 = new String("abc"); // char value = new char[]{'a','b','c'};
*
* StringBuffer sb1 = new StringBuffer(); // char value = new char[16]; 底层创建了一个长度16的数组
* System.out.println(sb1); //长度为:0
* sb1.append('a'); //value[0] = 'a';
*sb1.append('b'); //value[1] = 'b';
*
* StringBuffer sb2 = new StringBuffer("aba"); // char value = new char["aba".length() + 16]
*
* 问题1:
* System.out.println(sb2.length()); // 3
*
* 问题2:
* 扩容问题:如果要添加的数据底层数组盛不下了,那就扩容底层的数组
* 默认情况下,扩容为原来容量的2倍,同时将原有数组中的元素复制到新的数组中
*
* 因为String不可变,所以优先用StringBuffer和StringBuilder,再考虑有无多线程问题...
*
*
*
*
*
*
*
*
*/
@Test
public void test1(){
StringBuffer sb1 = new StringBuffer("aba");
sb1.setCharAt(0, 'm');
System.out.println(sb1);
}
}
|
StringBuffer中常用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
package cn.xpshuai.java1;
import org.junit.Test;
/**
* @author: 剑胆琴心
* @create: 2021-01-19 16:22
* @功能:StringBuffer与StringBuilder
*/
public class StringBufferTest {
/**
*
* String、StringBuffer、StringBuilder有何异同?
* String: 不可变的字符序列:底层使用char[]存储
* StringBuffer:可变的字符序列:线程安全的,效率偏低:底层使用char[]存储
* StringBuilder:可变的字符序列:线程不安全,效率高一些,jdk5.0新增:底层使用char[]存储
*
*
*
* 为啥第一个不可变,后俩可变呢?
*
*
* 【源码分析:】
* String s = new String(); //new char[0];
* String s1 = new String("abc"); // char value = new char[]{'a','b','c'};
*
* StringBuffer sb1 = new StringBuffer(); // char value = new char[16]; 底层创建了一个长度16的数组
* System.out.println(sb1); //长度为:0
* sb1.append('a'); //value[0] = 'a';
*sb1.append('b'); //value[1] = 'b';
*
* StringBuffer sb2 = new StringBuffer("aba"); // char value = new char["aba".length() + 16]
*
* 问题1:
* System.out.println(sb2.length()); // 3
*
* 问题2:
* 扩容问题:如果要添加的数据底层数组盛不下了,那就扩容底层的数组
* 默认情况下,扩容为原来容量的2倍,同时将原有数组中的元素复制到新的数组中
*
* 因为String不可变,所以优先用StringBuffer和StringBuilder,再考虑有无多线程问题...
*
*
*
*
*
*
*
*
*/
@Test
public void test1(){
StringBuffer sb1 = new StringBuffer("aba");
sb1.setCharAt(0, 'm');
System.out.println(sb1);
}
/**
* StringBuffer中常用方法
*关注:
* 增:append(xxx)
* 删:delete(int start, int end)
* 改:setCharAt(int n, char cn) / replace(int start, int end, String str)
* 查: charAt(int n)
* 插入:insert(int offset, xx)
* 长度:length()
* 遍历:for +里面用 charAt() / toString() 查看内容
*/
@Test
public void tets2(){
StringBuffer sb1 = new StringBuffer("aba");
sb1.append(1); //添加数据
sb1.append("aaa");
sb1.delete(1,3); //删除索引内的字符(左闭右开)
sb1.replace(1,3, "hello"); //替换索引内的字符(左闭右开)
sb1.insert(2, "H"); //在索引位置插入数据,后面的数据后移
sb1.reverse(); //反转本身
System.out.println(sb1);
sb1.indexOf("H"); //返回索引
String ss = sb1.substring(1,3); //返回一个从start到end索引结束的左闭右开区间的子串
sb1.charAt(1); //
sb1.setCharAt(0, 'm');
System.out.println(sb1);
}
@Test
public void test3(){
//三者的效率:由高到低:StringBuilder > StringBuffer > String
}
}
|
StringBuilder中常用方法是类似的,只不过加了同步方法
三者效率
**由高到低:**StringBuilder > StringBuffer > String
时间类
JDK8之前日期和之间API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
package cn.xpshuai.java2;
import org.junit.Test;
import java.util.Date;
/**
* @author: 剑胆琴心
* @create: 2021-01-19 17:19
* @功能: JDK8之前日期和时间的API测试
*/
public class DateTimeAPI {
// 1.System类中的currentTimeMillis()
@Test
public void test1() {
long time = System.currentTimeMillis();
//返回有计算机元年之间以毫秒为单位的时间差, 时间戳
System.out.println(time);
}
/**
* java.util.Date类
* | --- java.sql.Date类(对应数据库中的日期类型的变量)
*
* java.util.Date类:
* 1.两个构造器的使用
* Date()
* Date(1611048522028L);
* 2.两个常用方法:
* getTime():获取当前时间的时间戳
* toString():显示当前时间
*
*
* java.sql.Date类
* 1.如何实例化
* 2.如果有sql.Date,如何转换为util.Date对象 -->多态赋值就行
* 3.util.Date,如何转换为sql.Date对象(父类往子类转)
*
*
*/
@Test
public void test2() {
// 构造器1
Date date1 = new Date();
System.out.println(date1.toString()); // Tue Jan 19 17:28:03 CST 2021
System.out.println(date1.getTime()); //long型是值,毫秒数,时间戳
// 构造器2:创建指定毫秒数的Date对象
Date date2 = new Date(1611048522028L);
System.out.println(date2.toString()); // Tue Jan 19 17:28:03 CST 2021
//创建java.sql.Date对象
java.sql.Date date3 = new java.sql.Date(1611048522028L);
System.out.println(date3);
//util.Date,如何转换为sql.Date对象(父类往子类转)
// 情况1
// Date date4 = new java.sql.Date(1611048522028L);
// java.sql.Date date5 = (java.sql.Date)date4;
// 情况2:
Date date6 = new Date();
java.sql.Date date7 = new java.sql.Date(date6.getTime());
}
}
|
java.text.SimpleDateFormat类
java.util.Calendar类:
-
Calendar是一个抽象基类,主用用于完成日期字段之间相互操作的功能.
-
获取Calendar实例的方法
-
使用Calendar.getInstance()
方法
-
调用它的子类GregorianCalendar
的构造器。
-
一个Calendar的实例是系统时间的抽象表示,通过get(int field)
方法来取得想要的时间信息。比如YEAR、MONTH、DAY_OF_WEEK、HOUR_OF_DAYMINUTE、SECOND
1
2
3
4
|
public void set(int field,int value)
public void add(int field,int amount)
public final Date getTime()
public final void setTime(Date date)
|
-
注意:
-
获取月份时:一月是0,二月是1,以此类推,12月是11
-
获取星期时:周日是1,周二是2,。。。。周六是7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
package cn.xpshuai.java2;
import org.junit.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* @author: 剑胆琴心
* @create: 2021-01-23 21:35
* @功能:
*
*
* 1.system的currentTImeMills()
* 2. java.util.Date和java.sql.Date
* 3.java.util.SimpleDateFormat
* 4.Calendar
*/
public class SimpleDateFormatTest {
/*
SimpleDateFormat: 对日期Date类的格式化和解析
格式化:日期-->字符串
解析: 字符串 --> 日期
*/
@Test
public void testSimpleDateFormat() throws ParseException {
//实例化:使用默认构造器
SimpleDateFormat sdf = new SimpleDateFormat();
//格式化
Date date = new Date();
String format_Str = sdf.format(date);
System.out.println(format_Str);
//解析
String str = "2020-12-30 上午8:11";
Date date1 = sdf.parse(str); //抛异常
System.out.println(date1);
System.out.println(date1.getTime());
System.out.println("**********");
//实例化:调用带参构造器(开发中这么指定格式的方式用得多)
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String str2 = sdf2.format(date);
System.out.println(str2);
//解析: 要求字符串必须符合通过构造器参数体现的这个实例的格式
System.out.println(sdf2.parse("2021-01-23 09:47:37"));
}
/*
Calendar 日历类
*/
@Test
public void testCalendar(){
//1.实例化
//方式1: 创建其子类(GregorianCalendar)的对象
//方式2: 调用它的静态方法: getInstance() -- 常用
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getClass());
//2.常用方法
//get()
int days = calendar.get(Calendar.DAY_OF_MONTH); //当前时间是这个月的第几天
System.out.println(days);
System.out.println(calendar.get(Calendar.DAY_OF_YEAR)); // 当前是这个年的第几天
//set()
calendar.set(Calendar.DAY_OF_MONTH, 22); //修改值
int days2 = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days2); //现在本身已经改了(可变的)
//add()
calendar.add(Calendar.DAY_OF_MONTH, 3); //在原基础加上三天
calendar.add(Calendar.DAY_OF_MONTH, -3); //在原基础减去三天
//getTime():日历类-->Date
Date date = calendar.getTime(); //
//setTime(): Date --> 日历类
Date da = new Date();
calendar.setTime(da);
calendar.getTime();
}
}
|
JDK8中的日期和之间API
新API出现背景:
JDK11之后java.util.DAta被启用了了。而Calendar并不比Date好多少。
它们面临的问题是:
可变性: 像日期和时间这样的类应该是不可变的。
偏移性: Date中的年份是从1900开始的,而月份都从0开始。
格式化: 格式化只对Date有用,Calendar则不行。
此外,它们也不是线程安全的;不能处理闰秒等。
**总结:**对日期和时间的操作一直是Java程序员最痛苦的地方之一。
新时间日期API:
- 第三次引入的API是成功的,并且Java8中引入的java.time API已经纠正了过去的缺陷,将来很长一段时间内它都会为我们服务。
- Java 8吸收了Joda-Time 的精华,以一个新的开始为Java创建优秀的API.新的java.time中包含了所有关于**本地日期(LocalDate) 、本地时间(LocalTime) 、本地日期时间(LocalDateTime)、时区 (ZonedDateTime和持续时间(Duration)的类。**历史悠久的Date类新增了tolnstant()方法,用于把 Date转换成新的表示形式。这些新增的本地化时间日期API大大简化了日期时间和本地化的管理。
1
2
3
4
5
|
java.time -包含值对象的基础包
java.time.qhrono-提供对不同的日历系统的访问
java.time.format-格式化和解析时间和日期
java.time.temporal-包括底层框架和扩展特性
java.time.zone-包含时区支持的类
|
- LocalDate、LocalTime、LocalDateTime类是其中较重要的几个类,它们的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的本地日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。
- LocalDate代表IOS格式(yyyy-MM-dd)的日期,可以存储生日、纪念日等日期
- LocalTime表示一个时间,而不是日期。
- LocalDateTime是用来表示日期和时间的,这是一个最常用的类之一。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
/*
LocalDate, LocalTime, LocalDateTime(使用较多)
类似于Calendar
*/
@Test
public void test_LocalXXX(){
//实例化1:
//now() 获取当前日期或时间
LocalDate ldate = LocalDate.now();
LocalTime ltime = LocalTime.now();
LocalDateTime ldatetime = LocalDateTime.now(); //使用的较多
System.out.println(ldate);
System.out.println(ltime);
System.out.println(ldatetime);
//实例化2: of() 设置指定时间, 参数没有偏移量
LocalDateTime nowtime = LocalDateTime.of(2020,10,10,8,8,8);
System.out.println(nowtime);
System.out.println("*******");
//getXXX() 获取
System.out.println(ldatetime.getDayOfMonth()); // 获取时间是本月的第几天
System.out.println(ldatetime.getDayOfWeek());
System.out.println(ldatetime.getDayOfYear());
System.out.println(ldatetime.getMonthValue()); //获取月份
System.out.println(ldatetime.getMinute());
//withXXX() 设置
// 这种设置是不可变的
LocalDate localDate1 = ldate.withDayOfMonth(10); //修改设置值,修改当前是月的第几天
System.out.println(localDate1); //它改了
System.out.println(ldate); //原来的不受影响
LocalDateTime ll = ldatetime.withHour(5);
//加
LocalDateTime lll = ldatetime.plusMonths(3); //在原来基础上加上了三个月
System.out.println(lll); //加了之后的
System.out.println(ldatetime); //原来的还是不变的
//减
LocalDateTime llll = ldatetime.minusDays(6); //减去6天
System.out.println(llll);
}
|
Instant(瞬时):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
Instant只是简单的表示自元年开始的秒
类似java.util.Date
*/
@Test
public void testInstant(){
//now() 获取本初子午线对应的标准时间
Instant instant = Instant.now();
System.out.println(instant); //默认时间是不对的,按UTC时间算是在本初子午线位置的时间,咱在东八区需要加一下
// 我们采用这种方式,添加时间的偏移量
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8)); //东八区
System.out.println(offsetDateTime);
//获取毫秒数 --> Date.getTime()
long mi = instant.toEpochMilli();
System.out.println(mi);
//
//通过给定的毫秒数,获取Instant实例 --> Date(Long millis)
Instant instant2 = Instant.ofEpochMilli(1611413025604L);
System.out.println(instant2);
}
|
格式化与解析日期或时间:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
/*
DateTimeFormatter: 格式化或解析日期或时间
类似于SimpleDateFormat
*/
@Test
public void testDateTimeFormatter(){
// LocalDateTime, LocalDate, LocalTime
//实例化1:预定义的标准格式
DateTimeFormatter formatter1 = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
//格式化:日期-->str
LocalDateTime localDateTime = LocalDateTime.now();
String str1 = formatter1.format(localDateTime);
System.out.println(str1);
//解析: str-->日期
TemporalAccessor parse = formatter1.parse("2021-01-23T22:51:32.392");
//实例化2:本地化相关的格式(以某个函数或参数举例)
// ofLocalizedDateTime,ofLocalizedDate
DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
//格式化
String str2 = formatter2.format(localDateTime);
// ofLocalizedDate
DateTimeFormatter formatter3 = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);
String str3 = formatter3.format(LocalDate.now());
System.out.println(str3);
//解析...
//实例化3:自定义的格式(使用的最多 -->重点)
DateTimeFormatter format1 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
//格式化
String s1 = format1.format(LocalDateTime.now());
System.out.println(s1);
//解析
TemporalAccessor accessor = format1.parse("2020-8-8 08:08:08");
System.out.println(accessor);
}
|
java其他时间api:
与传统日期的处理转换:
用到的时候查一下就行
Java比较器(重要)
涉及到对象排序,就要用下面两种,至于用哪种就自己选吧
java.lang.Comparable 一劳永逸,一旦实现,在任何位置都可以比较大小
java.util.Comparator 临时用一下时候的比较
Comparable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package cn.xpshuai.java3;
import org.junit.Test;
import java.util.Arrays;
/**
* @author: 剑胆琴心
* @create: 2021-01-24 17:07
* @功能: Java比较器
*
* Java中的对象,正常情况下只能进行比较;==, !=, 不能使用: > <
* 但是在开发场景中,我们需要对多个对象进行排序可以比较大小
*实现对象排序的方式有两种:
* - 自然排序: java.lang.Comparable
* - 定制排序: java.util.Comparator
*
*
* 1.Comparable接口
*
*
* 2.Comparator接口
*/
public class ComparableTest {
/*
Comparable使用举例
1.String等包装类实现了Comparable接口,重写了compareTo()方法,给出了比较两个对象大小的实现
2.String等包装类重写compareTo()以后,进行了从小到大排列
3.实现compareTo()的规则:
如果当前对象this大于形参对象,则返回正整数;
如果当前对象this小于形参对象,则返回负整数;
如果当前对象this等于形参对象obj,则返回0。
*/
@Test
public void test1(){
String[] arr = new String[]{"DD", "GG", "BB", "JJ","MM","AA"};
Arrays.sort(arr); //排序(其实也是String实现了Comparable接口)
}
}
|
自定义类实现Comparable自然排序
自定义类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
package cn.xpshuai.java3;
/**
* @author: 剑胆琴心
* @create: 2021-01-24 17:26
* @功能:
*/
public class Goods implements Comparable{
private String name;
private double price;
public Goods() {
}
public Goods(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Goods{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
//实现这个方法
//指明商品比较大小的方式(这里自定义比较价格从低到高排序,如果需要,可以继续嵌套比较)
@Override
public int compareTo(Object o) {
if(o instanceof Goods){
Goods goods = (Goods)o;
if(this.price > goods.price){
return 1;
}else if(this.price < goods.price){
return -1;
}else {
// return 0;
return this.name.compareTo(goods.name); //如果需要,可以继续嵌套比较, 比如价格一样就再比较商品名称
}
}
throw new RuntimeException("传入的数据类型不一致!");
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package cn.xpshuai.java3;
import org.junit.Test;
import java.util.Arrays;
/**
* @author: 剑胆琴心
* @create: 2021-01-24 17:07
* @功能: Java比较器
*
* Java中的对象,正常情况下只能进行比较;==, !=, 不能使用: > <
* 但是在开发场景中,我们需要对多个对象进行排序可以比较大小
*实现对象排序的方式有两种:
* - 自然排序: java.lang.Comparable
* - 定制排序: java.util.Comparator
*
*
* 1.Comparable接口
*
*
* 2.Comparator接口
*/
public class ComparableTest {
/*
Comparable(自然排序)使用举例
1.String等包装类实现了Comparable接口,重写了compareTo()方法,给出了比较两个对象大小的实现
2.String等包装类重写compareTo()以后,进行了从小到大排列
3.实现compareTo()的规则:
如果当前对象this大于形参对象,则返回正整数;
如果当前对象this小于形参对象,则返回负整数;
如果当前对象this等于形参对象obj,则返回0。
4.对于自定义类,如果需要排序,可以让自定义类实现Comparable接口,重写compareTo()方法
在compareTo(obj)写如何排序
*/
@Test
public void test1(){
String[] arr = new String[]{"DD", "GG", "BB", "JJ","MM","AA"};
Arrays.sort(arr); //排序(其实也是String实现了Comparable接口)
}
/*
【自定义类实现Comparable自然排序】
这里自定义类是Goods.java
对于自定义类,如果需要排序,可以让自定义类实现Comparable接口,重写compareTo()方法
在compareTo(obj)写如何排序
*/
@Test
public void test2(){
Goods[] arr = new Goods[4];
arr[0] = new Goods("联想", 65);
arr[1] = new Goods("小米", 12);
arr[2] = new Goods("华为", 65);
arr[3] = new Goods("罗技", 101);
Arrays.sort(arr); //原始情况下,不能执行,需要在自定义类中实现并重写
System.out.println(Arrays.toString(arr)); // 继承并重写之后
}
}
|
Comparator接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
/*
Comparator接口(定制排序)
1.背景:
当元素的类型没有实现java.Lang.ComparabLe接口而又不方便修改代码,
或者实现了java.Lang.comparable接口的排序规则不适合当前的操作,
那么可以考怎使用Comparator的对象来排序
2.重写compare(Object o1,0bject o2)方法,比较o1和o2的大小:
如果方法返回正整数,则表示o1大于o2
如果方法返回负整数,则表示o1小于o2
如果方法返回0,则表示o1等于o2
*/
@Test
public void test3() {
String[] arr = new String[]{"DD", "GG", "BB", "JJ","MM","AA"};
Arrays.sort(arr, new Comparator<String>() { //使用匿名内部类
@Override
public int compare(String o1, String o2) { //实现compare方法
//指明o1和o2怎么排
if(o1 instanceof String && o2 instanceof String){
String s1 = (String)o1;
String s2 = (String)o2;
return -s1.compareTo(s2); //从大到小来排列
}
throw new RuntimeException("输入的类型不一致");
}
});
}
|
自定义类实现Comparator定制排序
自定义类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
/*
【自定义类实现Comparator定制排序】
这里自定义类是Goods.java
*/
@Test
public void test4(){
Goods[] arr = new Goods[4];
arr[0] = new Goods("联想", 65);
arr[1] = new Goods("小米", 12);
arr[2] = new Goods("华为", 65);
arr[3] = new Goods("罗技", 101);
arr[3] = new Goods("罗技", 100);
Arrays.sort(arr, new Comparator<Goods>() {
//指明商品比较大小的方式(这里自定义, 先按产品名称从低到高,再比较价格从高到低排序)
@Override
public int compare(Goods o1, Goods o2) {
if(o1 instanceof Goods && o2 instanceof Goods){
Goods goods1 = (Goods)o1;
Goods goods2 = (Goods)o2;
if(goods1.getName().equals(goods2.getName())){
return -Double.compare(goods1.getPrice(), goods2.getPrice()); //如果名称一样,就再比较价格
}else {
return goods1.getName().compareTo(goods2.getName());
}
}
throw new RuntimeException("传入的数据类型不一致!");
}
}); //原始情况下,不能执行,需要在自定义类中实现并重写
System.out.println(Arrays.toString(arr)); // 继承并重写之后
}
|
System类
Math类
BigInteger与BigDecimal