zoukankan      html  css  js  c++  java
  • 异常

    异常简介:
    1、很多事情你很难能够和正常情况下一样,比如出去旅游。
    出发-到达目的地-旅游-烧烤-回家,但是当你准备烧烤时来个倾盆大雨----这就是异常
    2、所以程序野有异常:表示在程序运行时的一种情况。
    3、所以java中的异常会终端执行的程序,所以为了代码健壮性,我们需要异常类处理。
    4、java发生异常的三种原因:
    a、java内部错误发生异常,也就是虚拟机产生的异常。
    b、编写程序产生的异常,如数组越界、空指针
    c、通过throw手动产生异常来告知调用者必要信息。
    5、 java是通过面向对象的方法来处理异常,在一个方法运行时发生了异常,则这个方法会产生一个异常
    的对象,交给运行时系统处理。
    a、交给运行时系统过程叫做抛出(throw)异常
    b、运行时系统通过调用栈找到该异常的的对象,叫做捕获(catch)异常
    看个例子:
    public class Test01{
    public static void main(String[] args){
    System.out.println("请输入1~3之间的整数");
    Scanner input = new Scanner(System.in);
    int num = input.nextInt();
    switch(num){
    case 1:
    System.out.println("one"); break;
    case 2:
    System.out.println("two");break;
    case 3:
    System.out.println("three");break;
    default:
    System.out.println("error");break;
    }
    }
    }
    当你输入一个非1~3的数时,如a.---那么会报Exception in thread "main" java.util.InputMismatchException

    6、异常类型都是java内置类Throwable的子类:如下图


    7、根据图分析:我们可以将异常分为Error和Exception异常
    a、Error是灾难性的,一般是非程序可以控制的,一般JVM错误产生,如堆栈溢出
    b、Exception是程序出现的错,分为运行时异常和非运行时异常。
    也可以叫做不检查异常和检查异常。
    运行时异常:RuntimeException. 如:ArithmeticException(算数异常)---可捕获处理也可不捕获处理
    非运行时异常:Exception子类非RuntimeException.如:ClassNotFoundException--必须捕获处理
    如下图:

    如下是常见的 Error 和 Exception:

    1)运行时异常(RuntimeException):
    NullPropagation:空指针异常;
    ClassCastException:类型强制转换异常
    IllegalArgumentException:传递非法参数异常
    IndexOutOfBoundsException:下标越界异常
    NumberFormatException:数字格式异常

    2)非运行时异常:
    ClassNotFoundException:找不到指定 class 的异常
    IOException:IO 操作异常

    3)错误(Error):
    NoClassDefFoundError:找不到 class 定义异常
    StackOverflowError:深递归导致栈被耗尽而抛出的异常
    OutOfMemoryError:内存溢出异常

    下面代码会导致 Java 堆栈溢出错误
    / 通过无限递归演示堆栈溢出错误
    class StackOverflow {
    public static void test(int i) {
    if (i == 0) {
    return;
    } else {
    test(i++);
    }
    }
    }
    public class ErrorEg {
    public static void main(String[] args) {
    // 执行StackOverflow方法
    StackOverflow.test(5);
    }
    }

    =========================================
    ==========================================

    Java 的异常处理通过 5 个关键字来实现:try、catch、throw、throws 和 finally。try catch 语句用于捕获并处理异常,finally 语句用于在任何情况下(除特殊情况外)都必须执行的代码,throw 语句用于拋出异常,throws 语句用于声明可能会出现的异常

    catch可以多个,throw抛出提示,throws向上抛出处理不来的异常

    public class FiveKeyWord {
    public static void main(String[] args) throws Exception {
    test(9);
    }
    private static void test(int i){
    int a;
    try{
    a=i/0;
    }catch(ArithmeticException e){
    System.out.println("算数异常");
    }catch(Exception e){
    throw(e);//向外抛出异常
    }finally {
    System.out.println("释放资源");
    }
    }
    }

    ============================

    ==================

    异常结构体一:

    try{发生异常位置}catch(ExceptionType e){处理异常}

    {}不能省略,try里没有异常程序就会跳过catch,有的话就跳到catch里执行。

      知道有三个输出异常方法就可以:

    • printStackTrace() 方法:指出异常的类型、性质、栈层次及出现在程序中的位置。
    • getMessage() 方法:输出错误的性质。---什么错误,需要输出流打印syso(e.getMessae())
    • toString() 方法:给出异常的类型与性质。---需要输出流打印syso(e.getMessae())

     异常结构体二:

    注意:a、当捕获的多个异常类之间存在父子关系时,捕获异常时一般先捕获子类,再捕获父类。所以子类异常必须在父类异常的前面,否则子类捕获不到。

       b、执行和上面一样。在多个 catch 代码块的情况下,当一个 catch 代码块捕获到一个异常时,其它的 catch 代码块就不再进行匹配。

    1. try {
    2. // 可能会发生异常的语句
    3. } catch(ExceptionType e) {
    4. // 处理异常语句
    5. } catch(ExceptionType e) {
    6. // 处理异常语句
    7. }

    例子:利用异常,写输入班级总人数,总分数,计算平均分

    public class ExceptionAvgScore {
    public static void main(String[] args) {
    Scanner input =new Scanner(System.in);
    try{
    System.out.println("请输入班级总人数:");
    int count = input.nextInt();
    System.out.println("请输入班级总成绩");
    int score = input.nextInt();
    int avg = score/count;//获取平均分
    System.out.println(avg);
    }catch(InputMismatchException e1){
    System.out.println("输入数值错误");
    }catch(ArithmeticException e2){
    System.out.println("计算总是不能为0");
    }catch(Exception e3){
    e3.printStackTrace();
    System.out.println("发生错误|"+e3.getMessage());
    }
    }
    }

    异常结构三

    1. try {
    2. // 可能会发生异常的语句
    3. } catch(ExceptionType e) {
    4. // 处理异常语句
    5. } finally {
    6. // 清理代码块
    1. try {
    2. // 逻辑代码块
    3. } finally {
    4. // 清理代码块
    5. }

    在实际开发中,根据 try catch 语句的执行过程,try 语句块和 catch 语句块有可能不被完全执行,而有些处理代码则要求必须执行。例如,程序在 try 块里打开了一些物理资源(如数据库连接、网络连接和磁盘文件等),这些物理资源都必须显式回收。

    Java的垃圾回收机制不会回收任何物理资源,垃圾回收机制只回收堆内存中对象所占用的内存

    所以为了确保一定能回收 try 块中打开的物理资源,异常处理机制提供了 finally 代码块,并且 Java 7 之后提供了自动资源管理。

    try必须一个,catch非必需可多个,finally非必需可多个。执行顺序t-c-f.

     ===============================================

    ============================================

    try-return---注意下面两种类型,基本类型和引用类型。

     private int testReturn1() {
     2         int i = 1;
     3         try {
     4             i++;
     5             System.out.println("try:" + i);
     6             return i;
     7         } catch (Exception e) {
     8             i++;
     9             System.out.println("catch:" + i);
    10         } finally {
    11             i++;
    12             System.out.println("finally:" + i);
    13         }
    14         return i;
    15     }
    输出:try:2
    finally:3
    2

     private List<Integer> testReturn2() {
     2         List<Integer> list = new ArrayList<>();
     3         try {
     4             list.add(1);
     5             System.out.println("try:" + list);
     6             return list;
     7         } catch (Exception e) {
     8             list.add(2);
     9             System.out.println("catch:" + list);
    10         } finally {
    11             list.add(3);
    12             System.out.println("finally:" + list);
    13         }
    14         return list;
    15     }
    输出:try:[1]
    finally:[1, 3]
    [1, 3]




    二、catch中带有return
    1     private int testReturn3() {
     2         int i = 1;
     3         try {
     4             i++;
     5             System.out.println("try:" + i);
     6             int x = i / 0 ;
     7         } catch (Exception e) {
     8             i++;
     9             System.out.println("catch:" + i);
    10             return i;
    11         } finally {
    12             i++;
    13             System.out.println("finally:" + i);
    14         }
    15         return i;
    16     }
    输出:try:2
    catch:3
    finally:4
    3



    三、finally中带有return
     1 private int testReturn4() {
     2         int i = 1;
     3         try {
     4             i++;
     5             System.out.println("try:" + i);
     6             return i;
     7         } catch (Exception e) {
     8             i++;
     9             System.out.println("catch:" + i);
    10             return i;
    11         } finally {
    12             i++;
    13             System.out.println("finally:" + i);
    14             return i;
    15         }
    16     }
    输出:try:2
    finally:3
    3
    ======================================
    ========================================================

    Java 9增强的自动资源管理

    自动关闭资源的 try 语句相当于包含了隐式的 finally 块(这个 finally 块用于关闭资源),因此这个 try 语句可以既没有 catch 块,也没有 finally 块。

    java7之前:当程序使用 finally 块关闭资源时,程序会显得异常臃肿

    1. public static void main(String[] args) {
    2. FileInputStream fis = null;
    3. try {
    4. fis = new FileInputStream("a.txt");
    5. } catch (FileNotFoundException e) {
    6. e.printStackTrace();
    7. } finally {
    8. // 关闭磁盘文件,回收资源
    9. if (fis != null) {
    10. try {
    11. fis.close();
    12. } catch (IOException e) {
    13. e.printStackTrace();
    14. }

    java7开始:Java 7 增加了一个新特性,该特性提供了另外一种管理资源的方式,这种方式能自动关闭文件,被称为自动资源管理(Automatic Resource Management)。该特性是在 try 语句上的扩展,主要释放不再需要的文件或其他资源。

    当 try 代码块结束时,自动释放资源。不再需要显式的调用 close() 方法,该形式也称为“带资源的 try 语句”。

    注意:

    1. try 语句中声明的资源被隐式声明为 final,资源的作用局限于带资源的 try 语句。
    2. 可以在一条 try 语句中声明或初始化多个资源,每个资源以;隔开即可。
    3. 需要关闭的资源必须实现了 AutoCloseable 或 Closeable 接口。

    Closeable 是 AutoCloseable 的子接口,Closeable 接口里的 close() 方法声明抛出了 IOException,

    因此它的实现类在实现 close() 方法时只能声明抛出 IOException 或其子类;

    AutoCloseable 接口里的 close() 方法声明抛出了 Exception,

    因此它的实现类在实现 close() 方法时可以声明抛出任何异常。

    1. public class AutoCloseTest {
    2. public static void main(String[] args) throws IOException {
    3. try (
    4. // 声明、初始化两个可关闭的资源
    5. // try语句会自动关闭这两个资源
    6. BufferedReader br = new BufferedReader(new FileReader("AutoCloseTest.java"));
    7. PrintStream ps = new PrintStream(new FileOutputStream("a.txt"))) {
    8. // 使用两个资源
    9. System.out.println(br.readLine());
    10. ps.println("C语言中文网");
    11. }
    12. }
    13. }

    java9:不要求在 try 后的圆括号内声明并创建资源,只需要自动关闭的资源有 final 修饰或者是有效的 final 

    Java 7 几乎把所有的“资源类”(包括文件 IO 的各种类、JDBC 编程的 Connection 和 Statement 等接口)进行了改写,改写后的资源类都实现了 AutoCloseable 或 Closeable 接口。

    1. public class AutoCloseTest {
    2. public static void main(String[] args) throws IOException {
    3. // 有final修饰的资源
    4. final BufferedReader br = new BufferedReader(new FileReader("AutoCloseTest.java"));
    5. // 没有显式使用final修饰,但只要不对该变量重新赋值,该变量就是有效的
    6. final PrintStream ps = new PrintStream(new FileOutputStream("a. txt"));
    7. // 只要将两个资源放在try后的圆括号内即可
    8. try (br; ps) {
    9. // 使用两个资源
    10. System.out.println(br.readLine());
    11. ps.println("C语言中文网");
    12. }
    13. }
    14. }

    ===============================================

    ==========================================================

    throws 声明异常

    throws Exception 1,Exception2,…{…}
    使用 throws 声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由向上一级的调用者处理;
    如果 main 方法也不知道如何处理这种类型的异常,也可以使用 throws 声明抛出异常,该异常将交给 JVM 处理。
    JVM 对异常的处理方法是,打印异常的跟踪栈信息,并中止程序运行,这就是前面程序在遇到异常后自动结束的原因。
    方法---main---JVM,停止运行。-----------一直网上知道解决为止。
    public void readFile() throws IOException,C,D... {}



    子类不能大于父类,不然编译失败如下:
    1. public class OverrideThrows {
    2. public void test() throws IOException {
    3. FileInputStream fis = new FileInputStream("a.txt");
    4. }
    5. }
    6. class Sub extends OverrideThrows {
    7. // 子类方法声明抛出了比父类方法更大的异常
    8. // 所以下面方法出错
    9. public void test() throws Exception {
    10. }
    11. }

    throw 拋出异常

    ExceptionObject 必须是 Throwable 类或其子类的对象。如果是自定义异常类,也必须是 Throwable 的直接或间接子类

    如果不是就会编译失败:throw new String("拋出异常"); // String类不是Throwable类的子类

    public class Throw {
    public static void main(String[] args) {
    Scanner input = new Scanner(System.in);
    System.out.println("请输入用户名: ");
    String username = input.next();
    boolean con ;
    try{
    con = validateUserName(username);
    if(con){
    System.out.println("用户名输入正确");
    }
    }catch(IllegalArgumentException e){
    System.out.println(e);
    }
    }
    private static boolean validateUserName(String username) {
    boolean flag =false;
    if(username.length()>8){
    for(int i=0;i<username.length();i++){
    char ch = username.charAt(i);
    if((ch>='0' &&ch<='9') ||(ch>='a'&&ch<='z')||(ch>='A'&& ch<='Z')){
    flag=true;
    }else{
    flag=false;
    throw new IllegalArgumentException("用户名只能由字母或数字组成");
    }
    }
    }else{
    throw new IllegalArgumentException("用户名必须大于8为");
    }
    return flag;
    }
    }

    throws 与 throw区别:未必有异常,后者一定抛出了异常对象;

              作用在方法或类上,方法或类内;

              解决不了可以往上抛,只能自己解决。

    ====================================================================

    ==================================================

    java7提供了多异常捕获,catch (IndexOutOfBoundsException | NumberFormatException | ArithmeticException e),

    这样就不用写多个catch了.但是还是要遵循先子后父的规律。

    ==================

    ==============

    自定义异常:

    <class><自定义异常名><extends><Exception>
    1. public class MyException extends Exception {
    2. public MyException() {
    3. super();
    4. }
    5. public MyException(String str) {
    6. super(str);
    7. }
    8. }
    1. public class Test07 {
    2. public static void main(String[] args) {
    3. int age;
    4. Scanner input = new Scanner(System.in);
    5. System.out.println("请输入您的年龄:");
    6. try {
    7. age = input.nextInt(); // 获取年龄
    8. if(age < 0) {
    9. throw new MyException("您输入的年龄为负数!输入有误!");
    10. } else if(age > 100) {
    11. throw new MyException("您输入的年龄大于100!输入有误!");
    12. } else {
    13. System.out.println("您的年龄为:"+age);
    14. }
    15. } catch(InputMismatchException e1) {
    16. System.out.println("输入的年龄不是数字!");
    17. } catch(MyException e2) {
    18. System.out.println(e2.getMessage());
    19. }
    20. }
    21. }

    ============================

    ====================

    写个用户名和密码验证

    假设在某仓库管理系统的登录界面中需要输入用户名和密码,其中用户名只能由 6~10 位数字组成,密码只能有 6 位,任何不符合用户名或者密码要求的情况都视为异常,并且需要捕获并处理该异常。

    1. public class LoginException extends Exception {
    2. public LoginException() {
    3. super();
    4. }
    5. public LoginException(String msg) {
    6. super(msg);
    7. }
    8. }
    1. public static void main(String[] args) {
    2. Scanner input = new Scanner(System.in);
    3. System.out.println("用户名:");
    4. String username = input.next();
    5. System.out.println("密码:");
    6. String password = input.next();
    7. Test08 lt = new Test08 ();
    8. boolean con = lt.validateLogin(username,password); // 调用 validateLogin() 方法
    9. if (con) {
    10. System.out.println("登录成功!");
    11. }
    12. }
    1. public boolean validateLogin(String username,String pwd) {
    2. boolean con = false; // 用户名和密码是否正确
    3. boolean conUname = false; // 用户名格式是否正确
    4. try {
    5. if (username.length() >= 6 && username.length() <= 10) {
    6. for (int i = 0;i < username.length();i++) {
    7. char ch = username.charAt(i); // 获取每一个字符
    8. if (ch >= '0' && ch <= '9') { // 判断字符是否为0~9的数字
    9. conUname = true; // 设置 conUname 变量值为 true
    10. } else {
    11. // 如果字符不是0~9的数字,则拋出LoginException异常
    12. conUname = false;
    13. throw new LoginException("用户名中包含有非数字的字符!");
    14. }
    15. }
    16. } else {
    17. // 如果用户名长度不在6~10位之间,拋出异常
    18. throw new LoginException("用户名长度必须在6〜10位之间!");
    19. }
    20. if (conUname) { // 如果用户名格式正确,判断密码长度
    21. if (pwd.length() == 6) {
    22. // 如果密码长度等于6
    23. con=true; // 设置con变量的值为true,表示登录信息符合要求
    24. } else { // 如果密码长度不等于6,拋出异常
    25. con = false;
    26. throw new LoginException("密码长度必须为 6 位!");
    27. }
    28. }
    29. } catch(LoginException e) {
    30. // 捕获 LoginException 异常
    31. System.out.println(e.getMessage());
    32. }
    33. return con;
    34. }

    =====================

    ==============================

    Java完善除法运算的错误信息

    1. public class Compute {
    2. private int[] num = new int[2];
    3. public int[] getNum() {
    4. return num;
    5. }
    6. public void setNum(int[] num) {
    7. this.num = num;
    8. }
    9. }
    1. public class Test04 {
    2. public static void main(String[] args) {
    3. Compute c = new Compute();
    4. int array[] = c.getNum();
    5. int res = 0;
    6. String YorN = null;
    7. Scanner in = new Scanner(System.in);
    8. try {
    9. System.out.println("请输入第一个整数:");
    10. array[0] = in.nextInt();
    11. System.out.println("请输入第二个整数:");
    12. array[1] = in.nextInt();
    13. res = array[0] / array[1];
    14. System.out.println("是否保存结果请输入Y或者N");
    15. YorN = in.next();
    16. if (YorN.equals("Y")) {
    17. array[2] = res;
    18. }
    19. System.out.println(array[0] + "除以" + array[1] + "的结果是:" + res);
    20. }
    21. }
    22. }
    1. catch (ArrayIndexOutOfBoundsException e) {
    2. System.out.println("出现数组越界错误,下标过大或者过小。");
    3. } catch(ArithmeticException e) {
    4. System.out.println("出现算术运算错误,被除数不能为0。");
    5. } catch(InputMismatchException e) {
    6. System.out.println("输入的数据类型不匹配,只能输入数字。");
    7. } catch(Exception e) {
    8. System.out.println("发生未知错误。");
    9. }

    =======================

    ====================

    异常使用注意点:

    1、不要自定义继承RuntimException,直接if判断就可以提高代码健壮性。

    2、try-catch:不要直接Exception,而是要一个一个从小到大写出所有异常。

    3、异常捕捉处理:一个一个写出来,不要直接用一个模板写出抛给用户提示。类似上面直接Exception

    4、事务:a、try在事物中,要手动提交事物;b、在catch要使事物有效,必须抛出

    5、异常与finally:比如io,要从里层到外层关闭---------------------------JDK 7 以上可以使用try-with-resources 方式。

    6、finally有return:执行完finally的return,方法结束

    7、抛出异常需要精确捕获,就像抛绣球也要看着人抛

    8、程序员的基本修养 & NPE(现有代码中出现率较高)---nullpointexception

    1.方法(接口)的返回值可以为 null ,但不推荐返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回 null 值。调用方需要进行 null 判断防止 NPE 问题。

    2、防止 NPE ,是程序员的基本修养,注意 NPE 产生的场景。

    a.查询数据库返回null ,包括null 对象和null 集合。

    b.集合内元素有null 对象。

    ========================

    =====================================

    Java的异常跟踪栈

    printStack();

    xception in thread "main" Test.SelfException: 自定义异常信息
            at Test.PrintStackTraceTest.thirdMethod(PrintStackTraceTest.java:26)
            at Test.PrintStackTraceTest.secondMethod(PrintStackTraceTest.java:22)
            at Test.PrintStackTraceTest.firstMethod(PrintStackTraceTest.java:18)
            at Test.PrintStackTraceTest.main(PrintStackTraceTest.java:14)

    Java.util.logging:JDK自带记录日志类

    日志用来记录程序的运行轨迹,方便查找关键信息,也方便快速定位解决问题。

    Logger.getGlobal().info("打印信息");

     Logger 的默认级别是 INFO,比 INFO 级别低的日志将不显示。Logger 的默认级别定义在 jre 安装目录的 lib 下面。

    # Limit the message that are printed on the console to INFO and above.
    java.util.logging.ConsoleHandler.level = INFO

    所以在默认情况下,日志只显示前三个级别,对于所有的级别有下面几种记录方法:

    logger.warning(message);
    logger.fine(message);

    或者指定级别

    logger.log(Level.FINE, message);

    1. log.info("info");
    2. log.warning("warning");
    3. log.severe("server");

    十一月 27, 2019 5:13:05 下午 Test.Test main
    信息: info
    十一月 27, 2019 5:13:05 下午 Test.Test main
    警告: warning
    十一月 27, 2019 5:13:05 下午 Test.Test main
    严重: server

    修改日志管理器配置

    可以通过编辑配置文件来修改日志系统的各种属性。在默认情况下,配置文件存在于 jre 安装目录下“jre/lib/logging.properties”

    java -Djava.util.logging.config.file = configFile MainClass

     .level=INFO

    Test.Test.level=FINE

    java.util.logging.ConsoleHandler.level=FINE

    注意:在日志管理器配置的属性设置不是系统属性,因此,用-Dcom.mycompany.myapp.level=FINE启动应用程序不会对日志记录器产生任何影响。

  • 相关阅读:
    动图+源码,演示 Java 中常用数据结构执行过程及原理
    16 个超级实用的 Java 工具类
    图解 Java 垃圾回收机制,写得非常好!
    一些值得收藏的开源框架
    JVM 发生内存溢出的 8 种原因、及解决办法
    VC的function类说明 -- 继续
    引用文章 如何在lambda中引入递归调用
    VC中function函数解析
    folly无锁队列正确性说明
    C++ Programming Language中的Calculator源代码
  • 原文地址:https://www.cnblogs.com/lkwcrystal/p/12375967.html
Copyright © 2011-2022 走看看