zoukankan      html  css  js  c++  java
  • Java面向对象 第6节 异常

    一、认识异常

      异常是指在程序运行过程中所发生的不正常事件,如文件找不到、网络连接不通或链接中断、算数运算出错、数组下标越界、装在一个不存在的类、对null对象操作、类型转换异常等。异常会中断正在运行的程序。

    package com.moreException;
    import java.util.Scanner;
    /**
     * 演示程序中的异常。
     * @author yu
     */
    public class Test1 {
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            System.out.print("请输入被除数:");
            int num1 = in.nextInt();
            System.out.print("请输入除数:");
            int num2 = in.nextInt();
            System.out.println(num1+"/"+ num2 +"="+ num1/ num2);
            System.out.println("感谢使用本程序!");
        }
    }
    

              

      此时,可以通过增加if-else语句对各种异常情况进行判断处理:

    package com.moreException;
    import java.util.Scanner;
    /**
     * 传统处理程序中的异常。
     * @author 北大青鸟
     */
    public class Test1{
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            System.out.print("请输入被除数:");
            int num1 = in.nextInt();
            System.out.print("请输入除数:");
            int num2 = 0;
            if (in.hasNextInt()) { // 如果输入的除数是整数
                num2 = in.nextInt();
                if (0 == num2) { // 如果输入的除数是0
                    System.err.println("输入的除数是0,程序退出。");
                    System.exit(1);
                }
                System.out.println(num1+"/"+ num2 +"="+ num1/ num2);
                System.out.println("感谢使用本程序!");
            } else { // 如果输入的除数不是整数
                System.err.println("输入的除数不是整数,程序退出。");
                System.exit(1);
            }
    
        }
    }
    

         

      if-else语句进行异常处理,有很多缺点:代码臃肿、异常处理代码占用时间精力,影响开发效率、很难列举所有的异常情况、影响代码可读性

    二、Java异常体系结构

      

      异常在Java中被封装成了各种异常类,所有的异常类都是Throwable类的子类,它派生了2个子类:Error类和Exception类。

      1)Error类:表示依靠程序本身无法恢复的严重错误,如内存溢出、动态链接失败、虚拟机错误等。出现这种错误,应尽力使应用程序安全退出

      2)Exception类:有java程序抛出和处理的非严重性错误,如算数运算出错、数组下标越界等。Exception类又可分为2大类异常:

        1.运行时异常:包括RuntimeException及其所有子类,如算数异常ArithmeticException

        2.Checked异常(非运行时异常):除了运行时异常外的其他从Exception继承来的异常类。

      常见异常:  

        算术异常类:ArithmeticExecption

        空指针异常类:NullPointerException

        类型强制转换异常:ClassCastException

        数组负下标异常:NegativeArrayException

        数组下标越界异常:ArrayIndexOutOfBoundsException

        违背安全原则异常:SecturityException

        文件已结束异常:EOFException

        文件未找到异常:FileNotFoundException

        字符串转换为数字异常:NumberFormatException

        操作数据库异常:SQLException

        输入输出异常:IOException

        方法未找到异常:NoSuchMethodException

     三、Java异常处理机制

      1.异常处理:通过5个关键字来实现,try  catch  finally  throw  throws

      2.使用try-catch处理异常

    package cn.yu.exception;
    import java.util.Scanner;
    /**
     * 使用try-catch进行异常处理。
     * @author yu
     */
    public class Test3 {
        public static void main(String[] args) {
    	    Scanner in = new Scanner(System.in);
                System.out.print("请输入被除数:");        
    	    try {
                   int num1 = in.nextInt();
                   System.out.print("请输入除数:");
                   int num2 = in.nextInt();
                   System.out.println(num1+"/"+ num2 +"="+ num1/ num2);
                   System.out.println("感谢使用本程序!");
                } catch (Exception e) {
                   System.err.println("出现错误:被除数和除数必须是整数,除数不能为零。");
                   e.printStackTrace();
            }
        }
    }
    

      try-catch语句块的执行流程比较简单,首先执行try语句块中代码,这时会有3种情况:

        1)try语句块所有代码正常执行完毕,将不会执行catch语句块中代码

           2)try语句块在执行过程中出现异常,并且这个异常与catch语句块中声明的异常类型匹配,那么try语句块中剩余代码将被忽略不再执行,而相应的catch语句块中代码将会被执行

        3)try语句块在执行过程中出现异常,并且这个异常与catch语句块中声明的异常类型不匹配,那么方法立刻退出

       在catch语句块中可以加入用户自定义处理信息,也可以调用异常对象的方法输出异常信息。
       常用异常信息打印方法:

        1)getMessage(): String 输出异常的描述信息

           2)getLocalizedMessage() 输出本地化的描述信息,一般此方法可被子类所覆盖,缺省实现与getMessage()输出信息一致

        3)printStackTrace() 将异常栈打印到输出流中,此为一类方法,默认打印到console控制台,也可以显式指定输出流。

        4)fillInStackTrace() 将当前的异常栈保存到一个Throwable中,返回这个Throwable。大部分情况下,用在保留异常栈嵌套调用的情况,尝试保留完整的异常栈,无需使用该方法。

       3.使用try-catch-finally处理异常

      此结构中try必选,catch和finally可选,但后2者必须存在其一。无论是否发生异常,finally语句块总会被执行。
      特别注意:
        1)即使在catch语句块中存在return语句,finally语句块也会执行,发生异常时执行顺序:try语句块-->catch语句块return之前的语句-->finally语句块-->执行return语句退出

        2)finally语句唯一不执行的情况是在异常处理代码中执行了System.exit()退出java虚拟机

    package cn.yu.exception;
    import java.util.Scanner;
    /**
     * 使用try-catch-finally进行异常处理。
     * @author yu
     */
    public class Test4 {
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            System.out.print("请输入被除数:");
            try {
                int num1 = in.nextInt();
                System.out.print("请输入除数:");
                int num2 = in.nextInt();
                System.out.println(num1+"/"+ num2 +"="+ num1/ num2);
                return; //finally语句块仍旧会执行
            } catch (Exception e) {
                System.err.println("出现错误:被除数和除数必须是整数,除数不能为零。");
                //System.exit(1); // finally语句块不执行的唯一情况
                return; //finally语句块仍旧会执行
            } finally {
                System.out.println("感谢使用本程序!");
            }
        }
    }
    

      4.使用多重catch处理异常

      可以再try语句块后跟多个catch语句块分别处理不同的异常,但排列顺序必须是从子类到父类,最后一个一般都是Exception类。

    package cn.yu.exception;
    import java.util.Scanner;
    import java.util.InputMismatchException;
    /**
     * 多重catch块
     * @author yu
     */
    public class Test6 {
      public static void main(String[] args) {
          Scanner in = new Scanner(System.in);
          System.out.print("请输入被除数:");
    	  	try {
              int num1 = in.nextInt();
              System.out.print("请输入除数:");
              int num2 = in.nextInt();
              System.out.println(num1+"/"+ num2 +"="+ num1/ num2);
          } catch (InputMismatchException e) {
              System.err.println("被除数和除数必须是整数。");
          } catch (ArithmeticException e) {
              System.err.println("除数不能为零。");
          } catch (Exception e) {  //该异常捕捉在前,报错
              System.err.println("其他未知异常。");
          } finally {
              System.out.println("感谢使用本程序!");
          }
       }
    }
    

      5.使用throws声明抛出异常

      如果在一个方法体中抛出了异常,并希望调用者能及时地捕获异常,可以用throws关键字声明某个方法可能抛出的各种异来通知调用者。

      通过throws声明异常,若果调用者不知道如何处理异常,可以继续通过throws声明异常,让上一级调用者处理异常。main()方法声明的异常由Java虚拟机处理。

    package cn.yu.exception;
    import java.util.Scanner;
    /**
     * 使用throws声明异常。
     * @author yu
     */
    public class Test7 {
        /**
         * 通过try-catch捕获并处理异常。
         * @param args
         */
        public static void main(String[] args) {
            try {
                divide();
            } catch (Exception e) {
                System.err.println("出现错误:被除数和除数必须是整数,除数不能为零");
                e.printStackTrace();
            }
        }
    //  /**
    //   * 通过throws继续声明异常。
    //   */
    //  public static void main(String[] args) throws Exception {  //main()方法声明的异常由Java虚拟机处理
    //      divide();
    //  }
    
        /**
         * 输入被除数和除数,计算商并输出。
         * @throws Exception
         */
        public static void divide() throws Exception {
            Scanner in = new Scanner(System.in);
            System.out.print("请输入被除数:");
            int num1 = in.nextInt();
            System.out.print("请输入除数:");
            int num2 = in.nextInt();
            System.out.println(num1+"/"+ num2 +"="+ num1/ num2);
        }
    }
    

             

      6.使用throw抛出异常

       除了系统自动抛出异常外,在编程过程中有些问题是体统无法自动发现并解决的,如性别输入不是”男”或“女”,此时要把问题提交给调用者去处理。

      可以使用throw关键字来自行抛出异常。 

    package cn.yu.exception;
    /**
     * 使用throw在方法内抛出异常。
     * @author yu
     */
    public class Person {
    	private String name = "";// 姓名
    	private int age = 0;// 年龄
    	private String sex = "男";// 性别
    	/**
    	 * 设置性别。
    	 * @param sex 性别
    	 * @throws Exception
    	 */
    	public void setSex(String sex) throws Exception {
    		if ("男".equals(sex) || "女".equals(sex))
    			this.sex = sex;
    		else {
    			throw new Exception("性别必须是“男”或者“女”!");  //抛出异常给调用者
    		}
    	}
    	/**
    	 * 打印基本信息。
    	 */
    	public void print() {
    		System.out.println(this.name + "(" + this.sex 
    				+ "," + this.age + "岁)");
    	}
    }
    

      

    package com.moreException;
    /**
     * 捕获throw抛出的异常。
     * @author yu
     */
    public class Test {
        public static void main(String[] args) {
            Person person = new Person();
            try {
                person.setSex("Male");
                person.print();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

      

      throws和throw区别:

    • 作用不同:throw用于程序员自行产生并抛出依会场,throws用于声明该方法内部抛出了异常
    • 使用的位置不同: throw位于方法体内部,可以作为单独语句使用;throws必须跟在方法参数列表后面,不能单独使用
    • 内容不同:throw抛出一个异常对象,只能是一个;throws后面跟异常类,可以跟多个

      7.自定义异常

      步骤:

        1)定义异常类,并继承Exception类或者RuntimeException类

        2)编写异常类的"构造方法",并继承弗父类的实现

        3)实例化自定义异常对象,并在程序汇总使用throw抛出

    package com.moreException;
    /*
    * 自定义异常类
    */
    public class GenderException extends Exception{
        //构造方法(常用4种)
    //    public GenderException() {
    //        super();
    //    }
        public GenderException(String message) {
            super(message);
        }
    //    public GenderException(String message,Throwable cause) {
    //        super(message,cause);
    //    }
    //
    //    public GenderException(Throwable cause) {
    //        super(cause);
    //    }
    
    }
    

      

    package com.moreException;
    
    class Person{
        private String name="";			//姓名
        private int age=0;				//年龄
        private String sex="男";		//性别
        //设置性别
        public void setSex(String sex) throws GenderException {
            if ("男".equals(sex) || "女".equals(sex))
                this.sex = sex;
            else {
                throw new GenderException("性别必须是“男”或者“女”!");
            }
        }
    
        //打印基本信息
        public void print(){
            System.out.println(this.name+"("+this.sex+","+this.age+"岁)");
        }
    }
    public class Test01{
        public static void main(String[] args){
            Person person = new Person();
            try {
                person.setSex("Male");
                person.print();
            } catch (GenderException e) {
                e.printStackTrace();
            }
    
        }
    }
    

      

      8.异常链

      在异常处理时有时会遇到如下情况:A方法调用B方法是B方法却抛出了异常,那么A方法是继续抛出原有的异常还是跑出一个新的异常?JDK1.4退出了异常链,正好解决了这个问题,他虽创建了新的异常,但去却保留了原有异常的信息。

       9.使用log4j记录日志

       

      1.如上图,需先引入log4j相关jar包

         2.编写log4j.properties文件

    • Logger 负责处理日志记录的大部分操作。其语法为:

        log4j.rootLogger = [ level ] , appenderName, appenderName, …

      其中,level 是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者自定义的级别。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,只有等于及高于这个级别的才进行处理,则应用程序中所有DEBUG级别的日志信息将不被打印出来。ALL:打印所有的日志,OFF:关闭所有的日志输出。 appenderName就是指定日志信息输出到哪个地方。可同时指定多个输出目的地。

    • 配置日志信息输出目的地 Appender:

        Appender 负责控制日志记录操作的输出。其语法为:

        log4j.appender.appenderName = fully.qualified.name.of.appender.class

        log4j.appender.appenderName.option1 = value1

        …

        log4j.appender.appenderName.optionN = valueN

        这里的appenderName为在第一步里定义的,可任意起名。其中,Log4j提供的appender有以下几种:
          org.apache.log4j.ConsoleAppender(控制台),
          org.apache.log4j.FileAppender(文件),
          org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),
          org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),可通过log4j.appender.R.MaxFileSize=100KB设置文件大小,还可通过       

          log4j.appender.R.MaxBackupIndex=1设置为保存一个备份文件。
          org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
          例如:log4j.appender.stdout=org.apache.log4j.ConsoleAppender
             定义一个名为stdout的输出目的地,ConsoleAppender为控制台。

    • 配置日志信息的格式(布局)Layout

        Layout 负责格式化Appender的输出。其语法为:
        log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
        log4j.appender.appenderName.layout.option1 = value1
        …
        log4j.appender.appenderName.layout.optionN = valueN
        其中,Log4j提供的layout有以下几种:
          org.apache.log4j.HTMLLayout(以HTML表格形式布局),
          org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
          org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
          org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

    • 关于格式化日志:

        Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下:

        %m 输出代码中指定的消息;
        %M 输出打印该条日志的方法名;
        %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL;
        %r 输出自应用启动到输出该log信息耗费的毫秒数;
        %c 输出所属的类目,通常就是所在类的全名;
        %t 输出产生该日志事件的线程名;
        %n 输出一个回车换行符,Windows平台为"rn”,Unix平台为"n”;
        %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy-MM-dd HH:mm:ss,SSS},输出类似:2015-12-14 16:25:55,921;
        %l 输出日志事件的发生位置,及在代码中的行数。

    ### 设置###
    log4j.rootLogger = debug,stdout,D,E
    
    ### 输出信息到控制抬 ###
    log4j.appender.stdout = org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.Target = System.out
    log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
    
    ### 输出DEBUG 级别以上的日志到文件F://logs/debug.log ###
    log4j.appender.D = org.apache.log4j.FileAppender
    log4j.appender.D.File = F:/logs/debug.log
    log4j.appender.D.Append = true
    log4j.appender.D.Threshold = DEBUG
    log4j.appender.D.layout = org.apache.log4j.PatternLayout
    log4j.appender.D.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
    
    ### 输出ERROR 级别以上的日志到文件F://logs/error.log ###
    log4j.appender.E = org.apache.log4j.FileAppender
    log4j.appender.E.File = F://logs/error.log
    log4j.appender.E.Append = true
    log4j.appender.E.Threshold = ERROR
    log4j.appender.E.layout = org.apache.log4j.PatternLayout
    log4j.appender.E.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
    

      3.测试代码

    package cn.yy;
    import java.util.InputMismatchException;
    import java.util.Scanner;
    import org.apache.log4j.Logger;
    /**
     * 使用log4j记录日志。
     */
    public class TestLog4j {
        private static Logger logger=Logger.getLogger(TestLog4j.class.getName());
        public static void main(String[] args) {
            try {
                Scanner in = new Scanner(System.in);
                System.out.print("请输入被除数:");
                int num1 = in.nextInt();
                logger.debug("输入被除数:" + num1);
                System.out.print("请输入除数:");
                int num2 = in.nextInt();
                logger.debug("输入除数:" + num2);
                System.out.println(String.format("%d / %d = %d",
                        num1, num2, num1/ num2));
                logger.debug("输出运算结果:" + String.format("%d / %d = %d",
                        num1, num2, num1 / num2));
            } catch (InputMismatchException e) {
                logger.error("被除数和除数必须是整数", e);
            } catch (ArithmeticException e) {
                logger.error(e.getMessage());
            } catch (Exception e) {
                logger.error(e.getMessage());
            } finally {
                System.out.println("欢迎使用本程序!");
            }
        }
    }
    

      

  • 相关阅读:
    【ST】lab01——Junit and Eclemma
    【SPM】hw1——个人房间装修
    【ST】hw2——find the error in the follow case
    【ST】Describe an error from my past projects
    ST homework4 --- 图覆盖
    ST lab1——Junit和覆盖测试的初探
    ST work12——failure,fault,error
    ST work1——印象最深的一个bug DJI 激活时报 SDK_ACTIVE_SDK_VERSION_ERROR
    C# note 06——delegate 和 event
    C# note 05——异常处理
  • 原文地址:https://www.cnblogs.com/yutianbao/p/10562300.html
Copyright © 2011-2022 走看看