##一、异常的产生

先看下面的这个demo:

/*
 * 异常的产生
 */

public class ExceptionDemo {
	public static void main(String[] args) {
		System.out.println(0/0);
		System.out.println("hello world");
	}
}

此时程序就产生 的异常如下:

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at exceptions.ExceptionDemo.main(ExceptionDemo.java:11)

分析:

  • JVM检测到了问题(0是除数的问题),于是程序发生了异常
  • JVM就把这个异常进行了对象的封装new ArithmeticException()
  • 异常对象被抛给调用者main中,main方法接收到这个异常后,由于程序中没有对异常进行处理的方法,因此mian方法也不能处理此异常,于是异常又被抛出到JVM中
  • JVM接受到异常后,采取了默认的处理措施,停止运行程序,于是后面的这个syso语句没有执行。

异常也是对象,上述例子中异常对象的描述类是ArithmeticException,它是用来描述数学中算数问题的 异常类。比如还有常见的空指针异常描述类NullPointerException。

二、异常的继承体系

Throwable类是所有异常和错误的父类

  • Error 错误
  • Exception 异常

  错误:程序出现了严重的问题,不修改代码,根本不能运行,人得了非典,艾滋,癌

  异常:程序出现了比较轻的问题,处理掉之后,继续运行,人得了阑尾炎,感冒

Exception类是所有异常的父类

  • 非RuntimeException
  • RuntimeException

Throwable中的方法:

  • String toString()重写Object类的方法,异常信息的详细描述?
  • String getMessage() 返回异常信息的简短描述
  • void printStackTrace() 异常信息输出到控制台

三、异常的两种处理方式

  1. 直接处理掉异常

    demo:

    try{
      尝试捕获异常的代码
    }catch(异常类 异常变量){
      异常处理代码
    }
    
  2. 第二种处理方式就是抛出异常

    • throw 手动抛出异常,后面写的是new异常的对象,写在方法中
    • throws 方法声明抛出异常,后面写的是异常类,写在方法的声明上

    demo:

    /*
      异常第二种处理方式,抛出异常
      throw throws的用法
    
      ExceptionDemo2.java:21: 错误: 未报告的异常错误Exception; 必须对其进行捕获或声明
      以便抛出
          throw new Exception();
      方法中,有异常抛出,但是没有处理过,因此编译失败
      异常的编译提示,是Java编译时的最后提示
    */
    
    class ExceptionDemo2 
    {
    	public static void main(String[] args) throws Exception
    	{
    		//System.out.println("Hello World!");
    		//main中调用了method方法,方法抛出了异常
    		//main有2个选择方法,一个是try...catch
    		//另外一个是,异常我也不处理,交给我的调用者处理
    		method(-5);
    	}
    	/*
    	   如果方法的参数小于0
    	   程序出现异常,如果参数大于0 ,程序是正常的
    	   方法自己,不想处理这个异常,把异常交给调用者处理
    	   在方法声明上,抛出异常,声明出来有异常,交给调用者
    	   throws 异常类
    	*/
    	public static void method(int x)throws Exception{
    	   if(x < 0)
    		   //程序出现了问题
    	       //手动抛出异常
    		   throw new Exception("程序出现了异常了");
       		
    		else
    			System.out.println("程序正常");
    	}
    }
    

四、多层异常的处理

demo

/*
  多层的异常处理方法调用
*/

class ExceptionDemo3 
{
	public static void main(String[] args) 
	{
		//System.out.println("Hello World!");
		try{
		methodA();
		}catch(Exception e){
		
		}
	}

	public static void methodA()throws Exception{
	   methodB();
	}

	public static void methodB()throws Exception{
		methodC();
	}

	public static void methodC()throws Exception{
	   throw new Exception();
	}
}

五、finally代码块

  • finally可以跟随try出现,也可以跟随try…catch出现
  • finally代码块中的程序,必须要运行
  • finally实际的开发意义,释放资源

demo

/*
   finally代码块
   一定要执行
*/

class ExceptionDemo4 
{
	public static void main(String[] args) 
	{
		try{
		method(1);
		}catch(Exception e){
		   e.printStackTrace();
		}finally{
	     	System.out.println("这里的程序必须执行");
		}
	}

	public static void method(int x)throws Exception{
	   if(x == 1)
		   throw new Exception("异常了!!");
	   else
		   System.out.println("程序正常了");
	}
}

一个finally的特例:

/*
demo:true or false
*/
class ExceptionDemo5{
  public static void main(String[] args){
    System.out.println(method());
  }
  public static boolean method(){
    
    try{
      return false;
    }catch(Exception e){
      
    }finally{
      return true;
    }
  }
}
//结果:true


/*
demo2:2 or 10
开发中,不要再try catch中写return
*/
class ExceptionDemo6{
  public static void main(String[] args){
    System.out.println(method());
  }
  public static int method(){
    int i = 1;
    try{
      return ++i;
    }catch(Exception e){
      return 100;
    }finally{
       i=10;
    }
  }
}
//结果:2

