JavaSE笔记6.14-面向对象-异常

一. 异常的概述

1. 什么是异常

异常:程序在运行过程中发生的由于外部问题导致的程序异常事件,发生的异常会中断程序的运行。

Java 通过面向对象的方法来处理异常。在一个方法的运行过程中,如果发生了异常,则这个方法会产生代表该异常的一个对象,并把它交给运行时的系统,运行时系统寻找相应的代码来处理这一异常

2. 异常的类型

JavaSE笔记6.14-面向对象-异常_第1张图片
Throwable有两个体系:Exception(异常)和Error(错误)。

(1)Error

是程序中无法处理的错误,此类错误一般表示代码运行时JVM出现问题,应用不处理此类错误。

class Demo
{
     
	int div(int a,int b)
	{
     
		return a/b;
	}
}
class ExceptionDemo 
{
     
	public static void main(String[] args) 
	{
     
		byte[] arr=new byte[1024*1024*600];
	}
}

在这里插入图片描述

  1. byte:字节,1byte=8bits,表示的范围是0~255
  2. 创建一个byte类型的数组,数组的大小是:
    1024byte * 1024 * 600=1KB * 1024 * 600=1MB * 600=600MB
  3. 600MB超出虚拟机划出的空间大小
(2)Exception

程序本身可以捕获并且可以处理的异常。

Exception又分为两类:运行时异常和编译异常。

  • 运行时异常(不可查异常):RuntimeException类及其子类。编译器不会进行检查并且不要求必须处理的异常。
  • 编译异常(可查异常):Exception中除RuntimeException及其子类之外的异常。 编译器要求必须处理的异常

二. 异常的处理

对于不同的异常,java采用不同的异常处理方式:

  • Error:JVM将自行处理该异常,因此java允许应用不抛出此类异常。
  • 运行时异常:由系统自动抛出,应用本身可以选择处理或者忽略该异常。
  • 编译异常:有2种处理方法
    (捕获异常)当前方法知道如何处理该异常,则用 try…catch 块来处理该异常;
    (抛出异常)当前方法不知道如何处理该异常,则在定义该方法时声明抛出该异常。
1. 捕获异常
try
{
     
    需要被检测的代码/容易出现问题的代码块
}
catch(异常类 变量)
{
     
    处理异常的代码/处理方式
}
finally
{
     
    一定会执行的语句
}
(1)try-catch-finally的用法
class Demo
{
     
    int div(int a,int b)
    {
     
        return a/b;
    }
}
class ExceptionDemo
{
     
    public static void main(String[] args)
    {
     
        Demo d=new Demo();

        try
        {
     
            int x=d.div(4,0);
            System.out.println("x="+x);
        }
        catch (Exception e)
        {
     
            System.out.println("除零啦");
        }

        System.out.println("over");
    }
}

输出结果是:
除零啦
over

分析:

  1. 执行a/b时:产生了一个ArithmeticException()异常
  2. 因为该问题已有描述,可直接将异常封装成一个对象new ArithmeticException()
  3. 将对象抛给调用这个功能的调用者,即主函数中div(4/0)的地方
  4. try检测到问题,把该对象抛给catch,try中错误的下一行代码不执行
  5. catch中已定义Exception类型的参数e来接收该对象
    Exception e=new ArithmeticException():多态
  6. 执行catch中的处理代码
  7. 错误被解决,继续执行主函数中的代码
(2)try-catch-finally的执行顺序

a) try没有捕获到异常时:
try代码块中的语句依次被执行;catch被跳过;如果存在finally则执行finally代码块;执行完finally代码块之后继续执行后续代码。

b) try捕获到异常时,并且存在与之匹配的catch:
try代码块出现异常之后的代码不会被执行;跳到该catch代码块执行处理;如果存在finally则执行finally代码块;执行完finally代码块之后继续执行后续代码。

c) try捕获到异常时,但不存在与之匹配的catch:
try代码块出现异常之后的代码不会被执行;如果存在finally,则其中的代码仍然被执行;finally之后的代码不会被执行;该异常交给JVM处理。

2. throws抛出异常:在函数上声明异常

便于提高安全性,让调用去进行处理,不处理即编译失败

(1)作了throws异常声明&不作处理

JavaSE笔记6.14-面向对象-异常_第2张图片

(2)作了throws异常声明&作了处理
//定义
class Demo
{
     
    //在功能上通过throws的关键字声明了该功能有可能会出现问题
    int div(int a,int b)throws Exception
    {
     
        return a/b;
    }
}
//使用
class ExceptionDemo2
{
     
