Java中的异常
分为Error和Exception,其中Error是指Java虚拟机无法解决的严重问题,Exception是指外在因素导致的一般性问题,这里我们讨论的是Exception。
常见的Exception
有空指针异常、数组下标越界异常、类型转换异常、算术异常。
Exception的处理
抓抛模型:抓是指抓住上一步抛出的异常;抛是指代码执行出现异常,将会在异常处生成一个对应的异常类对象,并将此对象跑出给方法的调用者。三种方式来处理,第一种时try-catch-finally(finally为可选),第二种是在方法定义的后面追加throws xxException,其中try-catch-finally是在此处真正处理掉异常的,以后如果再有方法调用这个异常代码块直接调用即可,不用做处理;而throws xxException形式的处理方式,只是单纯的抛出异常,并未做实际处理,所以没有只要有方法调用这个异常代码块还是要做异常处理,因为这个异常代码处理异常的方式是将异常抛给方法的调用者了。第三种是自定义异常类,用关键字throw手动抛出,如下两个代码段:
Scanner is = new Scanner(System.in);
try {
int s = is.nextInt();
System.out.println(s);
} catch (InputMismatchException e) {
System.out.println("类型转换出错了!");
} finally {
System.out.print("我是finally");
}
再看
public class TestExceptionWithThrows {
//这里mian()方法接收到method2抛出的异常,需要对他进行处理,如果这里在采用throws的继续往上抛出的话
//那么异常将会抛给JVM来处理,但是可以看出throws这样的声明式的抛出异常的方式并不是处理异常最根本的方法,最根本的方法应该
//是try-catch这样的方式,而且如何在异常代码块已经执行了try-catch方式处理过后,那么调用它的方法将不用再最它的异常进行处理
//不想throws方式不断往上抛出,上一级还要不断的对下一级的异常进行处理
public static void main(String[] args){
try {
method2();
} catch (FileNotFoundException e) {
System.out.println(e.getMessage());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//这里method()方法将异常抛给了method2(),然后这里method2()也是采用throws的方式将异常往上一级抛出
public static void method2 () throws FileNotFoundException,IOException {
method();
}
//这里将异常抛给调用者
public static void method() throws FileNotFoundException,IOException {
FileInputStream fis = new FileInputStream(new File("hellow.text"));
int b;
while((b = fis.read())!=-1){
System.out.println((char)b);
}
fis.close();
}
}
其中finally中的代码无论是否发生异常,都肯定会执行的,而且注意他们的执行顺序是先执行try{}中的代码块,如何发生异常,那么直接跳到finlly{}的代码块,先执行finally中的代码块,然后再返回来处理异常,执行catch{}中的代码块,如果没有finally{}代码块,则先处理异常再执行后续代码,并且如果有多个异常,那么catch的时候,就会从上到下的寻找对应的异常处理方式,处理完退出catch块,并且这里注意多个catch()中的异常顺序从上到下必须是范围小的异常到范围大的异常。如
catch(FileNotFoundException e){
}catch(Exception e){
}
这里看一下综合代码
public class ReturnExceptionDemo {
static void methodA() {
try{
System.out.println("进入方法A");
//手动抛出一个异常
throw new RuntimeException("制造异常");
} finally {
System.out.println("调用A方法的finally");
}
}
static void methodB() {
try{
System.out.println("进入方法B");
return;
} finally {
System.out.println("调用B方法的finally");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
methodA();
} catch (Exception e) {
System.out.println(e.getMessage());
}
methodB();
}
}
它的运行结果为:
进入方法A
调用A方法的finally
制造异常
进入方法B
调用B方法的finally
这里需要注意的还有就是当try{}代码块中有return时,finally{}块中的代码还是会执行,执行后才会执行return,执行完return之后该方法将会被销毁。
这里除了手动抛出已存在的异常,我们还可以自定义异常,自定的异常一定要继承已有的异常类,如Exception或者RuntimeException,然后重写父类的构造方法,一般只要重写一下(String msg, Throwable)和(String msg)就可以了,其他都不需要动,如下:
//自定义的异常类,必须继承现有的异常类,提供几个重载的构造器即可
public class MyException extends RuntimeException {
private static final long serialVersionUID = 1L;
public MyException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public MyException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}
调用异常
/**
* 手动抛出异常,自定义异常的信息
* @author Weiguo Liu
*
*/
public class TestExceptionWithHand {
public static void main(String[] args) {
Circle c1 = new Circle(2.1);
Circle c2 = new Circle(2.1);
System.out.println(c1.compareTo(c2));
System.out.println(c1.compareTo(new String ("asda")));
}
}
class Circle {
private double radius;//圆的半径
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public Circle(double radius) {
super();
this.radius = radius;
}
public int compareTo(Object obj) {
if(this == obj) {
return 0;
}else if(obj instanceof Circle){
Circle c = (Circle)obj;
if(this.radius>c.radius){
return 1;
}else if(this.radius == c.radius){
return 0;
}else{
return -1;
}
}else{
//手动的抛出异常
throw new MyException("传入的类型有误!");
}
}
}