黑马程序员-java异常

来源:互联网 发布:路由器网络延时 编辑:程序博客网 时间:2024/04/26 07:55

------- android培训、java培训、期待与您交流! ----------

java异常处理机制
java根据异常的不同,将异常分为错误和异常两种
1.错误:错误是指程序在执行过程中所遇到的硬件或操作系统的错误,如内存溢出还有虚拟机错误之类的,程序本身不能处理错误,得靠外界干预,否则无法正常运行
2.异常:是指java可以处理的错误,如数组下标越界啊,除数为0啊,java提供了强大的异常处理机制,使程序能够自动的捕获异常,并调整程序运行的方向,使程序可以继续运行
在java的异常处理机制中,有两个最常用的类,分别是Exception和Error,它们位于java.lang包中,是Throwable的子类,关系如下图


Error:指的是jvm的错误,一般在程序中无法处理
Exception:指的是程序中出现的问题,一般在代码块中加入try..catch语句进行处理,或者将它抛出交给调用者处理(抛出异常不代表程序停止 RuntimeException 及其子类除外)
RuntimeException :此异常与此异常的子类的对象在程序中被抛出后,无需对他进行捕捉,直接交给jvm处理,届时程序将停止工作。
异常这一章的几个关键字
throw:此关键字可以将一个异常对象抛出。
throws:这个关键字用在方法声明的后面,标示此方法可能会抛出异常,告诉调用者,调用者就会对这个对象进行捕捉或者继续向上抛出。

finally:此关键字用在try......catch语句之后,不管前面有没有捕捉到异常,finally后面的代码块一定会被执行,一般用在关闭数据库的各项连接上。

看下面几段代码

1.try...catch语句的使用

实例1