    public static void main(String[] args)
    {
     
        Demo d=new Demo();
        try{
     
            int x=d.div(4,1);
            System.out.println("x="+x);
        }
        catch (Exception e){
     
            System.out.println(e.toString());
        }
        System.out.println("over");
    }
}

输出结果是:
x=4
over

3. throw抛出异常:在方法内抛出异常
(1)用法

throw一个异常对象
例如:

throw new Exception();
(2)处理

必须要给出对应的处理动作:
a) 要么在内部try、catch处理;
b) 要么在函数上声明,让调用者处理。

一般情况下函数内出现异常,函数上需要声明

4. 对捕获到的异常进行常见方法操作
(1)Class Throwable下的常见方法
String getMessage()
//获取异常信息

String toString()
//获取异常类名和异常信息

void printStackTrace()
//获取异常类名和异常信息,以及异常出现在程序中的位置
//将此Thowable和其追溯打印到标准错误流

void printStackTrace(PrintString s)
//通常用该方法将异常内容保存在日志文件中,以便查阅
//将此Thowable和其追溯打印到指定的打印流

其实jvm默认的异常处理机制,就是在调用printStackTrace方法:打印异常的堆栈的跟踪信息。

(2)例子
class Demo
{
     
    int div(int a,int b)
    {
     
        return a/b;
    }
}
class ExceptionDemo
{
     
    public static void main(String[] args)
    {
     
        Demo d=new Demo();

        try
        {
     
            int x=d.div(4,0);
            System.out.println("x="+x);
        }
        catch (Exception e)
        {
     
            System.out.println("除零啦");
            System.out.println(e.getMessage());
            System.out.println(e.toString());
            e.printStackTrace();
        }

        System.out.println("over");
    }
}

输出结果是:
除零啦
/ by zero
java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
at Demo.div(Test.java:5)
at ExceptionDemo.main(Test.java:16)
over

5. 多异常处理
(1)注意

a) 声明异常时,建议声明更为具体的异常,这样处理可以更具体。
b) 对方声明了几个异常,就对应有几个catch块
c) 不要定义多余的catch块(不要定义一个父类异常catch,试图处理可能出现的其他异常)
d) 如果多个catch块中的异常出现继承关系,父类异常catch块放在最下面。(因为catch块是按照顺序执行的,都被父类异常catch块捕获的话,就不能执行该异常对应的具体catch块了)
e) 建议在进行catch处理时,catch中一定要定义具体处理方式

(2)例子

a) 除以0的异常

//定义
class Demo
{
     
	//在功能上通过throws的关键字声明了该功能有可能会出现问题
	int div(int a,int b)throws ArithmeticException,ArrayIndexOutOfBoundsException
	{
     
		int[] arr=new int[a];
		System.out.println(arr[4]); //数组脚标越界异常
		return a/b; //除以零的算术异常
	}
} 
//使用
class ExceptionDemo2 
{
     
	public static void main(String[] args) 
	{
     
		Demo d=new Demo();
		try
		{
     
			int x=d.div(5,0);
			System.out.println("x="+x);
		}
		catch (ArithmeticException e)
		{
     
			System.out.println(e.toString());
			System.out.println("被0除啦");
		}
		catch (ArrayIndexOutOfBoundsException e)
		{
     
			System.out.println(e.toString());
			System.out.println("角标越界啦!!");
		}
		System.out.println("over");
	}
}

输出结果是:
0
java.lang.ArithmeticException: / by zero
被0除啦
over

b) 角标越界的异常
抛出角标越界的异常(不继续执行除法运算)

//定义
class Demo
{
     
    //在功能上通过throws的关键字声明了该功能有可能会出现问题
    int div(int a,int b)throws ArithmeticException,ArrayIndexOutOfBoundsException
    {
     
        int[] arr=new int[a];
        System.out.println(arr[4]); //数组脚标越界异常
        return a/b; //除以零的算术异常
    }
}
//使用
class ExceptionDemo2
{
     
    public static void main(String[] args)
    {
     
        Demo d=new Demo();
        try
        {
     
            int x=d.div(4,1);
            System.out.println("x="+x);
        }
        catch (ArithmeticException e)
        {
     
            System.out.println(e.toString());
            System.out.println("被0除啦");
        }
        catch (ArrayIndexOutOfBoundsException e)
        {
     
            System.out.println(e.toString());
            System.out.println("角标越界啦!!");
        }
        System.out.println("over");
    }
}

输出结果是:
java.lang.ArrayIndexOutOfBoundsException: 4
角标越界啦!!
over

c) catch异常父类放在第一个,catch异常子类无法执行
JavaSE笔记6.14-面向对象-异常_第3张图片

