第9章 java常用类
第9章、java常用类
目录
- 一、字符串相关类(String 类、StringBuffer、StringBuilder)【面试常考】
- 二、JDK 8 之前的日期时间 API(System 静态方法、Date 类,Calendar 类、SimpleDateFormat 类)
- 三、JDK 8 中新日期时间 API(LocalDate、LocalTime、LocalDateTime、Instant、DateTimeFormatter、其他类)
- 四、Java 比较器(Comparable 接口、Comparator 接口)(重点)
- 五、System 类
- 六、Math 类
- 七、BigInteger 与 BigDecimal
- 面试
- 每日一考
一、字符串相关类(String类、StringBuffer、StringBuilder)【面试常考】
String:字符串,使用一对“”引起来表示
- String是声明为final的,不可被继承
- String类实现了Serializable接口:表示字符串是支持序列化的(可通过网络传输IO流)
- String类实现了Comparable接口:表示String对象可比较大小
- String类内部定义了final char[] value用于存储字符串数据(不可更改数组大小、内容)
- String:代表不可变的字符序列,不可变 性- 不可变性体现1:当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value赋值(在常量池中重新分配内存区域并将地址赋给引用)
- 不可变性体现2:对现有字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value赋值
- 不可变性体现3:当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value赋值
 
String对象的内存结构
- String实例化方式:- 通过字面量定义的方式:此时的字符串引用的数据声明在方法区中的字符串常量池中- 不用new的赋值,为字面量的定义方式。String str = "str";
- 通过字面量的方式给字符串赋值,此时的字符串值声明在字符串常量池中。
- 字符串常量池中不会存储相同内容的字符串(相同的字符串内容的引用都指向同一地址)
 
- 通过new+构造器的方式:此时字符串引用保存的地址值时数据在堆空间中开辟空间后对应的地址值。但堆空间中的char[]数组所指向的地址仍指向字符串常量池,且若在常量池中已经存在传入构造器的字符串内容时,char[]数组指向的地址也是该常量池已存在的字符串地址
 
- 通过字面量定义的方式:此时的字符串引用的数据声明在方法区中的字符串常量池中

- 结论(面笔试常考)- 常量(字面量)与常量(字面量)的拼接结果在常量池。且常量池中不会存在相同内容的常量。 - 1 
 2
 3
 4- String str = "strok"; 
 final String s = "str"; // 仍然是常量
 String s1 = s + "ok";
 sysout(s1 == str) // true,加了final就是常量,常量和常量拼接仍在常量池
- 只要其中有一个是变量,结果就在堆中 
- 如果字符串拼接的结果调用intern()方法,返回值就在常量池中 
 
