Java09—常用类解析

Object类

Object类是所有类的最终父类,就算某一个类的父类没有声明为Object
那它的最终的祖先肯定是Object
在API当中 java.lang这个包下面

public final native Class<?> getClass();

相当于获取对象最本质的数据类型

public native int hashCode();

返回对象的哈希码的值,如果该类没有重写hashCode的
hashCode默认值就是对象在堆内存中的真实物理地址

public boolean equals(Object obj) {
    return (this == obj);
}

Object本身equals比的是自身对象this和传入对象obj的地址值
如需重新定义等于,则子类重写,按需比较即可

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

Object本身的toString返回的是对象最本质的数据类型的名称+"@"+哈希值的十六进制形式
如需重新定义对象的字符串形式,则子类重写,按需拼接数据即可

protected native Object clone()

对象的拷贝
对于任何对象 x
表达式: x.clone() != x为 true
【保证肯定有新对象创建】
表达式: x.clone().getClass() == x.getClass()也为 true,但这些并非必须要满足的要求。
【保证对象和副本之间的数据类型是一致的】
一般情况下: x.clone().equals(x)为 true,但这并非必须要满足的要求。
【保证对象和副本之间的数据内容是一致的】

基本数据类型包装类

基本数据类型包装类,就是按照万事万物皆对象的思想
将我们原先的那些基本数据类型,当成类和对象来看

byte——Byte short——Short int——Integer long——Long float——Float
double——Double char——Character boolean——Boolean

最主要的使用场景 就是在基本数据与字符串之间的转换问题
将基本类型转字符串
将字符串转成基本类型
如IntegerDemo代码所示

StringBuilder与StringBuffer

都是String类的缓冲字符串
特点就是可以改变其大小,也可进行增删改查操作的字符串
字符串
1.无论是字面量还是new出来的 一律都是字符串对象
2.字符串一旦创建,其长度不可改变(底层就是一个字符数组)
3.内容也不能改变(如果真的要改变内容,只能重新创建字符串)
4.字符串本质上底层就是一个不可变长度且不可变内容的字符数组!
详情请看StringDemo和字符串内存图解

StringBuilder继承自AbstractStringBuilder实现了CharSequence和Serializable
AbstractStringBuilder实现了Appendable(可扩展的)接口,要实现的方法append()
AbstractStringBuilder实现了CharSequence字符序列接口(同数据结构中List的定义)
看到StringBuilder的构造函数是 创建一个默认容量为16的字符数组
随着后续内容的增加,底层在进行动态的扩容

void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        value = Arrays.copyOf(value, newCapacity);
}

说白了StringBuilder就是一个可变长可修改内容的String
具体详细的StringBuilder方法 参考API就可以了

StringBuffer和StringBuilder基本完全一样
StringBuffer是线程安全的——多线程环境下和单线程环境下不容易出错
StringBuilder是线程不安全——多线程环境下容易出错单线程环境下不容易出错
从代码的角度而言,StringBuffer里面的大部分成员函数都有synchronized关键字修饰里面的大部分成员函数都有synchronized关键字修饰
StringBuilder就相当于一个人在家上厕所 一般不会反锁门 一般也不会判断卫生间里有没有人
StringBuffer就相当于你在火车上厕所 一般会反锁们 一般也会判断卫生间里有没有人
在单线程情况下 StringBuilder比StringBuffer效率高一些 因为不需要判断锁这个问题
在多线程情况下 StringBuffer比StringBuilder安全高一些 因为需要判断锁这个问题

BigInteger与BigDecimal

在Java中,基本数据类型中整数最长的就是long类型8字节 64位 -2^63 ~ 2^63 - 1
如果我们在实际应用中的数据超过了这个范围怎么办?
BigInteger专门处理大整数运算
BigDecimal专门出来大小数运算

其实BigInteger底层就是用一个不可变的数组来维护计算我们所存储的数据的
如果通过运算得出新的数据的话 则返回新的BigInteger的对象
具体的方法参照API

BigDecimal底层其实是BigInteger和小数位记录scale组成的
123.4567 : BigInteger(1234567) + scale(4) = 123.4567

正则表达式

它不仅仅是Java的技术,在任何一门编程语言中都会存在,是一种通用的IT技术
其理念和用法在任何编程语言中基本一致,除了有一些由于语言不同而导致的一些语法不同
正则表达式,主要用于匹配(查找 替换 计数)字符串中的数据的,也叫做文本匹配技术

