Java中的常用类
Java 中的常用类
1. 字符串相关的类
1.1 String的特性
- String代表字符串。Java程序中通常用""来表示一串字符串。
- String声明为final类型,表示String类不可被继承。
- String实现了Serializable接口:表示String是可序列化的。
- String实现了Comparable接口:表示String是可以被比较的。
- String内部定义了final char[] value用来存储字符串数据。代表了不可变的字符序列,简称不可变性。
- 通过字面量的方式(区别于new)给一个字符串赋值,此时字符串的值声明在常量池中
- 字符串常量池中是不会存储内容相同的字符串的。
部分源码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0
1.2 String不可变性
- 当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进行赋值操作。
- 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行连接操作。
- 当调用String的replace()方法来替换字符或者字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行替换操作。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.summer.java; import org.junit.Test; /** * string 的使用 * @Author 安宁侯 * @create 2021-03-06 18:06 */ public class StringTest { @Test public void test1(){ String s1="abc";//字面量方式赋值(区别于new方法),此时字符串的值声明在字符串窜常量池中, // 字符串常量池不会出现两个相同的字符串的值(通过equals比较为true) String s2="abc"; System.out.println(s1 == s2); s1="hello";//此时重新在常量池中创建了一个“hello”的值, // 同时改变了s1的地址值。不能在原有的地址值上进行修改 System.out.println(s1); System.out.println(s2); System.out.println("**************"); String s3="abc"; s3+="def";//此时在常量池中新造了一个abcdef的值。体现为s2的值没有改变 System.out.println(s2); System.out.println(s3); System.out.println("**************"); String s4="abc"; String s5 = s4.replace("ab", "mm"); System.out.println(s4); System.out.println(s5); } }
1.3 String不同实例化方式的比较
String创建时有多种构造器,功能比较丰富,详细信息可以查看一下源码或者API。此处主要比较通过字面量和new String()两种方式。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.summer.java; import org.junit.Test; /** * string 的使用 * @Author 安宁侯 * @create 2021-03-06 18:06 */ public class StringTest { @Test public void test1(){ String s1="abc";//字面量方式赋值(区别于new方法),此时字符串的值声明在字符串窜常量池中, // 字符串常量池不会出现两个相同的字符串的值(通过equals比较为true) String s2="abc"; System.out.println(s1 == s2); s1="hello";//此时重新在常量池中创建了一个“hello”的值, // 同时改变了s1的地址值。不能在原有的地址值上进行修改 System.out.println(s1); System.out.println(s2); System.out.println("**************"); String s3="abc"; s3+="def";//此时在常量池中新造了一个abcdef的值。体现为s2的值没有改变 System.out.println(s2); System.out.println(s3); System.out.println("**************"); String s4="abc"; String s5 = s4.replace("ab", "mm"); System.out.println(s4); System.out.println(s5); } @Test 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 == s4);//false System.out.println(s1 == s3);//false System.out.println(s3 == s4);//false Person p1 = new Person("Tom", 12);//使用的是字面量的方式定义的name Person p2 = new Person("Tom", 12);//此时p1,p2的name都存储的是常量池中tom的地址值 System.out.println(p1.name.equals(p2.name));//true System.out.println(p1.name==p2.name);//true p1.name="Jeery"; System.out.println(p2.name);//Tom } } class Person{ String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } }
面试题一:String s1 = "abc" 与String s2=new String("abc")的区别?
此时s1存储的是字符串常量池中"abc"的地址值,s2存储的是堆空间中新开辟出的空间的地址值,s2对象中的value(也就是char[])中存的才是字符串常量池中"abc"的地址值。
面试题二:String s2=new String("abc")创建了几个对象?
两个:一个是堆空间new出的对象,另一个是char[]数组对应的常量池中的数据"abc"。
1.4 String类中不同拼接方式的区别
- 常量与常量拼接的结果在常量池,且常量池中不会存在相同内容的常量。
- 只要有一个变量参与拼接,结果就在堆空间中。
- 如果拼接的结果调用了intern()方法,返回值在常量池中。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Test 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; String s8=(s1+s2).intern(); System.out.println(s3 == s4);//true System.out.println(s3 == s5);//false System.out.println(s3 == s6);//false System.out.println(s5 == s6);//false System.out.println(s3 == s7);//false System.out.println(s5 == s7);//false System.out.println(s3 == s8);//true }
经典例题:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.summer.java; /** * @Author 安宁侯 * @create 2021-03-06 19:41 */ public class StringTest1 { String str=new String("good");//此时str的地址值是指向good的 char ch[]={'t','e','s','t'}; public void exchange(String str,char[] ch){ str="test ok";//此时该str1的地址值是指向test ok ch[0]='b'; } public static void main(String[] args) { StringTest1 test1=new StringTest1(); /* 在值传递中,基本数据类型传的是数据,引用数据类型传的是地址值。 此时将str的地址值作为参数传递给形参str,由于String类的不可变性,此时实参str的地址值并没有改变 */ test1.exchange(test1.str,test1.ch); System.out.println(test1.str);//good System.out.println(test1.ch);//best } }
1.5 String类中的常用方法
- 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():返回String的副本,忽略前导空白和尾部空白
- boolean equals(Object obj):比较字符串的内容是否相同
- boolean equalslgnoreCase(String anotherString):与equals类似,忽略了大小写
- String concat(String str):将指定的字符串连接到此字符串的尾部,等价于”+“
- int compareTo(String str):比较两个字符串的大小
- String subString(int beginIndex):将此字符串从下标beginIndex开始截取到尾部并返回
- String subString(int beginIndex,int endIndex):将此字符串从beginIndex到endIndex(不包含)截取并返回
- boolean endsWith(String suffix):测试当前字符串是否以指定的后缀结束
- boolean startsWith(String prefix):测试当前字符串是否以指定的前缀开始
- boolean startsWith(String prefix,int toffset):测试当前字符串是否从指定索引处开始以指定前缀开始
- boolean contains(CharSequence s):当且仅当此字符串包含指定的char值序列时,返回true。判断当前字符串是否包含指定字符串
- int indexOf(String str):返回子字符串第一次在当前字符串出现的索引。
- int indexOf(String str,int fromIndex):从指定索引开始,返回子字符串第一次在当前字符串出现的索引
- int lastIndexOf(String str):返回指定子字符串在当前字符串在最右边出现的索引
- int lastIndexOf(String str,int fromIndex):从指定索引处反向搜索子字符串最后一次出现的位置。
- String replace(char oldChar,char newChar):将newChar替换了oldChar之后(替换所有oldChar),返回一个新的字符串
- String replace(CharSequence target,CharSequence replaceMent):(类似replace)将指定的字面值序列替换此字符串所有匹配字面值的子字符串,返回一个新字符串
- Strng replaceAll(String regex,String replaceMent):使用给定的replaceMent字符串替换掉该字符串所有匹配给定的正则表达式的子字符串
- String replaceFirst(String regex,String replaceMent):使用给定的replaceMent字符串替换掉该字符串第一个匹配给定的正则表达式的子字符串
- boolean matches(String regex):告知此字符串是否匹配指定的正则表达式
- String[] split(String regex):根据给定的正则表达式来拆分此字符串
- String[] split(String regex,int limit):根据给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过,则全部放在最后一个元素中
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Test public void stringMethods(){ String s1=" helloWorld "; System.out.println(s1.length()); System.out.println(s1.charAt(5)); System.out.println(s1.isEmpty()); System.out.println(s1.toLowerCase()); System.out.println(s1.toUpperCase()); /*compare源码 public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length; int lim = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int k = 0; while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; } k++; } return len1 - len2; } */ System.out.println(s1.compareTo("HELLOWORLD")); System.out.println(s1.trim()); System.out.println(s1.equals("HELLOWORLD"));//区分大小写 System.out.println(s1.equalsIgnoreCase(" HELLOWorld ")); System.out.println(s1.concat(" Java")); System.out.println(s1.substring(5)); System.out.println(s1.substring(1, 8)); System.out.println("***********************"); System.out.println(s1.endsWith("ld ")); System.out.println(s1.startsWith("he")); System.out.println(s1.startsWith("he", 2)); System.out.println(s1.contains("ello")); System.out.println(s1.indexOf("he")); System.out.println(s1.indexOf("he",2)); System.out.println(s1.lastIndexOf("Wo")); System.out.println(s1.lastIndexOf("Wo", 3)); System.out.println("***************"); System.out.println(s1.replace("hello", "你好")); //正则表达式以后再写.... }
1.6 String与基本数据类型的转换
字符串类型转成基本数据类型、包装类:
Integer包装类的public static int parseInt(String str)可以将由”数字“字符组成的字符串转成整形。类似的,使用Java.Lang包中的Byte、Short、Long、Float、Double类调用相应的类方法可以将由”数字“字符组成的字符串转成相应的基本数据类型。(int sum=Integer.parseInt(String str))
基本数据类型、包装类转成字符串类型:
调用String类型的public String valueOf(int number)可以将int类型转成字符串。相应的valueOf(byte b)、valueOf(long l)、valueOf(short s)、valueOf(float f)、valueOf(double d)、valueOf(boolean b)可以由参数的相应类型转换成字符串
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Test public void test4(){ String str="123"; int sum=Integer.parseInt(str); System.out.println(sum); String str1=String.valueOf(sum); String str2=sum+""; System.out.println(str == str2); }
1.7 String与char[]之间的转换
- string-->char[]:调用string的toCharArray()方法
- char[]-->string:调用string的构造器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Test public void test5(){ String str="abc123"; //string转换成char数组 char[] chars = str.toCharArray(); for (int i = 0; i < chars.length; i++) { System.out.println(chars[i]); } //char数组转换成string char[] chars1=new char[]{'h','e','l','l','o'}; String s = new String(chars1); System.out.println(s); }
1.8 String与字节数组byte[]之间的转换
编码:string转成bytes数组,将看的懂的字符串转成看不懂的二进制,调用string的getBytes方法
解码:bytes数组转成string,将看不懂的二进制转成看得懂的字符串,调用string的构造器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Test
public void test6() throws UnsupportedEncodingException {
String str = "aabc北京欢迎您";
//编码:string转换成byte数组.调用String的getBytes()方法
//此时不带参数,中文转成字节使用了默认的编码集(我的是utf-8),在utf-8中,一个汉字占三个字节
byte[] bytes = str.getBytes();
/*
编码:字符串-->字节(看得懂-->看不懂的二进制)
解码:编码的逆过程(看不懂的二进制-->看得懂)
* */
//gbk中一个汉字占两个字节
byte[] gbks = str.getBytes("gbk"); //使用指定的编码集进行编码
System.out.println(Arrays.toString(gbks));
System.out.println(Arrays.toString(bytes));
System.out.println("*************************");
//解码:byte数组转换成String:调用String的构造器
String string = new String(bytes);//使用默认的字符集解码
System.out.println(string);
String s = new String(gbks);//此时由于编码时的字符集和解码时的字符集不同,所以会出现乱码
System.out.println(s);
String s1 = new String(gbks,"gbk");//指定解码集进行解码
System.out.println(s1);
}
注意:编码和解码时使用的字符集要做保证一致,否则会出现乱码的情况
1.9 String中常见的一些算法题目
1.10 StringBuffer和StringBuilder
二者类似,区别在于StringBuffer的内部方法是同步的,因此是线程安全的,但是效率较低,StringBuilder的内部方法不是同步的,线程不安全,但是效率较高。
关于扩容问题:StringBuffer和StringBuilder是一样的,底层源码中,如果开发者没有定义StringBuffer的长度,默认为16,当开发者append后的数据长度大于16,此时会扩容到原有长度的2倍+2.同时将原数组的元素复制到新的数组中,如果还不够会继续扩容。StringBuffer中的构造器有多个,详情请看源码,这里不一一举例。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; } //********************************** private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) { value = Arrays.copyOf(value, newCapacity(minimumCapacity)); } } //********************************** private int newCapacity(int minCapacity) { // overflow-conscious code int newCapacity = (value.length << 1) + 2; if (newCapacity - minCapacity < 0) { newCapacity = minCapacity; } return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) ? hugeCapacity(minCapacity) : newCapacity; }
1.11 String、StringBuffer、StringBuilder(jdk5.0新增的)三者的异同
- String:是不可变的,底层使用被final定义的char[]数组来存储数据
- StringBuffer:是可变的序列,底层使用char[]数组来存储数据,但是StringBuffer中的方法都是使用synchronized修饰的,因此是线程安全的,但是效率比较低
- StringBuilder:是可变序列,与StringBuffer类似,唯一的区别是StringBuilder中的方法不是同步方法,因此是线程不安全的,但是效率较高
在不涉及线程安全的情况下,尽量使用StringBuilder,可以提高效率
1.12 StringBuffer中的常用方法(StringBuilder同样)
- StringBuffer append(xxx):提供了很多append方法,用于进行字符串拼接
- StringBuffer delete(int start,int end):(左闭右开)删除指定位置的字符串
- StringBuffer replace(int start,int end,String str):(左闭右开)替换指定位置的字符串
- StringBuffer insert(int offset,xxx):在指定位置插入xxx
- StringBuffer reverse():反转当前字符串
- public int indexOf(String str):返回指定字符串首次出现的位置
- public String substring(int start,int end):截取指定位置的字符串(左闭右开)
- public int length():返回当前字符串的长度(不是底层的char[]长度,是已经存储数据的长度)
- public char charAt(int n):返回指定索引处的字符
- public void setCharAt(int n,char ch):将指定索引处的字符替换
2. JDK8之前的时间日期API
java.lang.System:
System类提供的public static long currentTimeMillis()用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。(适用于计算时间差)
java.util.Date:
表示特定的时间,精确到毫秒。
构造器:
Date():使用无参构造器创建的对象可以获取本地的当前时间。
Date(long date):创建指定毫秒数的date对象
常用方法:
getTime():返回自1970年...以来此date对象表示的毫秒数
toString():将此Date对象转换成String:dow mon dd hh:mm:ss zzz yyy(dow是一周中的某一天,zzz是时间标准)
还有一些其他方法,不过大多都过时了。
java.text.SimpleDateFormat:
该类允许进行格式化:日期-》文本,解析:文本-》日期。
格式化:
SimpleDateFormat():默认的环境和语言创建对象。
public SimpleDateFormate(String pattern):该构造方法可以使用参数pattern指定的格式创建一个对象
public String format(Date date):方法格式化时间对象。
解析:
public Date parse(String source):从给定字符串的开始解析文本,生成一个日期。
java.util.Calendar:
是一个抽象基类,主要用来完成日期字段之间相互操作的功能。
实例化:
方式一:使用Calendar.getInstance()方法。
方式二:使用子类GregorianCalendar的构造器。
常用方法:
public int get(int filed):获取一些常用的属性信息,比如当前时间使本月第几天、本年第几个月第几天....
public void set(int filed,int value):可以将指定的属性设置为一个新的值
public void add(int filed,int amount):在原有的基础上增加amount天
public final Date getTime():把一个日历类转成一个date对象。
public final void setTime(Date date):将date对象转成日历类。
注意:获取月份时,一月为0,二月为1...十二月为11。获取星期时,周日为1,周一为二...周六为7.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.summer.java; import org.junit.Test; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; /** * @Author 安宁侯 * @create 2021-03-07 17:56 */ public class MyDate { //system中的currentTimeMillis() @Test public void test1(){ long l = System.currentTimeMillis(); //时间戳:返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差 System.out.println(l); } /* java.util.Date类 |:java.sql.Date类(子类) 1.两个构造器的使用 2.两个方法的使用 >toString:显示当前年月日时分秒 >getTime:获取当前时间距离70年的毫秒数 */ @Test public void test2(){ //构造器一:返回的是当前时间的date的对象 Date date1 = new Date(); System.out.println(date1.getTime());//1615111914016 System.out.println(date1.toString());//Sun Mar 07 18:10:20 CST 2021Sun Mar 07 18:10:20 CST 2021 //构造器二:创建指定毫秒数的date对象 Date date = new Date(1615111914016l); System.out.println(date); } /* java.text.SimpleDateFormat类 两个操作 1.格式化:从date转成String 2.解析:格式化的逆过程,从String转成date */ @Test public void test3() throws ParseException { //类的实例化:使用默认的构造器 SimpleDateFormat format = new SimpleDateFormat(); //格式化日期-->字符串 Date date = new Date(); System.out.println(date); String s = format.format(date); System.out.println(s); //解析:字符串-->日期(要求字符串格式为:21-3-7 下午8:08) String str="21-3-7 下午8:08"; Date date1 = format.parse(str); System.out.println(date1); System.out.println("**********按照指定方式格式化*****************"); //实例化二:带参构造器,指定格式(年-y,月-M,天-d,时-h,分-m,秒-s) SimpleDateFormat format1 = new SimpleDateFormat("GGG yyyy-MM-dd aaa hh:mm:ss "); String s1 = format1.format(new Date()); System.out.println(s1); //解析:要求字符串必须符合format1的格式(通过构造器体现的)否则就会抛异常 Date date2 = format1.parse(s1); System.out.println(date2); } /* 练习一:将字符串“2020-08-09”转成java.sql.Date */ @Test public void test4() throws ParseException { String birth="2020-08-09"; SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date date = format.parse(birth); java.sql.Date date1 = new java.sql.Date(date.getTime()); System.out.println(date1); } /* 练习二:三天打鱼两天晒网,从1990-01-01开始,问2019-08-04渔夫在干什么? */ @Test public void test5() throws ParseException { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date date = format.parse("1990-01-01"); long time = date.getTime(); Date date1 = format.parse("2019-08-04"); long time1 = date1.getTime(); // System.out.println(time1-time); int days= (int)((time1-time)/(1000*60*60*24)+1); // System.out.println(1000*60*60*24); // System.out.println(days); if (days%5==1||days%5==2||days%5==3) System.out.println("打鱼"); else System.out.println("晒网"); } /* calendar */ @Test public void test6(){ //实例化方式一:调用getInstance()方法 Calendar instance = Calendar.getInstance(); //get() int i = instance.get(Calendar.DAY_OF_MONTH); System.out.println(i); //set() instance.set(Calendar.DAY_OF_MONTH,22); i=instance.get(Calendar.DAY_OF_MONTH); System.out.println(i); //add() instance.add(Calendar.DAY_OF_MONTH,11);//加上负数相当于减去几天 i=instance.get(Calendar.DAY_OF_MONTH); System.out.println(i); //getTime() Date time = instance.getTime(); System.out.println(time); //setTime() Date date = new Date(); instance.setTime(date); i=instance.get(Calendar.DAY_OF_MONTH); System.out.println(i); //实例化方式二:使用子类GregorianCalendar // GregorianCalendar calendar = new GregorianCalendar(); } }
3. JDK8中的时间日期API
LocalDate、LocalTime、LocalDateTime:
其中LocaclDateTime的使用频率要比另外两个类的使用频率高
三种类的实例化:now(),of()
一些常用方法:
getXxx():获取相关属性...
withXxx():设置相关属性(需要注意的是,此时只是返回结果改变了,本身并没有改变,体现了不可变习性,与Calendar类不同)...
plusXxx():在原有基础上添加了多少(同样是返回值改变,也体现了不可变性)...
minusXxx():在原有基础上减少了多少(与plusXxx一样体现了不可变性)...
Instant类:
时间线上的一个时间点。可能被用来记录应用程序中某个事件的时间戳。类似于java.util.Date类。
java.time.format.DateTimeFormatter:
日期时间格式化的类,类似于SimpleDateFormat。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*
LocalDate,LocalTime,LocalDateTime
*/
@Test
public void test7(){
//now():获取当前的的日期,时间,日期+时间
LocalDate now = LocalDate.now();
LocalTime time = LocalTime.now();
LocalDateTime dateTime = LocalDateTime.now();
System.out.println(now);
System.out.println(time);
System.out.println(dateTime);
//of():设置指定的年月日时分秒,没有偏移量
LocalDateTime localDateTime = LocalDateTime.of(2021, 3, 7, 23, 23, 23);
System.out.println(localDateTime);
//getXxx()
System.out.println(dateTime.getDayOfMonth());
System.out.println(dateTime.getDayOfWeek());
//with
}
@Test
public void test8(){
//now()获取的是本初子午线的便准时间
Instant now = Instant.now();
System.out.println(now);//2021-03-07T16:13:22.828Z(本初子午线的时间,与北京相差八个小时)
//添加偏移量
OffsetDateTime time = now.atOffset(ZoneOffset.ofHours(8));
System.out.println(time);//2021-03-08T00:16:51.963+08:00
//获取对应毫秒数(与1970年01月01日对比)
long l = now.toEpochMilli();
System.out.println(l);
//通过给定的毫秒数,获取一个instant实例
Instant instant = Instant.ofEpochMilli(1883247479l);
System.out.println(instant);
}
@Test
public void test9(){
//实例化方式一:预定义的标准格式
DateTimeFormatter date= DateTimeFormatter.ISO_LOCAL_DATE_TIME;
LocalDateTime now = LocalDateTime.now();
String format = date.format(now);//格式化:日期-->字符串
System.out.println(now);
System.out.println(format);
//解析
TemporalAccessor parse = date.parse(format);
System.out.println(parse);
//实例化方式二:
// DateTimeFormatter time = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
//DateTimeFormatter time = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
DateTimeFormatter time = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
String format1 = time.format(now);
System.out.println(format1);
//实例化方式三:重点:使用的比较多
DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
String format2 = pattern.format(now);
System.out.println(format2);
TemporalAccessor parse1 = pattern.parse(format2);
System.out.println(parse1);
}
4. Java 比较器
Java中的对象,正常情况下只能进行比较:==或者!=,不能使用>、<。但是在开发场景中,往往涉及到需要对对象进行排序,比如电商网站中对商品的排序。这个时候就需要我们对对象进行比较大小的操作,这个要如何实现呢?
Java 中涉及对象排序的方式有两种:
自然排序:Java.lang.Comparable
定制排序:Java.util.Comparator
Comparable的使用:自然排序
像String、包装类等实现了Comparable接口,重写了compareTo()方法,默认情况下是从小到大排列的,给出了比较两个对象大小的方式。
重写compareTo(Object obj)方法的规则:
如果当前对象this大于形参对象obj,返回正整数。
如果当前对象this小于形参对象obj,返回负整数。
如果当前对象this等于形参对象obj,返回0。
对于自定义类来说,如果想实现对象的排序,可以通过实现comparable接口,重写compareTo()方法来实现。在compareTo()方法中指定如何排序
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; return Double.compare(this.price,goods.price); } throw new RuntimeException("传入的数据类型不一致!"); } }
Comparator的使用:定制排序
当元素的类型没有实现Java.lang.Comparable接口又不方便修改代码,或者实现了Comparable接口但是对排序方式不太满意,这个时候可以考虑使用java.util.Comparator的对象来进行定制排序。重写compare(Object obj1,Object obj2)方法。如果方法返回正整数,说明obj1大于obj2,如果方法返回负整数,说明obj1小于obj2,如果方法返回0,说明obj1等于obj2。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//定制排序
Arrays.sort(myGoods,new Comparator(){
@Override
public int compare(Object o1, Object 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(myGoods));
Comparable接口与Comparator使用的对比:
Comparable接口一旦指定,可以保证实现类的对现象可以在任何位置都进行比较。Comparator属性临时性的定义,每次使用都需要定义。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class CompareTest {
@Test
public void test(){
//comparable的使用举例:自然排序
//像String、包装类等继承了Comparable类,重写了compareTo()方法,按照从小到大排列
/*
重写compareTo的规则:
如果当前对象this大于系形参obj,则返回正数,小于返回负数,等于返回0。
对于自定义类的对象需要排序,需要重写compareTo方法,并指明如何排序
*/
String[] str=new String[]{"aa","cc","mm","gg","jj","dd","zz","kk"};
Arrays.sort(str);
System.out.println(Arrays.toString(str));
}
@Test
public void goodsTest(){
Goods[] myGoods=new Goods[4];
myGoods[0]=new Goods("戴尔",4999);
myGoods[1]=new Goods("华硕",7999);
myGoods[2]=new Goods("联想",5999);
myGoods[3]=new Goods("苹果",14999);
//自然排序
// Arrays.sort(myGoods);
// System.out.println(Arrays.toString(myGoods));
//定制排序
Arrays.sort(myGoods,new Comparator(){
@Override
public int compare(Object o1, Object 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(myGoods));
}
/*
定制排序:comparator
*/
@Test
public void test1(){
String[] str=new String[]{"aa","cc","mm","gg","jj","dd","zz","kk"};
Arrays.sort(str, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return -o1.compareTo(o2);
}
});
System.out.println(Arrays.toString(str));
}
}