六、编译时期的异常

调用一个方法,这个方法抛出一个异常,此时调用者必须处理异常,否则编译失败。

Demo

/*
   编译时期的异常 
*/
class ExceptionDemo2{
    public static void main(String[] args){
        System.out.printf("hello wordl");
        method();
    }

    public static void method() throws Exception{}
}

结果如下:

ExceptionDemo2.java:10: 错误: 未报告的异常错误Exception; 必须对其进行捕获或声明
以便抛出
        method();
              ^
1 个错误

改进,加上try catch对异常进行处理,错误提示消失。

/*
   编译时期的异常 
*/

class ExceptionDemo2{
    public static void main(String[] args){
        System.out.printf("hello wordl");
        try{
        method();
        }catch(Exception e){}
    }

    public static void method() throws Exception{}
}

七、运行时期的异常

运行时期的异常一旦发生了,后面的所有程序都不会接着往下执行,所以设计运行时期的异常的初衷就很明显了,这个异常就是让开发人员看的,发生运行异常,就必须去修改原代码,而不是去处理异常。

Demo

/*
  运行时期的异常的特点
*/
class ExceptionDemo3{
    public static void main(String[] args){
        //调用者不知道方法会出现异常,所以就不用处理
        //这种时候,需要修改代码,而不需要处理
        method();
    }

    public static  void method(){
        //手动抛出一个异常

        throw new RuntimeException();
    }
}

Demo2

/*
  要求:计算正方形面积,边长的平方
  定义方法:求面积,返回结果
*/
class ExceptionDemo4{
    public static void main(String[] args){
        
        System.out.println(method(-8));
    }

    public static  int method(int num){
        if(num <= 0){
            //边长不合法,没有必要计算
            throw new RuntimeException("<= 0");
        }
        return num * num;
    }
}

常见的运行时期的异常:

异常对象 实际含义
IndexOutOfBoundsException 越界(字符串,和数组)
NullPointerException 空指针
ClassCastException 类型转换异常
NoSuchElementException 没有元素被取出
IllegalArgumentException 无效参数异常

八、自定义异常

Java中异常体系,将很多的情况都做了异常封装,但是实际的开发中,不可能把所有的异常都描述完毕,需要自定义的异常,用来描述自己程序中可能发生的异常。

自定义异常步骤:

  1. 定义类,后缀名Exception继承Exception类,或者继承RuntimeException
  2. 异常信息,自定义的异常类的构造方法,把异常信息使用super传递到父类

注意:只有异常类,才具备可抛性 throw new 异常体系的类

 通过刚才案例:

  1. 如果一个方法中,抛出多个异常,必须要throws声明多个异常(运行时起除外)
  2. 调用者,使用多个catch进行异常的捕获
  3. 多个catch中,最大(继承关系)的父类,写在最后面,否则编译失败

Demo

package exceptions;

/*
 * 自定义异常
 */

//负数异常
class FuShuException extends Exception{
     FuShuException(){}

     FuShuException(String info){
        super(info);
    }
}

//0异常
class ZeroException extends Exception{
    ZeroException(){}

    ZeroException(String message){
        super(message);
    }
}


public class ExceptionDemo5 {
	public static void main(String[] args) {
        try{
		getArea(0);
        }catch(FuShuException e){ //多异常,就多catch,范围越大的往后写
            e.printStackTrace();
        }catch(ZeroException e){
             e.printStackTrace();
        }
	}
    public static int getArea(int num) throws FuShuException,ZeroException{
        if(num < 0){
            throw new FuShuException("边长是负数");
        }
        else if(num == 0){
             throw new ZeroException("边长为0");
        }

        return (int)Math.pow(num,2);
    }
}

九、继承异常

前提:子类重写父类的方法

  • 父类的方法抛出了异常,子类重写后,异常:父类抛异常,子类可抛可不抛,但是,如果子类抛,不能抛出比父类还大的异常

  • 父类的方法不抛异常,子类重写后,子类不能抛出异常。如果子类重写的方法中,调用了一个抛异常的方法,子类别无选择,只能try...catch处理异常

demo

package exceptions;

/*
 * 自定义异常
 * 1.父类方法抛异常,子类重写后可抛可不抛,若是抛,则要小于父类(前提是子类重写了父类方法)
 * 2.父类方法不抛异常,子类也不能抛,若是子类调用的方法抛了异常,子类只能try catch
 */

//A是父类异常类
class AException extends Exception{

}
//B继承A
class BException extends AException{
    
}
//C和A是兄弟类
class CException extends Exception{
    
}

class Zi extends Fu{
    public void show() throws BException{}
}

class Fu{
    public void show() throws AException{}  
}

public class ExceptionDemo6 {
	public static void main(String[] args) throws AException{
        Fu f = new Zi();
       
          f.show();
    }
}