第八章 异常处理与抽象类

文章目录

  • 第八章 异常处理与抽象类
  • 一、异常处理
    • 1.概念
    • 2.Exception类的层次
    • 3.Java 内置异常类
    • 3.异常方法
    • 4.捕获异常
    • 5.多重捕获块
    • 6.throws/throw 关键字
    • 7.finally关键字
    • 8.声明自定义异常
    • 9.规定
  • 二、抽象类
    • 1.概念
    • 2.实例解析
    • 3.抽象方法
    • 4.规定
    • 6.思考:如果抽象父类中都是普通方法,那为什么要定义成抽象类?

一、异常处理

1.概念

异常发生的原因有很多,通常包含以下几大类:

  • 用户输入了非法数据
  • 要打开的文件不存在
  • 网络通信时连接中断,或者 JVM 内存溢出

这些异常有的是因为用户错误引起的,有的时程序错误引起的,还有其他一些是因为物理错误引起的

从大体来看异常分为两块:

  1. Error – 错误:是指程序无法处理的错误,表示应用程序运行时出现的重大错误。例如 JVM 运行时出现的 OutOfMemoryError 以及 Socket 编程时出现的端口占用等程序无法处理的错误
  2. Exception – 异常:异常可分为运行时异常和编译异常,异常能被程序本身处理,而错误无法处理
    1. 运行时异常:即 RuntimeException 及其子类的异常。这类异常在代码编写的时候不会被编译器所检测出来,可以不进行捕获,但也可以根据需要进行捕获抛出。常见的 RuntimeException 有:NullpointException(空指针异常),ClassCastException(类型转换异常),IndexOutofBoundsException(数组越界异常)等,
    2. 编译异常 :RuntimeException 以外的异常。这类异常在编译时编译器会提示需要捕获,如果不进行捕获则编译错误。常见的编译异常有:IOException(流传输异常),SQLException(数据库操作异常)等,编译异常也叫检查性异常

Java 处理异常的机制:抛出异常然后捕获异常。一个方法所能捕获的异常,一定是 Java 代码在某处所抛出的异常。或者说,异常总是先被抛出,然后被捕获

2.Exception类的层次

所有的异常类都是从 java.lang.Exception 类继承的子类
Exception 类是 Throwable 类的子类。除了 Exception 类外, Throwable 还有一个子类 Error
Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在 Java 程序处理的范畴之外
Error 用来指示运行时环境发生的错误
例如,JVM 内存溢出。一般地,程序不会从错误中恢复
异常类有两个主要的子类:IOException 类和 RuntimeException 类

在 Java 内置类中,有大部分常用检查性和非检查性异常

3.Java 内置异常类

Java 语言定义了一些异常类在 java.lang 标准包中
标准运行时异常类的子类是最常见的异常类。由于 java.lang 包是默认加载到所有的 Java 程序的。所以大部分从运行时异常类继承而来的异常都可以直接使用
Java 根据各个类库也定义了一些其他的异常,下面的表中列出了 Java 的非检查性异常

异常 描述
ArithmeticException 当出现异常的运算条件时,抛出此异常。例如,一个整数 “除以零” 时,抛出此类的一个实例
ArrayIndexOutOfBoundsException 用非法索引访问数组时抛出的异常,如果索引为负或大于数组大小,则该索引为非法索引
ArrayStoreException 试图将错误类型的对象存储到一个对象数组时抛出的异常
ClassCastException 当试图将对象强制转换为不是实例的子类时,抛出该异常
IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数
IllegalMonitorStateException 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程
IllegalStateException 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下
IllegalThreadStateException 线程没有处于请求操作所要求的适当状态时抛出的异常
IndexOutOfBoundException 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出
NegativeArraySizeException 如果应用程序试图创建大小为负的数组,则抛出该异常
NullPointerException 当应用程序试图在需要对象的地方使用 null 时,抛出该异常
NumberFormatException 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常
SecurityException 由安全管理器抛出的异常,指示存在安全侵犯
StringIndexOutOfBoundException 此异常由 String 方法抛出,指示索引为负或者超出字符串的大小
UnsupportedOperationException 当不支持请求的操作时,抛出该异常

下面的表中列出了 Java 定义在 java.lang 包中的检查性异常类

异常 描述
ClassNotFoundException 应用程序试图加载类时,找不到相应的类,抛出该异常
CloneNotSupportedException 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常
IllegalAccessException 拒绝访问一个类的时候,抛出该异常
InstantiationException 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常
InterruptedException 一个线程被另一个线程中断,抛出该异常
NoSuchFieldException 请求的变量不存在
NoSuchMethodException 请求的方法不存在