面试题
| 1 | public class StringTest { | 
方法的值传递机制中,当实参和形参都是引用类型变量时,传递的值确实是地址值,因为实参是栈中保存的值即对象在堆中的地址值,但需要澄清的一点是:方法中形参拿到该值后可以通过该值找到对象并修改对象中的属性值(类似数组传递后可更改数组中存储的值),但是方法内的形参不会影响实参的值,也就是说形参的值改变(意味着形参存储的地址值改变)时,实参仍是指向原来的地址的。
上面代码中的change方法中,对形参str又重新赋值,即形参改的是地址值,并没有改变实参所指向地址堆空间中的属性值。就像传入数组地址一样,你在方法内通过形参去索引数组的某个元素并修改他是可以影响到外面的,因为外面的数组指向的也是这个堆空间,但是这种方式并没有影响实参数组的指向地址,当你在方法内把形参数组重新赋值时改的也只是形参指向的地址,并不会改变实参指向的地址。
总结:实参只是传递给形参一个值的副本或值的copy,无论如何,形参的操作无法影响实参的存储数据(是栈中保存的数据,而不是堆中的数据,只要知道存储在堆中对象的地址就能通过调用修改对象中的属性)
【初级参数传递见 第四章 - 六、再谈方法】
String常用方法
- 常用方法1
| 1 | int length():返回字符串的长度: return value.length | 
- 常用方法2
| 1 | boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束 | 
- 常用方法3
| 1 | String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用newChar 替换此字符串中出现的所有oldChar 得到的。 | 
String和基本数据类型转换
- String --> 基本数据类型、包装类 - 1 - 包装类.parseXxx(str); 
- 基本数据类型、包装类 --> String - 1 - String.valueOf(xxx); 
String与字符数组转换
- String --> char[] - 1 
 2
 3
 4- String对象.toCharArray(); 
 //提供了将指定索引范围内的字符串存放到数组中的方法。
 public void getChars(int srcBegin, int srcEnd, char[] dst,int dstBegin)
- char[] --> String - 1 
 2
 3- // String 类的构造器: 
 String(char[]);
 String(char[],int offset,int length);
String与字节数组转换
- String --> byte[]:编码 - 1 
 2
 3
 4
 5
 6
 7
 8
 9- //使用平台的默认字符集将此 String 编码为byte 序列,并将结果存储到一个新的 byte 数组中。 
 byte[] bytes = String对象.getBytes(); // 使用默认字符集进行转换
 sysout(Arrays.toString(bytes));
 //使用指定的字符集将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组
 public byte[] getBytes(String charsetName)
 byte[] bytes = String对象.getBytes("gbk"); // 使用gbk字符集进行编码
 sysout(Arrays.toString(bytes));
- byte[] --> String:解码 - 1 
 2
 3
 4
 5
 6
 7- // 通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。 
 String(byte[]); // 使用默认字符集进行解码
 String(byte[], "gbk"); // 使用gbk字符集进行解码
 // 解码时要求解码使用的字符集必须与编码时使用的字符集一致
 // 用指定的字节数组的一部分,即从数组起始位置offset开始取length个字节构造一个字符串对象
 String(byte[],int offset,int length)
String常见算法
- 模拟一个trim方法,去除字符串两端的空格。 - 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- public class TrimAlike { 
 public static void main(String[] args) {
 String str = " 1 ";
 String str1 = str.trim();
 System.out.println(str1);
 String str2 = Trim(str);
 System.out.println(str2);
 }
 public static String Trim(String str) {
 char[] ch = str.toCharArray();
 int start = -1;
 int end = str.length();
 for (int i = 0; i < str.length(); i++) {
 if (ch[i] != ' ') {
 break;
 }
 start = i;
 }
 for (int i = str.length() - 1; i >= 0; i--) {
 if (ch[i] != ' ') {
 break;
 }
 end = i;
 }
 if(start < end)
 return new String(ch, start + 1, end - start - 1);
 else
 return "";
 }
 }
- 将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg”反转为”abfedcg” - 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- public class ReverseString { 
 public static void main(String[] args) {
 String str = "abcdefg";
 int start = 2;
 int end = 5;
 String str1 = reverseString(str, start, end);
 System.out.println(str1);
 }
 private static String reverseString(String str, int start, int end) {
 if (str != null && start < end && str.length() - 1 > end && start >= 0) {
 char[] chars = str.toCharArray();
 for (int i = 0; i < (end - start) / 2; i++) {
 char tmp = chars[start + i];
 chars[start + i] = chars[end - i];
 chars[end - i] = tmp;
 }
 return new String(chars);
 } else {
 return null;
 }
 }
 // 基于数组的操作
 private static String reverseString2(String str, int start, int end) {
 if (str != null && start < end && str.length() - 1 > end && start >= 0) {
 char[] chars = str.toCharArray();
 for (int x = start, y = end; x < y; x++, y--) {
 char tmp = chars[x];
 chars[x] = chars[y];
 chars[y] = tmp;
 }
 return new String(chars);
 } else {
 return null;
 }
 }
 // 使用String的拼接
 private static String reverseString3(String str, int start, int end) {
 if (str != null && start < end && str.length() - 1 > end && start >= 0) {
 String reverseStr = str.substring(0, start);
 for (int i = end; i >= start; i--) {
 reverseStr += str.charAt(i);
 }
 reverseStr += str.substring(end + 1);
 return new reverseStr;
 } else {
 return null;
 }
 }
 // 使用StringBuffer | StringBuilder
 private static String reverseString4(String str, int start, int end) {
 if (str != null && start < end && str.length() - 1 > end && start >= 0) {
 StringBuilder stringBuilder = new StringBuilder();
 stringBuilder.append(str, 0, start);
 for (int i = end; i >= start; i--) {
 stringBuilder.append(str.charAt(i));
 }
 stringBuilder.append(str.substring(end + 1))
 return new stringBuilder.toString();
 } else {
 return null;
 }
 }
 }
- 获取一个字符串在另一个字符串中出现的次数。比如:获取“ ab”在 “abkkcadkabkebfkabkskab” 中出现的次数 - 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- public class CountString { 
 public static void main(String[] args) {
 String str1 = "ab";
 String str2 = "abkkcadkabkebfkabkskab";
 System.out.println(countString(str1, str2));
 }
 /**
 * 计算str1在str2中出现次数
 * @param str1
 * @param str2
 * @return
 */
 private static int countString(String str1, String str2) {
 int cnt = 0;
 if(str1.length() > str2.length()){
 return 0;
 }else if(str1.length() == str2.length()){
 if(str1.compareTo(str2) != 0){
 return 0;
 }
 return 1;
 }else{
 for(int i = 0; i < str2.length();i++;){
 i = str2.indexOf(str1, i);
 System.out.println("index: " + i);
 if(i == -1)
 break;
 cnt++;
 }
 return cnt;
 }
 }
 }
- 获取两个字符串中最大相同子串。比如: str1 = "abcwerthelloyuiodef“;str2 = "cvhellobnm" 提示:将短的那个串进行长度依次递减的子串与较长的串比较。 
| 1 | public class LongestString { | 
- 对字符串中字符进行自然顺序排序。 提示:
1)字符串变成字符数组。 2)对数组排序,选择,冒泡,Arrays.sort(); 3)将排序后的数组变成字符串。
| 1 | public class StringSort { | 
StringBuffer类 & StringBuilder类(以StringBuffer为例)
- String、StringBuffer、StringBuilder三者异同(高频面试题) - String:不可变的字符序列;底层使用final char[]存储
- StringBuffer:可变的字符序列;线程安全,效率低;底层使用char[]存储
- StringBuilder:可变的字符序列;JDK5.0新增,线程不安全,效率高;底层使用char[]存储
 
- 底层实现 - String - 1 
 2- String str1 = new String(); // char[] value = new char[0]; 
 String str1 = new String("abc"); // char[] value = new char[]{'a', 'b', 'c'};
- StringBuffer - 1 
 2
 3
 4
 5
 6
 7
 8- StringBuffer sb1 = new StringBuffer(); // char[] value = new char[16]; 
 sysout(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];
 sysout(sb2.length())// 3- 如果要添加的数据底层数组盛不下了,则需要扩容底层数组。默认情况下扩容为原来容量的2倍+2,同时将原有数组中元素复制到新数组中
- 指导意义:开发中建议使用:StringBuffer(int capacity)构造器或StringBuilder(int capacity)指定底层数组长度以减少重新分配数组的内存空间;
 
 
- StringBuffer类常用方法(StringBuilder含有相同的方法,只是方法线程不同步) - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10- StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接 
 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() :把当前字符序列逆转
 
 public int indexOf(String str)
 public String substring(int start,int 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方法
 
- 效率比较: - StringBuilder > StringBuffer > String
 
二、JDK 8之前的日期时间API(System静态方法、Date类,Calendar类、SimpleDateFormat类)
System静态方法
- System类提供的public static long currentTimeMillis()- 用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。
- 称为时间戳
- 可用来计算代码执行的时长
 
java.util.Date类
java中的Date类有java.util.Date,java.sql.Date;java.util.Date为java.sql.Date父类
- 两个构造器的使用(java.util.Date) - 构造器一:创建一个对应当前时间的Date对象 - 1 
 2- Date date = new Date(); 
 sysout(date.toString());
- 构造器二:创建指定毫秒数的Date对象 - 1 - Date date2 = new Date(1555100545L); 
 
- 两个方法的使用 - toString():返回当前的年、月、日、时、分、秒字符串
- getTime():获取当前Date对象对应的毫秒数(时间戳)
 
- java.sql.Date对应着数据库中的日期类型的变量 - 创建java.sql.Date对象 - 1 - java.sql.Date date = new java.sql.Date(1555555555L); 
- 将java.util.Date对象转化为java.sql.Date对象 - 1 
 2- java.util.Date date = new java.util.Date(); 
 java.sql.Date dateSql = new java.sql.Date(date.getTime());
 
java.text.SimpleDateFormat类
SimpleDateFormat的使用:SimpleDateFormat对日期Date类的格式化和解析
- 两个操作 - 格式化:日期 --> 字符串 - 1 
 2
 3
 4
 5
 6
 7- // 默认构造器 
 SimpleDateFormat sdf = new SimpleDateFormat();
 // 格式化
 Date date = new Date();
 String format = sdf.format(date);
 System.out.println(format); // 21-9-16 下午4:32
- 解析:(格式化逆过程)字符串 --> 日期 - 1 
 2
 3
 4
 5
 6
 7
 8
 9- // 解析 
 String str = "21-9-16 下午4:23";
 Date date1 = null;
 try {
 date1 = sdf.parse(str);
 } catch (ParseException e) {
 e.printStackTrace();
 }
 System.out.println(date1); // Thu Sep 16 16:23:00 CST 2021
- 指定方式格式化:调用带参构造器 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17- //******指定方式格式化:调用带参构造器, 可参考API手册************** 
 //SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyy.MMMMM.dd GGG hh:mm aaa");
 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
 // HH: 24h制; hh: 12h制
 Date date = new Date();
 String format1 = simpleDateFormat.format(date);
 System.out.println(format1); // 2021.09.16 04:32:23
 // 解析: 要求字符串必须符合SimpleDateFormat识别的格式(通过构造器参数指定),否则会抛异常
 Date date2 = null;
 try {
 date2 = simpleDateFormat.parse("2021.09.16 04:32:23");
 } catch (ParseException e) {
 e.printStackTrace();
 }
 System.out.println(date2); // Thu Sep 16 04:32:23 CST 2021 
 
- 练习 - 字符串“2020-09-08”转换为java.sql.Date - 1 
 2
 3
 4
 5
 6
 7
 8
 public void test2() throws ParseException {
 String str = "2020-09-08";
 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
 Date date = simpleDateFormat.parse(str);
 java.sql.Date dateSql = new java.sql.Date(date.getTime());
 System.out.println(dateSql);
 }
- "三天打鱼两天晒网",如果该渔夫从1990-01-01开始按俗语执行,问到某年某月某日时该渔夫是在打鱼还是晒网 - 1 
 2
 3
 4- // 总天数 mod 5:若为1、2、3则为打鱼;若为0、4则为晒网 
 // 总天数计算
 // 方式一:(date2.getTime() - date1.getTime()) / (1000 * 60 * 60 * 24) + 1
 // 方式二:计算整年天数 + 在某年内的天数
 
Calendar类
Calendar是一个抽象基类,主用用于完成日期字段之间相互操作的功能。
获取Calendar实例的方法
- 使用Calendar.getInstance()方法
- 调用它的子类GregorianCalendar的构造器。
- 使用方式 - 实例化 - 方式一(不常用):调用它的子类GregorianCalendar的构造器。 
- 方式二:使用Calendar.getInstance()方法 - 1 - Calendar calendar = Calendar.getInstance(); 
 
- 常用方法 - get():
- set()
- add()
- getTime()
- setTime()
 - 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
 public void test(){
 Calendar calendar = Calendar.getInstance();
 // get()
 int daysInMonth = calendar.get(Calendar.DAY_OF_MONTH);
 int daysInYear = calendar.get(Calendar.DAY_OF_YEAR);
 System.out.println("today is " + daysInMonth + "-th day in this month, "
 + daysInYear + "-th day in this year");
 // today is 16-th day in this month, 259-th day in this year
 // set()
 calendar.set(Calendar.DAY_OF_MONTH, 22);
 daysInMonth = calendar.get(Calendar.DAY_OF_MONTH);
 daysInYear = calendar.get(Calendar.DAY_OF_YEAR);
 System.out.println("today is " + daysInMonth + "-th day in this month, "
 + daysInYear + "-th day in this year");
 // today is 22-th day in this month, 265-th day in this year
 // add()
 calendar.add(Calendar.DAY_OF_MONTH, 5);
 daysInMonth = calendar.get(Calendar.DAY_OF_MONTH);
 daysInYear = calendar.get(Calendar.DAY_OF_YEAR);
 System.out.println("today is " + daysInMonth + "-th day in this month, "
 + daysInYear + "-th day in this year");
 // today is 27-th day in this month, 270-th day in this year
 // getTime()
 Date date = calendar.getTime();
 System.out.println(date);
 // Mon Sep 27 21:27:07 CST 2021
 // setTime()
 Date dateSet = new Date();
 calendar.setTime(dateSet);
 // 获得的Month从0开始
 System.out.println(calendar.get(Calendar.YEAR) + "年"
 + (calendar.get(Calendar.MONTH)+1) + "月"
 + calendar.get(Calendar.DATE) + "日");
 // 2021年9月16日
 }
 
- 注意点 - 获取月份时:一月是0,二月是1,以此类推,12月是11
- 获取星期时:周日是1,周二是2 , 。。。。周六是7
 
三、JDK 8中新日期时间API(LocalDate、LocalTime、LocalDateTime、Instant、DateTimeFormatter、其他类)
JDK 1.0中包含了一个java.util.Date类,但是它的大多数方法已经在JDK 1.1引入Calendar类之后被弃用了。而Calendar并不比Date好多少。它们面临的问题是:
- 可变性:像日期和时间这样的类应该是不可变的。
- 偏移性:Date中的年份是从1900开始的,而月份都从0开始。
- 格式化:格式化只对Date有用,Calendar则不行。
- 此外,它们也不是线程安全的;不能处理闰秒等。
总结:对日期和时间的操作一直是Java程序员最痛苦的地方之一。
第三次引入API
第三次引入的API是成功的,并且Java 8中引入的java.time API 已经纠正了过去的缺陷,将来很长一段时间内它都会为我们服务。 Java 8 吸收了Joda-Time(开源jar)的精华,以一个新的开始为 Java 创建优秀的API。新的 java.time 中包含了所有关于本地日期(LocalDate)、本地时间(LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime)和持续时间(Duration)的类。历史悠久的 Date 类新增了 toInstant() 方法,用于把 Date 转换成新的表示形式。这些新增的本地化时间日期API 大大简化了日期时间和本地化的管理。
新日期API
- java.time – 包含值对象的基础包
- java.time.chrono – 提供对不同的日历系统的访问
- java.time.format – 格式化和解析时间和日期
- java.time.temporal – 包括底层框架和扩展特性
- java.time.zone – 包含时区支持的类
大多数开发者只会用到基础包和format包,也可能会用到temporal包。因此,尽管有68个新的公开类型,大多数开发者,大概将只会用到其中的三分之一。
常用的类:
LocalDate、LocalTime、LocalDateTime 类是其中较重要的几个类,它们的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的本地日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。
- LocalDate代表IOS格式(yyyy-MM-dd)的日期,可以存储 生日、纪念日等日期。
- LocalTime表示一个时间,而不是日期。
- LocalDateTime是用来表示日期和时间的,这是一个最常用的类之一。
新日期API的三个常用类的使用
- 实例化 - now()静态方法 - 1 
 2
 3
 4
 5
 6
 7
 8
 9- // now() 
 LocalDate localDate = LocalDate.now(); // 2021-09-16
 LocalTime localTime = LocalTime.now(); // 21:56:48.358
 LocalDateTime localDateTime = LocalDateTime.now();
 // 2021-09-16T21:56:48.358
 System.out.println(localDate);
 System.out.println(localTime);
 System.out.println(localDateTime);
- of()静态方法 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10- // of()方法: 设置指定的年、月、日、时、分、秒,无偏移量 
 LocalDate localDate1 = LocalDate.of(2021, 10, 16); // 2021-10-16
 LocalTime localTime1 = LocalTime.of(15, 59, 50, 500000000); // 15:59:50.500
 LocalDateTime localDateTime1 = LocalDateTime.of(2021, 10,
 16, 15, 59, 50, 500000000);
 // 2021-10-16T15:59:50.500
 System.out.println(localDate1);
 System.out.println(localTime1);
 System.out.println(localDateTime1);
 
- 相关方法 - get - 1 
 2
 3
 4
 5- getDayOfMonth()/getDayOfYear() // 获得月份天数(1-31) /获得年份天数(1-366) 
 getDayOfWeek() // 获得星期几(返回一个 DayOfWeek 枚举值)
 getMonth() // 获得月份, 返回一个 Month 枚举值
 getMonthValue() / getYear() // 获得月份(1-12) /获得年份
 getHour()/getMinute()/getSecond() // 获得当前对象对应的小时、分钟、秒
- set: 体现不可变性 - 1 
 2- withDayOfMonth()/withDayOfYear()/withMonth()/withYear()/withHour()/withXxx() 
 //将月份天数、年份天数、月份、年份、小时等修改为指定的值并返回新的对象
- add - 1 
 2- plusDays(), plusWeeks(), plusMonths(), plusYears(),plusHours() 
 // 向当前对象添加几天、几周、几个月、几年、几小时
- minus - 1 
 2- minusMonths() / minusWeeks()/minusDays()/minusYears()/minusHours() 
 // 从当前对象减去几月、几周、几天、几年、几小时
 
瞬时:Instant类
- Instant:时间线上的一个瞬时点。 这可能被用来记录应用程序中的事件时间戳。
- 在处理时间和日期的时候,我们通常会想到年,月,日,时,分,秒。然而,这只是时间的一个模型,是面向人类的。第二种通用模型是面向机器的,或者说是连续的。在此模型中,时间线中的一个点表示为一个很大的数,这有利于计算机处理。在UNIX中,这个数从1970年开始,以秒为的单位;同样的,在Java中,也是从1970年开始,但以毫秒为单位。
- java.time包通过值类型Instant提供机器视图,不提供处理人类意义上的时间单位。Instant表示时间线上的一点,而不需要任何上下文信息,例如,时区。概念上讲,它只是简单的表示自1970年1月1日0时0分0秒(UTC)开始的秒数。因为java.time包是基于纳秒计算的,所以Instant的精度可以达到纳秒级。
- (1 ns = 10-9 s) 1秒 = 1000毫秒 =106微秒=109纳秒
- 实例化 - now(): 获取本初子午线对应的标准时间 ( UTC ) - 1 
 2
 3- // now() 
 Instant instant = Instant.now();
 System.out.println(instant); // UTC: 2021-09-16T14:17:29.692Z
- atOffset(): 添加时间的偏移量 - 1 
 2
 3
 4- // 添加时间的偏移量 
 Instant instant = Instant.now();
 OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
 System.out.println(offsetDateTime); // 2021-09-16T22:17:29.692+08:00
- ofEpochMilli(long epochMilli): 通过给定毫秒数,获取Instant实例 - 1 
 2- //静态方法,返回在1970-01-01 00:00:00基础上加上指定毫秒数之后的Instant类的对象 
 ofEpochMilli(long epochMilli)
 
- 方法 - toEpochMilli() - 1 - toEpochMilli() // 返回1970-01-01 00:00:00到当前时间的毫秒数,即为时间戳 
 
格式化与解析日期或时间 java.time.format.DateTimeFormatter
- 格式化方法 - 预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME - 1 
 2
 3
 4- DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME; 
 String str = isoLocalDateTime.format(LocalDateTime.now());
 System.out.println(str); // 2021-09-16T22:31:23.101
 System.out.println(LocalDateTime.now());// 2021-09-16T22:31:23.101
- 本地化相关的格式。如:ofLocalizedDateTime(FormatStyle.LONG) - 1 
 2
 3
 4- DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG); 
 String str = isoLocalDateTime.format(LocalDateTime.now());
 System.out.println(str); // 2021年9月16日 下午10时31分23秒
 System.out.println(LocalDateTime.now()); // 2021-09-16T22:31:23.101
- 自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”) - 1 
 2
 3
 4- DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 
 String str = isoLocalDateTime.format(LocalDateTime.now());
 System.out.println(str); // 2021-09-16 22:33:03
 System.out.println(LocalDateTime.now()); // 2021-09-16T22:33:03.181
 
