今天的努力只为更好的明天。

String 类

一、概述

String:

  1. String 声明为 fianl 的,不可被继承。
  2. String 实现了 Serializable 接口,表示字符串是支持序列化的。实现了 Comparable 接口,表示 String 可以进行比较大小。
  3. String 内部定义的 fianl char[ ] value 用于储存字符串数据。
  4. 通过字面量的方式(区别new给一个字符串赋值,此时得到字符串声明在字符串常量池中)。
  5. 字符串常量池是不会存储相同的内容(使用 String 类的equals()**比较,返回true**)的字符串的。

二、String 的不可变性

说明:
① 当对字符串重新赋值,需要重写指定内存中区域赋值,不能使用原有的 value 进行赋值
② 当对现有的字符串进行操作时,也需要重新指定内存区域的赋值,不能使用原有的 value 进行赋值。
③ 当调用 String 的 replace()方式修改指定字符串时,也需要重新指定区域赋值,不能使用原有的 value 进行赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void test(){
// 字面量的定义
String s1 = "abc";
String s2 = "abc";
//比较s1和s2的地址值
System.out.println(s1 == s2); // true

System.out.println("*****************");

String s4 = "abc";
String s5 = s4.replace('a', 'm');
System.out.println(s4);//abc
System.out.println(s5);//mbc
}

三、String 实例化的不同方式

方式说明:
方式一:通过字面量定义的方式
方式二:通过 new+构造器的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void test1(){
// 通过字面定义的方式:此时的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
}

图示:
String实例化
问题:

1
2
String s = new String("abc"); 方式创建对象,在内存中创建了几个对象?
两个:一个是堆空间"new"结构,另一个是char[] 对应的常量池中的数据:"abc"

四、字符串拼接方式赋值的对比