3.异常方法

下面的列表是 Throwable 类的主要方法:

方法 说明
public String getMessage() 返回关于发生的异常的详细信息,这个消息在 Throwable 类的构造函数中初始化了
public Throwable getCause() 返回一个 Throwable 对象代表异常的原因
public String toString 使用 getMessage() 的结果返回类型的串级名字
public void printStackTrace() 打印 toString() 结果和栈层次到 System.err,即错误输出流
public StackTraceElement [] getStackTrace() 返回一个包含堆栈层次的数组。下标为 0 的元素代表栈顶,最后一个元素代表方法调用堆栈的堆底
public Throwable fillIn StackTrace() 用当前的调用栈层次填充 Throwable 对象栈层次,添加到栈层次任何先前信息中

4.捕获异常

使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方
try/catch 代码块中的代码称为保护代码,使用 try/catch 的语法如下:

try
{//程序代码
}catch(ExceptionName e1)
{//Catch 块
}

Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样的

实例:

import java.io.*;
public class ExcepTest{public static void main(String args[]){try{int a[] = new int[2];System.out.println("Access element three :" + a[3]);}catch(ArrayIndexOutOfBoundsException e){System.out.println("Exception thrown  :" + e);}System.out.println("Out of the block");}
}

以上代码编译运行输出结果如下:

Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block

5.多重捕获块

一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获
多重捕获快的语法如下所示:

try{//程序代码
}catch(异常类型1 异常的变量名1)//程序代码
}catch(异常类型2 异常的变量名2)//程序代码
}catch(异常类型3 异常的变量名3)//程序代码
}

上面的代码段包含了 3 个 catch 块
可以在 try 语句后面添加任意数量的 catch 块
如果保护代码中发生异常,异常被抛给第一个 catch 块
如果抛出异常的数据类型与 ExceptionType1 匹配,它在这里就会被捕获
如果不匹配,它会被传递给第二个 catch 块
如此,直到异常被捕获或者通过所有的 catch 块

6.throws/throw 关键字

如果一个方法没有捕获到一个检查性异常,那么该方法必须用 throws 关键字来声明。throws 关键字放在方法签名的尾部
也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的

下面的方法声明抛出一个 RemoteException 异常:

import java.io.*;
public class className
{public void deposit(double amount) throws RemoteException{// Method implementationthrow new RemoteException();}//Remainder of class definition
}

一个方法可以声明抛出多个异常,多个异常之间用逗号隔开
例如,下面的方法声明抛出 RemoteException 和 InsufficientFundsException

import java.io.*;
public class className
{public void withdraw(double amount) throws RemoteException,InsufficientFundsException{// Method implementation}//Remainder of class definition
}

区别:

  1. throws 跟在方法声明后面,后面跟的是异常类名;throw 用在方法体内后面跟的是异常类对象名
  2. throws 可以跟多个异常类名,用逗号隔开;throw 只能抛出一个异常对象名
  3. throws 表示抛出异常,由该方法的调用者来处理;throw 表示抛出异常,由该方法体内的语句来处理
  4. throws 表示有出现异常的可能性,并不一定出现这些异常;throw 则是抛出了异常,执行 throw 一定出现了某种异常

实例:

//区别1:public static void method1() throws ArithmeticException {// 跟在方法声明后面,后面跟的是异常类名int a=10;int b=0;if(b==0) {throw new ArithmeticException();//用在方法体内,后面跟的是异常类对象名}else {System.out.println(a/b);}}//区别2:public static void method2() throws ArithmeticException,Exception {//跟多个异常类名,用逗号隔开int a=10;int b=0;if(b==0) {throw new ArithmeticException();// 只能抛出一个异常对象名}else {System.out.println(a/b);}}//区别3:public class throwandthrows3 {public static void main(String[] args) {try {method3();//由该方法的调用者来处理}catch (ArithmeticException e) {e.printStackTrace();}}public static void method3() throws ArithmeticException {int a=10;int b=0;if(b==0) {throw new ArithmeticException();//由该方法体内的语句来处理}else {System.out.println(a/b);}}}//区别4:public class throwandthrows4 {public static void main(String[] args) {try {method4();}catch (ArithmeticException e) {e.printStackTrace();}}public static void method4() throws ArithmeticException,IndexOutOfBoundsException {int a=10;int b=0;if(b==0) {throw new ArithmeticException();}else {System.out.println(a/b);}}}

7.finally关键字

finally 关键字用来创建在 try 代码块后面执行的代码块
无论是否发生异常,finally 代码块中的代码总会被执行
在 finally 代码块中,可以运行清理类型等收尾善后性质的语句
finally 代码块出现在 catch 代码块最后,语法如下:

try{// 程序代码
}catch(异常类型1 异常的变量名1){// 程序代码
}catch(异常类型2 异常的变量名2){// 程序代码
}finally{// 程序代码
}

我们来看一个特殊的情况:

try{//待捕获代码
}catch(Exception e){System.out.println("catch is begin");return 1 ;
}finally{System.out.println("finally is begin");
}

执行结果为:

catch is begin
finally is begin

返回值为1
也就是说会先执行 catch 里面的代码,然后执行 finally 里面的代码,最后才 return 1
我们再来看下面的代码:

try{//待捕获代码
}catch(Exception e){System.out.println("catch is begin");return 1 ;
}finally{System.out.println("finally is begin");return 2 ;
}

执行结果为:

catch is begin
finally is begin

返回值为2
我们可以得出结论,finally 永远都会在 catch 的 return 前执行,且如果在 finally 里执行了 return 便不会执行 catch 中的return

8.声明自定义异常

在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点