三. 自定义异常

因为项目中会出现特有的问题,而这些问题并未被Java所描述并封装成对象。
所以对于这些特有的问题可以按照Java对问题封装的思想,将特有问题进行自定义的异常封装。

1. 例子

需求:在本程序中,对于除数是负数,也视为是错误的是无法运算的

(1)除数为0的异常已经被Java封装
class Demo
{
     
    int div(int a,int b)
    {
     
        //产生了Java自定义的ArrithmeticException异常,程序会自动抛给主函数
        return a/b;
    }
}
class ExceptionDemo3
{
     
    public static void main(String[] args)
    {
     
        Demo d=new Demo();
        int x=d.div(4,0);
        System.out.println("x="+x);
        System.out.println("over");
    }
}

输出结果为:
Exception in thread “main” java.lang.ArithmeticException: / by zero
at Demo.div(Test.java:6)
at ExceptionDemo3.main(Test.java:14)

(2)对除数为负数的异常进行自定义的描述
//1.自定义的异常类名也加上Exception,可以增强阅读性
//2.自定义的异常应继承Exception类
//3. 建立该异常类,可用于生成异常对象
class FuShuException extends Exception
{
     

}

class Demo
{
     
	//5.函数内部出现异常,需要在函数上声明
    int div(int a,int b)throws FuShuException
    {
     
   		 //4. 自定义的异常,Java不认识:需要手动建立对象并通过关键字throw抛出
        if(b<0){
     
            throw new FuShuException();
        }
        return a/b;
    }
}
class ExceptionDemo3
{
     
    public static void main(String[] args)
    {
     
        Demo d=new Demo();

		//6. 调用者进行try、catch处理
        try {
     
            int x=d.div(4,-1);
            System.out.println("x="+x);
        }
        catch (FuShuException e){
     
            //7. toString()打印异常的名称和信息
            System.out.println(e.toString());
            System.out.println("除数出现负数了");
        }

        System.out.println("over");
    }
}

输出结果是:
FuShuException
除数出现负数了
over

2. 自定义异常需要定义异常信息
(1)类Throwable中定义了构造函数和返回消息的函数
class Throwable
{
     
  private String message;
  Throwable(String message)
  {
     
      //构造一个具有指定的详细消息的新的throwable
      this.message=message;
  }
  public String getMessage()
  {
     
      //返回此throwable的详细消息字符串
      return message;
  }
}
(2)类Exception继承了类Throwable,可以直接用类Throwable中的这两个函数
class Exception extends Throwable
{
     
	//3.将这个message传给父类,因为父类中已经定义好了相关函数
    Exception(String message)
    {
     
        //1.子类的构造函数默认第一行有一条隐式的构造函数super()
        //2.如果父类中没有空参数的构造函数,可以手动定义super语句
       super(message); 
    }
}

//创建类Exception的对象
new Exception("haha").getMessage();
(3)自定义异常类继承类Exception
class FuShuException extends Exception
{
     
    FuShuException(String msg)
    {
     
        super(msg);
    }
}
new FuShuException("错误信息").getMessage();
(4)应用
class FuShuException extends Exception
{
     
    FuShuException(String msg)
    {
     
        super(msg);
    }
}

class Demo
{
     
    int div(int a,int b)throws FuShuException
    {
     
        if(b<0){
     
            throw new FuShuException("出现了除数是负数的情况———— /by FuShu");
        }
        return a/b;
    }
}
class ExceptionDemo3
{
     
    public static void main(String[] args)
    {
     
        Demo d=new Demo();
        try {
     
            int x=d.div(4,-1);
            System.out.println("x="+x);
        }
        catch (FuShuException e){
     
            //toString()打印异常的名称和信息
            System.out.println(e.toString());
            System.out.println("除数出现负数了");
        }

        System.out.println("over");
    }
}

输出结果是:
FuShuException: 出现了除数是负数的情况———— /by FuShu
除数出现负数了
over

(5)扩展特定的异常信息

需求:异常信息里面打印每次异常时具体的负数

class FuShuException extends Exception
{
     
    private int num;
    FuShuException(String msg,int num)
    {
     
        super(msg);
        this.num=num;
    }
    public int getNum()
    {
     
        return num;
    }
}

class Demo
{
     
    int div(int a,int b)throws FuShuException
    {
     
        if(b<0){
     
            throw new FuShuException("出现了除数是负数的情况———— /by FuShu",b);
        }
        return a/b;
    }
}
class ExceptionDemo3
{
     