说明: 1.常量与常量的拼接结果在常量池,且常量池中不会存在相同的常量 2.只要其中一个是变量,结果就在堆中。 3.如果拼接的结果调用 **intern()**方法,返回值就是在常量池中。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test
public void test2(){
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(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

System.out.println("**********************************");
String s8 = s6.intern(); // 返回值得到的s8使用的常量值中已经存在的"javaEEhadoop";
System.out.println(s3 == s8); // true

}
1
2
3
4
5
6
7
8
9
10
11
@Test
public void test3(){
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
}

五、常用方法

  • int length():返回字符串的长度: return value.length
  • char charAt(int index): 返回某索引处的字符 return value[index]
  • boolean isEmpty():判断是否是空字符串:return value.length == 0
1
2
3
4
String str = "abc";
System.out.println(str.length());// 3
System.out.println(str.charAt(2));// c
System.out.println(str.isEmpty());// false
  • String toLowerCase():使用默认语言环境,将 String 中的所字符转换为小写
  • String toUpperCase():使用默认语言环境,将 String 中的所字符转换为大写
  • String trim():返回字符串的副本,忽略前导空白和尾部空白
1
2
3
4
5
String str1 = "abcdefg";
String str2 = " abcdefg ";
System.out.println(str1.toLowerCase());// abcdefg
System.out.println(str1.toUpperCase());// ABCDEFG
System.out.println(str2.trim());// abcdefg
  • boolean equals(Object obj):比较字符串的内容是否相同
  • boolean equalsIgnoreCase(String anotherString):与 equals 方法类似,忽略大小写
  • String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+”
1
2
3
4
5
6
7
String str3 = "abcdefg";
String str4 = "ABCDEFG";
String str5 = "abcd";
String str6 = "efg";
System.out.println(str3.equals(str4)); // false
System.out.println(str3.equalsIgnoreCase(str4)); // true
System.out.println(str5.concat(str6));// abcdefg
  • int compareTo(String anotherString):比较两个字符串的大小
  • String substring(int beginIndex):返回一个新的字符串,它是此字符串的从 beginIndex 开始截取到最后的一个子字符串。
  • String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从 beginIndex 开始截取到 endIndex(不包含)的一个子字符串。
  • int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
1
2
3
4
5
6
7
String str7 = "abcdefg";
String str8 = "abcdefg";
String str9 = "abcd";
System.out.println(str7.compareTo(str8)); // 0 表示相等
System.out.println(str7.substring(2));// cdefg
System.out.println(str7.substring(0,5)); // abcde
System.out.println(str7.indexOf("d",0)); // 3
  • boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
  • boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
  • boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
1
2
3
4
5
6
7
String str1 = "helloworld";
boolean b1 = str1.endsWith("rld");
boolean b2 = str1.startsWith("hel");
boolean b3 = str1.startsWith("ll",2);
System.out.println(b1); //ture
System.out.println(b2); //ture
System.out.println(b3); //ture
  • boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true
1
2
3
String str1 = "helloworld";
String str2 = "wor";
System.out.println(str1.contains(str2)); // true
  • int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
1
2
System.out.println(str1.indexOf("ll")); // 2
System.out.println(str1.indexOf("lo",5));// -1
  • int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
  • int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
1
2
3
String str3 = "hellorworld";
System.out.println(str3.lastIndexOf("or")); // 7
System.out.println(str3.lastIndexOf("or",2)); // -1

注:indexOf 和 lastIndexOf 方法如果未找到都是返回-1

替换:

  • String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所 oldChar 得到的。
  • String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所匹配字面值目标序列的子字符串。
  • String replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串所匹配给定的正则表达式的子字符串。
  • String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
1
2
3
4
5
6
7
8
9
String str1 = "中国湖南";
// String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
String str2 = str1.replace("湖南","湖北");
System.out.println(str2); // 中国湖北
System.out.println("*********************************************");
// String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
String str3 = "12hello34world5java7891mysql456";
String str4 = str3.replaceAll("\\d+", ",").replaceAll("^,|,$", "");
System.out.println(str4); // hello,world,java,mysql

匹配:

  • boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
1
2
3
4
String str5 = "12345";
//判断str字符串中是否全部有数字组成,即有1-n个数字组成
boolean matches = str5.matches("\\d+");
System.out.println(matches); // true

切片:

  • String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
  • String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过 limit 个,如果超过了,剩下的全部都放到最后一个元素中。
1
2
3
4
5
6
7
8
9
10
11
String str6 = "hello|world|java";
String[] strs = str6.split("\\|");
for (int i = 0; i < strs.length; i++) {
System.out.println(strs[i]); // hello world java
}
System.out.println();
String str7 = "hello.world.java";
String[] strs2 = str7.split("\\.");
for (int i = 0; i < strs.length; i++) {
System.out.println(strs2[i]); // hello world java
}

String 与其他结构的替换

  • ① 与基本的数据类型、包装类之间的装换
    String  ----  基本数据类型、包装类:调用包装类的静态方法:parse**XXX**(str)
    

    基本数据类型、包装类 — String:调用 String 重载的 valueof(XXX)

代码示例

1
2
3
4
5
6
7
public void test1(){
String str = "123";
int num = Integer.parseInt(str);
System.out.println(num);
String str1 = String.valueOf(num);
System.out.println(str1);
}

String ====》char[ ]: 调用 String 的 toCharArray();
char[ ] ===》 String :调用 String 的构造器

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
 public void test2(){
// String --> char[] :调用String的toCharArray();
String str = "abc123";
char[] ch = str.toCharArray();
for(int i = 0; i < ch.length; i++){
System.out.println(ch[i]);
}
System.out.println("***************************");
// char[] --> String:调用String的构造器
char[] arr = new char[]{'h','l','l'};
String str2 = new String(arr);
System.out.println(str2);
}
  • ② 与字数组之间的转换

编码:String ===》 byte[ ]:调用 String 的 getBytes();
解码:byte[ ] ===》 String :调用 String 的构造器
编码:字符串 ===》 字节(二进制)
解码:编码的逆过程,字节 ===》 字符串
说明:解码时,要求解码的使用的字符集必须与编码的字符集一致,否则会出现乱码。

代码示例:

1
2
3
4
5
6
7
public void test3() throws UnsupportedEncodingException {
String str = "abc";
byte[] bytes = str.getBytes(); // 使用默认字符集,进行编码
System.out.println(bytes);
byte[] gbks = str.getBytes("gbk");
System.out.println(Arrays.toString(gbks));
}

运行结果:

补充:
与 StringBuffer、StringBuilder 之间的转换
String === 》 StringBuffer、StringBuilder:调用 StringBuffer、StringBuilder 构造器;
StringBuffer、StringBuilder ===》 String: ① 调用 String 构造器 ② StringBuffer、StringBuilder 的 toString();

JVM 中字符串常量池存放位置说明:

JDK 1.6: (jdk 6.0 , java 6.0):字符串常量池存储在方法区永久区
JDK 1.7: 字符串常量池存储在堆空间
JDK 1.8: 字符串常量池存储在方法区(云空间

StringBuffer、StringBuilder

一、String、String、StringBuilder 三者的对比

1
2
3
String:不可变的字符序列:底层使用char[] 储存
StringBuffer: 可变的字符序列:线程安全的,效率低:底层使用char[]存储
StringBuilder: 可变的字符序列:jdk5.0:新增的,线程不安全,效率高;底层使用char[] 存储

可变类型:

 比如列表:列表名(或称为列表引用)指向堆空间中的一个列表。列表引用当中存储的内容为地址,可以指向堆内存中的具体对象.

一方面,该列表可以保证在地址不变的前提下,改变列表内容;另一方面,该引用也可以直接指向堆空间中的另外一个列表..

不可变序列:

比如数字、字符、(元祖):对于基础数据类型,变量地址即变量本身,所以值发生变化了,内存地址就一定也变了。

比如字符串:被称为不可变的字符序列,每个字符底层均有规范的Unicode码对应,内存规定无法对单一的一个字符进行修改,要修改字符串必须重新指向一个全新的字符串

二、StringBuffer 与 StringBuilder 的内存解析

1
2
3
4
5
6
7
8
9
10
以Stringuffer为例:
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的数组。
System.out.println(sb1.length());//
sb1.append('a');//value[0] = 'a';
sb1.append('b');//value[1] = 'b';

StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length() + 16];

扩容问题:如果添加的数组底层数组盛不下,那就需要扩容底层的数组
默认的情况,扩容为原来的 2 倍+2;同时将原来的数组复制到新数组中。
在开发中优先使用:StringBuffer、StringBuilder

三、对比 String、StringBuffer 、StringBuilder 三者的执行效率

从高到低排序:StringBuiler > StringBuffer > String

四、StringBuffer、StringBuilder 中常用的方法

1
2
3
4
5
6
7
增:oppend(XXX)
删:delete(int start, int end)
改:setCharAt(int n , char ch) / replance(int start, int end , Sting str)
查:charAt(int n)
插: insert(int offset , XXX);
长度:Lenght();
*遍历:for() + charAt() / toString()

JDK 8 之前日期时间 API

一、获取系统之前当前时间

System 类中的 CurrentTimeMills()

1
2
3
4
long time = Sysem.cuurenTimeMillis()
//返回当前时间与1970年1月1日0时0分之间以毫秒为单位的时间差。
//称为时间戳
System.out.println(time);

二、java.util.Date 类与 java.sql.Date 类

1
2
3
4
5
6
7
8
9
10
11
java.util.Date类
|---java.sql.Date类
1.两个构造器的使用
>构造器一:Date()创建一个对应当前时间的Date对象
>构造器二:创建指定毫秒数的Date对象
2.两个方法的使用
>toString() 显示当前的年、月、日、分、秒
>getTime() 获取当前Date对象对应的毫秒数。(时间戳)
3.java.sql.Date对应着数据中日期类型的变量
> 实例化
> 将java.util.Date 转为 java.sql.Date

代码示例:

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
@Test
public void test1(){
long time = System.currentTimeMillis();
System.out.println(time);
}
@Test
public void test2(){
// 构造器一:Date():创建一个对应当前时间的Date对象
Date date = new Date();
System.out.println(date.toString()); // Sat Jun 13 17:07:02 CST 2020

System.out.println(date.getTime()); // 1592039273009
}
@Test
public void test3(){
// 构造器二:创建指定毫秒数的Date对象
Date date = new Date(1592039273009L);
System.out.println(date.toString()); // Sat Jun 13 17:07:53 CST 2020
System.out.println("***********************");
// 创建java.sql.Date对象
java.sql.Date date1 = new java.sql.Date(1592039273009L);
System.out.println(date1); // 2020-06-13
}

@Test
/**
* 将java.util.Date对象转换为java.sql.Date对象
*/
public void test4(){
// 情况一:
Date date = new java.sql.Date(1592039273009L);
java.sql.Date date1 = (java.sql.Date) date;

// 情况二:
Date date2 = new Date();
java.sql.Date date3 = new java.sql.Date(date2.getTime());
}

三、java.text.SimpleDateFormat 类

1
2
3
4
5
simpleDateFormat 对日期Date类的格式化和解析
1.两个操作
1.1 格式化:日期 --> 字符串
1.2 解析: 格式化的逆过程,字符串 --> 日期
2.SimpleDateFormat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 @Test
public void test5() throws ParseException {
// 照指定的方式格式化和解析:调用带参的构造器
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm aaa");
// 格式化
Date date = new Date();
String format = sdf.format(date);
System.out.println(format); // 2020-06-13 05:23 下午

//解析:要求字符串必须是符合SimpleDateFormat识别的格式(通过构造器参数体现),
//否则,抛异常
Date parse = sdf.parse("2020-06-13 05:23 下午");
System.out.println(parse); // Sat Jun 13 17:23:00 CST 2020
}

四、Calendar 类:日历类、抽象类

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
@Test
public void test6(){
//1.实例化
//方式一:创建其子类(GregorianCalendar的对象
//方式二:调用其静态方法getInstance()
Calendar calendar = Calendar.getInstance();
// System.out.println(calendar);

//2 常用方法
// get
int days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);// 13
System.out.println(calendar.get(Calendar.DAY_OF_YEAR)); // 165

// set()
calendar.set(Calendar.DAY_OF_MONTH,22);
int days1 = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days1); // 22