  • 所有异常都必须时 Throwable 的子类
  • 如果希望写一个检查性异常类,需要继承 Exception 类
  • 如果希望写一个运行时异常类,那么需要继承 RuntimeException 类

语法:

class MyException extends Exception{}

继承 Exception 类来创建的异常类时检查性异常类
一个异常类和其它任何类一样,包含有变量和方法

以下实例是一个银行账户的模拟,通过银行卡的号码完成识别,可以进行存钱和取钱的操作:

// 文件名InsufficientFundsException.java
import java.io.*;//自定义异常类,继承Exception类
public class InsufficientFundsException extends Exception
{//此处的amount用来储存当出现异常(取出钱多于余额时)所缺乏的钱private double amount;public InsufficientFundsException(double amount){this.amount = amount;} public double getAmount(){return amount;}
}

为了展示如何使用我们自定义的异常类,
在下面的 CheckingAccount 类中包含一个 withdraw() 方法抛出一个 InsufficientFundsException 异常

// 文件名称 CheckingAccount.java
import java.io.*;//此类模拟银行账户
public class CheckingAccount
{//balance为余额,number为卡号private double balance;private int number;public CheckingAccount(int number){this.number = number;}//方法:存钱public void deposit(double amount){balance += amount;}//方法:取钱public void withdraw(double amount) throwsInsufficientFundsException{if(amount <= balance){balance -= amount;}else{double needs = amount - balance;throw new InsufficientFundsException(needs);}}//方法:返回余额public double getBalance(){return balance;}//方法:返回卡号public int getNumber(){return number;}
}

下面的 BankDemo 程序示范了如何调用 CheckingAccount 类的 deposit() 和 withdraw()方法

//文件名称 BankDemo.java
public class BankDemo
{public static void main(String [] args){CheckingAccount c = new CheckingAccount(101);System.out.println("Depositing $500...");c.deposit(500.00);try{System.out.println("\nWithdrawing $100...");c.withdraw(100.00);System.out.println("\nWithdrawing $600...");c.withdraw(600.00);}catch(InsufficientFundsException e){System.out.println("Sorry, but you are short $"+ e.getAmount());e.printStackTrace();}}
}

编译上面三个文件,并运行程序 BankDemo,得到结果如下所示:

Depositing $500...Withdrawing $100...Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsExceptionat CheckingAccount.withdraw(CheckingAccount.java:25)at BankDemo.main(BankDemo.java:13)

9.规定