// 给定一个字符串"13088889090",如何判断它是手机号?
public boolean isPhoneNumber(String s) {
    if (s.length() != 11) {
        return false;
    }
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (c < '0' || c > '9') {
            return false;
        }
    }
    return true;
}

对于上述问题的求解而言,我们只解决了长度的问题,内容必须是数字字符的问题
但是,对于手机号这个特殊的数字而言
第1位能否为0?
前3位表示对应公司(联通 移动 电信)123 109 110??? 前3位它不是真随机的

为了每一种判断逻辑都要编写代码实现是太繁琐了,正则表达式就是出来解决这些问题的!
如果用正则表达式,去匹配手机号的话

public boolean isPhoneNumber(String s) {
    //确定了长度为11 并且也确定了必须是数字字符
    return s.matches("\\d{11}");
}

从目前的角度而言,问题的解决确实变简单了,代码可读性的难度就上来了
在Java标准库中java.uitl.regex包里内置了正则表达式引擎,在Java程序中使用正则非常方便
使用的正则表达式就是一个描述规则的字符串,所以,我们只需要编写正确的规则,我们就可以让正则表达式
引擎去判断目标字符串是否符合规则即可。

比如:判断一个年份 "20##"年,我们写出规则如下:
一个4个字符:2 0 0~9任意一个数字 0~9任意一个数字
对应的正则表达式:20\d\d 其中\d表示的是任意一个数字的意思
但是在Java中,正则表达式要用字符串来表示,“20\d\d"这样写是不对的,因为在Java中’'是转义字符
最终应该这么写"20\d\d”

匹配规则
正则表达式的匹配规则是从左到右按规则匹配的
我们首先来看如何用正则表达式进行精确匹配
对于正则表达式abc来说,它只能精确匹配字符串"abc",不能匹配"ab" “Abc” “abcd”
如果正则表达式要匹配特殊字符的话,就需要用’‘转义字符去转义
正则表达式a\&c,其中\&是用来匹配特殊字符’&’,它能精确匹配"a&c"
用过放到java当中去匹配的,正则表达式"a\&c"
用过想匹配中文的,用\u####来匹配中文 正则表达式a\u548cc,精确匹配"a和c"
其实像这种精确的匹配,就没必要使用正则,直接使用字符串equals()即可

String regex01 = "20\\d\\d";
System.out.println("2019".matches(regex01));
System.out.println("2100".matches(regex01));
System.out.println("20AB".matches(regex01));

String regex02 = "abc";
System.out.println("abc".matches(regex02));
System.out.println("ab".matches(regex02));

String regex03 = "a\\&c";
System.out.println("a&c".matches(regex03));
System.out.println("ac".matches(regex03));

String regex04 = "a\\u548cc";
System.out.println("a和c".matches(regex04));
System.out.println("a哈c".matches(regex04));

匹配任意字符
大多情况下,我们想要的匹配规则更多的是模糊匹配
. 匹配任意一个字符

String regex05 = "a.c";
System.out.println("abc".matches(regex05));
System.out.println("a&c".matches(regex05));
System.out.println("acc".matches(regex05));
System.out.println("ac".matches(regex05));
System.out.println("a&&c".matches(regex05));

匹配数字
.匹配任意字符的这个口子有点大,如果我们只想匹配数字0~9,用\d

String regex06 = "Demo\\d\\d\\.java";
System.out.println("Demo11.java".matches(regex06));
System.out.println("Demo123.java".matches(regex06));
System.out.println("Demo.java".matches(regex06));
System.out.println("Demo12.avi".matches(regex06));

匹配常用字符
\d任意一个数字 \w匹配任意一个字母、数字或下划线

String regex07 = "java\\w";
System.out.println("java_".matches(regex07));
System.out.println("javac".matches(regex07));
System.out.println("java".matches(regex07));
System.out.println("java!".matches(regex07));

匹配空格字符
\s,所谓的空格字符 除了空格’ ‘之外,还包括制表符’\t’的效果

String regex08 = "a\\sc";
System.out.println("a c".matches(regex08));
System.out.println("a  c".matches(regex08));
System.out.println("ac".matches(regex08));
System.out.println("a   c".matches(regex08));
System.out.println("a\tc".matches(regex08));