//add()
calendar.add(Calendar.DAY_OF_MONTH,-3);
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days); // 19

// getTime() 日历类 ---> Date
Date time = calendar.getTime();
System.out.println(time);// Fri Jun 19 17:41:29 CST 2020

//setTime():Date ---> 日历类
Date date = new Date();
calendar.setTime(date);
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days); // 13
}

JDK8 中新日期时间 API

一、日期时间的迭代

第一代:jdk 1.0 Date 类
第二代:jdk 1.1 Calender 类,一定的程度上替代了 Date 类
第三代:jdk 1.8 提出了新的一套 API

二、前两代存在的问题举例:

可变性:像时间和日期这样的类应该是吧不可变的。
偏移性:Date 中的年份是从 1900 年开始的,月份是从 0 开始的。
格式化:格式话只对 Date 有用,calender 则不行。
此外,它们也不是线程安全的,不能处理闰秒等。

三、java 8 中新的日期时间 API 涉及到的包

新日期API包

四、本地日期、本地时间、本地日期时间的使用:LocalDate/LocalTime/LocalDateTime

  • 一、说明

    ① 分别表示使用 ISO-8601 日历系统的日期、时间、和时间。它们提供了简单的本地时间信息,也不包含当前的时间信息,也不包含与时区相关的信息。
    ② LocalDatetime 相较于 LocalDate、LocalTIme,使用频率高
    ③ 类似于 Calender

  • 二、常用方法

