zoukankan      html  css  js  c++  java
  • JavaSE之异常处理

    在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美, 在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避 免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等。不同的语言中有自己的异常处理机制。因为在写代码中经常会出现异常,所以也把异常处理的内容归到Java基础中去介绍

    异常概述与异常体系结构

    异常概述

    /*
     * Error:
     * Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。
     * 
     * 一般不编写针对性的代码进行处理。
     * 
     * 
     */
    public class ErrorTest {
    	public static void main(String[] args) {
    		//1.栈溢出:java.lang.StackOverflowError
    //		main(args);
    		//2.堆溢出:java.lang.OutOfMemoryError 简称OOM
    		Integer[] arr = new Integer[1024*1024*1024];
    		
    	}
    }
    

    异常体系结构

    整个异常的体系结构是一种继承关系:

    /*
     * 一、异常体系结构
     * 
     * java.lang.Throwable
     * 		|-----java.lang.Error:一般不编写针对性的代码进行处理。
     * 		|-----java.lang.Exception:可以进行异常的处理
     * 			|------编译时异常(checked)
     * 					|-----IOException
     * 						|-----FileNotFoundException
     * 					|-----ClassNotFoundException
     * 			|------运行时异常(unchecked,RuntimeException)
     * 					|-----NullPointerException
     * 					|-----ArrayIndexOutOfBoundsException
     * 					|-----ClassCastException
     * 					|-----NumberFormatException
     * 					|-----InputMismatchException
     * 					|-----ArithmeticException
     * 
     * 
     * 
     * 面试题:常见的异常都有哪些?举例说明
     */
    

    运行时异常:

    • 是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序 员应该积极避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常。

    • 对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对

    程序的可读性和运行效率产生影响。

    编译时异常:

    • 是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一 般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。
    • 对于这类异常,如果程序不处理,可能会带来意想不到的结果

    编译时异常即在执行javac Java源文件编译的时候报的异常,不会生成相应的字节码文件

    运行时异常时执行java 字节码文件的时候报的异常

    常见异常

    import java.io.File;
    import java.io.FileInputStream;
    import java.util.Date;
    import java.util.Scanner;
    
    import org.junit.Test;
    
    /*
     * 一、异常体系结构
     * 
     * java.lang.Throwable
     * 		|-----java.lang.Error:一般不编写针对性的代码进行处理。
     * 		|-----java.lang.Exception:可以进行异常的处理
     * 			|------编译时异常(checked)
     * 					|-----IOException
     * 						|-----FileNotFoundException
     * 					|-----ClassNotFoundException
     * 			|------运行时异常(unchecked,RuntimeException)
     * 					|-----NullPointerException
     * 					|-----ArrayIndexOutOfBoundsException
     * 					|-----ClassCastException
     * 					|-----NumberFormatException
     * 					|-----InputMismatchException
     * 					|-----ArithmeticException
     * 
     * 
     * 
     * 面试题:常见的异常都有哪些?举例说明
     */
    public class ExceptionTest {
    	
    	//******************以下是运行时异常***************************
    	
    	//IndexOutOfBoundsException
    
    	@Test
    	public void test1(){
    		
    //		int[] arr = null;
    //		System.out.println(arr[3]);//空指针异常
    		
    		String str = "abc";
    		str = null;
    		System.out.println(str.charAt(0));
    		
    	}
        
    	//NullPointerException	
    	@Test
    	public void test2(){
    		//ArrayIndexOutOfBoundsException
    //		int[] arr = new int[10];
    //		System.out.println(arr[10]);
    		//StringIndexOutOfBoundsException
    		String str = "abc";//str底层就是一个char型数组
    		System.out.println(str.charAt(3));
    	}
    	
    	
        //ClassCastException 类型转换异常 
    	@Test
    	public void test3(){
    		Object obj = new Date();
    		String str = (String)obj;
    	}
        
        //NumberFormatException
    	@Test
    	public void test4(){
    		
    		String str = "123";
    		str = "abc";
    		int num = Integer.parseInt(str);
    		
    	}
        
        //InputMismatchException
    	@Test
    	public void test5(){
    		Scanner scanner = new Scanner(System.in);
    		int score = scanner.nextInt();
    		System.out.println(score);
    		
    		scanner.close();
    	}
        
        //ArithmeticException 算术异常
    	@Test
    	public void test6(){
    		int a = 10;
    		int b = 0;
    		System.out.println(a / b);
    	}
        
    	//******************以下是编译时异常***************************
    	@Test
    	public void test7(){
    //		File file = new File("hello.txt");
    //		FileInputStream fis = new FileInputStream(file);
    //		
    //		int data = fis.read();
    //		while(data != -1){
    //			System.out.print((char)data);
    //			data = fis.read();
    //		}
    //		
    //		fis.close();
    		
    	}
    }
    

    异常处理

    异常处理机制一:try-catch-finally

    代码来看:

    先简单的看try-catch结构:

    /*
     * 一、异常的处理:抓抛模型
     * 
     * 过程一:"抛":程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。
     *           并将此对象抛出。
     *           一旦抛出对象以后,其后的代码就不再执行。
     * 		
     * 		关于异常对象的产生:① 系统自动生成的异常对象
     * 					    ② 手动的生成一个异常对象,并抛出(throw)
     * 
     * 过程二:"抓":可以理解为异常的处理方式:① try-catch-finally  ② throws
     * 
     * 
     * 二、try-catch-finally的使用
     * 
     * try{
     * 		//可能出现异常的代码
     * 
     * }catch(异常类型1 变量名1){
     * 		//处理异常的方式1
     * }catch(异常类型2 变量名2){
     * 		//处理异常的方式2
     * }catch(异常类型3 变量名3){
     * 		//处理异常的方式3
     * }
     * ....
     * finally{
     * 		//一定会执行的代码
     * }
     * 
     * 说明:
     * 1. finally是可选的。
     * 2. 使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
     * 3. 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的
     *    try-catch结构(在没有写finally的情况)。继续执行其后的代码
     * 4. catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。
     *    catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错
     * 5. 常用的异常对象处理的方式: ① String  getMessage()    ② printStackTrace()
     * 6. 在try结构中声明的变量,再出了try结构以后,就不能再被调用
     * 7. try-catch-finally结构可以嵌套
     * 
     * 体会1:使用try-catch-finally处理编译时异常,使得程序在编译时就不再报错,但是运行时仍可能报错。
     *     相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
     *     
     * 体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。
     *      针对于编译时异常,我们说一定要考虑异常的处理。
     */
    public class ExceptionTest {
    	@Test
    	public void test1() {
    		String str = "123";
    		str = "abc";
    		//使用try将可能出现异常代码包装起来,在执行过程中
    		//一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
    		try {
    		int num = Integer.parseInt(str);
    		
    		System.out.println("hello---1");//上面抛出异常,这里不会执行
    		//一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。
    		//一旦处理完成,就跳出当前的try-catch结构(在没有写finally的情况)。继续执行其后的代码
    		}catch(NumberFormatException e ) {
    //			System.out.println("出现数制转换异常了,别慌~");
    			//处理异常对象 常用的方式 --输出错误信息:
    			//String getMessage() ---要用字符串类型去接收返回值
    			System.out.println(e.getMessage());
    			//printStackTrace() //void方法不用类型接收,错误信息更详细(包括那行出现问题了)
    //			e.printStackTrace();
    			
    		}catch(NullPointerException e) {
    			System.out.println("出现了空指针异常了,别慌~");
    			
    		// catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错
    		//因为进来后直接就会进入父类的catch结构,后面的没有执行的机会
    		}catch(Exception e) {
    			System.out.println("出现异常了,莫慌");
    		}
    		
    //		System.out.println(num);//报错,在try里面声明的变量出了结构后就不能再被调用
    		System.out.println("hello---2");
    	}
    
    }
    

    finally结构在上面其实是可选项,下面来看下他的使用

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    import org.junit.Test;
    
    /*
     * try-catch-finally中finally的使用:
     * 
     * 
     * 1.finally是可选的
     * 
     * 2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有return语句等情况。
     * 
     * 3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
     * 
     * 
     * 
     */
    public class FinallyTest {
    	@Test
    	public void test1(){
    		try{
    			int a = 10;
    			int b = 0;
    			System.out.println(a / b);
    			
    		}catch(ArithmeticException e){
    			e.printStackTrace();
    			
    //			int[] arr = new int[10];
    //			System.out.println(arr[10]);//catch中又出现异常,finally也会执行
    			
    		}catch(Exception e){
    			e.printStackTrace();
    		}
    //		System.out.println("我好帅啊!!!~~");
    		//这里发现结构体外的代码和finally在处理完异常后都会执行,这有啥区别?
    		//如果是在一个方法中try结构有了return 或catch中又出现异常了那么finally的作用就体现出来了:一定会被执行:
    		finally{
    			System.out.println("我好帅啊~~");
    		}
    		
    	}
    	//finally一定会被执行的例子---方法中return
    	@Test
    	public void testMethod(){
    		int num = method();
    		System.out.println(num);
    	}
    	
    	public int method(){
    		
    		try{
    			int[] arr = new int[10];
    			System.out.println(arr[10]);
    			return 1;
    		}catch(ArrayIndexOutOfBoundsException e){
    			e.printStackTrace();
    			return 2;
    		}finally{
    			System.out.println("我一定会被执行");
    			return 3;
    		}
    		
    		
    	}
        
        //try-catch-finally结构可以嵌套
    	@Test
    	public void test2(){
    		FileInputStream fis = null;
    		try {
    			File file = new File("hello1.txt");
    			fis = new FileInputStream(file);
    			
    			int data = fis.read();
    			while(data != -1){
    				System.out.print((char)data);
    				data = fis.read();
    			}
    			
    			
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}finally{
    			try {
    				if(fis != null)
    					fis.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    	
    

    上面可以看到,对于运行时异常,我们使用try-catch-finally好像处理的结果和自然报错的并没有什么不同(我们一般也会使用printStackTrace()打印错误信息),所以运行时异常一般去进行异常处理(可能发生问题的情况太多了,全都写显得代码臃肿的不像话)。而对于编译型异常我们不使用try-catch编译都过不去,所以try-catch-finally实际上是把编译异常推到运行时去了!

    记住一点:Java能自己捕获RuntimeException类和它的子类,并且编译也能通过!

    异常处理机制二:throws

    咱们直接写代码来体会:

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    /*
     * 异常处理的方式二:throws + 异常类型
     * 
     * 1. "throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
     *     一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常
     *     类型时,就会被抛出。异常代码后续的代码,就不再执行!
     *     
     * 2. 体会:try-catch-finally:真正的将异常给处理掉了。
     *        throws的方式只是将异常抛给了方法的调用者。  并没有真正将异常处理掉。  
     * 
     */
    public class ExceptionTest2 {
    	
    	
    	public static void main(String[] args){
    		//main方法再往上抛异常就不合适了,所以终究还是得在这里处理
    		try{
    			method2();
    			
    		}catch(IOException e){
    			e.printStackTrace();
    		}
    		
    //		method3();//已经处理好了异常这里调用就不用处理了
    		
    	}
    	
    	
    	public static void method3(){
    		try {
    			method2();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	
    	public static void method2() throws IOException{
    		//IOException是FileNotFoundException父类所以只写IOException也罩得住
    		method1();//method1的异常此时就抛到了method2去了
    	}
    	
    	//把异常抛给调用者
    	public static void method1() throws FileNotFoundException,IOException{
    		File file = new File("hello1.txt");
    		FileInputStream fis = new FileInputStream(file);
    		
    		int data = fis.read();
    		while(data != -1){
    			System.out.print((char)data);
    			data = fis.read();
    		}
    		
    		fis.close();
    		
    		System.out.println("hahaha!");
    	}
    	
    	
    }
    

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

    • 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理。
    • 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。

    手动抛出异常:throw

    关于异常对象的产生有两种方式:

    1. 系统自动生成的异常对象,如上面所学习的都是系统自动抛出的异常
    2. 手动的生成一个异常对象,并抛出(throw)

    要区分throwthrows:

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

    通过一个小例子来理解下throw手动抛出一个异常对象的作用:

    public class StudentTest {
    	
    	public static void main(String[] args) {
    		try {
    			Student s = new Student();
    			s.regist(-1001);
    			System.out.println(s);
    		} catch (Exception e) {
    //			e.printStackTrace();
    			System.out.println(e.getMessage());
    		}
    	}
    	
    }
    
    
    class Student{
    	
    	private int id;
    	
    	public void regist(int id) throws Exception {
    		if(id > 0){
    			this.id = id;
    		}else{
    //			System.out.println("您输入的数据非法!");
    			//手动抛出异常对象
    //			throw new RuntimeException("您输入的数据非法!");
    //			throw new Exception("您输入的数据非法!");
    			throw new MyException("不能输入负数");
    			//错误的
    //			throw new String("不能输入负数");
    		}
    		
    	}
    
    	@Override
    	public String toString() {
    		return "Student [id=" + id + "]";
    	}
    	
    	
    }
    

    用户自定义异常类

    我们上面使用的都是JAVA的API给我们提供好的异常类。其实我们也可以自定义自己的异常类。怎么去写?参考jdk源码的模样写就行了!自定义异常类需要遵循以下几点:

    • 一般地,用户自定义异常类都是RuntimeException的子类。
    • 自定义异常类通常需要编写几个重载的构造器。
    • 自定义异常需要提供serialVersionUID (是一个全局常量,可以理解为是对类的一个标识)
    • 自定义的异常通过throw抛出。
    • 自定义异常最重要的是异常类的名字,当异常出现时,可以根据名字判断异常类型。


    ps:只有异常体系的类能throw,其他的类是不能throw的

    总结:

    最后有一首有意思的小诗送给大伙:

    世界上最遥远的距离,是我在if里你在else里,似乎一直相伴又永远分离;
    世界上最痴心的等待,是我当case你是switch,或许永远都选不上自己;
    世界上最真情的相依,是你在try我在catch。无论你发神马脾气,我都默 默承受,静静处理。到那时,再来期待我们的finally


    本笔记记录的学习资源来自于尚硅谷_宋康红老师的学习资料,讲的特别好!强烈推荐

  • 相关阅读:
    HDU 2116 Has the sum exceeded
    HDU 1233 还是畅通工程
    HDU 1234 开门人和关门人
    HDU 1283 最简单的计算机
    HDU 2552 三足鼎立
    HDU 1202 The calculation of GPA
    HDU 1248 寒冰王座
    HDU 1863 畅通工程
    HDU 1879 继续畅通工程
    颜色对话框CColorDialog,字体对话框CFontDialog使用实例
  • 原文地址:https://www.cnblogs.com/deehuang/p/14394755.html
Copyright © 2011-2022 走看看