栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java异常

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java异常

Java的基本理念是“结构不佳的代码不能运行”。

异常体系
  • Throwable(任何可以作为异常被抛出的类)

    • Exception 受检异常(在编译时被强制检查的异常)

    • RuntimeException 非受检异常(运⾏时异常,代表⼀种预料之外的异常,因此不需要声明)

      • 几个常见的RuntimeException:IndexOutOfBoundsException、ClassCastException、NullPointerException、NumberFormatException
    • Error 编译时和系统错误,用于虚拟机报告系统错误

RuntimeException与Exception的区别?
  • RuntimeException是Exception的子类;

  • RuntimeException标注的异常可以不需要进行强制性处理,而Exception异常必须强制性处理;

  • 只能在代码中忽略RuntimeException(及其子类)类型的异常,其他类型异常的处理都是由编译器强制实施的。究其原因,RuntimeException代表的是编程错误:

    1. 无法预料的错误。比如从你控制范围之外传递进来的null引用

    2. 作为程序员,应该在代码中进行检查的错误。(比如对于ArrayindexOutOfBoundsException,就得注意一下数组的大小了。)在一个地方发生的异常,常常会在另一个地方导致错误。

自定义异常类

自定义异常类分为继承Exception和RuntimeException两种情况。

  • Exception:main方法要处理异常,此异常处理方式为抛出异常

  • RuntimeException:main不用处理异常

public class Main {
        public static void main(String[] args) {
            Food.eat(11);
        }
    }
    class BombException extends RuntimeException{
        public BombException(String msg){
            super(msg);
        }
    }
    class Food{
        public static void eat(int num) throws BombException{
            if(num > 10){
                throw new BombException("eat too much");
            }else{
                System.out.println("eating");
            }
        }
    }

总结:通过这一个自定义异常demo可以更清楚区分出:Exception和RuntimeException的区别。

异常匹配

抛出异常的时候,异常处理系统会按照代码的书写顺序找出「最近」的处理程序。找到匹配的处理程序之后,它就认为异常将得到处理,然后就不再继续查找。

查找的时候并不要求抛出的异常同处理程序所声明的异常完全匹配。派生类对象也可以匹配其基类的处理程序

  • demo : Catching exception hierarchies
// Catching exception hierarchies(等级制度).

class Annoyance extends Exception {}
class Sneeze extends Annoyance {}

public class Human {
    public static void main(String[] args) {
        // Catch the exact type:
        try {
            throw new Sneeze();
        } catch (Sneeze s) {
            System.out.println("Caught Sneeze");
        } catch (Annoyance a) {
            System.out.println("Caught Annoyance");
        }
        // Caught the base type:
        try {
            throw new Sneeze();
        } catch (Annoyance a) {
            System.out.println("Caught Annoyance");
        }
    }
}
//:~
  • demo : 把捕获基类的catch子句放在最前面
        try {
            throw new Sneeze();
        } catch (Annoyance a) {
            System.out.println("Caught Annoyance");
        //! Exception 'com.company.exception.Sneeze' has already been caught
        } catch (Sneeze s) {
            System.out.println("Caught Sneeze");
        }

此时编译器会发现Sneeze的catch子句永远也执行不到,因此它会向你报告错误。

catch的级联与合并,父子级异常直接抓父亲, 处理方式一样的异常用|

  • demo
        try {
            throwCheckedException();
        } catch (NullPointException | IllegalAccessException e) {
            //打印完整异常信息
            e.printStackTrace();
        }
捕获所有异常 栈轨迹

e.printStackTrace() (排查问题最重要的信息,没有之⼀)

printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问。

  • demo : Programmatic access to stack information
// Programmatic access to stack information
public class WhoCalled {
    static void f() {
        // Generate an exception to fill in the stack trace
        try{
            throw new Exception();
        }catch(Exception e){
            for (StackTraceElement ste : e.getStackTrace()){
                System.out.println("ste.getMethodName() = " + ste.getMethodName());
            }
            e.printStackTrace();
        }
    }

    static void g(){ f(); }
    static void h(){ g(); }

    public static void main(String[] args) {
        f();
        System.out.println("--------------------");
        g();
        System.out.println("--------------------");
        h();
    }
}
//:~
异常链

捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来,这被称为异常链。JDK1.4之后,所有Throwable的子类在构造器中都可以接受一个cause对象作为参数。这个cause就用来表示原始异常,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也能通过这个异常链追踪到异常最初发生的位置。异常向上抛出时,每多包一层,就多一个Caused by。

在Throwable的子类中,只有三种基本类型的异常类提供了带cause参数的构造器。他们是Error、Exceptio以及RuntimeException。如果要把其他类型的异常链接起来,应该使用initCause()方法,而不是构造器。

  • demo:使用initCause()方法把异常链接起来
// A Class that dynamically adds fields to itself.
// Demonstrates exception chaining.(展示异常链)
package com.company.exception;

class DynamicFieldsException extends Exception {
}

public class DynamicFields {
    private Object[][] fields;

    public DynamicFields(int initialSize) {
        fields = new Object[initialSize][2];
        for (int i = 0; i < initialSize; i++) {
            fields[i] = new Object[]{null, null};
        }
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        for (Object[] obj : fields) {
            builder.append(obj[0]);
            builder.append(": ");
            builder.append(obj[1]);
            builder.append("n");
        }
        return builder.toString();
    }