- 解析 parse - 1 
 2
 3- DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 
 TemporalAccessor parse = isoLocalDateTime.parse("2021-09-16 23:20:20");
 System.out.println(parse); // {},ISO resolved to 2021-09-16T23:20:20
其他API
| 1 | ZoneId:该类中包含了所有的时区信息,一个时区的ID,如 Europe/Paris | 
| 1 | //ZoneId:类中包含了所有的时区信息 | 
| 1 | // TemporalAdjuster:时间校正器 | 
四、Java比较器(Comparable接口、Comparator接口)(重点)
java中的对象正常情况下只能进行== 或 != ,不能使用 > 或 <。但在开发场景中,我们需要对多个对象进行排序, Java实现对象排序的方式有两种:
- 自然排序:java.lang.Comparable - 像String、包装类等实现了Comparable接口,重写了compareTo()方法,给出了比较两个对象大小的方式
- 像String、包装类重写compareTo()方法后,在通过Arrays.sort()或集合的排序方法调用时会对数组或集合中的元素进行从小到大的排序
- 重写compareTo()的规则- Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序。
- 实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过 compareTo(Object obj) 方法的返回值来比较大小。如果当前对象this大于形参对象obj,则返回正整数,如果当前对象this小于形参对象obj,则返回负整数,如果当前对象this等于形参对象obj,则返回零。
- 实现Comparable接口的对象列表(和数组)可以通过 Collections.sort 或Arrays.sort进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
- 对于类 C 的每一个 e1 和 e2 来说,当且仅当 e1.compareTo(e2) == 0 与e1.equals(e2) 具有相同的 boolean 值时,类 C 的自然排序才叫做与 equals一致。建议(虽然不是必需的)最好使自然排序与 equals 一致。
 