匹配非数字
\d任意一个数字 '\D’任意一个非数字
\s任意一个空格 \S任意一个非空格
\w任意一个数字字母下划线 '\W’任意一个非数字字母下划线

String regex09 = "\\d\\w\\W\\D";
System.out.println("1234".matches(regex09));
System.out.println("9w!c".matches(regex09));

重复匹配
\d任意一个数字,如果A\d可以匹配的"A0",“A1”,~,“A9”
如果匹配多个字符怎么办?

// 使用`*`可以匹配任意个字符(包含0)
    String regex10 = "A*";  //连续0个或多个A
    System.out.println("A".matches(regex10));
    System.out.println("Animal".matches(regex10));
    System.out.println("A123123".matches(regex10));
    System.out.println("AAAAAAA".matches(regex10));
    String regex11 = "A\\d*";
    System.out.println("A12313123123".matches(regex11));
    System.out.println("A".matches(regex11));
    System.out.println("Aasdhjahsjdk1123".matches(regex11));
    System.out.println("".matches(regex11));

// 使用`+`可以匹配至少一个字符
    String regex12 = "A\\d+";
    System.out.println("A12313123123".matches(regex12));
    System.out.println("A".matches(regex12));
    System.out.println("Aasdhjahsjdk1123".matches(regex12));
    System.out.println("".matches(regex12));

// 使用`?`可以匹配0个或一个字符
    String regex13 = "A\\d?";
    System.out.println("A12313123123".matches(regex13));
    System.out.println("A".matches(regex13));
    System.out.println("A1".matches(regex13));
    System.out.println("".matches(regex13));

// 如果指定重复的个数 使用`{n}`匹配n个,使用`{n,m}`匹配n~m个,使用`{n,}`匹配至少n个
    System.out.println("A123".matches("A\\d{3}"));
    System.out.println("A123".matches("A\\d{3,5}"));
    System.out.println("A12345".matches("A\\d{3,5}"));
    System.out.println("A123".matches("A\\d{4,}"));

// 比如匹配一个座机号 010-33167854 3个数字-8个数字 `\d{3}\-\d{8}`
    System.out.println("010-12345678".matches("\\d{3}\\-\\d{8}"));
    System.out.println("0101-2345678".matches("\\d{3}\\-\\d{8}"));

