文章目录
(一)错误的分类
通常程序中的错误可分为三种类型:
1.编译错误:编译器能够检测到的错误,一般是语法错误。此时不能将源代码(.java)编译成可执行的字节码文件(.class)。
2.运行错误:程序运行时产生的错误,例如被0除、数组下标越界等等。
3.逻辑错误:实际结果与所期结果不同,这是机器本身无法检测的,需要程序员对运行结果及程序逻辑进行分析才能发现,逻辑错误可能会导致运行结果错误,有时也可能会导致运行错误。
(二)异常处理机制的概念
1.异常又称为例外,是特殊的运行错误对象。
2.程序在运行期间可能会遇到一些非预期的或不正常的错误,这些错误将会阻止程序的正常运行。
3.当发生异常时,系统中需要有一个相应的机制来处理它,确保程序可以继续正常运行,而不会产生死机、死循环或其他对操作系统的损害,从而保证整个程序运行的安全性。这就是异常处理机制。
4.应用Java的异常处理机制,在程序出现异常时,程序员可以捕获该异常,并进行相应的处理。
(三)异常和错误的区别
1.在Java中有两种类型的错误:Exception类(异常)和Error类(错误)。
2.Exception类是所有异常类的父类,包括了一些由程序本身及环境所产生的错误。应用Java的异常处理机制,异常(Exception)可以被捕获并进行相应的处理。例如数组下标越界、被0除等等。
3.Error类则包括了一些较少发生的内部系统错误。这些错误都是严重的错误,用户程序无法进行处理,只能通知使用者关闭程序。例如内存不足、虚拟机内部错误等等。
4.Exception类和Error类都是Throwable类的子类。
注意:一个方法一次只能出现一个异常。到异常处会结束程序运行。
(四)异常的介绍
(只有RunTimeException才可以不用显示的throws出来,即在方法头部写出,其他的异常则必须显示的抛出)
(1)检查异常(受检异常、已检查异常)
该异常不可避免,必须进行异常处理,否则无法通过编译。
- 非检查型异常以外的异常称为检查型异常。
- 方法中可能抛出的检查型异常必须在方法定义部分使用throws语句进行声明。
- 对于任何方法,如果它调用的方法会抛出一个检查型异常,那么调用者就必须捕获该异常或者也在方法定义部分使用throws语句声明会抛出该类型的异常。
- 如果未捕获该异常也未使用throws声明会发生编译错误。
public static void main(String args[]){
Class.forName("oracle.jdbc.driver.OracleDriver");
}
(2)非检查异常(运行异常、非受检异常)(RuntimeException)
该异常可以避免,不必强制处理,编译时不会报错,但在运行时会出现异常,所以叫运行异常。比如除0。
- 所有继承自RuntimeException的异常称为非检查型异常。
- 编译器对非检查型异常不做编译检查。
(1.)常见的非检查异常
- ArithmeticException:算术异常
public static void main(String args[]){
int i = SystemIn.nextInt();
System.out.println(10/i);
}
- ArrayIndexOutOfBoundException:数组下标越界异常
public static void main(String args[]){
String[] s = new String[3];
System.out.println(s[4]);
}
- NullPointerException:空指针异常,对象为空时,仍然可以通过编译,在引用对象方法时出现异常。
public static void main(String[] args) {
String name = null;
System.out.println(name.hashCode());
}
-ClassCastException:类型转换异常
public static void main(String[] args) {
Object o = new Integer(10);
String s = (String)o;
}
-NumberException:数据格式异常
(五)throw 关键字
作用:通过构式可以手动抛一个异常。
格式:throw 异常对象。
- 当遇到关键字“throw”时就抛出一个例外
- 将控制转移到相关的“catch”块中处理
- 如果产生异常的方法没有处理该异常的“catch”语句块,则继续将该异常往外抛
- 退出当前方法并转向上一级调用此方法的方法的“catch”语句块,若始终没有“catch”块来处理该异常则由运行系统处理
public static void main(String[] args) {
//代码 1
throw new NullPointerException();
//代码 2
}
(六)异常的传递
沿方法调用链反向传递
main()
m1()
m2() throw new NullPointerException();
(七)异常处理
(1)throws:声明式异常处理,
- 可以一次声明多个异常,每个异常之间用逗号隔开即可。如果多个异常有公共父类,可以用公共父类替换。
(2)try…catch:捕获异常处理。
-
可以处理多个异常,一个try块可以对应多个catch块(catch的嵌套使用)
-
发生的异常类型与catch中异常的类型相同则可以被捕获
-
发生的异常类型是catch中异常的类型的子类也可以被捕获
-
如果try部分的代码没有发生错误,catch块的代码不会执行
-
如果一段代码肯定不会发生异常,则可以不用try{}catch{}块。
-
嵌套时内层的catch捕捉的异常类型比外层异常的父类更具体。
muti-catch(多重catch),一个try可以使用多个catch语句块 -
按从上到下顺序捕获(注意排列顺序)
多个catch块中,如果异常类型没有父子关系,则catch块的顺序可以随意,如果有父子关系,则子类catch块必须写在前面,父类catch块写在后面 -
对catch做一个小解释,catch语块块只有try语句块捕获到异常时才会执行,并且会寻找对应的catch异常块执行若存在多个符合异常的话都会执行,但可能会出逻辑上的错误,在书写代码时尽量避免,
try{
//可能出现异常的地方
}catch(NullpointerException e1){
//出现异常才会执行
}catch(ClassCastException e2){//在异常代码块重复时可以通过这种方法替换catch(FileNotFoundException | EOFException e)
//也可使用异常的父类替换
//异常代码块
}
(八)try…catch…finally异常处理方式
==finally中的内容肯定执行 ==
public static void m2(){
try{
//代码1
throw new FileNotFoundException();
//代码2
}catch(FileNotFoundException e){
//异常处理代码
}finally{
//肯定执行的内容
}
}
在一些情况下finally也可能不执行这里两种可能。
原文出处:https://www.cnblogs.com/lanxuezaipiao/p/3440471.html
(1)try语句没有被执行到,如在try语句之前就返回了,这样finally语句就不会执行,这也说明了finally语句被执行的必要而非充分条件是:相应的try语句一定被执行到。
(2)在try块中有System.exit(0);这样的语句,System.exit(0);是终止Java虚拟机JVM的,连JVM都停止了,所有都结束了,当然finally语句也不会被执行到。
还有finally与return的关系
1. finally语句是在try的return语句执行之后,return返回之前执行。
public static void main(String[] args) {
System.out.println(test1());
}
public static int test1() {
int b = 20;
try {
System.out.println("try block");
return b += 80;
}
catch (Exception e) {
System.out.println("catch block");
}
finally {
System.out.println("finally block");
if (b > 25) {
System.out.println("b>25, b = " + b);
}
}
return b;
}
输出结果:
try block
finally block
b>25, b = 100
100
-------------------例子二---------------------
public class FinallyTest1 {
public static void main(String[] args) {
System.out.println(test11());
}
public static String test11() {
try {
System.out.println("try block");
return test12();
} finally {
System.out.println("finally block");
}
}
public static String test12() {
System.out.println("return statement");
return "after return";
}
}
输出结果:
try block
return statement
finally block
after return
说明try中的return语句先执行了但并没有立即返回,等到finally执行结束后再
2.finally块中的return语句会覆盖try块中的return返回。
public class FinallyTest2 {
public static void main(String[] args) {
System.out.println(test2());
}
public static int test2() {
int b = 20;
try {
System.out.println("try block");
return b += 80;
} catch (Exception e) {
System.out.println("catch block");
} finally {
System.out.println("finally block");
if (b > 25) {
System.out.println("b>25, b = " + b);
}
return 200;
}
// return b;
}
}
输出结果:
try block
finally block
b>25, b = 100
200
这说明finally里的return直接返回了,就不管try中是否还有返回语句,这里还有个小细节需要注意,finally里加上return过后,finally外面的return b就变成不可到达语句了,也就是永远不能被执行到,所以需要注释掉否则编译器报错。
3.如果finally语句中没有return语句覆盖返回值,那么原来的返回值可能因为finally里的修改而改
测试1:
public static void main(String[] args) {
System.out.println(test3());
}
public static int test3() {
int b = 20;
try {
System.out.println("try block");
return b += 80;
} catch (Exception e) {
System.out.println("catch block");
} finally {
System.out.println("finally block");
if (b > 25) {
System.out.println("b>25, b = " + b);
}
b = 150;
}
return 2000;
}
运行结果:
try block
finally block
b>25, b = 100
100
测试2:
public class FinallyTest6
{
public static void main(String[] args) {
System.out.println(getMap().get("KEY").toString());
}
public static Map<String, String> getMap() {
Map<String, String> map = new HashMap<String, String>();
map.put("KEY", "INIT");
try {
map.put("KEY", "TRY");
return map;
}
catch (Exception e) {
map.put("KEY", "CATCH");
}
finally {
map.put("KEY", "FINALLY");
map = null;
}
return map;
}
}
运行结果:
FINALLY
············未完成
(九)方法覆盖中的异常
子类的覆盖方法不能比父类的被覆盖方法抛出更多的异常
子类重写父类方法时,重写的方法不能不被重写的方法抛出更多更大的异常,只能抛出其异常子类或者相干的异常,(可以是多个子类用逗号隔开),(不相干的或者不抛出都可以。)
(十)message属性与异常追踪
- Exception.printStackTrace()会打印所有异常信息到控制台。
- Exception类中定义了一个message属性,并提供了一个。
- 使用getMessage()方法获得message属性的值,同时提供了一个构造方法对其属性赋值Exception,会保存(只)异常字符串,但是需要手动打印。
public void test3()
{
try {
System.out.println("------test3------");
throw new NullPointerException("这是空指针异常");
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
System.out.println("------test3------");
}
输出结果:
------test3------
这是空指针异常//这是getMessage()打印出来的.
java.lang.NullPointerException: 这是空指针异常//这是printStackTrace()打印的异常.
at com.Exception.Demo2.test3(Demo2.java:36)
at com.Exception.Demo2.test2(Demo2.java:29)
at com.Exception.Demo2.test1(Demo2.java:23)
at com.Exception.Demo2.test(Demo2.java:17)
at com.Exception.Demo2.main(Demo2.java:10)
------test3------
(十一)自定义异常
条件:需要继承Exception或子类RuntimeException ,编写构造方法传递给对应父类。
第一步建立一个异常的子类,
然后传递一个字符串参数给含参的构造方法。
即可然后在测试类中通过 throw new这个对应构造方法的类即可。
被继承的两个父类:
- extends Exception 检测异常 当产生时需要异常处理
- extends RuntimeException 运行异常 不强制处理
public class FlowException extends Exception {
public static void main(String[] args) throws FlowException {
test();
}
public static void test() throws FlowException{
throw new FlowException("这里出现的了异常");
}
public FlowException(){
}
public FlowException(String msg){
super(msg);
}
}
运行结果:
Exception in thread "main" com.Exception.FlowException: 这里出现了异常
at com.Exception.FlowException.test(FlowException.java:7)
at com.Exception.FlowException.main(FlowException.java:3)
这里出一个测试题方便理解:
题目:1.设计一个异常类FlowException,该类继承自Exception,getMsg()方法返回错误信息。(格式:id的值:msg的值)
2.定义一个异常测试类ExceptionTest
定义一个方法test,该方法接收2个整型的参数num1和num2,如果num1 - num2 <= 0,则抛出异常FlowException,错误信息为“404:Under Value Err”;如果num1 - num2 >= 1000,则抛出异常FlowException,错误信息为”405:Over Value Err”
在main方法中通过键盘输入2个数,测试方法test并显示错误信息。
public class FlowException extends Exception {//这里如果继承RuntimeException在测试类的方法中可以不进行异常声明
FlowException(){
}
public FlowException(String msg) {
super(msg);
}
public String getMsg() {
return super.getMessage();
}
}
public class ExceptionTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
System.out.println("请输入第一个数字");
int x = sc.nextInt();
System.out.println("请输入第二个数字");
int y = sc.nextInt();
try { //此处进行异常捕获,所以main可以不进行异常声明,但在其他抛出异常的方法中需要声明异常。
test(x,y);
} catch (FlowException e) {
// e.printStackTrace();
System.err.println(e.getMsg());
}
}
public static void test(int x,int y) throws FlowException {
if(x-y<=0) {
throw new FlowException("404:Under Value Err");
}
if(x-y>=1000) {
throw new FlowException("405:Over Value Err");
}
}
}