五 时间点:Instant

  • 一、说明

    ① 时间线上的一个瞬时点,概念上讲。它只是简单的表示自 1970 年 1 月 1 日 0 时 0 秒(UTC 开始的秒数)
    ② 类似于 java.util.Date 类

  • 二、常用方法

六、时间日期格式化类:DateTimeFormatter

  • 一、说明

① 格式化或解析日期、时间
② 类似于 SimpleDateFormat

  • 二、常用方法
    ① 实例化方式:

预定义的标准格式,如:ISO_LOCAL_DATE_TIME; ISO_LOCAL_DATE;ISO_DATE;ISO_LOCAL_TIME
本地相关的格式:如:ofLocalizedDateTime(FormatStyle.LONG)
自定义的格式:如:ofPattern(“yyyy-MM-dd hh:mm:ss”)

② 常用方法
在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
public void test1(){

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
// 格式化
String str = formatter.format(LocalDateTime.now());
System.out.println(str); // 2020-06-20 03:38:37

// 解析
TemporalAccessor accessor = formatter.parse("2020-06-20 03:38:37");
System.out.println(accessor); // {MinuteOfHour=38, HourOfAmPm=3, MilliOfSecond=0, MicroOfSecond=0, NanoOfSecond=0, SecondOfMinute=37},ISO resolved to 2020-06-20
}