正则表达式总结
正则表达式 规则 可以匹配
A 固定字符A “A”
\u548c 指定Unicode字符 “和”
. 任意一个字符 “a” “1” " "
\d 一个数字字符0~9 “0”~“9”
\w 一个数字或字母或下划线
\W 非\w
\D 非\d
\s 一个空格字符(\t ’ ')
\S 非\s
A* A有任意个 “” “A” “AA” “AAA”
A+ A至少一个 “A” “AA” “AAA”
A? A0个或1个 “” “A”
A{3} A出现3次 “AAA”
A{3,5} A出现3~5次 “AAA” “AAAA” “AAAAA”
A{2,} A出现至少2次 “AA” “AAA” “AAAA”
A{0,3} A出现0~3次 “” “A” “AA” “AAA”

匹配开头和结尾
匹配开头用^ 匹配结尾用$
String regex01 = “^Demo\w*\.java$”;
System.out.println(“Demo01.java”.matches(regex01));
匹配指定范围
\d表示所有数字0~9,如果不许使用0和1[2-9]

    // 对于一个十六进制数字每一位[0-9a-fA-F]
        String regex02 = "[2-9]\\d{5}";
        System.out.println("345678".matches(regex02));
        System.out.println("123456".matches(regex02));

        String regex03 = "[0-9a-fA-F]{4}";
        System.out.println("12ab".matches(regex03));
        System.out.println("wc99".matches(regex03));
/*
逻辑或匹配规则
    我们的内容可以匹配两种正则,可以使用`|`将两个正则表达式或起来
    `AB | CD`表示可以匹配"AB"也可以匹配"CD"
    如何匹配一个字符串中有没有编程语言的单词
*/
 `java|c|python|go`
        String regex04 = ".*java|c|python|go.*";
        System.out.println("I like java".matches(regex04));
        System.out.println("I like c++".matches(regex04));
使用()进行合并匹配
    如果想要匹配"learn java" "learn php" "learn go"
    `learn\sjava|learn\sphp|learn\sgo` => `learn\s(java|php|go)`

分组匹配
分组匹配 还是使用到了(),但是我们一般在使用正则表达式,除了判断是否匹配外
还需要将匹配的内容进行提取,如果一个字符串中有多个地方被匹配到
匹配电话\d{3,4}-\d{6,8} 区号-号码 “010-123456” “0110-12345678”
分别提取区号和号码
\d{3,4}-\d{6,8} => (\d{3,4})-(\d{6,8})
回顾String的matches方法,底层实际上先创建一个关于正则表达式规则的对象Pattern
然后规则对象Pattern对数据进行匹配,生成一个匹配对象Matcher
匹配的结果 都在Matcher里 有一个方法Matcher中的mathces()才是最终判断字符串是否匹配规则的结果 boolean

Pattern p = Pattern.compile("(\\d{3,4})-(\\d{6,8})");
Matcher m = p.matcher("010-123456789");
if (m.matches()) {
    //获取的结果是整个电话号
    System.out.println(m.group(0));
    //获取的第一个部分 区号
    System.out.println(m.group(1));
    //获取的第二个部分 电话
    System.out.println(m.group(2));
}

非贪婪匹配问题
给定一个数字字符串,判断其末尾0的个数
“123000” 3个0
“10100” 2个0
“1001” 0个0
(\d+)(0*)

Pattern p1 = Pattern.compile("(\\d+)(0*)");
Matcher m1 = p1.matcher("123000");
if (m1.matches()) {
    System.out.println("group1 = " + m1.group(1));
    System.out.println("group2 = " + m1.group(2));
}
对于上述代码而言 结果
group1 = 123000
group2 =
发现group2没有内容的
对于123000 希望得到 123 000
对于10100 希望得到 101 00
对于1001 希望得到 1001 ""
实际上123000  123000 ""
实际上10100   10100  ""
`(\d+)(0*)`其实这个正则表达式能得到这个结果也是合理的
正则表达式默认使用贪婪匹配:任何一个规则,它尽可能多地向后进行匹配
因此\d+顺带着把0也算进去了
非贪婪的问题就是要让\d+尽可能的去少匹配 让0*尽可能的去多匹配,在规则\d+之后添加?即可
\d? 表示该处一个或零个数字
\d?? 第一个问号表示该处一个或零个数字 第二个问号表示非贪婪匹配
    如果`(\d??)(9*)`去匹配"9999","" "9999"
    Pattern p1 = Pattern.compile("(\\d??)(9*)");
    Matcher m1 = p1.matcher("9999");
    if (m1.matches()) {
        System.out.println("group1 = " + m1.group(1));
        System.out.println("group2 = " + m1.group(2));
    }
    结果为:
    group1 =
    group2 = 9999
对于原问题 `(\d+)(0*)` => `(\d+?)(0*)`
    Pattern p1 = Pattern.compile("(\\d+?)(0*)");
    Matcher m1 = p1.matcher("123000");
    if (m1.matches()) {
        System.out.println("group1 = " + m1.group(1));
        System.out.println("group2 = " + m1.group(2));
    }
    group1 = 123
    group2 = 000

分隔字符串

String.split(String regex)
System.out.println(Arrays.toString("a b c".split("\\s")));
System.out.println(Arrays.toString("a  b      c".split("\\s")));
System.out.println(Arrays.toString("a  b      c".split("\\s+")));
System.out.println(Arrays.toString("a  ,  b ;; c ,,;; d ,   e".split("[\\,\\;\\s]+")));
结果为:
[a, b, c]
[a, , b, , , , , , c]
[a, b, c]
[a, b, c, d, e]

搜索字符串

String s = "the quick brown fox jumps over the lazy dog";
Pattern p2 = Pattern.compile("\\wo\\w");
Matcher m2 = p2.matcher(s);
while (m2.find()) {
    //Matcher的start和end是从左到右每一个匹配到的字符串在原字符串中的起始位置
    String sub = s.substring(m2.start(),m2.end());
    System.out.println(sub);
}

替换字符串

String.replace String.replaceAll
    String s1 = "the   quick   brown   fox     jumps   over     the     lazy    dog";
    s1 = s1.replaceAll("\\s+"," ");
    System.out.println(s1);

反向引用
如果我们要把搜索到的指定字符串按规则替换,比如搜索倒了某一个字符串 给其前后加一些内容
时间日期
我们之前在使用时间的话 System.currentMiliis()这个函数
这个函数返回的是从1970年1月1日00:00开始至今所经历的毫秒数 long类型
拿到这个毫秒数 就可以进行换算 年 月 日 星期
有没有专门计算时间和日期的类的,有

java.util.Date
    java.sql.Date
    java.sql.Time
    java.sql.Timestamp
java.text.DateFormat
java.text.SimpleDateFormat
java.util.Calendar
关于java.util.Date
是java.sql.Date和java.sql.Time和java.sql.Timestamp的父类,也是Java中的时间标准库
表示的是一个特定的瞬间从1970年1月1日00:00开始至今所经历的毫秒数,内部进行计算来获取
具体的时间参数年 月 日 时 分 秒
但是从JDK1.1(JDK8-JDK1.8)开始,推荐使用Calendar这个类来表示时间

关于java.text.DataFormat
它是一个抽象类,表示的是时间/日期格式化类,也可以进行时间的提取和时间的转换
时间字符串解析出具体的时间,具体的时间转换成字符串表现形式

java.text.SimpleDateFormat
它是DataFormat的实现子类,功能和DataFormat一样,但是会有更加丰富的操作
对于格式化参数:
y   年   yyyy年-2021年
M   月   MM月-11月
d   日   dd日-06日   `MM yyyy  dd E a` - 08 2021 16 riday AM
E   星期  E-星期日
a   上下午 a-PM/AM

H   小时(24小时制)
h   小时(12小时制)     `HH:mm:ss` - 11:13:58
m   分钟
s   秒

关于java.util.Calendar抽象类
用于封装日历信息(包含时间),其主要作用在于其方法可以对时间分量进行运算(基于当前时间的偏移)

关于时间的操作主要分为如下几个大类:
一、时间日期获取
Date date = new Date();
System.out.println(date);
//过时 千年时间虫 不推荐使用
System.out.println(date.getHours());
System.out.println(date.getYear());
System.out.println(date.getDay());
System.out.println(date.getMonth());

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = df.format(System.currentTimeMillis());
String time2 = df.format(date);
System.out.println(time);
System.out.println(time2);
二、日期转换
    日期转字符串 字符串转日期
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//日期转字符串
Calendar calendar = Calendar.getInstance();
Date date  = calendar.getTime();
String s1 = sdf.format(date);
System.out.println(s1);
//字符串解析日期
String s2 = "2021-08-16 11:30:52";
Date date2 = sdf.parse(s2);
System.out.println(date2);

字符串转时间,用Calendar来获取详细的内容
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
Date date = df.parse("2021年8月16日 14时16分52秒");
Calendar cal = df.getCalendar();
System.out.println(cal.getTime());
//获取当前日历时间 Calendar cal = Calendar.getInstance();
int year = cal.get(Calendar.YEAR);  //获取年
int month = cal.get(Calendar.MONDAY); //获取月
int day = cal.get(Calendar.DATE);   //获取日
int hour = cal.get(Calendar.HOUR);  //获取时
int minute = cal.get(Calendar.MINUTE);//获取分
int second = cal.get(Calendar.SECOND); //获取秒
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);//一周中的第几天
int dayOfYear = cal.get(Calendar.DAY_OF_YEAR);//一年中的第几天
System.out.println(year + " " + month + " " + day);//month从0(一月)算的
System.out.println(hour + " " + minute + " " + second);
System.out.println(dayOfWeek);
System.out.println(dayOfYear);

