欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

81.Java异常概述

程序员文章站 2022-03-02 11:24:36
...

没有人敢保证说它写的程序永远没有错。即使写的程序没有错,也不要指望你的用户能按照你的意愿来执行程序,比如,你不要指望用户的网络是畅通的,你不要指望你需要的某个文件一定会在它应该存在的位置,你不要期待用户一定会在需要数字的地方输入数字而不是字母甚至更奇怪的符号。

 

作为程序设计人员,你应该尽可能多的去想象可能会碰到的错误、尽可能糟糕地去考虑用户不规范的输入、尽可能的取考虑运行环境的恶劣,所谓“有备无患”,不要等到出了问题再去补救。


但是,且慢,我们需要针对每一个错误都自己去编写错误处理程序么?在一些编程语言中,答案也许是的,或许也没有这么糟糕,而对于Java,答案是“否”,基本上,Java将这些都替你考虑到了。


在Java中,有两个类用于处理两种方式用于处理错误:Error和Exception。


Error处理的是Java运行环境中的内部错误或者硬件问题,比如,内存资源不足等。对于这种类型错误,程序基本上是无能为力的,除了退出运行外别无它法。

 

而对于Exception,它处理的是因为程序设计的瑕疵而引起的问题或者外在的输入等引起的一般性问题,例如:在开平方的方法中输入了一个负数,对一个为空的对象进行操作以及网络不稳定引起的读取网络问题等,都可以通过Exception来处理。

 

在Java中,异常对象分为两大类:Error和Exception。

 

Error类和Exception类都是Throwable类的子类。Error类只有四个子类:AWTError、LinkageError、VirtualMachineError以及ThreadDeath,正如前面所述,它处理的是Java运行系统中的内部错误以及资源耗尽等情况,这种情况是程序员所无法掌握的,我们只有通知用户并安全退出程序的运行。

 

而Exception的子类就很多了,可以大致将它的子类分为三类:有关I/O的IOException,有关运行时的异常RuntimeException以及其他的异常。RuntimeExcepiton异常是由于程序编写过程中的不周全的代码引起的,而IOException是由于IO系统出现阻塞等原因引起的。

 

我们来看一个异常的例子。

 

源文件:ExceptionExam.java
public class ExceptionExam {
 public static void main(String args[]) {
  int a, b;
  double c;
  a = Integer.parseInt(args[0]);
  b = Integer.parseInt(args[1]);

  c = a / b;
  System.out.println(a + "/" + b + " = " + c);
 }
}


在这个程序中,我们从控制台接收两个参数,并对它们进行一个除法运算,你或许会期望用户总是能输入两个正确的操作数,但是,现实总不是你想象中的那么美好:也许,用户会输入一个0来作为除数,这个时候,情况就不如你想象的那样了,我们来

 

想象一下用户输入如下的命令来执行这个程序:
java ExceptionExam 12 0


这个时候,我们就会从控制台得到一个异常信息,如下:
Exception in thread "main" java.lang.ArithmeticException: / by zero
        at ExceptionExam.main(ExceptionExam.java:10)


在这个例子中,可能会因为用户输入的数据而引起数学计算的错误,它是一个RuntimeException(ArithmeticException是RuntimeException的子类)。

我们再来看一个例子。

public class ExceptionExam1 {
 public static void main(String args[]) {
  java.util.Date d = null;
  System.out.println(d.getTime());
 }
}


这个程序很简单,就是试图从一个Date对象中得到时间。运行这个程序,将会在控制台上得到如下的信息:
Exception in thread "main" java.lang.NullPointerException
        at ExceptionExam1.main(ExceptionExam1.java:6)

 

在这个程序中,我们没有给引用变量一个真正的Date对象,因此,当试图通过这个变量调用Date对象中的方法时,将会出现一个“空指针引用”的异常。它也是一个RuntimeException(NullPointerException也是RuntimeException的子类)。
当然,上面这两个程序是很容易避免这种错误的,我们完全可以在调用它的方法之前判断,如判断除数参数是否为0,若为0,就不进行除法运算;或者首先判断对象是否为null,再决定是否调用这个对象的方法。

 

这种异常通常都是运行时异常,它们都有一个共同的父类RuntimeException。虽然我们可以通过程序的控制来将这种异常扼杀在程序设计阶段,但在一些更复杂的应用中,我们常常无法避免类似这种情况的出现。这个时候就可能需要使用Java的异常处理机制来对这些情况进行处理了。


还有一些异常,我们无法通过程序的控制来避免它的出现,比如,我们不太可能将客户可能输入的url地址作一个准确的判断,以决定是否进行网络连接等,这个时候,我们就必须使用Java的异常处理机制来进行处理。如果没有使用Java的异常处理机制,在编译程序的时候,编译器会强制让你必须进行处理。

 

例如,我们来看下面这个程序。

 

import java.io.*;

public class ExceptionExam2 {
 public static void main(String[] args) {
  FileInputStream fis = new FileInputStream("c:/a.txt");
  // 其他处理代码
 }
}


在这个程序中,用到了流的概念。在这里先简单将相关的FileInptStream这个类简单介绍一下。FileInputStream类通常用于从指定的文件中读取数据,它的方法read()的功能是每次从相应的(本地为ASCII码编码格式)文件中读取一个字节,并转换成0~255之间的int型整数返回,到达文件末尾时则返回-1。


这个程序试图建立一个到指定文件的输入流,但是,我们无法总是保证文件存在或者文件是可读的,因此,在这个地方应该对文件是否存在的异常进行处理,如果没有进行处理,就象上面这段代码,那么,在编译的时候,编译器将会报告一个错误:
ExceptionExam2.java:7: unreported exception java.io.FileNotFoundException; must
be caught or declared to be thrown
                FileInputStream fis = new FileInputStream("c:/a.txt");
                                      ^
1 error


因此,对于一些异常,是必须进行处理的,否则编译将会出错。比如,可以将上面的代码改成如下:

import java.io.*;

public class ExceptionExam2 {
 public static void main(String[] args) {
  try {
   FileInputStream fis = new FileInputStream("c:/a.txt");
   // 其他处理代码
  } catch (FileNotFoundException e) {
   // Exception handling code here
  }
 }
}


Java语言规范中将Error的子类和RuntimeException的子类都称为“未检查(unchecked)的异常”,而将其他的异常均称为“已检查(checked)的异常”。

 

对于RuntimeException,都是可以通过程序中编写代码来将这种异常避免的,比如,首先对操作数进行判断,就可以避免出现被0除这种数学计算异常,或首先对数组的长度进行判断,就不会出现数组索引(下标)越界异常了。但我们没有办法通过编写代码来避免其他“已检查异常”的出现,比如,我们没办法编写一段代码用来判断用户是否输入了合法的或者真实存在的URL。如果程序中有可能出现“已检查异常”而没有对它进行处理,则在编译的时候会发生错误。