    public static void main(String[] args)
    {
     
        Demo d=new Demo();
        try {
     
            int x=d.div(4,-1);
            System.out.println("x="+x);
        }
        catch (FuShuException e){
     
            //toString()打印异常的名称和信息
            System.out.println(e.toString());
            System.out.println("错误的负数是:"+e.getNum());
        }

        System.out.println("over");
    }
}

输出结果是:
FuShuException: 出现了除数是负数的情况———— /by FuShu
错误的负数是:-1
over

3. 总结

自定义异常:
必须是自定义类继承Exception(或Error/Throwable)

继承Exception的原因:
异常体系有一个特点:因为异常类和异常对象都需要被抛出
(函数内部抛出异常对象给函数,函数抛出异常类给调用者)
他们都具备可抛性,这个可抛性是Throwable这个体系中独有的特点
只有这个体系中的类和对象可被throws和throw操作

四. throw和throws的区别

1. 位置

throws使用在函数上,throw使用在函数内

2. 抛出的内容

throws后面跟的是异常类可以跟多个,用逗号隔开;
throw后面跟的是异常对象

五. finally

1. finally的特性

无论是否捕获异常,finally代码总会被执行。
如果try代码块或者catch代码块中有return语句时,finally代码块将在方法返回前被执行。

class FuShuException extends Exception
{
     
    FuShuException(String msg)
    {
     
        super(msg);
    }
}
class Demo
{
     
    int div(int a,int b)throws FuShuException
    {
     
        if(b<0)
            throw new FuShuException("除数为负数");
        return a/b;
    }
}
class ExceptionDemo5
{
     
    public static void main(String[] args)
    {
     
        Demo d=new Demo();
        try
        {
     
            int x=d.div(4,-1);
            System.out.println("x="+x);
        }
        catch (FuShuException e)
        {
     
            System.out.println(e.toString());
            return;
        }
        finally
        {
     
            System.out.println("finally");//finally中存放的是一定会被执行的代码
        }
        System.out.println("over");
    }
}

输出结果是:
FuShuException: 除数为负数
finally

2. finally的作用

finally代码块:定义一定执行的代码
通常用于关闭资源

public void method()
{
     
    连接数据库;
    数据操作
        (可能:要删除的数据不存在;要添加的数据不符合规则...throw new SQLException();
        (抛出异常,程序要被停止)
    关闭数据库
        (无论数据操作是否成功,一定要执行关闭数据库)
}
public void method()
{
     
    try
    {
     
        连接数据库;
        数据操作 throw new SQLException();
    }
    catch(SQLException e)
    {
     
        会对数据库进行异常处理;
    }
    finally
    {
     
        关闭数据库
    }
}

六. 异常处理语句其他格式

  • 第一个格式
try
{
         
}
catch(异常类 异常对象)
{
        
}
  • 第二个格式
try
{
        
}
catch(异常类 异常对象)
{
         
}
finally
{
      
}
  • 第三个格式
    catch是用于处理异常,如果没有catch就代表异常没有被处理过。
try
{
     
}
finally
{
     
}

七. 异常在子父类覆盖中的体现

1. 子类在覆盖父类时,如果父类的方法抛出异常

那么子类的覆盖方法只能抛出父类的异常/父类异常的子类/不抛

class AException extends Exception
{
     
}
class BException extends AException
{
     
}
class CException extends Exception
{
     
}

class Fu
{
     
	void show()throws AException
	{
     
	}
}
class Zi extends Fu
{
     
	void show()throws AException/BExcpetion/不抛
	{
     
	}
}

为什么有这个规律?

class AException extends Exception
{
     
}
class BException extends AException
{
     
}
class CException extends Exception
{
     
}

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

class Test
{
     
	//调用了类Fu中的show函数,选择在内部处理AException异常
    void function(Fu f) 
    {
     
        try
        {
     
            f.show();
        }
        catch(AException e)
        {
     
        }
    }
}

class Zi extends Fu
{
     
	//抛出了CException异常
    void show()throws CException 
    {
     
    }
}

class ExceptionDemo
{
     
    public static void main(String[] args)
    {
     
        Test T=new Test();
        T.function(new Fu());//异常的处理没问题
        T.function(new Zi());//多态:Fu f=new Zi();
                             //异常处理有问题,function方法不能处理CException异常
    }
}
2. 如果父类方法抛出多个异常

那么子类在覆盖该方法时,只能抛出父类异常的子集/子集的子类

3. 如果父类或者接口的方法中没有异常抛出

那么子类在覆盖方法时,也不可以抛出异常
如果子类方法发生了异常,就必须要进行try处理,绝对不可以抛出

你可能感兴趣的:(JavaSE,java,exception,面向对象编程)