获取当前时间 显示 上午 下午
Date date = new Date();
//年 月 日
DateFormat df1 = DateFormat.getDateInstance();
System.out.println(df1.format(date));
//年 月 日 时 分 秒
DateFormat df2 = DateFormat.getDateTimeInstance();
System.out.println(df2.format(date));
//时 分 秒
DateFormat df3 = DateFormat.getTimeInstance();
System.out.println(df3.format(date));
//2021年8月16日 星期一 下午02时33分40秒 CST
DateFormat df4 = DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.FULL);
System.out.println(df4.format(date));
//2021年8月16日 下午02时34分44秒
DateFormat df5 = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG);
System.out.println(df5.format(date));
//21-8-16 下午2:35
DateFormat df6 = DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.SHORT);
System.out.println(df6.format(date));
//2021-8-16 14:36:17
DateFormat df7 = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.MEDIUM);
System.out.println(df7.format(date));
三、日期加减
以某一个日期为基准 计算几天前/...
    获取当前时间的前一年时间
    Calendar now  = Calendar.getInstance();
    print(now);
    now.add(Calendar.YEAR,1);   //是现在时间的1年后
    print(now);
    now.add(Calendar.YEAR,-1);  //是现在时间的1年前
    print(now);
    now.add(Calendar.HOUR,-1);
    print(now);

    Calendar specialDate = Calendar.getInstance();
    specialDate.setTimeInMillis(System.currentTimeMillis() / 2);
    print(specialDate);
    specialDate.add(Calendar.YEAR,-1000);
    print(specialDate);
    private static void print(Calendar cal) {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(df.format(cal.getTime()));
    }

    根据时间然后对小时、分钟、秒进行相加
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    Calendar nowTime = Calendar.getInstance();
    nowTime.add(Calendar.MINUTE , -30);
    String s = df.format(nowTime.getTime());
    System.out.println(s);

    计算201711日 距离现在多少天
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String dateString = "2017-01-01 14:52:30";
    Calendar calendar = Calendar.getInstance();
    long nowDate = calendar.getTime().getTime();    //获取当前时间的毫秒值
    long specialDate = sdf.parse(dateString).getTime(); //获取指定日期的毫秒值
    long betweenDate = (nowDate - specialDate) / (1000 * 60 * 60 * 24);//计算间隔多少天
    System.out.println(betweenDate);

    求两个日期 相差多少小时 分钟 秒
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date d1 = df.parse("2021-08-16 15:18:23");
    Date d2 = df.parse("2021-08-17 17:45:32");
    long nd = 1000 * 24 * 60 * 60;  //一天多少毫秒
    long nh = 1000 * 60 * 60;   //一小时多少毫秒
    long nm = 1000 * 60;    //一分钟多少毫秒
    long ns = 1000; //一秒多少毫秒
    long diff = d2.getTime() - d1.getTime();
    //计算差多少天
    long day = diff / nd;
    //计算差多少小时
    long hour = diff % nd / nh;
    //计算差多少分钟
    long minute = diff % nd % nh / nm;
    //计算差多少秒
    long second = diff % nd % nh % nm / ns;
    System.out.println(day + " " + hour + " " + minute + " " + second);