  • catch 不能独立于 try 存在
  • 在 try/catch 后面添加 finally 块并非强制要求的
  • try 代码后不能既没有 catch 块,也没有 finally 块
  • try、catch、finally 块之间不能添加任何代码
  • 父类方法被重写时,重写他的方法必须抛出相同的异常或异常的子类
  • 如果父类抛出多个异常,则重写方法必须抛出那些异常的一个子集,不能抛出新的异常

二、抽象类

1.概念

在面向对象的概念中,所有的对象都是通过类来描述的,但是反过来,并不是所有的类都是用来描述对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样
由于抽象类不能实例化对象,所以抽象类必须被继承才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法
在 Java 中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口

2.实例解析

在 Java 语言中使用 abstract class 来定义抽象类:

/* 文件名 : Employee.java */
public abstract class Employee
{private String name;private String address;private int number;public Employee(String name, String address, int number){System.out.println("Constructing an Employee");this.name = name;this.address = address;this.number = number;}public double computePay(){System.out.println("Inside Employee computePay");return 0.0;}public void mailCheck(){System.out.println("Mailing a check to " + this.name+ " " + this.address);}public String toString(){return name + " " + address + " " + number;}public String getName(){return name;}public String getAddress(){return address;}public void setAddress(String newAddress){address = newAddress;}public int getNumber(){return number;}
}

注意到该 Employee 类没有什么不同,尽管该类是抽象类,但是它仍然有 3 个成员变量,7 个成员方法和 1 个构造方法。现在如果你尝试如下的例子:

/* 文件名 : AbstractDemo.java */
public class AbstractDemo
{public static void main(String [] args){/* 以下是不允许的,会引发错误 */Employee e = new Employee("George W.", "Houston, TX", 43);System.out.println("\n Call mailCheck using Employee reference--");e.mailCheck();}
}

当你尝试编译 AbstractDemo类时,会产生如下错误:

Employee.java:46: Employee is abstract; cannot be instantiatedEmployee e = new Employee("George W.", "Houston, TX", 43);^
1 error

我们可以通过以下方式继承 Employee 类的属性:

/* 文件名 : Salary.java */
public class Salary extends Employee
{private double salary; //Annual salarypublic Salary(String name, String address, int number, doublesalary){super(name, address, number);setSalary(salary);}public void mailCheck(){System.out.println("Within mailCheck of Salary class ");System.out.println("Mailing check to " + getName()+ " with salary " + salary);}public double getSalary(){return salary;}public void setSalary(double newSalary){if(newSalary >= 0.0){salary = newSalary;}}public double computePay(){System.out.println("Computing salary pay for " + getName());return salary/52;}
}

尽管我们不能实例化一个 Employee 类的对象,但是如果我们实例化一个 Salary 类对象,该对象将从 Employee 类中继承 7 个成员方法,且通过该方法可以设置获取三个成员变量

/* 文件名 : AbstractDemo.java */
public class AbstractDemo
{public static void main(String [] args){Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);System.out.println("Call mailCheck using Salary reference --");s.mailCheck();System.out.println("\n Call mailCheck using Employee reference--");e.mailCheck();}
}

以上程序编译运行结果如下:

Constructing an Employee
Constructing an Employee
Call mailCheck using  Salary reference --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0Call mailCheck using Employee reference--
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.

3.抽象方法

如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法
Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体
抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号

声明抽象方法会造成以下两个结果:

  • 如果一个类包含抽象方法,那么该类必须是抽象类
  • 任何子类都必须重写父类的所有抽象方法,除非也声明自身为抽象类

继承抽象方法的子类必须重写该方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该方法,否则,从最初的父类到最终的子类都不能用来实例化对象
如果 Salary 类继承了 Employee 类,那么它必须实现 computePay() 方法:

4.规定

  • 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象
  • 抽象类中不一定包含抽象方法,但是有抽象方法必定是抽象类
  • 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现,也就是方法的具体功能
  • 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法
  • 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类

6.思考:如果抽象父类中都是普通方法,那为什么要定义成抽象类?

不要想得太复杂,因为抽象类不可以被实例化,如果不想让别人创建本类对象就可以把普通类定义成抽象类
但是你告诉人家不就好了吗?大家都是一个团队的,你创建一个普通父类跟别人说好了不要实例化它,人家还非要跟你对着干,就非要实例化不成?
我也曾经这么想过,觉得这个问题很蠢,这个答案也很蠢,这个答案压根就不该被问出来,关键是被问出来,答案还那么无聊
但是或许可以换一个思路,你觉得如果一个抽象父类中都是普通方法只是不想被别人实例化没有什么意义,那你不用就好了,你不用它也不会影响到你,它只是提供了更多的可能性,任何一种可能性都是有意义的

【JAVA SE】第八章 异常处理与抽象类相关推荐

  1. 初始Java Java SE 包,继承,组合,多态,抽象类,接口

    目录 包 1.静态导入 2.将类放到包中 3.包的访问权限控制 继承 基本语法: 注意事项: protected 关键字 final 关键字 组合 多态 向上转型: 动态绑定 方法重写 重写的规则 : ...

