异常处理模块

java异常处理

学完java基础课程,一些知识点掌握的不是很牢固,运用博客的形式每天解决掉几个难点,巩固基础

问题一

主要的异常都有哪些?分为什么类型?为什么会报这种异常?如何处理?

  1. 从程序执行过程,看编译时异常和运行时异常

在这里插入图片描述

编译时异常:执行javac.exe命名时,可能出现的异常
运行时异常:执行java.exe命名时,出现的异常

注:.class文件(字节码)很重要
①写好一个HelloWorld.java文件之后,javac HelloWorld.java 运行生成字节码文件
java Hello直接打印出结果
②如果删掉.java文件,留下.class文件,只需java Hello既可运行出结果

  1. 异常分类

    要理解Java异常处理是如何工作的,需要掌握以下三种类型的异常:
    编译时异常:最具代表的编译时异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。

    运行时异常: 运行时异常是可能被程序员避免的异常。与编译时异常相反,运行时异常可以在编译时被忽略。

    错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。 从图中可以看出所有异常类型都是内置类Throwable的子类,因而Throwable在异常类的层次结构的顶层。

    详细异常分类见下图

在这里插入图片描述

public class ExceptionTest {		
    //*********************以下是编译时异常*******************************	
    @Test	public void test7(){		
        File file = new File("hello.txt");		
        FileInputStream fis = new FileInputStream(file);//FileNotFoundException				
        byte[] buffer = new byte[1024];		
        int len;		
        while((len = fis.read(buffer)) != -1){//IOException			
            String str = new String(buffer,0,len);			
            System.out.print(str);		}				
        fis.close();//IOException	
    }		
    //*********************以下是运行时异常*******************************		
    //InputMismatchException	
    @Test	public void test6(){		
        Scanner scanner = new Scanner(System.in);		
        int num = scanner.nextInt();				
        System.out.println(num);		
        scanner.close();	}		
    
    //ArithmeticException	
    @Test	public void test5(){		
        int i = 10;		
        int j = 0;		
        System.out.println(i / j);	
    }		
    
    //NumberFormatException	
    @Test	public void test4(){		
        String str = "123a";		
        int num = Integer.parseInt(str);		
        System.out.println(num);	
    }		
    
    //ClassCastException	
    @Test	public void test3(){
        //		Object obj = new Date();
        //		String str = (String) obj;			
    }		
    
    //NullPointerException	
    @Test	
    public void test2(){		
        //1.
        //		int[] arr = new int[10];
        //		arr = null;//		
        System.out.println(arr[0]);				
        //2.
        //		int[][] arr1 = new int[4][];
        //		System.out.println(arr1[0][0]);				
        
        //3.		
        Date date = null;		
        System.out.println(date.toString());	
    }
    
    //ArrayIndexOutOfBoundsException

	@Test

	public void test1(){

		int[] arr = new int[10];

		System.out.println(arr[10]);

	}

问题二

异常的处理(抓抛模型)

  1. 过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。

    并将此对象抛出。一旦抛出对象以后,其后的代码就不再执行。

    关于异常对象的产生:① 系统自动生成的异常对象② 手动的生成一个异常对象,并抛出(throw)

  2. 过程二:“抓”:可以理解为异常的处理方式:① try-catch-finally ② throws

异常处理方式一:try-catch-finally

使用说明:
try{

  • //可能出现异常的代码

  • }catch(异常类型1 变量名1){

  • //处理异常的方式1

  • }catch(异常类型2 变量名2){

  • //处理异常的方式2

  • }catch(异常类型3 变量名3){

  • //处理异常的方式3

  • }

  • finally{

  • //一定会执行的代码

  • }


  • 说明:

    1. finally是可选的。
    1. 使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
    1. 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没写finally的情况。继续执行其后的代码)
    1. catch中的异常类型如果没子父类关系,则谁声明在上,谁声明在下无所谓。
  • catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错

    1. 常用的异常对象处理的方式: ① String getMessage() ② printStackTrace()
    1. 在try结构中声明的变量,在出了try结构以后,就不能再被调用
    1. try-catch-finally结构可以嵌套

体会:

使用try-catch-finally处理编译时异常,使得程序在编译时就不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。

开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。针对于编译时异常,我们说一定要考虑异常的处理。


finally的再说明:

  • 1.finally是可选的
  • 2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中return语句,catch中return语句等情况。
  • 3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。

异常处理方式二:"throws + 异常类型"写在方法的声明处。

指明此方法执行时,可能会抛出的异常类型。一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!

对比两种处理方式

try-catch-finally:真正的将异常给处理掉了。
throws的方式只是将异常抛给了方法的调用者。并没真正将异常处理掉。

开发中如何选择try-catch-finally 还是使用throws?

  1. 如果父类中被重写的方法没throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中异常,必须使用try-catch-finally方式处理。

  2. 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。

    注意:子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型