- 如果自定义类的对象需要排序,应让自定义类实现Comparable接口,重写compareTo()方法
 
- 定制排序:java.util.Comparator - 当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序,强行对多个对象进行整体排序的比较。
- 重写compare(Object o1,Object o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
- 可以将 Comparator 传递给 sort 方法(如 Collections.sort 或Arrays.sort),从而允许在排序顺序上实现精确控制。
- 还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。
 
- 代码实现 - 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- public class CompareTest { 
 
 public void test(){
 Goods[] goods = new Goods[5];
 goods[0] = new Goods("lenovoMouse", 34);
 goods[1] = new Goods("dellMouse", 43);
 goods[2] = new Goods("xiaomiMouse", 12);
 goods[3] = new Goods("huaweiMouse", 65);
 goods[4] = new Goods("microsoftMouse", 43);
 // 通过Comparable接口中的compareTo()方法实现排序
 Arrays.sort(goods);
 System.out.println(Arrays.toString(goods));
 // [Goods{name='xiaomiMouse', price=12.0}, Goods{name='lenovoMouse', price=34.0},
 // Goods{name='dellMouse', price=43.0}, Goods{name='microsoftMouse', price=43.0},
 // Goods{name='huaweiMouse', price=65.0}]
 
 // 通过Comparator接口的匿名类的匿名对象实现排序
 Arrays.sort(goods, new Comparator() {
 
 public int compare(Object o1, Object o2) {
 if(o1 instanceof Goods && o2 instanceof Goods){
 // <时返回正整数,>时返回负整数;调用sort方法时将为从大到小排序
 if(((Goods) o1).price < ((Goods) o2).price){
 return 1;
 }else if(((Goods) o1).price > ((Goods) o2).price){
 return -1;
 }else
 return 0;
 }
 throw new RuntimeException("数据类型不一致");
 }
 });
 System.out.println(Arrays.toString(goods));
 //[Goods{name='huaweiMouse', price=65.0}, Goods{name='dellMouse', price=43.0},
 //Goods{name='microsoftMouse', price=43.0}, Goods{name='lenovoMouse', price=34.0},
 //Goods{name='xiaomiMouse', price=12.0}]
 }
 // Comparable接口实现类
 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 double getPrice() {
 return price;
 }
 public void setName(String name) {
 this.name = name;
 }
 public void setPrice(double price) {
 this.price = price;
 }
 
 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;
 }
 }
 throw new RuntimeException("类型不一致");
 }
 
 public String toString() {
 return "Goods{" +
 "name='" + name + '\'' +
 ", price=" + price +
 '}';
 }
 }
 }