  2. JAVA SE学习day_07:异常处理、TCP通信

    一.异常中常见的方法 public static void main(String[] args) {System.out.println("程序开始了");try {String ...

  3. java se7 变化_[转] Java se 7新特性研究(二)

    今天主要研究Java se 7中异常处理的新功能.从今天开始正在将jdk7的说法改为java se 7跟oracle官网的一致 一.新增了try-with-resource 异常声明 在JDK7中只要 ...

  4. 零基础入门 自学 JAVA SE 基础篇(九)instanceof final 开闭原则 多态 抽象(abstract)方法与抽象类 接口(interface)

    JAVA SE自学 基础篇 多态 instanceof final 开闭原则 多态 抽象(abstract)方法与抽象类 接口(interface) 目标 父类型与子类型之间的转换及instanceo ...

  5. 零基础学JAVA]Java SE基础部分-01. Java发展及JDK配置

    1.课程名称:Java发展及JDK配置 本季介绍了JAVA的发展过程,包括JDK的发展历程,path路径的配置和classpath的配置及作用.并简单讲解了一个简单的JAVA程序,并通过此程序讲解了J ...

  6. 面试必会系列 - 1.1 Java SE 基础

    本文已收录至 github,完整图文:https://github.com/HanquanHq/MD-Notes Java SE 基础 面向对象 Java 按值调用还是引用调用? 按值调用指方法接收调 ...

  7. Java复习总结(二)Java SE 面试题

    Java SE基础知识 目录 Java SE 1. 请你谈谈Java中是如何支持正则表达式操作的? 2. 请你简单描述一下正则表达式及其用途. 3. 请你比较一下Java和JavaSciprt? 4. ...

  8. Java SE 基础部分经典100道笔试题

    这100道题的范围主要是Java SE部分,正在学习Java SE的同学可以做一下,巩固一下学习的知识,也比较适合准备面试的同学复习使用.本题有90道选择题和10道填空题,选择题大部分是单选题,少数为 ...

  9. JAVA SE学习笔记(七):终章:Java程序设计基础笔记(全10万字)

    Java程序设计入门 ​                                              copyright © 2020 by 宇智波Akali 目录 文章目录 第1章 J ...

最新文章

  1. 深入掌握JMS(一):JMS基础
  2. Spark MLlib
  3. Mac电脑如何输入command(⌘)、option(⌥)、shift(⇧)等特殊符号
  4. java读mysql增量_在Java中检索MySQL自动增量
  5. 8、不与最大值相同的数字之和
  6. linux java 终端命令大全_在java中执行linux终端命令?
  7. 数论基本定理及应用(三)
  8. Atitit. Api 设计 原则 ---归一化
  9. laravel框架操作数据库
  10. 【手把手教你】搭建神经网络(3D点云分类)
  11. win7用友u8安装教程_win7系统安装用友u8的方法 win7用友u8安装教程
  12. 高斯分布估计子的性能与克拉默劳下界的讨论
  13. 计算机与模拟实验的论文,作业模拟论文,关于森林作业的计算机模拟相关参考文献资料-免费论文范文...
  14. 电脑常识——屏幕亮度无法调节
  15. uniapp设置输入框金额效果demo(整理)
  16. 10.Go复合类型-切片
  17. MindMapper使用技巧分享
  18. MySQL攻略 -DAO和增删改查通用方法-BasicDAO的认知与实现、案例练习
  19. Unity3D中第三人称视角的镜头跟随和目标锁定
  20. 费斯托FB33阀岛简介

热门文章

  1. 浪潮服务器开启远程管理,浪潮服务器远程管理
  2. 京东抢购机器人_戴森、科沃斯、SKG...超多大牌低价秒杀!京东电器等你来
  3. dedecms php5.4 无法退出后台,DedeCMS 织梦在 Windows 的 PHP5.4 环境下登录后台空白的解决办法...
  4. 一加7pro保存的录音文件在哪一个文件夹?
  5. 网页Object标签 遮盖DIV标签解决方法
  6. LightOJ 1013 LCS+记忆化搜索
  7. maven简单工具命令
  8. 图片圆角边框自适应宽高(深夜原创)
  9. 电芯容量在前期循环中容量增加_锂离子电池容量“跳水”背后元凶找到了!——非均匀压力...
  10. 深入Istio架构和功能--理解数据面/控制面/流量管理/安全/可观察性