欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

异常

程序员文章站 2022-04-21 21:37:38
...

“因为想过自己想要的生活,所以只有努力,不断地努力”

异常

异常是程序中的⼀些错误,但并不是所有的错误都是异常,并且错误有时候可以避免

异常体系

Throwable 所有错误和异常的⽗类
     Error ⽤于指⽰运⾏时环境发⽣的错误(服务器宕机,数据库崩溃等等)   
           Exception 可处理的异常   

JVM默认处理异常⽅式:

  1. ⾃⼰将问题处理,然后继续运⾏。
  2. ⾃⼰没有合适的处理⽅式,交给main⽅法的调⽤者JVM来处理,⽽JVM的默认
    处理⽅式是将错误信息直接打印到控制台,此时程序停⽌运⾏。
public class Demo01 {
       public static void main(String[] args) {
            System.out.println(10 / 0);
       }
}

异常处理的方式

格式:

try {
    可能出现异常的代码
} catch (异常类型 变量名) {
     处理异常的代码
} finally {
     释放资源代码
}

案例1: 捕获一个异常

public class Demo01 {
	public static void main(String[] args) {
		Demo01 d = new Demo01();
		try {
			d.test();
			System.out.println(10 / 2);
		} catch (ArithmeticException e) {
			System.out.println("除数为了0,别这样");
		}
	}

	public void test() {
		System.out.println(10 / 0);
	}
}

案例2:捕获多个异常

package 异常;

public class Demo05 {
	public static void main(String[] args) {
		Demo01 d = new Demo01();
		int a = 10;
		int b = 0;
		int[] arr = { 11, 22, 33, 44 };
		try {

			System.out.println(a / b);

			System.out.println(arr[10]);
			arr = null;
			System.out.println(arr[0]);
			// 注意:前面捕获的异常类型不能比后面捕获的异常类型大
		} catch (ArithmeticException e) {
			System.out.println("除数为了0,别这样");
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("你过线了,回去把");
		}
	}
}

异常分类

编译时异常

编译时异常的java程序必须进⾏显式的处理,否则程序不能通过编译。

运⾏时异常

⽆需显式处理,也可以和编译时异常⼀样进⾏处理,所有的RuntimeException及其⼦类的实例都被称为运⾏时异常。

注意:运⾏时异常实际上就是你的代码错误,请修改你的代码,不需要处理这种异常

案例1:编译时异常

public static void main(String[] args) {
		try {
			FileInputStream fis = new FileInputStream("xxx.txt");
		} catch (FileNotFoundException e) {
			System.out.println("文件找不到");
		}
	}

常⻅⽅法

getMessage(); 获取异常信息,返回字符串
toString(); 获取异常类名和信息,返回字符串
printStackTrace(); 打印异常类名和信息,以及异常出现在程序中的位置,返回值void

案例1

public static void main(String[] args) {
		try {
			System.out.println(10 / 0);
		} catch (Exception e) {
			System.out.println(e.getMessage());
			System.out.println(e.toString());
			e.printStackTrace();
		}
	}

异常处理的方式

抛出异常

定义功能⽅法时,需要把出现的问题暴露给调⽤者来处理,那么就通过throws在⽅法上标识。
格式:

修饰符 返回值类型 方法名() throws 异常类型 {
}

案例1:

创建Person类
public void setAge(int age) throws Exception {
     if (age > 0
     age < 150) {
      this.age = age;
     } else {
    System.out.println("年龄不正常");
      throw new Exception("年龄不正常");
    }
}
测试类
public static void main(String[] args) throws Exception {
      Person p = new Person();
      p.setAge(-18);
      System.out.println(p.getAge());
}

throw关键字

作用:
在⽅法中出现某种情况,程序不能继续运⾏,需要进⾏跳转时,就⽤throw把异常对象抛出.

throwsthrow的区别
throws
   1.⽤在⽅法声明上⾯,后⾯跟的是异常类型
   2.可以跟多个异常类型,⽤逗号隔开
   3.表⽰该⽅法可能出现的异常类型,该⽅法的异常由调⽤者来处理