- Comparable和Comparator接口使用的对比 - Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可通过调用Arrays或集合的sort方法实现排序
- 属于临时性的比较
 
五、System类
System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。该类位于java.lang包。 由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员变量和成员方法都是static的,所以也可以很方便的进行调用。
成员变量
- System类内部包含in、out和err三个成员变量,分别代表标准输入流(键盘输入),标准输出流(显示器)和标准错误输出流(显示器)。
成员方法
- native long currentTimeMillis(): 该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。 
- void exit(int status): 该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表异常退出。使用该方法可以在图形界面编程中实现程序的退出功能等。 
- void gc(): 该方法的作用是请求系统进行垃圾回收。至于系统是否立刻回收,则取决于系统中垃圾回收算法的实现以及系统执行时的情况。 
- String getProperty(String key): 该方法的作用是获得系统中属性名为key的属性对应的值。系统中常见的属性名以及属性的作用如下表所示:  
六、Math类
java.lang.Math提供了一系列静态方法用于科学计算。其方法的参数和返回值类型一般为double型。
| 方法 | 方法功能 | 
|---|---|
| abs | 绝对值 | 
| acos,asin,atan,cos,sin,tan | 三角函数 | 
| sqrt | 平方根 | 
| pow(double a,doble b) | a的b次幂 | 
| log | 自然对数 | 
| exp | e为底指数 | 
| max(double a,double b) | |
| min(double a,double b) | |
| random() | 返回0.0到1.0的随机数 | 
| long round(double a) | double型数据a转换为long型(四舍五入) | 
| toDegrees(double angrad) | 弧度—>角度 | 
| toRadians(double angdeg) | 角度—>弧度 | 
七、BigInteger与BigDecimal
BigInteger
- Integer类作为int的包装类,能存储的最大整型值为231-1,Long类也是有限的,最大为263-1。如果要表示再大的整数,不管是基本数据类型还是他们的包装类都无能为力,更不用说进行运算了。 
- java.math包的BigInteger可以表示不可变的任意精度的整数。BigInteger 提供所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。另外,BigInteger 还提供以下运算:模算术、GCD 计算、质数测试、素数生成、位操作以及一些其他操作。 
- 构造器 - BigInteger(String val):根据字符串构建BigInteger对象
 
