异常处理:
异常概述与异常体系结构
首先,我们有一个顶级的父类:class Throwable,这个父类下面有两个子类:class Error and class Exception 查看继承顺序,快捷键:ctrl+t
总的来说分为两个大类:Error / Exception
第一个是系统性的错误:比如Stack overflow Error and out of memory 这一类我们不展开讨论,主要说第二类
第二个又分为编译时异常(受检异常checked)与运行时异常(非受检异常unchecked)
- 编译时异常(checked)
- IOException—FileNotFoundException
- ClassNotFoundException
- 运行时异常(unchecked)
- NullPointerException
- ArrayIndexOutOfBoundsException
- ClassCastException
- NumberFormatException
- InputMismatchException
- ArithmeticException
public static void main(String[] args) {
main(args);//StackoverflowError
int[] arr=new int[2048*2048*2048];//heap space outof memory
int[] arr1=null;
System.out.println(arr1[3]);//NullPointerException
int[] arr2=new int[5];
System.out.println(arr2[6]);//ArrayIndexoutofBoundsException
Object obj= new Date();
String str=(String)obj;//ClassCastException
String str1="123";
str1="abc";
int a=Integer.parseInt(str1);//NumberformatException
Scanner scanner=new Scanner(System.in);
int b=scanner.nextInt();//input number.if input other type,InputMismatchException
System.out.println(b);
int c=10,d=0;
System.out.println(c/d);//ArithmeticException
}
面试题:你都遇见过哪些常见的异常?
try—catch—finally 快捷键:alt+shift+z
一、异常的处理:抓抛模型
- “抛”: 程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出,一旦抛出对象后,其后的代码将不再执行
- ”抓“:可以理解为异常的处理方式,try—catch—finally ; throws
二、 try—catch—finally的使用
try{
//可以出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}catch(异常类型3 变量名3){
//处理异常的方式3
}...
finally{
//一定会执行的代码
}
说明:
- finally是可选的
- 使用try将可能出现异常的代码另外装起来,如果执行到被装起来的语句出现了异常,就抛出对应异常类的对象,然后在到catch中进行匹配
- 一旦匹配到了一个catch,就进入相应的catch进行异常处理,一旦执行完对应catch里面的程序,就跳出try-catch结构(在没有finally的情况下),继续执行其后的代码
- catch中的异常类型如果满足了子父类关系,则要求子类声明在父类的上面,不然子类无法执行,则会报错
- 常见的异常对象处理方式:String getMessage() /printStackTrace()
- 在try结构中声明的变量,再出了try结构以后,就不能再被调用
- 使用了try—catch—finally以后,虽然编译没有异常了,但是运行时依旧可能会出现异常,需要继续去处理
- 开发中运行时异常比较常见,一般不会去try—catch—finally,因为没有意义。一般来说使用try—catch—finally就是在编译的时候。
finally的使用:
- finally是可选的
- finally中声明的是一定会被执行的代码。即使catch中又出现了异常,try中有return,catch中也有return等情况
- 像数据库连接、输入输出流、网络编程Socker等资源,JVM是不能自动回收的。我们需要进行资源释放,因为就将资源释放声明在finally中
package com.ErrorException.nonworking;
public class DealwithException {
public static void main(String[] args) {
String string="123";
String string2="abc";
try {
int num=Integer.parseInt(string2);
}catch(NumberFormatException o) {
System.out.println("数字转换异常");}
catch(NullPointerException o) {
System.out.println("空指针了哟");}
int[] arr=null;
try {
System.out.println(arr[3]);
}catch(NumberFormatException o) {
System.out.println("数字转换异常");}
catch(NullPointerException o) {
System.out.println("空指针了哟");}
finally {
System.out.println("finally为必须执行的语句");
}
int[] arr1=new int[5];
try {
System.out.println(arr1[6]);
}catch(NumberFormatException o) {
System.out.println("数字转换异常");}
catch(NullPointerException o) {
System.out.println("空指针了哟");}
catch(ArrayIndexOutOfBoundsException o) {
System.out.println("索引越界了哟");
}
}
}
throws+ 异常类型
抛出了异常类的对象,但是没用,并没有解决实际问题,异常处往下的代码也不执行了。try-catch-finally这种模式是实际解决问题的,两种方法一般是嵌套使用;如下代码
package com.ErrorException.nonworking;
import java.security.PublicKey;
import com.myobjectoriented3.nonworking.StaticTest;
public class ThrowsException {
public static void main(String[] args) {
method3();
System.out.println("能否执行这一行?");
}
public static void method3() throws ArithmeticException{
try {
method2();
}catch(ArithmeticException o) {
System.out.println("A方法的异常以处理");
}
}
//注意:被抛出的异常要满足异常类的对象,才会被抛出,不然还是异常
public static void method2() throws ArithmeticException {
int c=10,d=0;
System.out.println(c/d);
System.out.println("method2异常下面的一行");
}
}
总结:子类抛出的异常不能比父类抛出的大。如果父类的某个方法没有抛出异常,则子类重写的方法也不能抛!
上面讲的都是自动产生异常对象,接下来我们聊聊手动的:
手动产生一个异常对象,并抛出throw
- throws是处理异常的一种方法,通常写在方法声明的后面。而throw是手动产生一个异常,然后抛出,通常写在方法体中
package com.ErrorException.nonworking;
public class ThrowTest {
public static void main(String[] args) {
Student stu=new Student();
try {
stu.setId(-100);
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
}
}
}
class Student{
private int id;
public void setId(int id)throws Exception{
if(id>0)
this.id=id;
else
throw new Exception("输入的学号有误");
}
}
用户自定义异常类:
根据具体情况去定义特定的异常类,见名知意
下面的第一份代码是我们自己定义的一个异常类,第二份代码是它的一个使用
package com.ErrorException.nonworking;
public class SelfException extends RuntimeException {
@java.io.Serial
static final long serialVersionUID = -7034897685745766939L;
public SelfException() {
super();
}
public SelfException(String message) {
super(message);
}
}
package com.ErrorException.nonworking;
public class ThrowTest {
public static void main(String[] args) {
Student stu=new Student();
try {//try--catch具体的处理
stu.setId(-100);
} catch (RuntimeException e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
}
}
}
class Student{
private int id;
public void setId(int id) throws RuntimeException{//抛给父类
if(id>0)
this.id=id;
else
throw new SelfException("您输入的学号有误,不能输入负数");//自定义的异常类
}
}
主类代码:
package com.ErrorException.nonworking;
public class EcmDef {
public static void main(String[] args) {
try {
int i=Integer.parseInt(args[0]);
int j=Integer.parseInt(args[1]);
int result=ecm(i,j);
System.out.println(result);//刚开始忘记写这一行代码了,导致没有结果,哈哈哈
}catch(NumberFormatException e) {
System.out.println("数据类型不一致");
}catch(ArrayIndexOutOfBoundsException e) {
System.out.println("缺少命令行参数");
}catch(ArithmeticException e) {
System.out.println("除0");
}catch(EcDef e) {
System.out.println("输入的数不能为负数!");
}finally {}
}
public static int ecm(int i,int j) throws EcDef {
if(i<0||j<0)
throw new EcDef("输入的数不能为负数!");
return i/j;
}
}
异常类代码
package com.ErrorException.nonworking;
public class EcDef extends Exception{
@java.io.Serial
static final long serialVersionUID = -3387516993924229948L;
public EcDef() {
super();
}
public EcDef(String message) {
super(message);
}
}