异常处理

(一)错误的分类

通常程序中的错误可分为三种类型:
1.编译错误:编译器能够检测到的错误,一般是语法错误。此时不能将源代码(.java)编译成可执行的字节码文件(.class)。
2.运行错误:程序运行时产生的错误,例如被0除、数组下标越界等等。
3.逻辑错误:实际结果与所期结果不同,这是机器本身无法检测的,需要程序员对运行结果及程序逻辑进行分析才能发现,逻辑错误可能会导致运行结果错误,有时也可能会导致运行错误。

(二)异常处理机制的概念

1.异常又称为例外,是特殊的运行错误对象。
2.程序在运行期间可能会遇到一些非预期的或不正常的错误,这些错误将会阻止程序的正常运行。
3.当发生异常时,系统中需要有一个相应的机制来处理它,确保程序可以继续正常运行,而不会产生死机、死循环或其他对操作系统的损害,从而保证整个程序运行的安全性。这就是异常处理机制。
4.应用Java的异常处理机制,在程序出现异常时,程序员可以捕获该异常,并进行相应的处理。
在这里插入图片描述

(三)异常和错误的区别

1.在Java中有两种类型的错误:Exception类(异常)和Error类(错误)。
2.Exception类是所有异常类的父类,包括了一些由程序本身及环境所产生的错误。应用Java的异常处理机制,异常(Exception)可以被捕获并进行相应的处理。例如数组下标越界、被0除等等。
3.Error类则包括了一些较少发生的内部系统错误。这些错误都是严重的错误,用户程序无法进行处理,只能通知使用者关闭程序。例如内存不足、虚拟机内部错误等等。
4.Exception类和Error类都是Throwable类的子类。
在这里插入图片描述注意:一个方法一次只能出现一个异常。到异常处会结束程序运行。

(四)异常的介绍

在这里插入图片描述
(只有RunTimeException才可以不用显示的throws出来,即在方法头部写出,其他的异常则必须显示的抛出)

(1)检查异常(受检异常、已检查异常)

该异常不可避免,必须进行异常处理,否则无法通过编译。

  • 非检查型异常以外的异常称为检查型异常。
  • 方法中可能抛出的检查型异常必须在方法定义部分使用throws语句进行声明。
  • 对于任何方法,如果它调用的方法会抛出一个检查型异常,那么调用者就必须捕获该异常或者也在方法定义部分使用throws语句声明会抛出该类型的异常。
  • 如果未捕获该异常也未使用throws声明会发生编译错误。
   public static void main(String args[]){
         Class.forName("oracle.jdbc.driver.OracleDriver");
   }

(2)非检查异常(运行异常、非受检异常)(RuntimeException)

该异常可以避免,不必强制处理,编译时不会报错,但在运行时会出现异常,所以叫运行异常。比如除0。

  • 所有继承自RuntimeException的异常称为非检查型异常。
  • 编译器对非检查型异常不做编译检查。

(1.)常见的非检查异常

- ArithmeticException:算术异常
  public static void main(String args[]){
	  int i = SystemIn.nextInt();
	  System.out.println(10/i);
  }
- ArrayIndexOutOfBoundException:数组下标越界异常
  public static void main(String args[]){
	  String[] s = new String[3];
	  System.out.println(s[4]);
  }
- NullPointerException:空指针异常,对象为空时,仍然可以通过编译,在引用对象方法时出现异常。
  public static void main(String[] args) {
	   String name = null;
	   System.out.println(name.hashCode());
  }
-ClassCastException:类型转换异常
  public static void main(String[] args) {
	  Object o = new Integer(10);
	  String s = (String)o;
  }
-NumberException:数据格式异常

(五)throw 关键字

作用:通过构式可以手动抛一个异常。
格式:throw 异常对象。

  • 当遇到关键字“throw”时就抛出一个例外
  • 将控制转移到相关的“catch”块中处理
  • 如果产生异常的方法没有处理该异常的“catch”语句块,则继续将该异常往外抛
  • 退出当前方法并转向上一级调用此方法的方法的“catch”语句块,若始终没有“catch”块来处理该异常则由运行系统处理
public static void main(String[] args) {
	  	//代码 1
	  	throw new NullPointerException();
	  	//代码 2
  	}

(六)异常的传递

沿方法调用链反向传递
main()
m1()
m2() throw new NullPointerException();

(七)异常处理

(1)throws:声明式异常处理,

  • 可以一次声明多个异常,每个异常之间用逗号隔开即可。如果多个异常有公共父类,可以用公共父类替换。

