任何好的编程语言和编程人员都不会忽视对异常的处理,作为比较热门的面向对象编程的语言——Java,异常处理机制自然也是其重要特色之一。
一般解释异常,都将其说为:编程中的错误。但是,实际上这个错误可是非常频繁,有多种,如:编译错误、运行错误(具体上又分为:系统运行错误和逻辑运行错误,这个什么系统运行错误,自己倒很少将其算作是编程中的错误了,之前。)
在JAVA中,异常是一个类,它继承自Throwable类。每个异常类代表了运行错误( 注意:是运行错误)。异常类中包含了该运行错误的信息及处理错误的方法等内容。
Java的异常处理机制:
每当Java程序运行过程中发生一个可识别的运行错误时,(即该错误有一个异常类与之相对应时),系统都会产生一个相应的该异常类的对象,( 注意:叫做产生一个异常类对象。)即产生一个异常。
一旦一个异常对象产生了,系统中就一定要有相应的机制来处理它,确保不会产生死机、死循环或其他对操作系统的损害,从而保证了整个程序运行的安全性
异常和异常类:
Error:由Java虚拟机生成并抛出,Java程序不做处理.
Runtime Exception(被0除等系统错误,数组下标超范围):由系统检测, 用户的Java 程序可不做处理,系统将它们交给缺省的异常处理程序(注意:有缺省的异常处理).
Exception(程序中的问题,可预知的): Java编译器要求Java程序必须捕获或声明所有的非运行时异常
用户自己产生异常
Exception类
构造函数:
public Exception();
public Exception(String s);可以接受字符串参数传入的信息,该信息通常是对该异常所对应的错误的描述。
Exception类从父亲Throwable那里还继承了若干方法,其中常用的有:
1)public String toString();
toString()方法返回描述当前Exception 类信息的字符串。
2)public void printStackTrace();
printStackTrace()方法没有返回值,它的功能是完成一个打印操作,在当前的标准输出(一般就是屏幕)上打印输出当前例外对象的堆栈使用轨迹,也即程序先后调用执行了哪些对象或类的哪些方法,使得运行过程中产生了这个例外对象。
系统定义的运行异常
这些子类有些是系统事先定义好并包含在Java类库中的,称为系统定义的运行异常
用户自定义的异常
对于某个应用所特有的运行错误,则需要编程人员根据程序的特殊逻辑在用户程序里自己创建用户自定义的异常类和异常对象
用户定义的异常通常采用Exception作为异常类的父类
但是这里有一个还未弄懂的问题:发生一个错误,系统怎么知道是可识别的?又是怎么产生相应异常类对象?异常类对象怎么就知道去用相应方法解决?每个处理相应异常的异常类对象难道就只有一个异常处理方法?————————————原来,由用户自定义的异常,是通过语句throw才抛出异常。
创建用户自定义异常时,一般需要完成如下的工作:
1)声明一个新的异常类,使之以Exception类或其他某个已经存在的系统异常类或用户异常为父类。
2)为新的异常类定义属性和方法,或重载父类的属性和方法,使这些属性和方法能够体现该类所对应的错误的信息。
异常的抛出
Java程序在运行时如果引发了一个可以识别的错误,就会产生一个与该错误相对应的异常类的对象,把这个过程叫做异常的抛出,
实际是相应异常类对象的实例的抛出。
根据异常类的不同,抛出异常的方式有系统自动抛出和用户抛出两种:
1、系统自动抛出
所用的系统定义的运行错误异常都是由系统自动地抛出
2、用户抛出
用户自定义的异常不可能依靠系统自动抛出,而必须由用户用Java语句抛出,在Java语句中,throw语句用来明确地抛出一个“异常”
用throw语句抛出的格式
返回类型 方法名(参数列表) throws 要抛出的异常类名列表{
…
throw 异常类实例;//注意这里
…
}
注意:
1)一般当程序中满足某个条件时才抛出异常;
往往把throw语句放在if语句的if分支中,
if(I>100)
throw (new MyException());
2)含有throw的语句的方法,应当在方法头定义中增加如下的部分:
throws 要抛出的异常类名列表
这样做主要是为了通知欲调用这个方法的上层方法,准备接受和处理它在运行中可能会抛出的异常
如果方法中的throw语句不止一个,则应该在方法头throws中列出所有可能的异常
3)Java语言要求所有用throws关键字声明的类和用throw抛出的对象必须是Throwable类或其子类。如果你试图抛出一个不是可抛出(Throwable)对象,Java编译器将会报错
异常处理:
主要考虑如何捕捉异常,捕捉异常后程序如何跳转,以及如何写异常处理语句
1.try…catch…finally 块
1)try
在try语句的{ }中包含了可能会抛出一个或多个异常的一段程序代码
这些代码实际上指定了它后面的catch块所能捕捉的异常的范围。
Java程序运行到try块中的语句时如果产生了异常,就不再继续执行该try块中其他的语句,而是直接进入catch块中寻找第一个与之匹配的异常类型并进行处理。
2)catch块
catch语句的参数类似于方法的定义,包括一个异常类型和一个异常对象。
异常类型必须为Throwable类的子类,它指明了catch语句所处理的异常类型;
异常对象则由Java运行时系统在try所指定的程序代码块中抛出的大括号中包含异常对象的处理的方法代码。
catch语句可以有多个,分别处理不同类型的异常。
Java运行时系统从上到下分别对每个catch语句处理的异常类型进行检测,直到找到与之相匹配的catch语句为止。
这里,类型匹配指catch中的异常类型与生成的异常对象的类型完全一致或者是异常对象的父类,因此,catch语句的排序顺序应该是从特殊到一般。(考虑为什么?)
3)finally块
finally语句可以说是为异常处理事件提供的一个清理机制,一般用来关闭文件或释放其他系统资源
在try-catch-finally语句中可以没有finally部分的语句。
如果没有finally部分,则当try指定的程序代码抛出一个异常时,其他的程序代码就不会被执行;
如果存在finally部分,则不论try块中是否发生了异常,是否执行过catch部分的语句,都要执行finally部分的语句。
可见,finally部分的语句为异常处理提供了一个统一的出口。
多异常处理
一个try块可能会产生多种不同的异常,如果希望能采取不同的方法来处理这些例外,就需要使用多异常处理机制。
多异常处理是通过在一个try块后面定义若干个catch块来实现的,每个catch块用来接收和处理一种特定的异常对象
通过catch块的参数来判断一个异常对象是否应为本catch块接收和处理的异常。
被哪个catch块获取,根据异常对象与catch块的异常参数的匹配情况:当它们满足下面三个条件的任何一个时,认为异常对象和参数匹配:
1)异常对象与参数属于相同的例外类。
2)异常对象属于参数例外类的子类。
3)异常对象实现了参数所定义的接口。
如果try块产生的异常对象被第一个catch块所接收,则程序的流程将直接跳转到这个catch语句块中,语句块执行完后就退出当前方法,try块中尚未执行的语句和其他的catch块将被忽略
如果try块产生的异常对象与第一个catch块不匹配,系统将自动转到第二个catch块进行匹配,如果第二个仍不匹配,就转向第三个、第四个……直到找到一个可以接收该异常对象的catch块,完成流程的跳转。
如果try块产生的异常对象被第一个catch块所接收,则程序的流程将直接跳转到这个catch语句块中,语句块执行完后就退出当前方法,try块中尚未执行的语句和其他的catch块将被忽略
如果try块产生的异常对象与第一个catch块不匹配,系统将自动转到第二个catch块进行匹配,如果第二个仍不匹配,就转向第三个、第四个……直到找到一个可以接收该异常对象的catch块,完成流程的跳转。
若try块中所有语句的执行都没有引发异常,则所有的catch块都会被忽略而不执行。
注意:
1)catch块中的语句应根据异常的不同执行不同的操作
一般解释异常,都将其说为:编程中的错误。但是,实际上这个错误可是非常频繁,有多种,如:编译错误、运行错误(具体上又分为:系统运行错误和逻辑运行错误,这个什么系统运行错误,自己倒很少将其算作是编程中的错误了,之前。)
在JAVA中,异常是一个类,它继承自Throwable类。每个异常类代表了运行错误( 注意:是运行错误)。异常类中包含了该运行错误的信息及处理错误的方法等内容。
Java的异常处理机制:
每当Java程序运行过程中发生一个可识别的运行错误时,(即该错误有一个异常类与之相对应时),系统都会产生一个相应的该异常类的对象,( 注意:叫做产生一个异常类对象。)即产生一个异常。
一旦一个异常对象产生了,系统中就一定要有相应的机制来处理它,确保不会产生死机、死循环或其他对操作系统的损害,从而保证了整个程序运行的安全性
异常和异常类:
Error:由Java虚拟机生成并抛出,Java程序不做处理.
Runtime Exception(被0除等系统错误,数组下标超范围):由系统检测, 用户的Java 程序可不做处理,系统将它们交给缺省的异常处理程序(注意:有缺省的异常处理).
Exception(程序中的问题,可预知的): Java编译器要求Java程序必须捕获或声明所有的非运行时异常
用户自己产生异常
Exception类
构造函数:
public Exception();
public Exception(String s);可以接受字符串参数传入的信息,该信息通常是对该异常所对应的错误的描述。
Exception类从父亲Throwable那里还继承了若干方法,其中常用的有:
1)public String toString();
toString()方法返回描述当前Exception 类信息的字符串。
2)public void printStackTrace();
printStackTrace()方法没有返回值,它的功能是完成一个打印操作,在当前的标准输出(一般就是屏幕)上打印输出当前例外对象的堆栈使用轨迹,也即程序先后调用执行了哪些对象或类的哪些方法,使得运行过程中产生了这个例外对象。
系统定义的运行异常
这些子类有些是系统事先定义好并包含在Java类库中的,称为系统定义的运行异常
用户自定义的异常
对于某个应用所特有的运行错误,则需要编程人员根据程序的特殊逻辑在用户程序里自己创建用户自定义的异常类和异常对象
用户定义的异常通常采用Exception作为异常类的父类
但是这里有一个还未弄懂的问题:发生一个错误,系统怎么知道是可识别的?又是怎么产生相应异常类对象?异常类对象怎么就知道去用相应方法解决?每个处理相应异常的异常类对象难道就只有一个异常处理方法?————————————原来,由用户自定义的异常,是通过语句throw才抛出异常。
创建用户自定义异常时,一般需要完成如下的工作:
1)声明一个新的异常类,使之以Exception类或其他某个已经存在的系统异常类或用户异常为父类。
2)为新的异常类定义属性和方法,或重载父类的属性和方法,使这些属性和方法能够体现该类所对应的错误的信息。
异常的抛出
Java程序在运行时如果引发了一个可以识别的错误,就会产生一个与该错误相对应的异常类的对象,把这个过程叫做异常的抛出,
实际是相应异常类对象的实例的抛出。
根据异常类的不同,抛出异常的方式有系统自动抛出和用户抛出两种:
1、系统自动抛出
所用的系统定义的运行错误异常都是由系统自动地抛出
2、用户抛出
用户自定义的异常不可能依靠系统自动抛出,而必须由用户用Java语句抛出,在Java语句中,throw语句用来明确地抛出一个“异常”
用throw语句抛出的格式
返回类型 方法名(参数列表) throws 要抛出的异常类名列表{
…
throw 异常类实例;//注意这里
…
}
注意:
1)一般当程序中满足某个条件时才抛出异常;
往往把throw语句放在if语句的if分支中,
if(I>100)
throw (new MyException());
2)含有throw的语句的方法,应当在方法头定义中增加如下的部分:
throws 要抛出的异常类名列表
这样做主要是为了通知欲调用这个方法的上层方法,准备接受和处理它在运行中可能会抛出的异常
如果方法中的throw语句不止一个,则应该在方法头throws中列出所有可能的异常
3)Java语言要求所有用throws关键字声明的类和用throw抛出的对象必须是Throwable类或其子类。如果你试图抛出一个不是可抛出(Throwable)对象,Java编译器将会报错
异常处理:
主要考虑如何捕捉异常,捕捉异常后程序如何跳转,以及如何写异常处理语句
1.try…catch…finally 块
1)try
在try语句的{ }中包含了可能会抛出一个或多个异常的一段程序代码
这些代码实际上指定了它后面的catch块所能捕捉的异常的范围。
Java程序运行到try块中的语句时如果产生了异常,就不再继续执行该try块中其他的语句,而是直接进入catch块中寻找第一个与之匹配的异常类型并进行处理。
2)catch块
catch语句的参数类似于方法的定义,包括一个异常类型和一个异常对象。
异常类型必须为Throwable类的子类,它指明了catch语句所处理的异常类型;
异常对象则由Java运行时系统在try所指定的程序代码块中抛出的大括号中包含异常对象的处理的方法代码。
catch语句可以有多个,分别处理不同类型的异常。
Java运行时系统从上到下分别对每个catch语句处理的异常类型进行检测,直到找到与之相匹配的catch语句为止。
这里,类型匹配指catch中的异常类型与生成的异常对象的类型完全一致或者是异常对象的父类,因此,catch语句的排序顺序应该是从特殊到一般。(考虑为什么?)
3)finally块
finally语句可以说是为异常处理事件提供的一个清理机制,一般用来关闭文件或释放其他系统资源
在try-catch-finally语句中可以没有finally部分的语句。
如果没有finally部分,则当try指定的程序代码抛出一个异常时,其他的程序代码就不会被执行;
如果存在finally部分,则不论try块中是否发生了异常,是否执行过catch部分的语句,都要执行finally部分的语句。
可见,finally部分的语句为异常处理提供了一个统一的出口。
多异常处理
一个try块可能会产生多种不同的异常,如果希望能采取不同的方法来处理这些例外,就需要使用多异常处理机制。
多异常处理是通过在一个try块后面定义若干个catch块来实现的,每个catch块用来接收和处理一种特定的异常对象
通过catch块的参数来判断一个异常对象是否应为本catch块接收和处理的异常。
被哪个catch块获取,根据异常对象与catch块的异常参数的匹配情况:当它们满足下面三个条件的任何一个时,认为异常对象和参数匹配:
1)异常对象与参数属于相同的例外类。
2)异常对象属于参数例外类的子类。
3)异常对象实现了参数所定义的接口。
如果try块产生的异常对象被第一个catch块所接收,则程序的流程将直接跳转到这个catch语句块中,语句块执行完后就退出当前方法,try块中尚未执行的语句和其他的catch块将被忽略
如果try块产生的异常对象与第一个catch块不匹配,系统将自动转到第二个catch块进行匹配,如果第二个仍不匹配,就转向第三个、第四个……直到找到一个可以接收该异常对象的catch块,完成流程的跳转。
如果try块产生的异常对象被第一个catch块所接收,则程序的流程将直接跳转到这个catch语句块中,语句块执行完后就退出当前方法,try块中尚未执行的语句和其他的catch块将被忽略
如果try块产生的异常对象与第一个catch块不匹配,系统将自动转到第二个catch块进行匹配,如果第二个仍不匹配,就转向第三个、第四个……直到找到一个可以接收该异常对象的catch块,完成流程的跳转。
若try块中所有语句的执行都没有引发异常,则所有的catch块都会被忽略而不执行。
注意:
1)catch块中的语句应根据异常的不同执行不同的操作
所以在处理多异常时应注意认真设计各catch块的排列顺序。一般地处理较具体和较常见的异常的catch块应放在前面,而可以与多种异常相匹配的catch块应放在较后的位置。
/*尝试用户运行错误的异常处理:
问题是这样的,当输入的用户工资初值少于800则是错误的,当然工资的变化如果超过20%,则也是错误的
*/
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
public class UserExceptionApplet extends Applet implements ActionListener{
Label prompt1=new Label("请输入雇员姓名和工资初值:");
Label prompt2=new Label("请输入欲修改的工资");
TextField name,isal,nsal;
String msg;
Employee Emp;
Button okBtn=new Button("OK");
Button cancelBtn=new Button("Cancel");
public void init(){
name=new TextField(5);
isal=new TextField(5);
nsal=new TextField(5);
add(prompt1);
add(name);
add(isal);
add(prompt2);
add(nsal);
add(okBtn);
okBtn.addActionListener(this);
cancelBtn.addActionListener(this);
add(cancelBtn);
}
public void paint(Graphics g){
g.drawString(msg,0,80);
}
public void CreateEmp(String empName,double sa){
try{
Emp=new Employee(empName,sa);
msg=new String(Emp.toString());
}
catch(IllegalSalaryException ise){
msg=new String(ise.toString());
}
}
public void ChangeEmpSal(double changeSal){
try{
Emp.setEmpSalary(changeSal);
msg=new String(Emp.toString());
}
catch(IllegalSalaryException illSal){
msg=new String(illSal.toString());
}
catch(IllegalSalaryChangeException illSalChange){
msg=new String(Emp.toString()+illSalChange.toString());
}
}
public void actionPerformed(ActionEvent e){
String empName;
double empSal,changeSal;
Object obj=e.getSource();
if(obj==okBtn){
empName=new String(name.getText());
if(empName==null){
msg=new String("请先输入雇员姓名工资并创建之");
}
if(nsal.getText()==null){
empSal=Double.valueOf(isal.getText()).doubleValue();
CreateEmp(empName,empSal);
}
else{
changeSal=Double.valueOf(nsal.getText()).doubleValue();
ChangeEmpSal(changeSal);
}
}
if(obj==cancelBtn){
naem.setText("");
isal.setText("");
nsal.setText("");
}
repaint();
}
}
class Employee{
String m_EmpName;
double m_EmpSalary;
Employee(String name,double initsalary)throws IllegalSalaryException{
m_EmpName=name;//看这里有问题没,参考代码为m_EmpName=new String(name);
if(initsalary<800){
throw(new IllegalSalaryException(this,initsalary));//throw语句
}
m_EmpSalary=initsalary;
}
public String getEmpName(){
return m_EmpName;
}
public double getEmpSalary(){
return m_EmpSalary;
}
public boolean setEmpSalary(double newSal) throws IllegalSalaryException,IllegalSalaryChangeException{
if(newSal<800)
throw(new IllegalSalaryException(this,newSal));
else if(getEmpSalary()==0.0){
m_EmpSalary=newSal;
return true;
}
else if(Math.abs(newSal-getEmpSalary())/getEmpSalary()>=0.2)
throw(new IllegalSalaryChangeException(this,newSal-getEmpSalary()));
else{
m_EmpSalary=newSal;
return true;
}
}
public String toString(){
String s;
s="姓名:"+m_EmpName+"工资: "+m_EmpSalary;
return s;
}
}
class IllegalSalaryException extends Exception{
private Employee m_ConcernedEmp;
private double m_IllegalSalary;
IllegalSalaryException(Employee emp,double isal){
super("工资低于最低工资");
m_ConcernedEmp=emp;
m_IllegalSalary=isal;
}
public String toString(){
String s;
s="为雇员提供的工资不合法:雇员:"+m_ConcernedEmp.getEmpName()+"非法工资:"+m_IllegalSalary+"低于最低工资数额800元";
return s;
}
}
class IllegalSalaryChangeException extends Exception{
private Employee m_ConcernedEmp;
private double m_IllegalSalaryChange;
IllegalSalaryChangeException(Employee emp,double csal){
super("工资变动太大");
m_ConcernedEmp=emp;
m_IllegalSalaryChange=csal;
}
public String toString(){
String s;
s="为雇员提供的工资变动不合法:雇员:"+m_ConcernedEmp.getEmpName()+"非法变动工资变化:"+m_IllegalSalaryChange+"高于原工资的20%";
return s;
}
}