七、其他

  • 带时区的日期时间:ZonedDateTime / ZoneId
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
/**
* ZoneId:类中包含的失去信息
*/
@Test
public void test3(){
// getAvailableZoneIds():获取所有的ZoneId
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
for (String s : zoneIds) {
System.out.println(s);
}
System.out.println("***********************");
LocalDateTime localDateTime = LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println(localDateTime); // 2020-06-20T16:53:34.885
}

/**
* ZonedDateTime: 带时区的时间
*/
@Test
public void test4(){
// now() 获取本时区的ZoneDateTime对象
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now); // 2020-06-20T15:57:00.163+08:00[Asia/Shanghai]

// now(ZoneId id) 获取指定时区的ZonedTime对象
ZonedDateTime zonedDateTiem = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(zonedDateTiem);
}
  • 时间间隔:Duration–用于计算两个“时间”间隔,以秒和纳秒为基准
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void test5(){
LocalTime localTime = LocalTime.now();
LocalTime localTime2 = LocalTime.of(15, 23, 32);
// between() 静态方法,返回Duration对象,表示两个时间间隔
Duration between = Duration.between(localTime, localTime2);
System.out.println(between);

System.out.println(between.getSeconds());
System.out.println(between.getNano());

LocalDateTime localdatetime = LocalDateTime.of(2016, 6, 12, 15, 23, 32);
LocalDateTime localdatetime1 = LocalDateTime.of(2017, 6, 12, 15, 23, 32);
Duration between1 = Duration.between(localdatetime, localdatetime1);
System.out.println(between1.toDays()); // 365
}
  • 日期间隔:Period –用于计算两个“日期”间隔,以年、月、日衡量

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void test6(){
LocalDate localdate = LocalDate.now();
LocalDate localdate1 = LocalDate.of(2028, 3, 18);

Period period = Period.between(localdate, localdate1);
System.out.println(period); // P7Y8M27D

System.out.println(period.getYears()); // 7
System.out.println(period.getMonths()); // 8
System.out.println(period.getDays()); // 27

Period period1 = period.withYears(2);
System.out.println(period1); // P2Y8M27D
}
  • 日期时间校正器:TemporalAdjuster
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void test7(){
// 获取当前日期的下一个周日是哪天?
TemporalAdjuster temporalAdjuster = TemporalAdjusters.next(DayOfWeek.SUNDAY);
LocalDateTime localDateTime = LocalDateTime.now().with(temporalAdjuster);
System.out.println(localDateTime); // 2020-06-21T16:42:13.229

// 获取下一个工作日食哪天
LocalDate localDate = 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("下一个工作日是:"+localDate);
}

Java 比较器

一、java 比较器的使用背景

① Java 中的对象,正常情况下,只能进行比较: == 或 != ,不能使用 > 或 < 但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。使用两个接口中的任何一个: ComparableCompartor