- 常用方法 - 1 
 2
 3
 4
 5
 6
 7
 8- public BigInteger abs():返回此 BigInteger 的绝对值的 BigInteger。 
 BigInteger add(BigInteger val) :返回其值为 (this + val) 的 BigInteger
 BigInteger subtract(BigInteger val) :返回其值为 (this - val) 的 BigInteger
 BigInteger multiply(BigInteger val) :返回其值为 (this * val) 的 BigInteger
 BigInteger divide(BigInteger val) :返回其值为 (this / val) 的 BigInteger。整数相除只保留整数部分。
 BigInteger remainder(BigInteger val) :返回其值为 (this % val) 的 BigInteger。
 BigInteger[] divideAndRemainder(BigInteger val):返回包含 (this / val) 后跟(this % val) 的两个 BigInteger 的数组。
 BigInteger pow(int exponent) :返回其值为 (thisexponent) 的 BigInteger。
BigDecimal
- 一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中,要求数字精度比较高,故用到java.math.BigDecimal类。 
- BigDecimal类支持不可变的、任意精度的有符号十进制定点数。 
- 构造器 - public BigDecimal(double val)
- public BigDecimal(String val)
 
- 常用方法 - public BigDecimal add(BigDecimal augend)
- public BigDecimal subtract(BigDecimal subtrahend)
- public BigDecimal multiply(BigDecimal multiplicand)
- public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
 
