异常
-
首先是上期的String对象一共生成了11个(包括底层生成的存于哈希表中的那个哈~!) -
首先要知道异常有两大类,一个是编译期异常、一个是运行时异常 -
编译器异常:如System.out.println写成system.out.println,此时编译时就会报错,或者说在你的IDEA里此代码当场报红 -
运行时异常:当程序跑起来了,才在IDEA底下的结果窗口中弹出异常的信息,所以说此时代码是可以通过编译的
- 使用异常的好处:可以让我们的异常都统一放在一起,方便管理,看着也舒服;要不然采用我们平时写代码的习惯,每个都用个if语句去判断,可能造成正常运行的代码和出现异常的代码都混在一起的现象。
异常的基本用法😋
try{
有可能出现异常的语句都丢在这;
}catch(异常类型 异常对象){
处理异常的语句;
}finally{
异常的出口;
}
public class Test {
public static void main(String[] args) {
int n=0;
try{
if(n==0){
throw new Exception("抛出异常咯!");
}
}catch(Exception e){
e.printStackTrace();
}
System.out.println("hehe");
}
}
上述代码给我们的启示是:即使有异常出现(被抛出),我们将其处理之后,不影响后续代码的执行,不至于整个程序都崩溃。而如果我们自己不处理,就交由JVM处理,其结果就是程序将终止与第六行代码。
- 其次我们try{}内的语句从上往下依次执行时,一旦出现某个语句抛出异常,那这个{}内该代码以下的代码不会再执行!
关于异常打印的顺序🍊
- 我们知道JVM处理异常后,将异常信息栈的异常都将打印出来,因为这是一个栈,所以说先进去的异常信息后出来,后进去的异常信息会先出来,就比如:
public class Test {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
System.out.println(n);
}
}
当我们从键盘键入一些字母之后,查看最后的异常信息:
sasa
Exception in thread "main" java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:864)
at java.util.Scanner.next(Scanner.java:1485)
at java.util.Scanner.nextInt(Scanner.java:2117)
at java.util.Scanner.nextInt(Scanner.java:2076)
at Test.main(Test.java:13)
由上面提到的先进后出的异常信息可知,第一个出现的异常是main类的第13行,而上面的异常信息都是内置类的源码出错!(我们不可能怀疑源码),所以我们就乖乖去修改第13行代码就可以啦!改完(我们自己可以去用try-catch去包裹它,包裹:surround)即可。
处理多个异常的情况🍨
public class Test {
public static void main(String[] args) {
int[] arr={1,2,3};
try{
arr=null;
System.out.println(arr[5]);
System.out.println(arr.length);
}catch (ArrayIndexOutOfBoundsException e){
e.printStackTrace();
System.out.println("捕捉到了数组越界异常!");
}catch (NullPointerException e){
e.printStackTrace();
System.out.println("捕捉到了空指针异常!");
}
System.out.println("嘻嘻!");
}
}
还是上面提到的原则:try里第一个抛出异常的语句执行完,try内的之后的语句都终止执行。
当然我们可以多捕获多个异常的代码换一种写法:
public class Test {
public static void main(String[] args) {
int[] arr={1,2,3};
try{
arr=null;
System.out.println(arr[5]);
System.out.println(arr.length);
}catch (ArrayIndexOutOfBoundsException | NullPointerException e){
e.printStackTrace();
}
System.out.println("嘻嘻!");
}
}
异常的体系结构🌊
- 顶层类Throwable派生出两个而重要子类:Error和Exceptiom
- 其中Error代表java运行时内部错误或者资源耗尽错误,应用程序不会抛出这个异常,这种内部错误一旦出现,除了告知用户并使程序终止之外,无能为力,这种情况很少出现
- Exception是我们程序员所使用的异常类的父类
- 其中Exception有一个子类叫RuntimeException,这里面又派生出许多我们常见的异常类,如:NullpointerException等
-
说上面的是因为:我们用catch去捕捉异常类时是可以用父类异常去捕捉子类异常的,从逻辑上来说,我们可以用Exception去捕捉所有的它的子类异常!但不建议这样去做,因为Exception涵盖的异常类太多,可能把控不好。最好是写我们想捕捉的具体的异常类,尽量不要使用父类异常去捕捉子类异常,但没说不可用哈。 -
若一段代码中既有父类异常去捕获一个异常,也有具体的异常去捕获异常,那书写顺序是:先写子类异常去捕获,然后再父类,写反了,当场报错。 -
当我们不知道一个异常的父类到底是谁时:我们可以左键点击一下异常类,然后右键→Diagram→show就可以看见整个继承关系了!:(社区办可能没有这个功能,想不花钱用旗舰版,那只能破解版了) -
其次很重要的一点:函数处理异常的时候,本函数抛出异常如果么有当场解决,这个异常就会自动抛给调用这个函数的函数,调用者(主函数)如果还不处理这个异常,那就只能交由JVM去处理这个异常了!换句话说就是:异常会沿着异常的信息调用栈进行传递!!!!
finally的使用🌴
public class Test {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
try{
int n=scanner.nextInt();
System.out.println(10/n);
}catch(InputMismatchException e){
e.printStackTrace();
System.out.println("捕捉到了输入不匹配异常!");
}catch(ArithmeticException e){
e.printStackTrace();
System.out.println("捕捉到了算术异常,可能除数为0");
}finally {
scanner.close();
}
}
}
针对上述代码,这里有一个小技巧一步到位:
public class Test {
public static void main(String[] args) {
try(Scanner scanner=new Scanner(System.in)){
int n=scanner.nextInt();
System.out.println(10/n);
}catch(InputMismatchException e){
e.printStackTrace();
System.out.println("捕捉到了输入不匹配异常!");
}catch(ArithmeticException e){
e.printStackTrace();
System.out.println("捕捉到了算术异常,可能除数为0");
}
}
}
- 对上述代码:一个函数抛出异常(本函数内部不作处理!)我想把异常传给主函数,要对被调用的函数做异常申明:
public class Test {
public static void func()throws Exception{
throw new Exception();
}
public static void main(String[] args) {
try{
func();
}catch (Exception e){
e.printStackTrace();
System.out.println("捕捉到了被调用函数的异常啦!");
}
System.out.println("haha");
}
}
- 针对finally里面的语句,我们要知道,即使try{}内有return 的语句,我们finally里面的语句也会在此之前执行(所以说我们尽量不要在finally里面写return 的语句!),如:
public class Test {
public static int func(){
int a=10;
try{
return a;
}catch(ArithmeticException e){
e.printStackTrace();
}finally{
return 20;
}
}
public static void main(String[] args) {
int ret=func();
System.out.println(ret);
}
}
异常的处理流程?
- 程序先执行try中的代码
- 如果try中的代码出现异常,就会结束try中的代码,看catch 中的异常是否匹配
- 如果找到匹配的异常,就会执行对应catch中的代码
- 如果没有找到匹配的catch的异常,就会传递给调用者
- 无论是否找到匹配的异常类型,finally里面的代码都会被执行,finally所在方法结束之前执行!
- 如果上层调用者没有处理异常,继续往上面传
- 一直穿到了main函数也没有人去处理这个异常,那这个异常将会交由JVM处理,此时程序就会出现异常终止,结果代码是code 1
手动抛出异常(throw)🍉
- 除了java中内置的一些异常类之外,程序员也可以手动抛出某个异常,使用throw关键字来完成这个操作(前述已提及throws做异常声明)。
public class Test{
public static void func(int x){
if(x==0){
throw new ArithmeticException("抛出一个异常,你可以在这里描述这个异常")
}
}
public static void main(String[] args){
func(0);
}
}
Exception in thread "main" java.lang.ArithmeticException: 抛出一个异常,你可以在这里描述这个异常
at Test.func(Test.java:14)
at Test.main(Test.java:18)
Process finished with exit code 1
受查异常必须显式去处理是什么意思?🙋?♂
- 受查异常前面也有所提及,它是指Exception子类中除了RuntimeException之外的类,这些类一旦出现,我们在程序运行之前就可以看到代码报红了,这里意思就是说,你不处理这个异常,你连编译都别想过去。
- 而显式处理这种异常的方法主要有两种,一个是用try-catch进行包裹;一个就是“往上抛”,所谓“往上抛”就是说,当前方法只管用这个类,出不出错我都不管,我就把这个可能的异常抛给调用者,调用者如果想处理,就去catch它,或者调用者也不想处理,调用者也将这个可能的异常往上抛,就连我们的main函数都可以这样做,当main函数也将这个可能的异常往上抛时,那就相当于交给JVM处理了。因为中间没有一个人想处理这个“烂摊子”。
举个例子:比如我们常用的接口Clonable(注意事项已写在代码里):
class Person implements Cloneable{
public int age=10;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test1 {
public static void main(String[] args) throws CloneNotSupportedException {
Person person=new Person();
Person person1=(Person)person.clone();
System.out.println(person1.age);
}
}
用try-catch进行包裹,不再赘述,就是前面的例子,就是我们当场就把那个可能的异常给处理了的意思。
什么是自定义异常?怎么使用???
- java中虽然已经内置了丰富的异常类,但是实际场景中我们可能还需要对异常类进行拓展,创建符合我们自己的异常类。
- 那使用自定义异常类是必须继承一个内置的异常类的,内置的异常类无非就是两种:一种是受查异常(Exception)、一种就是非受查异常(RuntimeException)。
举例(注意事项已写在代码内部)
class MyException1 extends Exception{
public MyException1(String message) {
super(message);
}
}
class MyException2 extends RuntimeException{
public MyException2(String message) {
super(message);
}
}
public class Test2 {
public static void func1(int x){
try{
if(x==0){
throw new MyException1("hehe");
}
}catch (MyException1 e){
e.printStackTrace();
}
}
public static void func2(int x)throws MyException2{
if(x==0){
throw new MyException2("HAHA");
}
}
public static void main(String[] args) {
func1(0);
func2(0);
}
}
MyException1: hehe
at Test2.func1(Test2.java:22)
at Test2.main(Test2.java:34)
Exception in thread "main" MyException2: HAHA
at Test2.func2(Test2.java:30)
at Test2.main(Test2.java:35)
Process finished with exit code 1
class NameException1 extends Exception{
public NameException1(String message) {
super(message);
}
}
class PasswordException2 extends Exception{
public PasswordException2(String message) {
super(message);
}
}
public class Test3 {
private static final String name="hehe";
private static final String psaaword="123";
public static void login(String name,String password)throws NameException1,PasswordException2{
if(!Test3.name.equals(name)){
throw new NameException1("用户名错误!");
}
if(!Test3.psaaword.equals(password)){
throw new PasswordException2("密码错误!");
}
}
public static void main(String[] args) {
try{
login("hehe","1234");
}catch (NameException1 e){
e.printStackTrace();
System.out.println("用户名错误!");
}catch(PasswordException2 e){
e.printStackTrace();
System.out.println("密码错误!");
}
}
}
PasswordException2: 密码错误!
at Test3.login(Test3.java:27)
at Test3.main(Test3.java:33)
密码错误!
Process finished with exit code 0
一个练习题:使用while循环建立类似“恢复模型”的异常处理行为,他将不断重复,直到异常不再抛出!下期揭晓答案(答案不唯一,起到类似的效果即可)🍂
|