二、自然排序:使用 comparable 接口

  • 说明

    ① 像 String、包装类等实现了 Comparable 接口,重写了 comparaTo(obj)方法,给出了比较两个对象大小的方式。
    ② String、包装类重写了 comparaTo()的规则:
    如果当前的对象 this 大于形参对象 obj,则返回正整数.
    如果当前对象 this 小于形参 obj,则返回负整数
    如果当前对象 this 等于形对象 obj,则返回零。
    ③ 对于自定义类来说,如果需要排序,我们可以自定义类实现 Comparablej 接口,重写 comparaTo(obj)方法。在 comparaTo()方法中指明如何排序

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
public class Goods implements Comparable{
private Sting name;
private double price;
//指明商品比较大小的方式:照价格从低到高排序,在照产品名称从高到低排序
@Override
public int compareTo(Object o) {
// System.out.println("**************");
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);
}
//方式二:
// return Double.compare(this.price,goods.price);
}
// return 0;
throw new RuntimeException("传入的数据类型不一致!");
}
// getter、setter、toString()、构造器:省略
}


}
}

三、定制排序:使用 Comparator 接口

说明:

① 背景:

当元素的类型没实现 java.lang.comparable 接口而又不方便修改代码,或实现了 java.lang.Comparable
接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象类排序

② 重写 comparator(Object o1, Object o2)方法,比较 o1 和 o2 的大小:

重写方法返回正整数,则表示 o1 大于 o2
如果返回 0,表示相等
返回负整数,表示 o1 小于 o2

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Comparator com = 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("输入的数据类型不一致");
}
}
  • 使用:
1
2
3
Arrays.sort(goods,com);
Collections.sort(coll,com);
new TreeSet(com);
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
 public void test3(){
Goods[] arr = new Goods[6];
arr[0] = new Goods("lenovoMouse",34.0);
arr[1] = new Goods("dellMouse",43.0);
arr[2] = new Goods("xiaomiMouse",12.0);
arr[3] = new Goods("huaweiMouse",65.0);
arr[4] = new Goods("huaweiMouse",224.0);
arr[5] = new Goods("microsoftMouse",43.0);

Arrays.sort(arr);
System.out.println("Comparable排序"+Arrays.toString(arr));

// 使用匿名内部类,可以在一定的程度上优化代码 new Comparator<T>(){}
Arrays.sort(arr, new Comparator<Goods>() {
@Override
public int compare(Goods o1, Goods o2) {
// name 相同 就比较 价格(降序)
if(o1.getName().equals(o2.getName())){
return -Double.compare(o1.getPrice(),o2.getPrice());
}else{
return o1.getName().compareTo(o2.getName());
}
}
});
System.out.println("Comparator:"+Arrays.toString(arr));
}
}

其他类

一、System 类

① System 类代表系统,系统级的很多属性和控制方法都放置在该类的内部。该类位于 java.lang 包。
② 由于该类的构造器是 private 的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员变量和成员方法都是 static 的,所以也可以很方便的进行调用。

  • 方法:
1
2
3
4
native long currentTimeMillis()
void exit(int status)
void gc()
String getProperty(String key)

二、Math 类

1
java.lang.Math提供了一系列静态方法用于科学计算。其方法的参数和返回值类型一般为double型。

三、BigInteger 类、BigDecimal 类

说明:

① java.math 包的 BigInteger 可以表示不可变的任意精度的整数。
② 要求数字精度比较高,用到 java.math.BigDecimal 类

1
2
3
4
5
6
7
8
9
10
11
12
public class BigIntegerTest {
@Test
public void test1(){
BigInteger bi = new BigInteger("12323443544535");
BigDecimal bd = new BigDecimal("233434.35445");
BigDecimal bd2 = new BigDecimal("2113");
System.out.println(bi); // 12323443544535

System.out.println(bd.divide(bd2,BigDecimal.ROUND_HALF_UP)); // 110.47532
System.out.println(bd.divide(bd2,15,BigDecimal.ROUND_HALF_UP)); // 110.475321557027922
}
}