- 例子 - 1 
 2
 3
 4
 5
 6
 7
 8
 9- public void testBigInteger() { 
 BigInteger bi = new BigInteger("12433241123");
 BigDecimal bd = new BigDecimal("12435.351");
 BigDecimal bd2 = new BigDecimal("11");
 System.out.println(bi);
 // System.out.println(bd.divide(bd2));
 System.out.println(bd.divide(bd2, BigDecimal.ROUND_HALF_UP));
 System.out.println(bd.divide(bd2, 15, BigDecimal.ROUND_HALF_UP));
 }
P495
面试
- String str1 = “abc”;与String str2 = new String(“abc”);的区别? 
- String s = new String("abc");方式创建对象,在内存中创建了几个对象? - 两个:1个是堆空间中new结构;另一个是char[]对应的常量池中的数据。如果常量池中已经有了“abc”,则char[]数组只需指向该地址即可
 
- 什么情况下,indexOf(str)和lastIndexOf(str)返回值相同 - 存在唯一的str,或者不存在str
 
每日一考
- 画出如下几行代码的内存结构 - 1 
 2
 3
 4- String s1 = "hello"; 
 String s2 = "hello";
 String s3 = new String("hello");
 s1 += "world";
- 如何理解String类的不可变性 
- String类是否可以被继承?为什么? - 不可被继承,因为String类被声明为final 
- String s = new String("hello");在内存中创建了几个对象?请说明 - 两个:一个是堆中的String对象,还有一个是在字符串常量池中的hello数组 
- String、StringBuffer、StringBuilder三者对比 
- String的常用方法有哪些?(至少七个) - 1 - length() / charAt() / indexOf() / subString() / equals() / startsWith() / endsWith() / contains() / lastIndexOf() / getBytes() / toCharArray() / valueOf() 
- 将字符串“2017-08-16”转换为对应的java.sql.Date类的对象(使用JDK8之前或JDK8中的API皆可) 
- 解释何为编码?解码?何为日期时间的格式化?解析? - 编码:将字符串转换为二进制数;解码:将二进制数据转化为字符串
- 日期时间的格式化:日期转化为字符串;解析:字符串转换为日期
 
- 自定义Person类如下,如何实现自然排序(按姓名从小到大排序) 
| 1 | class Person implements Comparable{ | 
- 提供定制排序涉及到的接口的实现类对象,并按Person类的年龄从大到小排序 
- JDK8之前和JDK8中日期、时间相关的类分别有哪些?