public class TestException {public static void main(String[] args) {String str1 = "8";String str2 = "2";TestException.getResult(str1, str2);//调用静态方法System.out.println("ok");}//定义一个静态方法,用于取得两数之商public static int getResult(String a, String b) {int value = 0;/*int num1 = Integer.parseInt(a);int num2 = Integer.parseInt(b);value = num1/num2;*/try {//将字符串转为int值int num1 = Integer.parseInt(a);int num2 = Integer.parseInt(b);value = num1/num2; System.out.println("运算结果是" + value);} catch (ArithmeticException e) {System.out.println(e.getMessage());} catch (NumberFormatException e) {//打印出错信息System.out.println(e.getMessage()); //打印出错的类型和信息System.out.println(e.toString());//打印出错的栈堆信息,就是方法的层次调用e.printStackTrace();} catch (Exception e) {e.printStackTrace();} finally {System.out.println("我一定会出现的");}return value;}}

运行总结
由这段代码可看出,因为被除数的数值是不确定的,有可能是0,也有可能说起他的数,如若在getResult方法中没有对可能出现的ArithmeticException异常进行捕捉,程序会立刻中断并报错,最后的ok将不会被打印,但如果在方法中用try...catch 语句进行捕捉,异常捕捉后打印相关的信息,最后ok还是会被打印出来的,在finally后面代码块中的语句一定会被执行,“我一定会出现这句话” 不管有没有捕捉到异常都会出现。一个方法中抛出异常并且被本方法中的 catch 语句捉住的话,处理相关完错误信息,之后程序将无法回到错误产生的下一行代码执行,只能跳到 try..catch 语句外执行,直到方法结束,然后从调用这个方法的下一行继续执行,直到程序结束,还有,在 finally 语句中如果抛出异常,这个异常无法被上面的 catch 捕捉住,只有在方法的后面用 throws 关键字声明该方法可能会抛出某个异常才行

在此需要注意以下几点
1.
try...catch语句中最后的finally代码块可写可不写,如若不写,编译不会报错,如果写了,就一定会被执行。
2.
在 catch 语句中,如果有 Exception 子类的情况下,一定要把子类异常写在上面,如若不然,所有异常都会被Exception捉住,后面的catch语句就相当于废话了。
3.
如果在getResult方法中没有对异常进行捕捉,而是对他进行抛出,交给调用者处理,这样也是可以的,后面的代码将对此进行体现。

在Throwable类中封装了几个常用的方法,常用的有toString(),将打印错误的类型和信息,toMessage()打印的是出错的信息,而printStackTrace()将会打印出错的栈堆信息(方法的层次调用)。

2.自定义异常类


在java中,一切皆是对象,所谓的异常也就是一个用异常类创建出来的对象,java提供了非常多的异常对象,同时java也允许程序员创建属于自己的异常类,如果想自己定义一个异常的类,
一般是使这个类继承Exception(当然继承RuntimeException也是可行的)


看下面一段代码
实例2

public class TestMyException {public static void main(String[] args) {try {TestMyException.getResult(8, -2);} catch (MyException e) {System.out.println(e.toString());}}public static int getResult(int a, int b) throws MyException {if(a <= 0 || b <= 0) {throw new MyException("输入的数据不符合规则"); } else {return a*b;}//return a*b;}}class MyException extends Exception {public String toString() {return super.toString();}//String message = "信息错误";MyException(String message) {//调用父类构造方法super(message);}}

这段代码自定义异常是继承Exception类的,继承了 Exception 的异常在某一方法中被抛出后,方法后必须更上throws 自定义异常类名,否则编译无法通过,与此相对的如果继承 RuntimeException就不需要进行throws声明,在调用者中也不需要用try...catch语句进行捕捉,直接交给jvm进行处理即可,jvm调用e.printStackTrace()后程序将停止.并不是说一旦抛出 RuntimeException 及其子类的异常对象程序就会停止,你如果像 Exception 一样捕捉他们的话,程序还是会继续运行的.

3.关于标示异常方法的重写

实例3

/* 该程序用于测试如何重写抛出异常的方法 */public class TestMyException {public static void main(String[] args) {try {System.out.println(new Rectangle(5,-8).getArea());} catch (MyException2 e) {System.out.println(e.toString());}}}//自定义一个异常class MyException extends Exception {MyException(String message) {super(message);}}//自定义另一个异常,继承上一个异常class MyException2 extends MyException {String message;MyException2(String message) {//调用父类构造方法super(message);this.message = message;}public String toString() {return message;}} //定义一个几何图形抽象类,abstract class Shape {//声明一个计算面积的抽象方法 public abstract int getArea() throws MyException2;}//定义一个矩形类class Rectangle extends Shape {private int len, width;public Rectangle(int len,int width) {this.len = len;this.width = width;}//重写父类的抽象方法//注意:覆写父类的方法时只能抛出父类的异常以及父类异常的子类,不能抛出比父类异常还大的异常,如果父类方法没有抛出异常,子类也不能抛,只能用try catch语句捉住public int getArea() throws MyException2 {if(len <= 0 | width <= 0) {throw new MyException2("输入的数据错误");} else {return len*width;}}}

4.使用 finally 关键字应该注意的地方,众说周知,finally 中的语句总是能得到执行,这时候应该注意一个问题,就是try语句的嵌套,很可能会套出问题来,致使你的异常对象被吞了
实例4

import java.io.FileNotFoundException;import java.io.IOException;public class TestFinally {public static void main(String[] args) {TestFinally.runTest();}public static void runTest() throws RuntimeException {try {try {throw new RuntimeException("asdasd");} finally {throw new NullPointerException("sdfsadfasdfasdf");}} catch(RuntimeException e) {System.out.println(e.toString());}}}

这个后面的异常对象明显将前面的覆盖了,前面的异常对象被吞了

多线程与 RuntimeException 
抛出 RuntimeException ,不捕获的话,主线程将停止,当前程序将关闭,这是单线程,如果是多线程的话,有一条线程抛出了 RuntimeException 异常,是整个程序停止,还是那个线程停止
让我们来试试
实例4

public class TestThreadException {public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {public void run() {while(true) {try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName());}}}, "线程1");Thread t2 = new Thread(new Runnable() {int i = 1;public void run() {while(true) {try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}if(i == 150) {//try {throw new RuntimeException();/*} catch(RuntimeException e) {}*/}System.out.println(Thread.currentThread().getName() + "----" + i);i++;}}}, "线程2");t1.start();t2.start();}}

这个程序很有意思,线程2如果执行到到第150次循环会抛出一个 RuntimeException 异常,该线程停止,但是程序还没有停止,线程1还在循环执行,说明一个程序如果抛出 RuntimeException异常,程序不一定会停止,只是抛出异常的线程会停止,这个一定要注意.

原创粉丝点击