    private int hasField(String id) {
        for (int i = 0; i < fields.length; i++) {
            if (id.equals(fields[i][0])) {
                return i;
            }
        }
        return -1;
    }

    private int getFieldNumber(String id) throws NoSuchFieldException {
        int fieldNum = hasField(id);
        if (fieldNum == -1) {
            throw new NoSuchFieldException();
        }
        return fieldNum;
    }

    private int makeField(String id) {
        for (int i = 0; i < fields.length; i++)
            if (fields[i][0] == null) {
                fields[i][0] = id;
                return i;
            }
        // No empty fields.Add one:
        Object[][] tmp = new Object[fields.length + 1][2];
        for (int j = 0; j < fields.length; j++) {
            tmp[j] = fields[j];
        }
        for (int k = fields.length; k < tmp.length; k++) {
            tmp[k] = new Object[]{null, null};
        }
        fields = tmp;
        // Recursive call with expanded fields:
        return makeField(id);
    }

    public Object getField(String id) throws NoSuchFieldException {
        return fields[getFieldNumber(id)][1];
    }

    public Object setField(String id, Object value) throws DynamicFieldsException {
        if (value == null) {
            // Most exceptions don't have a "cause" constructor.
            // In these cases you must use initCause(),
            // available in all Throwable subclasses.
            DynamicFieldsException dfe = new DynamicFieldsException();
            dfe.initCause(new NullPointerException());
            throw dfe;
        }
        int fieldNumber = hasField(id);
        if (fieldNumber == -1) {
            fieldNumber = makeField(id);
        }
        Object result = null;
        try {
            result = getField(id); // Get old value
        } catch (NoSuchFieldException e) {
            // Use constructor that takes "cause"
            throw new RuntimeException(e);
        }
        fields[fieldNumber][1] = value;
        return result;
    }

    public static void main(String[] args) {
        DynamicFields df = new DynamicFields(3);
        System.out.println(df);
        try {
            df.setField("d", "A value for d");
            df.setField("number", 47);
            df.setField("number2", 48);
            System.out.println(df);
            df.setField("d", "A new value for d");
            df.setField("number3", 11);
            System.out.println(df);
            System.out.println("df.getField("d") = " + df.getField("d"));
            Object field = df.setField("d", null); // Exception
        } catch (NoSuchFieldException e) {
            e.printStackTrace(System.out);
        } catch (DynamicFieldsException e) {
            e.printStackTrace(System.out);
        }
    }
}

//:~
  • demo : Exception类型的方法
// Demonstrating the Exception Methods.
public class ExceptionMethods {
    public static void main(String[] args) {
        try {
            throw new Exception("My Exception");
        } catch (Exception e) {
            System.out.println("Caught Exception");
            System.out.println("e.getMessage() = " + e.getMessage());
            System.out.println("e.getLocalizedMessage() = " + e.getLocalizedMessage());
            System.out.println("toString() = " + e);
            System.out.println("printStackTrace():");
            e.printStackTrace(System.out);
        }
    }
}
//:~
使用finally进行清理 finally用来做什么

当要把除内存之外的资源恢复到它们的初始状态时,就要用到finally子句。无论异常是否被抛出,finally子句总能被执行。(JDK8:try-with-resources)

甚至在异常没有被当前的异常处理程序捕获的情况下,异常处理机制也会在跳到更高一层的异常处理程序之前,执行finally语句

  • demo : AlwayFinally
package com.company.exception;

class FourException extends Exception {}

public class AlwaysFinally {
    public static void main(String[] args) {
        System.out.println("Entering first try block");
        try {
            System.out.println("Entering second try block");
            try {
                throw new FourException();
            } finally {
                System.out.println("finally in 2nd try block");
            }
        } catch (FourException e) {
            System.out.println("Caught FourException in 1st try block");
        } finally {
            System.out.println("finally in 1st try block");
        }
    }
}

当涉及break和continue语句的时候,finally子句也会得到执行。

在return中使用finally

因为finally子句总是会执行的,所以在一个方法中,可以从多个点返回,并且可以保证重要的清理工作仍会执行。

  • demo : MultipleReturns
package com.company.exception;

public class MultipleReturns {
    public static void f(int i){
        System.out.println("Initialization that requires cleanup");
        try{
            System.out.println("Point 1");
            if(i == 1) return;
            System.out.println("Point 2");
            if(i == 2) return;
            System.out.println("Point 3");
            if(i == 3) return;
            System.out.println("End");
        }finally {
            System.out.println("Performing cleanup");
        }
    }
    public static void main(String[] args) {
        for (int i = 1; i < 4; i++) {
            f(i);
        }
    }
}

异常的抛出原则
  • 能⽤if/else处理的,不要使⽤异常

  • 尽早抛出异常

  • 异常要准确、带有详细信息

  • 抛出异常也⽐悄悄地执⾏错误的逻辑强的多

异常的处理原则
  • 本⽅法是否有责任处理这个异常?

    • 不要处理不归⾃⼰管的异常
  • 本⽅法是否有能⼒处理这个异常?

    • 如果⾃⼰⽆法处理,就抛出
  • 如⾮万分必要,不要忽略异常

断言
  • 使用 -ea 开启断言
public class Main {
    public static void main(String[] args) {
        int x = 10;
        assert x == 100 : "x的内容不是100";
        System.out.println(x);
    }
}
//:~
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/584366.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号