(2)try…catch:捕获异常处理。

  • 可以处理多个异常,一个try块可以对应多个catch块(catch的嵌套使用)

  • 发生的异常类型与catch中异常的类型相同则可以被捕获

  • 发生的异常类型是catch中异常的类型的子类也可以被捕获

  • 如果try部分的代码没有发生错误,catch块的代码不会执行

  • 如果一段代码肯定不会发生异常,则可以不用try{}catch{}块。

  • 嵌套时内层的catch捕捉的异常类型比外层异常的父类更具体。
    muti-catch(多重catch),一个try可以使用多个catch语句块

  • 按从上到下顺序捕获(注意排列顺序)
    多个catch块中,如果异常类型没有父子关系,则catch块的顺序可以随意,如果有父子关系,则子类catch块必须写在前面,父类catch块写在后面

  • 对catch做一个小解释,catch语块块只有try语句块捕获到异常时才会执行,并且会寻找对应的catch异常块执行若存在多个符合异常的话都会执行,但可能会出逻辑上的错误,在书写代码时尽量避免,

try{
//可能出现异常的地方
}catch(NullpointerException e1){
//出现异常才会执行
}catch(ClassCastException e2){//在异常代码块重复时可以通过这种方法替换catch(FileNotFoundException | EOFException e)
                              //也可使用异常的父类替换
//异常代码块
}

(八)try…catch…finally异常处理方式

==finally中的内容肯定执行 ==

public static void m2(){
     try{
    	 //代码1
    	 throw new FileNotFoundException();
	 //代码2
     }catch(FileNotFoundException e){
    	 //异常处理代码
     }finally{
    	 //肯定执行的内容
     }
}

在一些情况下finally也可能不执行这里两种可能。
原文出处:https://www.cnblogs.com/lanxuezaipiao/p/3440471.html
(1)try语句没有被执行到,如在try语句之前就返回了,这样finally语句就不会执行,这也说明了finally语句被执行的必要而非充分条件是:相应的try语句一定被执行到。

(2)在try块中有System.exit(0);这样的语句,System.exit(0);是终止Java虚拟机JVM的,连JVM都停止了,所有都结束了,当然finally语句也不会被执行到。
还有finally与return的关系

1. finally语句是在try的return语句执行之后,return返回之前执行。

	  public static void main(String[] args) {
	        System.out.println(test1());
	    }
	    public static int test1() {
	        int b = 20;
	        try {
	            System.out.println("try block");
	            return b += 80; 
	        }
	        catch (Exception e) {
	            System.out.println("catch block");
	        }
	        finally {     
	            System.out.println("finally block");     
	            if (b > 25) {
	                System.out.println("b>25, b = " + b);
	            }
	        }
	        return b;
	    }
输出结果:
try block
finally block
b>25, b = 100
100
-------------------例子二---------------------
public class FinallyTest1 {

    public static void main(String[] args) {
        
        System.out.println(test11());
    }
    
    public static String test11() {
        try {
            System.out.println("try block");

           return test12();
      } finally {
           System.out.println("finally block");
       }
  }

  public static String test12() {
       System.out.println("return statement");

       return "after return";
   }
    
}
输出结果:
try block
return statement
finally block
after return
说明try中的return语句先执行了但并没有立即返回,等到finally执行结束后再

2.finally块中的return语句会覆盖try块中的return返回。

public class FinallyTest2 {
    public static void main(String[] args) {
        System.out.println(test2());
    }
    public static int test2() {
        int b = 20;
        try {
            System.out.println("try block");
            return b += 80;
        } catch (Exception e) {
            System.out.println("catch block");
        } finally {
            System.out.println("finally block");
            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }
            return 200;
        }
        // return b;
    }

}
输出结果:
try block
finally block
b>25, b = 100
200
这说明finally里的return直接返回了,就不管try中是否还有返回语句,这里还有个小细节需要注意,finally里加上return过后,finally外面的return b就变成不可到达语句了,也就是永远不能被执行到,所以需要注释掉否则编译器报错。

3.如果finally语句中没有return语句覆盖返回值,那么原来的返回值可能因为finally里的修改而改

测试1:

public static void main(String[] args) {
        System.out.println(test3());
    }
    public static int test3() {
        int b = 20;
        try {
            System.out.println("try block");
            return b += 80;
        } catch (Exception e) {
            System.out.println("catch block");
        } finally {
            System.out.println("finally block");
            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }
            b = 150;
        }
        return 2000;
    }
    运行结果:
try block
finally block
b>25, b = 100
100

测试2:

public class FinallyTest6
{
    public static void main(String[] args) {
        System.out.println(getMap().get("KEY").toString());
    }     
    public static Map<String, String> getMap() {
        Map<String, String> map = new HashMap<String, String>();
        map.put("KEY", "INIT");
        try {
            map.put("KEY", "TRY");
            return map;
        }
        catch (Exception e) {
            map.put("KEY", "CATCH");
        }
        finally {
            map.put("KEY", "FINALLY");
            map = null;
        }        
        return map;
    }
}
运行结果:
FINALLY

············未完成

(九)方法覆盖中的异常

子类的覆盖方法不能比父类的被覆盖方法抛出更多的异常
子类重写父类方法时,重写的方法不能不被重写的方法抛出更多更大的异常,只能抛出其异常子类或者相干的异常,(可以是多个子类用逗号隔开),(不相干的或者不抛出都可以。)

(十)message属性与异常追踪

  • Exception.printStackTrace()会打印所有异常信息到控制台。
  • Exception类中定义了一个message属性,并提供了一个。
  • 使用getMessage()方法获得message属性的值,同时提供了一个构造方法对其属性赋值Exception,会保存(只)异常字符串,但是需要手动打印。
  public void test3()
  {
	  try {
		  System.out.println("------test3------");
		    throw new NullPointerException("这是空指针异常");
	} catch (Exception e) {
		System.out.println(e.getMessage());
		e.printStackTrace();
	}
       System.out.println("------test3------");
  }
	输出结果:
	------test3------
这是空指针异常//这是getMessage()打印出来的.
java.lang.NullPointerException: 这是空指针异常//这是printStackTrace()打印的异常.
	at com.Exception.Demo2.test3(Demo2.java:36)
	at com.Exception.Demo2.test2(Demo2.java:29)
	at com.Exception.Demo2.test1(Demo2.java:23)
	at com.Exception.Demo2.test(Demo2.java:17)
	at com.Exception.Demo2.main(Demo2.java:10)
------test3------

(十一)自定义异常

条件:需要继承Exception或子类RuntimeException ,编写构造方法传递给对应父类。
第一步建立一个异常的子类,
然后传递一个字符串参数给含参的构造方法。
即可然后在测试类中通过 throw new这个对应构造方法的类即可。
被继承的两个父类:

  • extends Exception 检测异常 当产生时需要异常处理
  • extends RuntimeException 运行异常 不强制处理
public class FlowException extends Exception {

	public static void main(String[] args) throws FlowException {
		test();
	}
	public static void  test() throws FlowException{
	throw new FlowException("这里出现的了异常");
	}
	public FlowException(){
	}
	public FlowException(String msg){
		super(msg);
	}
}
运行结果:
Exception in thread "main" com.Exception.FlowException: 这里出现了异常
at com.Exception.FlowException.test(FlowException.java:7)
at com.Exception.FlowException.main(FlowException.java:3)

这里出一个测试题方便理解:
题目:1.设计一个异常类FlowException,该类继承自Exception,getMsg()方法返回错误信息。(格式:id的值:msg的值)
2.定义一个异常测试类ExceptionTest
定义一个方法test,该方法接收2个整型的参数num1和num2,如果num1 - num2 <= 0,则抛出异常FlowException,错误信息为“404:Under Value Err”;如果num1 - num2 >= 1000,则抛出异常FlowException,错误信息为”405:Over Value Err”
在main方法中通过键盘输入2个数,测试方法test并显示错误信息。

public class FlowException extends Exception {//这里如果继承RuntimeException在测试类的方法中可以不进行异常声明
  FlowException(){
  }
	public FlowException(String msg) {
		super(msg);
	}
	public String getMsg() {
		return super.getMessage();
	}
}
public class ExceptionTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		System.out.println("请输入第一个数字");
		int x = sc.nextInt();
		System.out.println("请输入第二个数字");
		int y = sc.nextInt();
		try {                           //此处进行异常捕获,所以main可以不进行异常声明,但在其他抛出异常的方法中需要声明异常。
			test(x,y);
		} catch (FlowException e) {
			
//			e.printStackTrace();
			System.err.println(e.getMsg());
		}
	}
	
	public static void test(int x,int y) throws FlowException  {
		if(x-y<=0) {
			throw new FlowException("404:Under Value Err");
		}
		if(x-y>=1000) {
			throw new FlowException("405:Over Value Err");
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值