四、日期比较
一种是通过before和after比较,或者compareTo来比较
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String dateString1 = "2021-08-16 11:11:11";
    String dateString2 = "2021-08-17 11:11:11";
    Date date1 = sdf.parse(dateString1);
    Date date2 = sdf.parse(dateString2);

    System.out.println(date1.before(date2));
    System.out.println(date1.after(date2));
    System.out.println(date1.compareTo(date2));

异常
就是程序在运行过程中出现的一些错误,我们通过面向对象的思想,把这些错误也用类来描述,那么一旦
产生一个错误,即就是创建了某一个错误的对象,这个对象就是我们所说的异常对象
之前见过的
IndexOutOfBoundsException
ArrayIndexOutOfBoundsException
数组角标越界异常 角标不在数组范围内
StringfIndexOutOfBoundsException
字符串角标越界异常 角标不在字符串范围内
NullPointerException
空指针异常 对null调用其成员
ArithmeticException
数学运算异常 非法的数学运算
ClassCastException
类型转换异常 将类进行错误的强制转换
NumberFormatException
数字格式化异常 将数字字符串进行解析
InputMismatchException
输入不匹配异常 在输入时输入非法数据
ParseException
时间解析异常 非法的时间格式
StackOverFlowError
栈内存溢出异常 函数递归
OutOfMemoryError
堆内存异常 数组空间开辟过大 程序中对象太多

  public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        int num = getNumber(arr,10);
        System.out.println(num);
    }
    private static int getNumber(int[] arr, int i) {
        return arr[i];
        //如果arr为null NullPointerException
        //如果i越界 ArrayIndexOutOfBoundsException
    }
