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

一. 异常的概述

1. 什么是异常

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

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

2. 异常的类型

在这里插入图片描述
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异常声明&不作处理

在这里插入图片描述

(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异常子类无法执行
在这里插入图片描述

三. 自定义异常

因为项目中会出现特有的问题,而这些问题并未被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处理,绝对不可以抛出

面向对象编程是一种编程范式,它将程序的构建和设计思路以面向对象的方式进行组织和实现。在Java中,面向对象编程是基于Java SE(Standard Edition)的一种编程方式。第07讲主要介绍了面向对象编程中的一些基本概念和关键术语。 在面向对象编程中,我们将程序中的数据和对数据的操作(方法)封装在一起,形成一个对象。对象由两部分构成:属性和方法。属性是用来描述对象的特征,而方法则是对象可以执行的操作。对象之间通过消息(方法调用)进行通信和交互。面向对象的核心思想是通过封装、继承和多态实现程序的复用和扩展。 封装是面向对象编程中的一个重要概念,它指的是将类的属性和方法进行封装,使得外部无法直接访问和修改对象的内部状态,只能通过公共的方法来操作属性和执行方法。封装提供了一种将数据和行为组合在一起的方式,可以保护数据的完整性和安全性。 继承是面向对象编程中的另一个重要概念,它指的是通过定义一个新的类来继承现有类的属性和方法。通过继承,子类可以继承父类的属性和方法,并可以在此基础上进行扩展和修改。继承提供了一种代码复用的机制,可以减少重复编码的工作量。 多态是面向对象编程的又一个重要概念,它指的是同一类型的对象在不同的情况下可以有不同的表现形式。多态通过方法的重写和方法的重载实现。方法的重写指的是在子类中重新定义和实现父类的方法,方法的重载指的是在同一个类中可以定义多个同名但参数列表不同的方法。 总结来说,面向对象编程是一种将程序组织和设计思路以对象为中心的编程方式。在JavaSE中,我们可以通过封装、继承和多态来实现面向对象编程的目标。封装可以提高程序的可维护性和可复用性,继承可以减少重复编码的工作量,多态可以灵活地操作对象。掌握这些基本概念和关键术语,可以帮助我们更好地理解和应用面向对象编程的思想。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值