throw
   1.⽤在⽅法内,后⾯跟的是异常对象
   2.只能抛出⼀个异常对象
   3.表⽰抛出异常,由⽅法体内的语句进⾏处理

finally关键字

特点:

  1. 被finally控制的语句体⼀定会执⾏
  2. 特殊情况:在执⾏finally语句体之前,jvm退出了

作⽤:⽤于释放资源,在IO流操作和数据库操作中会⻅到
案例一:

public static void main(String[] args) {
		try {
			System.out.println(10 / 0);
		} catch (Exception e) {
			System.out.println("除数为0了");
			System.exit(0);
			return;
		} finally {
			System.out.println("finally的语句体");
		}
	}
常见面试题:
1. final,finally和finalize的区别
     final可以修饰类,不能被继承
	 修饰方法,不能被重写
	 修饰变量,只能赋值一次

     finallytry语句中的一个语句体,不能单独使用,用来释放资源
     finalize是一个方法,当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
2.static int test() {
    int x = 1;
     try {
     x++;
    return x;
   } finally {
     ++x;
    return x; 不推荐在finally中使用return
   }
}
   
   Output:x=3
3.public abstract class Test {
    public static void main(String[] args) {
        System.out.println(beforeFinally());
    }
     
    public static int beforeFinally(){
        int a = 0;
        try{
            a = 1;
            return a;
        }finally{
            a = 2;
        }
    }
}
/**output:
1
*/
	 
解析:从结果上看,貌似`finally` 里的语句是在`return` 之后执行的,其实不然,实际上`finally` 里的语句是在在`return` 之前执行的。那么问题来了,既然是在之前执行,那为什么`a` 的值没有被覆盖了?
实际过程是这样的:当程序执行到try{}语句中的return方法时,它会干这么一件事,将要返回的结果存储到一个临时栈中,然后程序不会立即返回,而是去执行finally{}中的程序, 在执行`a = 2`时,程序仅仅是覆盖了a的值,但不会去更新临时栈中的那个要返回的值 。执行完之后,就会通知主程序“finally的程序执行完毕,可以请求返回了”,这时,就会将临时栈中的值取出来返回。这下应该清楚了,要返回的值是保存至临时栈中的。
再来看一个例子,稍微改下上面的程序:
public abstract class Test {
    public static void main(String[] args) {
        System.out.println(beforeFinally());
    }
     
    public static int beforeFinally(){
        int a = 0;
        try{
            a = 1;
            return a;
        }finally{
            a = 2;
            return a;
        }
    }
}
/**output:
2
*/
解析:在这里,finally{}里也有一个return,那么在执行这个return时,就会更新临时栈中的值。同样,在执行完finally之后,就会通知主程序请求返回了,即将临时栈中的值取出来返回。故返回值是2.

扩展:

1. 不管有没有出现异常,finally中的代码都会执⾏
2. 当try和catch中有return时,finally仍然会执⾏。
3. finally是在return后⾯的表达式运算后执⾏的(此时并没有返回运算后的值,⽽是先把要返回的值保存起
来,在finally中没有return新值的情况下,返回值不会改变,仍然是之前保存的值),所有⽅法返回值是
在finally执⾏前就确定了
4. finally中最好不要使⽤return,否则程序会提前退出,返回值重新确定。

⾃定义异常

继承Exception或者RuntimeException,然后编写需要的⽅法即可
案例1:

public class AgeOutOfBoundException extends Exception {
		public AgeOutOfBoundException() {
			super();
		}

		public AgeOutOfBoundException(String message) {
			super(message);
		}
	}

异常注意事项

1. ⼦类重写⽗类⽅法时,⼦类的⽅法必须抛出相同的异常或⽗类异常的⼦类
2. 如果⽗类抛出了多个异常,⼦类重写⽗类时,只能抛出相同的异常或它的⼦类,⼦类不能抛出⽗类没有的异
常
3. 如果被重写的⽅法没有异常抛出,那么⼦类的⽅法不能抛出异常,如果⼦类⽅法内有异常发⽣,只能使⽤
try..catch