问题三

异常处理的重要性(意义)

对于一个应用系统,设计一套良好的异常处理体系是很重要的,因为它对于程序的后期维护有十分重要的意义。应用系统的异常处理体系或者说框架,应该在系统设计初期的时候就考虑清楚,这样可以避免开发阶段由于异常处理体系的混乱而引起的不必要的重构工作。异常处理框架在项目开发中是十分重要的。对于项目依赖的框架及工具所产生的异常,需要事先进行调查,对于项目自身的code可能产生的异常也需要事先调查,最后对这些异常进行分类处理。处理的方法根据异常类型的不同而有所差别。

问题四

如何自定义异常类?

    1. 继承于现的异常结构:RuntimeException 、Exception
    1. 提供全局常量:serialVersionUID
    1. 提供重载的构造器
public class MyException extends Exception{
	
	static final long serialVersionUID = -7034897193246939L;
	
	public MyException(){
		
	}
	
	public MyException(String msg){
		super(msg);
	}
}

面试题

异常处理常见面试题

  1. Error和Exception的区别?

    error表示系统级的错误,是java运行环境内部错误或者硬件问题,不能指望程序来处理这样的问题,除了退出运行外别无选择,它是Java虚拟机抛出的。Error类对象由 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。例如,Java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止;还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在Java中,错误通常是使用Error的子类描述。

    exception 表示程序需要捕捉、需要处理的异常,是由与程序设计的不完善而出现的问题,程序必须处理的问题。Exception:在Exception分支中有一个重要的子类RuntimeException(运行时异常),该类型的异常自动为你所编写的程序定义ArrayIndexOutOfBoundsException(数组下标越界)、NullPointerException(空指针异常)、ArithmeticException(算术异常)、MissingResourceException(丢失资源)、ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;而RuntimeException之外的异常我们统称为非运行时异常,类型上属于Exception类及其子类,从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

  2. throw 和 throws区别?

    throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
    throws 属于异常处理的一种方式,声明在方法的声明处。

    两者都是消极处理异常的方式,只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。

    class Student{
    	
    	private int id;
    	
    	public void regist(int id) throws Exception {
    		if(id > 0){
    			this.id = id;
    		}else{
    			//手动抛出异常对象
    //			throw new RuntimeException("您输入的数据非法!");
    //			throw new Exception("您输入的数据非法!");
    			throw new MyException("不能输入负数");
    
    		}
    		
    	}
    
  3. 区分final\finally\finalize

    final是表示最终的,是一个修饰符,修饰类时表示不能被继承,修饰方法时表示不能被子类重写,修饰属性和局部变量时表示值不能被修改,是个常量。

    finally是表示最终块,是异常处理的一部分,和try…catch一起使用,不管是否发生异常都要执行的代码放在finally块中。

    finalize是表示最终方法,是java.lang.Object类的一个方法,在对象被垃圾回收时调用。

  4. 运行时异常和一般异常有何不同

    Java提供了两类主要的异常:runtimeException和checkedException
    一般异常(checkedException)主要是指IO异常、SQL异常等。对于这种异常,JVM要求我们必须对其进行cathc处理,所以,面对这种异常,不管我们是否愿 意,都是要写一大堆的catch块去处理可能出现的异常。
    运行时异常(runtimeException)我们一般不处理,当出现这类异常的时候程序会由虚拟机接管。比如,我们从来没有去处理过NullPointerException,而且 这个异常还是最常见的异常之一。 出现运行时异常的时候,程序会将异常一直向上抛,一直抛到遇到处理代码,如果没有catch块进行处理,到了最上层,如果是多线程就有Thread.run()抛出,如果不是多线程那么就由main.run()抛出。抛出之后,如果是线程,那么该线程也就终止了,如果是主程序,那么该程序也就终止了。 其实运行时异常的也是继承自Exception,也可以用catch块对其处理,只是我们一般不处理罢了,也就是说,如果不对运行时异常进行catch处理,那么结果不
    是线程退出就是主程序终止。 如果不想终止,那么我们就必须捕获所有可能出现的运行时异常。如果程序中出现了异常数据,但是它不影响下面的程序执行,那么我们就该在catch块里面将异 常数据舍弃,然后记录日志。如果,它影响到了下面的程序运行,那么还是程序退出比较好些。

  5. Java中异常处理机制的原理

    Java通过面向对象的方式对异常进行处理,Java把异常按照不同的类型进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它都是Throwable
    或其子类的实例。当一个方法出现异常后就会抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并对异常进行处理。Java的
    异常处理是通过5个关键词来实现的:try catch throw throws finally。
    一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throws),我们可以通过它的类型来捕捉它,或最后由缺省处理器来处理它(finally)。
    try:用来指定一块预防所有异常的程序
    catch:紧跟在try后面,用来捕获异常
    throw:用来明确的抛出一个异常
    throws:用来标明一个成员函数可能抛出的各种异常
    finally:确保一段代码无论发生什么异常都会被执行的一段代码。

  6. 你平时在项目中是怎样对异常进行处理的。

    (1)尽量避免出现runtimeException 。例如对于可能出现空指针的代码,带使用对象之前一定要判断一下该对象是否为空,必要的时候对runtimeException 也进行try catch处理。
    (2)进行try catch处理的时候要在catch代码块中对异常信息进行记录,通过调用异常类的相关方法获取到异常的相关信息,返回到web端,不仅要给用户良好 的用户体验,也要能帮助程序员良好的定位异常出现的位置及原因。例如,以前做的一个项目,程序遇到异常页面会显示一个图片告诉用户哪些操作导致程序出现 了什么异常,同时图片上有一个按钮用来点击展示异常的详细信息给程序员看的。

  7. final、finally、finalize的区别

    (1)、final用于声明变量、方法和类的,分别表示变量值不可变,方法不可覆盖,类不可以继承
    (2)、finally是异常处理中的一个关键字,表示finally{}里面的代码一定要执行
    (3)、finalize是Object类的一个方法,在垃圾回收的时候会调用被回收对象的此方法。

  8. try()里面有一个return语句,那么后面的finally{}里面的code会不会被执行,什么时候执行,是在return前还是return后?

    会在return中间执行!

    try 中的 return 语句调用的函数先于 finally 中调用的函数执行,也就是说 return 语句先执行,finally 语句后执行,但 return 并不是让函数马上返回,而是 return 语句执行后,将把返回结果放置进函数栈中,此时函数并不是马上返回,它要执行 finally 语句后才真正开始返回!但此时会出现两种情况:

    ①、如果finally中也有return,则会直接返回并终止程序,函数栈中的return不会被完成!;

    ②、如果finally中没有return,则在执行完finally中的代码之后,会将函数栈中的try中的return的内容返回并终止程序;

    public class Test1 {
        public static void main(String[] args) {
            try {
                System.out.println(new Test1().testname());
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
        public String testname() throws Exception {
            String t = "";
    
            try {
                t = "try";
                System.out.println("try");
                return t;
            } catch (Exception e) {
                // result = "catch";
                t = "catch";
                return t;
            } finally {
    
                System.out.println("finally");
                // return t = "finally";
            }
        }
    }
    /*
    打印结果如下:
    try
    finally
    try
    将finally中的注释放开,打印结果如下:
    try
    finally
    finally
    */

    练习题

    public static void main(String[] args) {	
        int test = test(3,5);		
        System.out.println(test);	
    } 	
    public static int test(int x, int y){		
        int result = x;		
        try{			
            if(x<0 || y<0){				
                return 0;			
            }			
            result = x + y;			
            return result;		
        }finally{			
            result = x - y;		
        }	
    }
    /*
    (1)finally块中的代码是无论try中是否发生异常,也无论catch是否可以捕获异常,也不管try和catch中是否有return语句,都会执行的部分。 
    (2)如果finally中有return语句,那么try...catch...finally结构一定从finally中的return回去。 
    (3)如果finally中没有return语句,那么try...catch...finally结构才会从try或catch中的return回去,但是finally值中代码不会影响最终的返回值。
    public class Test02 {	
        {		
            System.out.println("a");	
        }	
        static{		
            System.out.println("b");	
        }	
        Test03(){		
            System.out.println("c");	
        }	
        public static String getOut(){		
            try{			
                return "1";		
            }catch(Exception e){			
                return "2";		
            }finally{			
                return "3";		
            }	
        }	
        public static void main(String[] args) {		
            System.out.println(getOut());	
        }
    }
    
    /*
    (1)main()执行之前,需要先完成所在类的初始化。  
    (2)类初始化由两部分代码组成:①静态变量的显式赋值代码②静态代码块。 
    (3)没有创建对象,不会执行实例初始化相关的代码。  
    (4)finally块中的代码是无论try中是否发生异常,也无论catch是否可以捕获异常, 也不管try和catch中是否有return语 句,都会执行的部分。  
    (5)如果finally中有return语句,那么try...ca.tch...finally结构一定从finally中的return回去。
    

3";
}
}
public static void main(String[] args) {
System.out.println(getOut());
}
}

/*
(1)main()执行之前,需要先完成所在类的初始化。
(2)类初始化由两部分代码组成:①静态变量的显式赋值代码②静态代码块。
(3)没有创建对象,不会执行实例初始化相关的代码。
(4)finally块中的代码是无论try中是否发生异常,也无论catch是否可以捕获异常, 也不管try和catch中是否有return语 句,都会执行的部分。
(5)如果finally中有return语句,那么try…ca.tch…finally结构一定从finally中的return回去。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值