异常的类别
超类 Throwable:所有异常由 Throwable 类继承而来,它分为
- Error类:描述 java 运行时系统的内部错误和资源耗尽错误
- Exception类:又分解为
- RunTimeException类:由程序错误导致的异常
- 其它异常:程序本身没问题,但出现像I/O错误等问题导致的异常
通常来讲,如果程序出现 RunTimeException 异常,那么就一定是你的问题,如发生错误的类型转换、数组访问越界、访问空指针等
异常的声明
方法应该在其首部声明可能抛出的异常,如某个类的构造器声明
class Demo
{
public FileInputStream(String s)throws FileNotFoundException
}
当发生问题时,构造器不会初始化一个新的 FileInputStream 对象,而是抛出一个 FileNotFoundException 类对象,然后系统开始搜索异常处理器进行处理。
在什么情况下应该抛出异常:
- 调用一个抛出已检查异常的方法,如,FileInputStream 构造器
- 程序运行过程中发现错误,并利用 throw 语句抛出一个已检查异常
- 程序本身出现错误,如,a[-1]=0 会抛出一个 ArrayIndexOutOfBoundsException 的未检查的异常
- java虚拟机和运行时库出现的内部错误
出现前两种情况,必须告诉调用该方法的程序员有可能抛出异常,在方法的首部声明;
如果一个方法有可能 抛出多个异常,那么应该列出所有的异常类,并以逗号隔开
对于 java 的内部错误(从 Error 继承的类)不需要声明,因为我们对它们没有任何控制能力
class ImageDemo
{
...
public Image loadImage(String s)throws FileNotFoundException,EOFException
{
...
}
}
注意:C++中,如果没有给出 throw 说明,函数可能会抛出任何异常,但在 java 中,没有 throws 声明符的方法不能抛出任何已检查异常。
如何抛出异常
对于一个已存在的异常类,通常的做法:
- 找到了一个合适的异常类
- 创建这个类的一个对象
- 将对象抛出
当方法抛出异常后,该方法就不可能返回到调用者。
String readData(Scanner in)throws EOFException
{
...
while(...)
{
if(!in.hasNext())// EOF encounterd
{
if(n < len)
{
String gs = "Content-length:" + len + ",Received:" + n;
throw new EOFException(s);
}
}
...
}
return s;
}
注意:throws 和 throw 的区别
- throws 使用在函数上(小括号与大括号之间),throw 使用在函数内。
- throws 后面接的是异常类,且可以有多个,throw 后面接的是异常对象。
自定义异常类
有时,在某个特殊的场景中,需要根据需求人为的设计出符合当前要求的异常类,来帮助解释问题。此时我们需要做的只是定义一个继承于 Exception 类或者其子类的类,如定义一个继承于 IOException 的类。
\color{red}{当程序发生异常,无法继续运算时,可以让自定义异常继承于 RunTimeException 类。}
class FileFormatException extends IOException
{// 习惯上,所有继承的异常类都支持一个默认的构造器和一个带有详细描述信息的构造器
public FileFormatException()// 构造的对象没有详细的描述信息
{
super();
}
public FileFormatException(String s)// 带有详细的描述信息
{
super(s);
// 父类中已经把异常信息测操作完成了,子类在构造时,将异常信息通过 super 传递给父类,就能直接由 getMessage 方法获取自定义异常的信息
}
}
String readData(BufferReader in)throws FileFormatException
{
...
while(...)
{
if(ch == -1)// EOF encounterd
{
if(n < len)
throw new FileFormatException(...);
}
...
}
return gs;
}
特殊的子类异常
对于 Exception 类,如果没有在函数上声明,那么在函数内抛出异常时会编译失败。但是Exception 中有一个特殊的子类异常 RunTimeException(运行时异常)。
\color{red}{如果在函数内抛出 RunTimeException 异常或其子类异常,函数上可以不用声明,编译也可通过。}
\color{red}{如果在函数上声明了RunTimeException 异常或其子类异常,调用者可以不用进行处理,编译也能通过。}
之所以会这样,是因为不需要让调用者来处理该异常,当异常发生,虚拟机希望程序停止,因为运行时出现了无法继续运算的情况,虚拟机希望程序停止后,程序员能对代码进行修正。
class Demo
{
int div(int a, int b)throws ArithmeticException// ①
{
return a/b;
}
/*int div(int a, int b)// ②
{
if(b==0)
throw new ArithmeticException("除零啦");
return a/b;
}*/
/*int div(int a, int b)// ③
{
if(b==0)
throw new Exception("除零啦");
return a/b;
}*/
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
int x = d.div(4,0);
System.out.println("x=" + x);
System.out.println("END!");
}
}
/*
特殊情况是 ①或②单独出现时都能编译成功,③,编译会失败。
*/