如上述代码所示,对于一个有返回值的函数而言
如果操作计算是正确的话,肯定会有一个正常的返回值
如果操作计算是错误的话,从函数的角度而言 会不会有正常的返回值呢?不会
本质上,大部分的错误,其实都是由JVM发现并抛出的 当然我们也可以手动去判断
private static int getNumber(int[] arr, int i) {
    //return arr[i];//是由JVM来进行判断的
    //模拟JVM的操作 用手动的判断模拟JVM自动的发现
    if (arr == null) {
        //产生一个空指针异常的问题对象
        //用throw(抛出)关键 将产生的问题告知调用者
        throw new NullPointerException();
        //一旦上述抛出一个问题 此函数立马中断
        //类似于return 正常结束(弹栈)
        //throw非正常结束(中断 强制弹栈 并抛出问题给调用者)
    }
    if (i < 0 || i >= arr.length) {
        //产生一个数组角标越界异常的问题对象
        throw new ArrayIndexOutOfBoundsException();
        //throw new StackOverflowError();
        //PS 一旦出现错误 抛出问题即可 但是最好抛出最相关最精确的问题
    }
    return arr[i];
}
getNumber如果发生了问题是直接将问题抛给主函数的,但是主函数也没有处理这个问题,主函数接着将问题抛给调用者JVM
JVM才不帮你解决 结果就是程序中断了!
警察发现你犯事了,你不会处理,让你爸出面处理,但是你爸也不会处理,最终交给警察处理 毙了
JVM-调用main-调用getNumber-getNumber出现异常-抛出给main-main没有处理-抛出传递给JVM-JVM直接给你中断
PS:当然 函数内部如果出现问题 也可以将问题在内部处理 处理后 就不需要向上层抛出

既然错误的原因有很多,描述错误的类也有很多,那么这种情况就产生了一个大的家族/体系,这里面全都是
异常错误类,我们叫做异常体系
对于异常体系而言,大家都是错误,只不过所产生的原因不太一样,不断的进行向上抽取共性,最终
抽出Throwable这个接口(可以被抛出的),但凡是异常对象,无论原因,都可以被抛出
Throwable类是 Java 语言中所有错误或异常的超类,只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。

Throwable
    Error是Throwable的子类,用于指示合理的应用程序不应该试图`捕获`的严重问题
        一旦出现这种Error级别的错误,那肯定是底层级别的错误了,这种一般是很严重的错误
        在运行期间没有办法挽回了,只能中断程序,让程序员重新修改代码
        Error的子类不多 但是都比较狠

    Exception类及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件
        一旦出现这种问题,是可以挽回的(不是必须的),挽回之后,接着正常运行即可,一般的小错误可以被纠正
        Exception的子类很多 但是都比较菜

        运行时异常:RuntimeException是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类
        可能在执行方法期间抛出但未被捕获的 RuntimeException 的任何子类都无需在 throws 子句中进行声明
        指的就是这些问题不需要提前被预防(本质上也可以的,只不过没必要)
        因为只有在真正运行的时候才能发现是否发生问题,一旦在运行期间发生了问题,则一般不会修正,程序直接终端

        编译时异常:Exception及其子类(除了RuntimeException),在编译时期抛出的异常
        在编译期间检查程序是否可能会出现问题,如果可能会有,则预先防范:捕获 声明
        还没出门呢,就先考虑可能路上发生的问题,例如:肚子疼带卫生纸,感冒带药带口罩,饿了带钱
        指的就是这些问题可以提前被预防到,预防的方式有两种:
        捕获 try-catch-finally 程序不至于中断
        声明 throws 在函数后面 跟异常的类名 throw在函数内部 后面跟一个异常对象的 程序会可能会中断

总之:Error和运行时异常基本都是在运行期间发生的问题,这些问题一旦发生基本没有可挽回的余地,程序直接中断
      只不过相对而言Error的严重性要大于运行时异常,运行时异常RuntimeException其实本质是Exception的子类
      也就意味着其本身是可以在编译期间被提前预防的,只不过没必要,看心情

运行时异常可不可以被捕获和声明?可以 参照ExceptionDemo03

案例:张老师用电脑给大家上课
电脑——蓝屏 冒烟
参照ExceptionDemo04

try-catch-finally
try语句块中 放的是可能出现问题的代码 尽量不要把不相关的代码放入到里面 否则会出现截断的问题
try{
    codeA
    throw ...
    codeB
}

如果throw这个地方一旦抛出异常 codeB就不会执行了 建议把codeB放到后面
catch语句块总 放的是出现问题并捕获后 处理问题的代码code 如果问题在try语句块中没有出现 则catch中不会运行

catch(Exception e) {
    code
}
finally语句块中 放的是不管问题异常是否产生 都要执行的代码code
finally{
    code//关闭资源(IO 数据库 网络),结尾处理的一些工作
}
如果try-catch-finally中都包含return 参照ExceptionDemo05
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值