目录
2.StringBuffer类、StringBuilder类
3.SimpleDateFormat类 、Calendar类
LocalDateTime、Instant类、DateTimeFormatter类
一、字符串相关类
1.String类
/* String:字符串,使用一对""引起来表示。 1.String声明为final的,不可被继承 2.String实现了Serializable接口:表示字符串是支持序列化的。 【序列化:io流,讲到对象流-->序列化。面向对象,数据都封装到对象里,对象是可以传输的。传输过程中:是流,字节方式传。传到目的地,再变成对象。 Serializable接口让String类的对象能网络传输】 实现了Comparable接口:表示String可以比较大小 3.String内部定义了final char[] value用于存储字符串数据 4.String:代表不可变的字符序列。简称:不可变性。 数组不能再被重新赋值,数组的元素也不能再被修改了 体现:1.当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进行赋值。 2. 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。 【连接操作 +】 3. 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。 不可变性总结:只要对字符串的内容有改变,都必须重新造,原有的都不能动 5.通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。 【字面量方式,区别于new方式】 6.字符串常量池中是不会存储相同内容的字符串的。 */
@Test
public void test1(){
String s1 = "abc";//字面量的定义方式
//【String类的对象不用new,可以直接赋值】
String s2 = "abc";
s1 = "hello";
System.out.println(s1 == s2);//比较s1和s2的地址值
//【两个对象用==,比较的是它们的地址值。equals(如果该类重写了equals方法)比较的是内容】
System.out.println(s1);//hello
System.out.println(s2);//abc
System.out.println("*****************");
String s3 = "abc";
s3 += "def";//【在现有的基础上,后面拼了一个内容,是在原有的位置往后加吗?不是。看s2变没变就知道了。
// 那放在哪里呢?常量池又新建了一个abcdef】
System.out.println(s3);//abcdef
System.out.println(s2);//【abc】
System.out.println("*****************");
String s4 = "abc";
String s5 = s4.replace('a', 'm');//【提示构造器参数:ctrl+alt+/】
System.out.println(s4);//【abc】【s4没变】
System.out.println(s5);//mbc
}
(1)String类实例化方式
String的实例化方式:
方式一:通过字面量定义的方式
方式二:通过new + 构造器的方式
面试题:String s = new String("abc");方式创建对象,在内存中创建了几个对象?
两个:一个是堆空间中new结构,另一个是char[]对应的常量池中的数据:"abc"
@Test
public void test2(){
//通过字面量定义的方式:此时的s1和s2的数据javaEE声明在方法区中的【字符串常量池】中。
String s1 = "javaEE";
String s2 = "javaEE";
//通过new + 构造器的方式:此时的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(s1 == s4);//false
System.out.println(s3 == s4);//false
System.out.println("***********************");
Person p1 = new Person("Tom",12);
Person p2 = new Person("Tom",12);
System.out.println(p1.name.equals(p2.name));//true
System.out.println(p1.name == p2.name);//true
// 【name是String类型的(引用数据类型),只能保存地址值或null】
//【直接传"Tom",String name = "Tom",是字面量定义,地址相同】
p1.name = "Jerry";
System.out.println(p2.name);//Tom
System.out.println("***********************");
//【这个是我自己想的】
Person p3 = new Person(new String("Tom"),12);
Person p4 = new Person(new String("Tom"),12);
System.out.println(p3.name);
System.out.println(p3.name == p4.name);
}
(2)String的拼接
/* 结论: 1.常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。 2.只要其中有一个是变量,结果就在堆中。 //【有变量就是new的】 3.如果拼接的结果调用intern()方法,返回值就在常量池中 */
@Test
public void test3(){
String s1 = "javaEE";
String s2 = "hadoop";
String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";//【连接符左右都是字面量,链接后后也在常量池中】
String s5 = s1 + "hadoop";//【连接符,有一个及以上,变量名参与了。就不再常量池了,在堆空间中。相当于new】
String s6 = "javaEE" + s2;
String s7 = s1 + s2;
//【相当于new,s5、s6、s7记录的都是堆中对象的地址值,都不一样】
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
String s8 = s6.intern();//返回值得到的s8使用的常量值中已经存在的“javaEEhadoop”
System.out.println(s3 == s8);//true
}
@Test//【爆红就alt+enter】
public void test4(){
String s1 = "javaEEhadoop";
String s2 = "javaEE";
String s3 = s2 + "hadoop";//【其中一个是变量】
//【s2是变量】
System.out.println(s1 == s3);//false
final String s4 = "javaEE";//s4:常量
//【加了final,s4就是常量】
String s5 = s4 + "hadoop";//【常量与常量的拼接】
System.out.println(s1 == s5);//【true】
}
(3)参数值传递与String不可变
public class StringTest {
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) {
StringTest ex = new StringTest();
ex.change(ex.str, ex.ch);
System.out.println(ex.str);//good
System.out.println(ex.ch);//best
}
}
(4)
(5)String的常用方法
public class StringMethodTest {
/*
替换:
String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
//【替换一个字符】
String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
//【替换字符串】
String replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
//【regex:正则表达式,将web页面的时候再讲】
String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
匹配:
boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
切片:
String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。
*/
@Test
public void test4(){
String str1 = "北京尚硅谷教育北京";
String str2 = str1.replace('北', '东');
System.out.println(str1);
System.out.println(str2);
String str3 = str1.replace("北京", "上海");
System.out.println(str3);
System.out.println("*************************");
String str = "12hello34world5java7891mysql456";
//把字符串中的数字替换成,,如果结果中开头和结尾有,的话去掉
//String string = str.replaceAll("\\d+", ",");
//,hello,world,java,mysql,
//如果开头或者结尾有逗号,去掉
String string = str.replaceAll("\\d+", ",").replaceAll("^,|,$", "");
//hello,world,java,mysql
//【d:数字 ,+:多个数字 ,^:开头,$:结尾】
System.out.println(string);
System.out.println("*************************");
str = "12345";
//判断str字符串中是否全部有数字组成,即有1-n个数字组成
boolean matches = str.matches("\\d+");
System.out.println(matches);
String tel = "0571-4534289";
//判断这是否是一个杭州的固定电话
boolean result = tel.matches("0571-\\d{7,8}");//【规定开头0571-,后面数字是7到8位】
//【注册邮箱时,通过正则,给出规则,看注册的邮箱是否合规,调了matches】
System.out.println(result);
System.out.println("*************************");
str = "hello|world|java";
String[] strs = str.split("\\|");//【以|进行切割,strs = {hello,world,java}】
for (int i = 0; i < strs.length; i++) {
System.out.println(strs[i]);
}
System.out.println();
str2 = "hello.world.java";
String[] strs2 = str2.split("\\.");//【以.进行切割,strs = {hello,world,java}】
for (int i = 0; i < strs2.length; i++) {
System.out.println(strs2[i]);
}
}
/*
boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true
//【String类实现了CharSequence接口。多态】
int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
//【字符串底层是数组,从0~...,所以没找到就返回-1
//数据库是从1~...,所以没找到就返回0】
int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
//【可以用来找指定字符串在字符串中出现了几次,先用indexOf,看有没有,有就返回索引。
假设字符串中不止一个,索引加指定字符串长度,就可以得到fromIndex,就可以继续找】
int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
//【前两个是从前往后找,后两个是从后往前找。返回的索引还是从前往后的索引】
int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
注:indexOf和lastIndexOf方法如果未找到都是返回-1
*/
@Test
public void test3(){
String str1 = "hellowworld";
boolean b1 = str1.endsWith("rld");
//【没说结尾的指定字符串多长】
System.out.println(b1);
boolean b2 = str1.startsWith("He");
//【没说开始的指定字符串多长】
System.out.println(b2);
boolean b3 = str1.startsWith("ll",2);
System.out.println(b3);//【true】
String str2 = "wor";
System.out.println(str1.contains(str2));
System.out.println(str1.indexOf("lol"));
System.out.println(str1.indexOf("lo",5));
String str3 = "hellorworld";
System.out.println(str3.lastIndexOf("or"));
System.out.println(str3.lastIndexOf("or",6));
//【什么情况下,indexOf(str)和lastIndexOf(str)返回值相同?
//情况一:存在唯一的一个str。情况二:不存在str】
}
/*
int length():返回字符串的长度: return value.length
【本质上是底层数组长度】
char charAt(int index): 返回某索引处的字符return value[index]
boolean isEmpty():判断是否是空字符串:return value.length == 0
String toLowerCase():使用默认语言环境,将 String 中的所有字符转换为小写
String toUpperCase():使用默认语言环境,将 String 中的所有字符转换为大写
String trim():返回字符串的副本,忽略前导空白和尾部空白
//【输入用户名时有用】
boolean equals(Object obj):比较字符串的内容是否相同
//【回顾==比较运算符和equals()方法之间的区别】
boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
//【字符串严格区分大小写,大小写是不一样的】
String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+”
//【一般直接用连接符号】
int compareTo(String anotherString):比较两个字符串的大小
//【String类实现了Comparable接口:表示String可以比较大小】
String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
//【string的s小写】
String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
//【左闭右开】
*/
@Test
public void test2() {
String s1 = "HelloWorld";
String s2 = "helloworld";
System.out.println(s1.equals(s2));
System.out.println(s1.equalsIgnoreCase(s2));
String s3 = "abc";
String s4 = s3.concat("def");
System.out.println(s4);
String s5 = "abc";//【c:99】
String s6 = new String("abe");//【e:101】
System.out.println(s5.compareTo(s6));//-2
//涉及到【字符串排序】
//【手机联系人,按abcd排序,就是调了compareTo方法】
String s7 = "北京尚硅谷教育";
String s8 = s7.substring(2);
System.out.println(s7);
System.out.println(s8);
String s9 = s7.substring(2, 5);
System.out.println(s9);
}
@Test
public void test1() {
String s1 = "HelloWorld";
System.out.println(s1.length());
System.out.println(s1.charAt(0));
System.out.println(s1.charAt(9));
// System.out.println(s1.charAt(10));
// s1 = "";
System.out.println(s1.isEmpty());
String s2 = s1.toLowerCase();
System.out.println(s1);//s1不可变的,仍然为原来的字符串
//【String 不可变性的体现,没对s1本身修改】
System.out.println(s2);//改成小写以后的字符串
String s3 = " he llo world ";
String s4 = s3.trim();
System.out.println("-----" + s3 + "-----");
System.out.println("-----" + s4 + "-----");
}
}
(6) String与基本数据类型转换
/* 复习: String 与基本数据类型、包装类之间的转换。 String --> 基本数据类型、包装类:调用包装类的静态方法:parseXxx(str) 基本数据类型、包装类 --> String:调用String重载的valueOf(xxx) 基本数据类型变量+"" */
@Test
public void test1(){
String str1 = "123";
// int num = (int)str1;//错误的。【只有子父类关系才能强转,基本数据类型之间没有子父类关系】
int num = Integer.parseInt(str1);
String str2 = String.valueOf(num);//"123"
String str3 = num + "";
//【这不就是之前的s1 + "字面量"吗?变量+字面量。
// 在堆里new了一个values数组,str3指向values数组,
// values再指向常量池里的"123"】
System.out.println(str1 == str3);//false
}
(7)String与字符数组转换
/* String 与 char[]之间的转换 String --> char[]:调用String的toCharArray() char[] --> String:调用String的构造器 */
@Test
public void test2(){
String str1 = "abc123"; //题目: 把字符串"abc123" 改成 a21cb3
//【相当于让bc12反转:把String变成char[],char[]讲过如何反转。然后把反转过的数组转成String】
char[] charArray = str1.toCharArray();
for (int i = 0; i < charArray.length; i++) {
System.out.println(charArray[i]);
}
char[] arr = new char[]{'h','e','l','l','o'};
String str2 = new String(arr);
//【str1是声明字面量创建字符串,str1直接指向常量池中的abc123
// str2是构造器创建字符串,str2指向堆中new的values数组,values指向常量池中的hello】
System.out.println(str2);
}
(8)String与字节数组转换
/* String 与 byte[]之间的转换 【和字节数组的转换,后面讲到io流的时候,就会涉及这些操作】 编码:String --> byte[]:调用String的getBytes() 解码:byte[] --> String:调用String的构造器 【String和char[]之间的转换不叫编码解码,因为都看得懂】 编码:字符串 -->字节 (看得懂 --->看不懂的二进制数据) 解码:编码的逆过程,字节 --> 字符串 (看不懂的二进制数据 ---> 看得懂) 【比如下载电影,下下来,说没有解析,看不了。此时这个下载的电影文件是字节的,都还是二进制】 说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码。 */
@Test
public void test3() throws UnsupportedEncodingException {
String str1 = "abc123中国";
byte[] bytes = str1.getBytes();//使用默认的字符集,进行编码。
System.out.println(Arrays.toString(bytes));//【数组的遍历,除了for循环,还可以直接Arrays.toString(数组名)】
//【输出结果[97, 98, 99, 49, 50, 51, -28, -72, -83, -27, -101, -67]】
//【ascii码,a:97,b:98,c:97,1:49,2:50,3:51,
// 汉字不在ascii码中。utf-8,一个汉字三位,中:-28 -72 -83,国:-27 -101 -67】
byte[] gbks = str1.getBytes("gbk");//【会抛异常】
//使用gbk字符集进行编码。【gbk是专门的汉字编码集】
//【指定编码集。编码集等到io流再讲】
System.out.println(Arrays.toString(gbks));
//[97, 98, 99, 49, 50, 51, -42, -48, -71, -6]【一个汉字两位】
//【utf-8包括全世界的文字,gbk仅针对汉字】
System.out.println("******************");
String str2 = new String(bytes);//使用默认的字符集,进行解码。
System.out.println(str2);
String str3 = new String(gbks);
System.out.println(str3);//出现【乱码】。原因:编码集和解码集不一致!
//【讲web时,浏览器-后台服务器-数据库
// 浏览器发送数据给后台,后台把数据存到数据库里,编码集必须相同;
// 数据库提取数据给后台,后台传给前端,编码集必须相同,否则会出现乱码】
String str4 = new String(gbks, "gbk");
System.out.println(str4);//没有出现乱码。原因:编码集和解码集一致!
}
2.StringBuffer类、StringBuilder类
@Test
public void test1(){
StringBuffer sb1 = new StringBuffer("abc");//【底层是个没被final修饰的char[]】
//【此时数组长度不是3,再添加def,还是在原有数组values里添加-->体现可变,在原有基础上可变】
sb1.setCharAt(0,'m');
System.out.println(sb1);
StringBuffer sb2 = new StringBuffer();
System.out.println(sb2.length());//【0】
/*
【为什么是0?】
【子类StringBuffer中:】
public synchronized int length() {
return count;
}
【父类AbstractStringBuilder中:】
int count;//The count is the number of characters used.实际使用的
*/
}
(1)源码分析
/* String、StringBuffer、StringBuilder三者的异同? String:不可变的字符序列;底层使用char[]存储 StringBuffer:可变的字符序列;线程安全的,效率低;底层使用char[]存储 //【synchronized】 StringBuilder:可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储 //【开发中考虑是不是多线程共享数据问题, 是,就用StringBuffer, 不是,就用StringBuilder,提高效率】 //【StringBuffer和StringBuilder父类都是一样的,区别就是一个是synchronized另一个不是】 //【String与StringBuffer/StringBuilder之间的转换:】 //【1.String->StringBuffer/StringBuilder: //调用StringBuffer/StringBuilder的构造器 // public StringBuffer(String str) { // super(str.length() + 16); // append(str); // }】 //【2.StringBuffer/StringBuilder->String:①调String的构造器②调StringBuffer/StringBuilder的toString方法 // public String(StringBuffer buffer) { // synchronized(buffer) { // this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); // } // } 【源码分析:】 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的数组。 // public StringBuffer() { // super(16); // } 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]; // public StringBuffer(String str) { // super(str.length() + 16); // append(str); // } //问题1. System.out.println(sb2.length());//【3】 //问题2. 扩容问题:如果要添加的数据底层数组(超过16了)盛不下了,那就需要扩容底层的数组。 //【看append方法】 默认情况下,扩容为原来容量的2倍 + 2,同时将原有数组中的元素复制到新的数组中。 指导意义:开发中建议大家使用:StringBuffer(int capacity) 或 StringBuilder(int capacity) //【大致先确定容量多少,避免频繁扩容,效率更高】 //【集合中也会涉及源码分析和扩容】 */
(2)StringBuffer的常用方法
/* StringBuffer的常用方法: StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接 //【String用连接符+,concat】 StringBuffer delete(int start,int end):删除指定位置的内容 //【涉及开始和结束位置的,都是左闭右开】 StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str StringBuffer insert(int offset, xxx):在指定位置插入xxx StringBuffer reverse() :把当前字符序列逆转 //【String无法做这样的事】 public int indexOf(String str) public String substring(int start,int end):返回一个从start开始到end索引结束的左闭右开区间的子字符串 //【没有改变原来的数组】 public int length() public char charAt(int n ) public void setCharAt(int n ,char ch) 总结: 增:append(xxx) 删:delete(int start,int end) 改:setCharAt(int n ,char ch) //【改一个字符】 replace(int start, int end, String str) //【改多个字符,字符串】 查:charAt(int n ) 插:insert(int offset, xxx) 长度:length(); *遍历:for() + charAt() / toString() */
@Test
public void test2(){
StringBuffer s1 = new StringBuffer("abc");
s1.append(1);//【s1本身就变了】
s1.append('1');
System.out.println(s1);
// s1.delete(2,4);
// System.out.println(s1);//【s1本身就变了】
// s1.replace(2,4,"hello");//【可以替换不一样长度的,因为StringBuffer可扩容】
// s1.insert(2,false);//abfalsec11
// System.out.println(s1.length());//【怎么看待false,s1长度是多少?看成五个字符,10。String的连接符连接false,也是看成五个字符】
// s1.reverse();//11cba
String s2 = s1.substring(1, 3);
System.out.println(s1);//s1没变
System.out.println(s1.length());
System.out.println(s2);
}
(3)String、StringBuffer、StringBuilder效率对比
/*
对比String、StringBuffer、StringBuilder三者的效率:
//【要想对比效率,就要让它们做同样的事,看谁快】
从高到低排列:StringBuilder > StringBuffer > String
第一次运行:
StringBuffer的执行时间:16
StringBuilder的执行时间:15
String的执行时间:1153
第二次运行:
StringBuffer的执行时间:16
StringBuilder的执行时间:0【why?】
String的执行时间:1165
*/
@Test
public void test3(){
//初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime));
}
二、JDK8之前的日期时间API
* jdk 8之前的日期时间的API测试 * 1. System类中currentTimeMillis(); * 2. java.util.Date和子类java.sql.Date * 3. SimpleDateFormat * 4. Calendar
1.System类
//1.System类中的currentTimeMillis()
@Test
public void test1(){
long time = System.currentTimeMillis();
//返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。
//称为时间戳
//【1.可以用于生成淘宝京东的订单编号
// 2.用两个currentTimeMillis判断中间代码运行花费的时间】
System.out.println(time);
}
2.java.util.Date类
/* java.util.Date类 |---java.sql.Date类 java.sql.Date类是java.util.Date类的子类 1.两个构造器的使用 >构造器一:Date():创建一个对应当前时间的Date对象 //【创建当前时间的对象】 >构造器二:创建指定毫秒数的Date对象 //【创建指定时间的对象】 2.两个方法的使用 >toString():显示当前的年、月、日、时、分、秒 //【String类、包装类、File类、Date类重写过toString】 >getTime():获取当前Date对象对应的毫秒数。(时间戳) 返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。 3. java.sql.Date对应着数据库中的日期类型的变量 >如何实例化 >如何将java.util.Date对象转换为java.sql.Date对象 //向下转型 >如何将java.sql.Date对象转换为java.util.Date对象 //【多态】 */
@Test
public void test2(){
//构造器一:Date():创建一个对应当前时间的Date对象
Date date1 = new Date();
//【不会自动导包,因为不知道是util还是sql的,alt+enter选一下】
System.out.println(date1.toString());//Sun Mar 20 13:39:09 CST 2022
System.out.println(date1);//Sun Mar 20 13:39:09 CST 2022
//【sout,相当于调了toString】
System.out.println(date1.getTime());//1550306204104
//构造器二:创建指定毫秒数的Date对象
Date date2 = new Date(155030620410L);
System.out.println(date2.toString());
//创建java.sql.Date对象
java.sql.Date date3 = new java.sql.Date(35235325345L);
System.out.println(date3);//1971-02-13【也有毫秒,但是只输出年月日】
//如何将java.util.Date对象转换为java.sql.Date对象
//情况一:
// Date date4 = new java.sql.Date(2343243242323L);//【多态】
// java.sql.Date date5 = (java.sql.Date) date4;//【向下转型】。没错
//情况二:
Date date6 = new Date();//【java的date,放到数据库里】
// java.sql.Date date7 = (java.sql.Date) date6;
//【运行报错
// Person p = new Person();
// Student s = (Student)p;
// new的就是Person,怎么可能强转成Student,p指向的对象里都没有Student类的东西。
//
// Person p = new Student();new的是Student,里面有Person的东西,肯定能往下转了
// Student s = (Student)p;】
java.sql.Date date7 = new java.sql.Date(date6.getTime());//【通过毫秒数转换】
//【 public Date(long date) {
// // If the millisecond date value contains time info, mask it out.
// super(date);
//
// }
// sql.Date类的构造器正好也只有这一个能用,其他的过时了】
}
3.SimpleDateFormat类 、Calendar类
public class DateTimeTest {
/*
SimpleDateFormat的使用:SimpleDateFormat对日期Date类的格式化和解析
//【Date是util下的,父类util.Date满足了,子类sql.Date就更满足了】
1.两个操作:
1.1 格式化:日期 Date--->字符串 String
1.2 解析:格式化的逆过程,字符串 String---> 日期 Date
2.SimpleDateFormat的实例化
*/
@Test
public void testSimpleDateFormat() throws ParseException {
//实例化SimpleDateFormat:使用默认的构造器
SimpleDateFormat sdf = new SimpleDateFormat();
//格式化:日期 --->字符串
Date date = new Date();
System.out.println(date);//Sun Mar 20 17:47:18 CST 2022
String format = sdf.format(date);
System.out.println(format);//22-3-20 下午5:47
//解析:格式化的逆过程,字符串 ---> 日期
//【字符串有格式要求】
String str = "19-12-18 上午11:43";//
Date date1 = sdf.parse(str);//【会抛异常,怕传的字符串不靠谱。ParseException】
System.out.println(date1);
//*************按照指定的方式格式化和解析:调用带参的构造器*****************
// SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyy.MMMMM.dd GGG hh:mm aaa");
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
//格式化
String format1 = sdf1.format(date);//2022-03-20 05:57:39
System.out.println(format1);
//解析:要求字符串必须是符合SimpleDateFormat识别的格式(通过构造器参数体现),
//否则,抛异常
Date date2 = sdf1.parse("2020-02-18 11:48:27");
System.out.println(date2);
}
/*
练习一:字符串"2020-09-08"转换为java.sql.Date
//【用户在浏览器注册,填写生日,传给后台,此时以字符串的方式保存的,"2020-09-08",
//数据存到数据库里,数据库中存的是sql.Date类型,然后才能放到数据库的表中】
//【字符串->util.Date->sql.Date】
练习二:"三天打渔两天晒网" 1990-01-01 xxxx-xx-xx 打渔?晒网?
//【相当于五天一个周期】
举例:2020-09-08 ? 总天数
总天数 % 5 == 1,2,3 : 打渔
总天数 % 5 == 4,0 : 晒网
1,2,3打渔
4,5晒网,5 % 5 == 0,所以0是晒网
总天数的计算?
方式一:( date2.getTime() - date1.getTime()) / (1000 * 60 * 60 * 24) + 1
方式二:1990-01-01 --> 2019-12-31 + 2020-01-01 -->2020-09-08
*/
@Test
public void testExer() throws ParseException {
String birth = "2020-09-08";
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf1.parse(birth);
// System.out.println(date);
java.sql.Date birthDate = new java.sql.Date(date.getTime());
System.out.println(birthDate);
}
/*
Calendar日历类(抽象类)的使用
*/
@Test
public void testCalendar(){
//1.实例化
//方式一:创建其子类(GregorianCalendar)的对象
//方式二:调用其静态方法getInstance()
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getClass());//class java.util.GregorianCalendar
//【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可变性
calendar.set(Calendar.DAY_OF_MONTH,22);
//【这里并不是改DAY_OF_MONTH的值,DAY_OF_MONTH是final static的,是常量
// 而是更改形参,给value赋值
// public void set(int field, int value)
// {
// }】
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
//add()
calendar.add(Calendar.DAY_OF_MONTH,-3);
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
//【Calendar类和Date类之间的转换】
//getTime():日历类---> Date
Date date = calendar.getTime();
System.out.println(date);
//setTime():Date ---> 日历类
Date date1 = new Date();
calendar.setTime(date1);//【修改了calendar,是date1转换后的日历类型】
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
}
}
三、idea Debug
import org.junit.Test;
/**
* 代码量小的时候可以看源码分析
* 代码量大的话要自己debug
* @author shkstart
* @create 2019 上午 11:23
*/
public class IDEADebug {
@Test
public void testStringBuffer(){
String str = null;
StringBuffer sb = new StringBuffer();
//StringBuffer new了对象,所以debug时,sb有个地址值
sb.append(str);//
System.out.println(sb.length());//4
System.out.println(sb.toString());//【"null"】
//【肯定不是null,如果是null第17行就应该报空指针异常。】
StringBuffer sb1 = new StringBuffer(str);//抛异常NullPointerException
//【1.抛空指针异常,说明是null.什么,15、17、22设断点
// 2.单元测试方法,双击选中方法名,右键debug
// 3.↓:一步一步往下执行。蓝色红色↘:看方法里面怎么执行。↗:从方法里出来
// resume:从一个断点到另一个断点,到最后一个断点,再点resume,就结束debug了
// stop:停止
// 臭虫:重新开始debug】
System.out.println(sb1);//
}
}
四、JDK8中新日期时间API
LocalDateTime、Instant类、DateTimeFormatter类
public class JDK8DateTimeTest {
@Test
public void testDate(){
//偏移量
Date date1 = new Date(2020 - 1900,9 - 1,8);
System.out.println(date1);//Tue Sep 08 00:00:00 GMT+08:00 2020
}
/*
LocalDate、LocalTime、LocalDateTime 的使用
说明:
1.LocalDateTime相较于LocalDate、LocalTime,使用频率要高
2.类似于Calendar
//【代替前面的Date类和Calendar类】
*/
@Test
public void test1(){
//now():获取当前的日期、时间、日期+时间
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDate);
System.out.println(localTime);
System.out.println(localDateTime);
//of():设置指定的年、月、日、时、分、秒。没有偏移量
//【字符串不用转成Date,指定时间直接放进构造器里,时分秒不想要就LocalDate.of(xxxx,xx,xx)】
LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 6, 13, 23, 43);
System.out.println(localDateTime1);
//getXxx():获取相关的属性
System.out.println(localDateTime.getDayOfMonth());
System.out.println(localDateTime.getDayOfWeek());
System.out.println(localDateTime.getMonth());//MARCH
System.out.println(localDateTime.getMonthValue());//3
System.out.println(localDateTime.getMinute());
//体现不可变性
//withXxx():设置相关的属性
//【Calendar类的set会改自己】
LocalDate localDate1 = localDate.withDayOfMonth(22);
System.out.println(localDate);
System.out.println(localDate1);
LocalDateTime localDateTime2 = localDateTime.withHour(4);
System.out.println(localDateTime);
System.out.println(localDateTime2);
//不可变性
LocalDateTime localDateTime3 = localDateTime.plusMonths(3);
System.out.println(localDateTime);
System.out.println(localDateTime3);
LocalDateTime localDateTime4 = localDateTime.minusDays(6);
System.out.println(localDateTime);
System.out.println(localDateTime4);
}
/*
Instant的使用
类似于 java.util.Date类
//【代替System.currentTimeMillis()方法】
*/
@Test
public void test2(){
//now():获取本初子午线对应的标准时间
Instant instant = Instant.now();
System.out.println(instant);//2019-02-18T07:29:41.719Z
//添加时间的偏移量
//【不同国家时区不同,所以要加偏移值,伦敦1点,北京九点,所以偏移值是8】
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);//2019-02-18T15:32:50.611+08:00
//toEpochMilli():获取自1970年1月1日0时0分0秒(UTC)开始的毫秒数 ---> Date类的getTime()
long milli = instant.toEpochMilli();
System.out.println(milli);
//ofEpochMilli():通过给定的毫秒数,获取Instant实例 -->Date(long millis)
Instant instant1 = Instant.ofEpochMilli(1550475314878L);
System.out.println(instant1);
}
/*
DateTimeFormatter:格式化或解析日期、时间
类似于SimpleDateFormat
//【代替前面的SimpleDateFormat类】
*/
@Test
public void test3(){
//【三种实例化方法,用得最多的是第三种】
// 方式一:预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
//格式化:日期-->字符串
LocalDateTime localDateTime = LocalDateTime.now();
String str1 = formatter.format(localDateTime);
System.out.println(localDateTime);
System.out.println(str1);//2019-02-18T15:42:18.797
//解析:字符串 -->日期
TemporalAccessor parse = formatter.parse("2019-02-18T15:42:18.797");
//【LocalDate、LocalTime、LocalDateTime都是接口TemporalAccessor的实现类,
// 因为不知道是这三个之中的哪个,所以干脆用接收TemporalAccessor,多态】
System.out.println(parse);
// 方式二:
// 本地化相关的格式。如:ofLocalizedDateTime()
// FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT :适用于LocalDateTime
DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
//格式化
String str2 = formatter1.format(localDateTime);
System.out.println(str2);//2019年2月18日 下午03时47分16秒
// 本地化相关的格式。如:ofLocalizedDate()
// FormatStyle.FULL / FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT : 适用于LocalDate
DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
//格式化
String str3 = formatter2.format(LocalDate.now());
System.out.println(str3);//2019-2-18
// 重点: 方式三:自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)
DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
//格式化
String str4 = formatter3.format(LocalDateTime.now());
System.out.println(str4);//2019-02-18 03:52:09
//解析
TemporalAccessor accessor = formatter3.parse("2019-02-18 03:52:09");
System.out.println(accessor);
}
}
五、Java比较器
基本数据类型运算符
1.算术运算符
2.赋值运算符 =也适用于引用数据类型
3.比较运算符 == !=也适用于引用数据类型比较地址值
4.逻辑运算符
5.位运算符
6.三元运算符
现在希望引用数据类型(java对象)是可以比较大小的-->java比较器
1.自然排序:java.lang.Comparable
/* Comparable接口的使用举例: 自然排序 1.像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。 //【直接调compare方法,从小到大排序】 2.像String、包装类重写compareTo()方法以后,进行了从小到大的排列 3. 重写compareTo(obj)的规则: //【像重写equals方法一样】 如果当前对象this大于形参对象obj,则返回正整数, 如果当前对象this小于形参对象obj,则返回负整数, 如果当前对象this等于形参对象obj,则返回零。 4. 对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法。 在compareTo(obj)方法中指明如何排序 【数组和集合,之后可以Collections.sort或者Arrats.sort】 */
@Test
public void test1(){
String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
@Test
public void test2(){
Goods[] arr = new Goods[5];
arr[0] = new Goods("lenovoMouse",34);
arr[1] = new Goods("dellMouse",43);
arr[2] = new Goods("xiaomiMouse",12);
arr[3] = new Goods("huaweiMouse",65);
arr[4] = new Goods("microsoftMouse",43);
Arrays.sort(arr);//【sort会调重写的compareTo(obj)和compare(obj,obj)方法】
//【告诉sort,Goods类按照指明商品比较大小的方式:按照价格从低到高排序】
System.out.println(Arrays.toString(arr));
//【一开始Goods没实现Comparable,
// 没重写Goods类的compareTo方法,
// java.lang.ClassCastException: com.atguigu.java.Goods cannot be cast to java.lang.Comparable】
//【Comparable接口是怎么和sort方法联系到一起去的】
}
/**
* 商品类
* @author shkstart
* @create 2019 下午 4:52
*/
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) {//【写法和equals类似】
System.out.println("**************");//【证明sort()会调重写的compareTo(obj)和compare(obj,obj)方法】
if(o instanceof Goods){//【先看是不是个商品】
Goods goods = (Goods)o;
//方式一:
if(this.price > goods.price){
return 1;
// return (int) (this.price - goods.price);
//【错误写法。因为84.5-84.3=0.2,强转后就变成0了】
}else if(this.price < goods.price){
return -1;
}else{//【this.price == goods.price】//【当价格相同时,判断首字母顺序】
// return 0;
return -this.name.compareTo(goods.name);//【加了负号从高到低排】
//【name是String类,已经重写过compareTo方法了】
//return this.name.compareTo(goods.name);
//【这是从低到高排】
}
//方式二:
// return Double.compare(this.price,goods.price);
}
// return 0;
throw new RuntimeException("传入的数据类型不一致!");
}
}
2.定制排序:java.util.Comparator
/* Comparator接口的使用:定制排序 //【String类已经实现了Comparable接口,从小到大排, //但现在希望从大到小排-->这相当于定制】 1.背景: 当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码, //【jdk中的类,没有实现Comparable接口,又不可能去改源码-->用Comparator】 或者实现了java.lang.Comparable接口的排序规则不适合当前的操作, //【Goods类实现了compareTo,但现在又不想按方法体那样排。具体的不同问题,又想要不同方式排】 那么可以【考虑使用 Comparator 的对象来排序】 2.重写compare(Object o1,Object o2)方法,比较o1和o2的大小: 如果方法返回正整数,则表示o1大于o2; 如果返回0,表示相等; 返回负整数,表示o1小于o2。 //【Comparator接口中除了compare()是抽象方法外,其他都是静态方法,jdk1.8以后,接口中可以有静态方法】 */
@Test
public void test3(){
String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"};
// Arrays.sort(Object[] a);
// Arrays.sort(<T>,Comparator);//【T可以看作是Object】
Arrays.sort(arr,new Comparator(){
//按照字符串从大到小的顺序排列
@Override
public int compare(Object o1, Object o2) {//【讲了泛型以后,可以保证形参是String】
if(o1 instanceof String && o2 instanceof String){
String s1 = (String) o1;
String s2 = (String) o2;
// return s1.compareTo(s2);//【String类实现了Comparable接口,重写过compareTo,从小到大排】
return -s1.compareTo(s2);//【从大到小排】
}
// return 0;
throw new RuntimeException("输入的数据类型不一致");
}
});
System.out.println(Arrays.toString(arr));
}
@Test
public void test4(){
Goods[] arr = new Goods[6];
arr[0] = new Goods("lenovoMouse",34);
arr[1] = new Goods("dellMouse",43);
arr[2] = new Goods("xiaomiMouse",12);
arr[3] = new Goods("huaweiMouse",65);
arr[4] = new Goods("huaweiMouse",224);
arr[5] = new Goods("microsoftMouse",43);
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类