1.异常机制概念:
package 异常机制;
public class Test1 {
public static void main(String[] args) {
/*
* 在java中,对于遇到的问题,有一个类来描述:
* Throwable是Exception和Error的父类,是顶级父类
*
* */
// Exception用来描述一般性的问题异常,可以解决
/*对于异常也分为两种:
* (1)编译期异常:必须解决,不解决程序无法运行
* (2)运行期异常:可以解决,也可以不解决.
* */
// Error 错误,描述严重性问题错误,我们无法解决,比如内存溢出
// Throwable是Exception和Error的父类,Excepiton类是可以解决的异常类,Error类不能解决,Exception类的子类一为发生在编译期异常的异常(为非RuntimeException类及其子类,),这样的异常必须解决,否则编译器无法运行,二为运行期异常(为RuntimeException类及其子类),发生在运行期间,其异常可以解决,也可以不解决
}
}
package 异常机制;
public class Test3 {
public static void main(String[] args) {
// Throwable 类是 Java 语言中所有错误或异常的超类(顶级类)。
/* 运行期异常(为RuntimeException类及其子类):可以解决,也可以不解决.
*/
int a=10;
int b=0;
// ArithmeticException:运行期异常,算数异常,为RuntimeException子类
// System.out.println(a/b);
/*运行期异常,你可以选择处理,也可以选择不处理,上面这段代码,你并没有对这段代码进行处理,你没有处理,JAVA就是用的默认处理方式
* 默认处理方式是真么处理的?
* 当遇到异常时JAVA虚拟机会打印异常的堆栈信息,然后会退出JAVA虚拟机,加入这个语句下面还有代码,后面的代码就不会执行了
* 也就是说JAVA虚拟机默认处理运行期异常的方式不够友好,如果你觉得这种方式不好,你可以自己去处理运行期异常:
*=======================================================================================================
* */
// 自己处理这个运行期异常:遇到问题了,我们可以自己提示异常信息,但是不退出虚拟机,让代码继续往下执行,那怎么处理呢?
// 使用关键字:try catch,使用了之后Java就不会自动处理异常了,转变为try和catch里面的方法处理异常
// 如:这个如果让JAVA自己处理就会报异常并且退出虚拟机
// System.out.println(a/b);那如果我们自己处理,方法是这样的:
try {
System.out.println(a/b); //try里面是放的可能会出现异常的一句,如果有异常了就马上进入catch语句,执行catch里面的语句
}
catch ( ArithmeticException aa){//catch括号里面放的是捕获何种类型的异常类+形参名,这个类是提前要确认好的,大括号里面放的是:
// 一旦你出现这种异常,你的处理逻辑是啥
// 如果没有出现你要捕获的异常类型,那么catch里面的代码不会执行,记住了.catch里面的异常类一定要和try对应的异常要对应上,如果不一致,则最后还是交给虚拟机自动处理了,所以发生的异常要和捕获的异常类要一致
System.out.println("除数不能为0");
}
// 这样的话后面的语句会继续执行
System.out.println("111111111111111111");
System.out.println("111111111111111111");
System.out.println("111111111111111111");
System.out.println("111111111111111111");
System.out.println("111111111111111111");
System.out.println("111111111111111111");
}
}
package 异常机制;
public class Test4 {
public static void main(String[] args) {
// 运行期异常
int[] arr={20,30};
int a=10;
int b=0;
arr=null;
// System.out.println(a/b);
// System.out.println(arr[3]);//运行期异常 :ArrayIndexOutOfBoundsException(角标越界异常)
// 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
// 遇到异常,如果是默认处理方式的话,后面的语句不再回运行
try {
System.out.println(a/b);//如果是只有一个catch的情况下,则try只会执行到出现第一个异常的语句就进了catch了,如果恰巧那个catch的参数不是对应的异常类的,则直接交给虚拟机自动处理异常,下面的代码都不会执行到
// 还是上述这种情况,如果正好catch的异常类对应的是try的异常,那么就会执行catch里面的语句,但是try中的出现异常语句下面的那些语句就不会执行了
// 如果说try中可能会有多个异常,你都想处理掉,那么就用多个catch就可以了.但是还是一个意思,只要成功try了其语句中的一个异常,那么try代码块中出现异常问题的下面的语句都不会在执行了
// 所以最好在try的时候把所有可能出现的异常都catch到,后面就不会有事了,异常处理完了,trycatch后面的代码该咋执行咋执行
/*注意:
* try里面要放的是有可能出现问题的代码,对于没有可能出现问题的代码就不要往里面放了,会影响性能
* 还有一点啊,语法错误跟异常没关系
* */
System.out.println(arr[3]);
}
catch (ArithmeticException aa){
System.out.println("除数不能为0!");
}
catch (ArrayIndexOutOfBoundsException bb){
System.out.println("角标有问题了!");
}catch (NullPointerException n){
System.out.println("空指针异常");
}catch (Exception e){//如果是不太清楚其中会出现什么异常,那直接上顶级异常就可以了,它包括了所有的异常(记住就行了,就这么理解),拿不准的时候就写这个就行,这个可以写,但是最好不要这样写,能明确的异常一定要写明确,实在不知道的就只能用这个最终的大异常了,这样写不太规范,如果只写一个这个,但是如果出现异常了,你不能明显分辨到底是什么异常
/*注意:
* 如果捕获的多个异常中有父子类继承关系,父类异常要放到最后,并列关系的异常(即共同继承一个父类异常类)谁前谁后无所谓
* */
System.out.println("其他异常");
}
}
}
package 异常机制;
public class Test5 {
public static void main(String[] args) {
// JDK1.7以后提供了一种捕获多种异常的语法,不推荐,了解(pass掉)
int[] arr={20,30};
int a=10;
int b=0;
arr=null;
try{
System.out.println(a/b);
}catch (ArrayIndexOutOfBoundsException|ArithmeticException|NullPointerException e){//不能写父子关系类异常,只能写并列关系类异常,且这种方法不好,因为你最终也无法判断到底是哪种异常,当然可以通过if语句去判断具体是哪种异常,但是这样太麻烦了,还不如用之前的方法
if(e instanceof ArithmeticException)
{System.out.println("分母不能为0");
}else if(e instanceof ArrayIndexOutOfBoundsException){
System.out.println("角标越界");
}else if(e instanceof NullPointerException){
System.out.println("空指针异常");}
}
System.out.println("=======================");
System.out.println("=======================");
System.out.println("=======================");
System.out.println("=======================");
System.out.println("=======================");
System.out.println("=======================");
}
}
package 异常机制;
import java.util.Scanner;
public class test6 {
public static void main(String[] args) {
// finally:
int a = 10;//(ctrl+shift+方向键能上下移动这行代码)
int b=0;
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第一个整数");
int i = scanner.nextInt();
int i1 = scanner.nextInt();
try {
System.out.println(a/2);
System.out.println(i/i1);
}catch (ArithmeticException aa){
System.out.println("除数不能为0");
}finally {
// finally关键字表示 不管try里面的代码不管有没有遇到异常,即不管遇没遇到异常,finally里面的代码一定会执行,对于一些没有trycatch的语句同样适用
System.out.println("zhixingle");
// 一般会在finally里面写善后收尾的代码,做一些善后工作,比如善后资源:
if(scanner!=null){//这写的比较全,主要是考虑scanner是成员变量且初始值为null时,方法中new对象,但是在new对象前的一个语句为异常语句,这样的话下面的语句就不会执行了,是预防这样的情况发生
scanner.close(); //释放资源,类似叫java垃圾回收器回收的那个语句一样
}
}
}
}
package 异常机制;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
//就记住一句话就完事了:只要是这个个方法体重出现了异常机制,如果使用throws的话就在这个方法的声明后面new 该异常类就行,然后方法A在调用了这个方法,如果还是想抛出异常的话,继续在方法A的后面throws该异常类,一般工具类的方法都是直接throws,到了main方法里面才会选择trycatch手动处理,一般只有编译期异常才会选择throws或者trycatch,运行期异常一般不处理或者trycatch
public class Test7 {
public static void main(String[] args) /*throws ParseException*/ {//main方法使用或者调用其代码块儿中的变量或方法
// 编译期异常:发生在编译期间(非RunTimeException及其子类),编译期异常必须手动处理,不处理程序就无法运行
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//日期类转换为日期字符串
String format = simpleDateFormat.format(date);
System.out.println(format);
// 日期字符串转换为日期类
String date1 = "2020-07-04 21:04:57";
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Date parse = simpleDateFormat1.parse(date1);//在这里就是编译期异常,必须要手动处理,不然程序就无法运行了,这里抛出了异常,抛给了调用这个方法的地方(这里指main方法),在这里是main调用这个语句,所以抛给了main方法
// 上面这个是把字符串日期类转换为日期类
// 处理编译期异常有两种方式:
// 方式一:
// 使用throws向上抛出,抛给调用者去处理,谁调用谁处理,俗称甩锅,注意这里是throws,语句在main方法中执行,所以main方法调用这条语句,但是main方法是由Java虚拟机去调用的,所以最终是由虚拟机处理
// 最后甩锅给虚拟机后,跟之前的那些没有trycatch的异常一样,完全一样,很暴力
//方式二:
// 使用try catch捕获处理,用catch处理语句
try {
Date parse = simpleDateFormat1.parse(date1);
}catch (ParseException p){
System.out.println("解析失败");//手动解决的
}
}
}
package 异常机制;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test8 {
public static void main(String[] args) {
String date="2020-08=-04 21:04:57";
try {
Date date1 = parseDate(date);
} catch (ParseException e) {
System.out.println("日期异常");
}
}
private static Date parseDate(String date) throws ParseException {//(只要不是RunTimeException类及其子类的异常类就是编译期异常)
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date parse = simpleDateFormat1.parse(date);//这里抛异常给了parseDate方法,因为parseDate方法调用这个方法,然后main方法又调用parseDate方法,所以在main方法里就需要在处理,有那两种方式,如果选方式一就继续向上抛,最终抛给了虚拟机
return parse;
// 编译期异常,抛出的话可以抛给调用者,谁调用谁就处理这个异常,但是一般抛到main方法里面就不在抛了,因为main方法在抛的话抛给虚拟机了,虚拟机处理异常方式不太友好,所以抛到mian方法中的话一般用try catch处理
}
}
package 异常机制;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test9 {
//工具类里面的异常直接抛出去,不要抓,让调用这个方法的方法去想办法处理异常
private Test9(){};
public static Date psraseDateStr(String dateStr,String formate) throws ParseException {
Date date = new Date();
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat(formate);
Date parse = simpleDateFormat1.parse(dateStr);//这里就有异常,一般在工具类类里面就直接抛出异常就完事了,交给调用这个方法的方法去处理
return parse;
}
//工具类里面的异常就抛出去就可以了,不用自己抓,抛给调用工具类的方法去处理
}
2.自定义异常
package 异常机制.自定义异常;
import java.util.Scanner;
public class Test {
static double money=100;
public static void main(String[] args) {
// 在我们开发项目中,会遇到各种各样的异常,Java给我们提供的异常类并不能完全描述我们遇到的各种异常
// 比如面部识别异常,指纹识别异常
// 对于Java中没有提供的异常,但是我们业务需求要用这种异常,那么我们可以自定义异常
// 如定义余额不足异常:
Scanner scanner = new Scanner(System.in);
System.out.println("请输入您的取款金额");
double v = scanner.nextDouble();
quQuan(v);
}
private static void quQuan(double v) {
if(money<=v){//如果是正常情况的话,正常取钱
money-=v;
System.out.println("取款成功");
}else {
//如果取得比存得多的话,就不是正常情况,就要抛出一个异常,不让程序执行,程序终止
throw new NoMoneyException("余额不足");
}
money-=v;//如果负数的话,就要抛出一个异常
}
}
package 异常机制.自定义异常;
public class NoMoneyException extends RuntimeException{//只要把我们自定义的类纳入到RuntimeException异常类体系中,成为其子类,就是一个异常类了,妙啊,构造方法直接全部继承父类的就行了,如果你方法内部想定义有参,则这里的构造方法须调用父类有参构造,如果直接用无参,则就直接调用无参构造就行了
// 余额不足的类
public NoMoneyException() {
}//这里默认不写东西,直接就调用父类的无参构造
public NoMoneyException(String message) {
super(message);//子类继承父类,在子类的构造方法内部一定先执行父类的构造方法,默认优先执行父类无参的,如果想执行父类有参的或者父类没有无参的构造方法,也可在子类构造方法中调用父类有参的构造方法
}
}
package 异常机制.自定义异常;
public class MyTest {
public static void main(String[] args) {
//子类在重写父类方法时:
// (1)如果父类方法没有抛出过异常,则子类不能抛出异常,如果子类遇到了异常,则直接内部try catch
// (2)子类不能抛出父类没有抛出过的异常,子类抛出的异常数量只能比父类少或者一样,不能比父类大,
// (3)子类抛出的异常类的范围只能比父类的小,或者一样
// (4) 子类在重写父类方法时,如果父类方法有抛出异常,则子类可以选择抛,也可以选择不抛,在内部处理。
}
}
class Fu{
public void show() throws RuntimeException{
}
}
class Son extends Fu {
@Override
public void show() throws NullPointerException {
super.show();
}
}
3.异常的快捷键方式:
package 异常机制.自定义异常.异常快捷键搞定;
public class Test {
public static void main(String[] args) {
// 方式一:
// ctrl+alt+t快捷键:自动异常处理(所选中的这一行代码没有任何注释的时候才能用这个方法)
try {
System.out.println(1/0);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(1/0);
//ctrl+w 选中这一行代码
// 或者对写完之后的语句 ;后面.try,也可以自动补全了
try {
System.out.println(1/0);
} catch (Exception e) {
e.printStackTrace();
}
// 方式二: 在分号前的语句用.try也可以自动生成异常语句
try {
System.out.println(1/0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//对于编译期异常,就按alt+enter选择要么捕获要么抛出
//总结就是:对于运行期异常,可处理,也可不处理,如果处理只有一种方式:try catch在加个finally
//对于编译期异常:就一定要处理,对应处理方式有两种:一种是 try catch 加finally 另一种是throws 或在方法体内部throws,throws和throw这两种区别已总结过
//编译期异常IDEA一定会提示你,因为不处理程序就运行不了,这个时候按照之前的处理方式处理,运行期异常IDEA不会提醒你,因为运行期异常可处理,也可不处理,你需要自己去留心哪个语句会出问题,然后try catch就可以,当然你也可以不处理,那样就会交给JAVA虚拟机处理,它的方式很暴力,不建议用
4.异常处理
package 异常机制.异常处理;
//异常处理的方法
public class Test {
public static void main(String[] args) {
int a=10;
int b=0;
try {
System.out.println(a/b);
}catch (ArithmeticException aa){//这里是对异常方式的处理
System.out.println("除数为0");//对异常方式的处理不要用空处理(就是什么都不写),哪怕只是一句输出语句的提示都可以
aa.printStackTrace();//打印异常信息的堆栈信息,一般异常处理方式只选这一个就行了,别的不用选
aa.getMessage();//可以查看异常的名称
System.out.println(aa.toString());
}
}
}
package 异常机制.异常处理;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test1 {
public static void main(String[] args) {
// 有时候出现异常不是仅仅为了打印异常信息,知道这有了异常了,有时候出现异常还是为了去做一个判断,比如
while (true) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入整数");
try {
int i = scanner.nextInt();
break;//输入对了,就继续走try下面的代码,且跳过catch语句,执行catch语句下面的语句
} catch (InputMismatchException i) {
System.out.println("输的不对重新输");
}//这个就是输入类型不跟规定类型一致时出现的异常,如果输错了,那么就执行catch中的方法,执行完在执行下面的方法,发现时循环,然后就继续执行循环
// 这个方法跟之前那个 hasnextInt()方法一样
}
}
}
package 异常机制.异常处理;
import java.util.Scanner;
public class Test2 {
// throw和throws区别:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!精髓,记住!
// 相同点:
// 他俩都可以进行异常的抛出
// 不同点:
// throws是用在方法声明后面,throw是用在方法内部抛出异常
// throws可以跟多个异常类名,用逗号隔开,throw只能抛出一个异常对象名
// throws表示抛出异常,由该方法的调用者来处理,throw表示抛出异常,由其在的方法体内的语句来处理(一般就是在构造方法里面输点跟异常类有关的语句)
// throws表示出现异常的一种可能性,并不一定会出现这些异常。throw则是抛出了异常,执行了throw表示一定出现了异常
// 当然throw和throws可以同时用,即使throw和throws抛出的异常完全一样都可以
// 例如:
public static void main(String[] args)/*throws ArithmeticException*/ {
Scanner scanner = new Scanner(System.in);
int i = scanner.nextInt();
int i1 = scanner.nextInt();
int r=test(i,i1);
}
private static int test(int i, int i1) /*throws ArithmeticException*/{//方法上可以直接用throws抛异常,当然,如果你愿意,你可以在任何方法上抛任何异常,一般只在出现异常的工具类的方法上抛异常
int aa=0;
if(i1==0){
// 如果除数为0,直接就抛出异常
throw new ArithmeticException("分母为0了");//这里如果写有参构造的话,异常的那个红字里面就会把这个参数打印出来
// 这里经过判断之后主动抛出异常,让程序不在往下执行,其后果于throws一样
}else {
aa=i/i1;
}
return aa;
}
}
//final 是修饰字符:修饰类不能被继承,修饰方法,方法能被继承但不能被重写,修饰常量时,常量不能被修改
//finally是try catch 的后备语句,当有善后工作要做时,就用finally,用fially时,不管异常有没有触发,finally都会执行其代码块儿
//finalize是Object类里面的一个方法,用于垃圾回收器的回收
//只要有return的地方,就会将其所在的方法结束掉,因此只要方法里面有个return,就去看其所在的方法,就知道这个方法结束了
//哪里看到虚拟机退出,后面的代码管你是return还是finally都不会在执行了
//当看到catch{
// sout("asdsdsad");
// return;
// }
// finally{
// sout("123");
// }
//问:finally的语句会执行嘛?会,只要不是虚拟机退出,遇到这种情况,会先执行finally,在执行return,因为不管异常发没发生,都要执行finally,加了个return的话也得是这样,不然return完了没执行finally,矛盾了,所以会先执行finally,在return.