本文已参加[新人创造礼]活动,一同开启创造之路。

哈喽,咱们好!我是Why,一名在读学生,目前刚刚开端进入自己的编程学习生涯。虽然学习起步较晚,但我坚信做了才有0或1的或许。学了javaee一段时间今后也是选择在上分享自己的日常笔记,也希望能够在很多道友的咱们庭中浑然一体。 本文首要讲解java反常,假如咱们读后觉得有用的话,还请咱们多多支持博主:欢迎 ❤️点赞、收藏⭐、留言 ✨✨✨个人主页:JinHuan

反常

1、什么是反常,java供给反常处理机制有什么用?
   以下程序履行进程中发生了不正常的状况,而这种不正常的状况叫做:反常
   java言语是很完善的言语,供给了反常的处理办法,以下程序履行进程中呈现了不正常状况,
   java把该反常信息打印输出到控制台,供程序员参阅。程序员看到反常信息之后,能够对
   程序进行修改,让程序愈加的强健。
​
   什么是反常:程序履行进程中的不正常状况。
   反常的效果:增强程序的强健性。
​
2、以下程序履行控制台呈现了:
   Exception in thread "main" java.lang.ArithmeticException: / by zero
       at com.bjpowernode.javase.exception.ExceptionTest01.main(ExceptionTest01.java:14)
    这个信息被咱们称为:反常信息。这个信息是JVM打印的。

栗子

public class ExceptionTest01 {
  public static void main(String[] args) {
    int a = 10;
    int b = 0;
    // 实践上JVM在履行到此处的时分,会new反常目标:new ArithmeticException("/ by zero");
    // 并且JVM将new的反常目标抛出,打印输出信息到控制台了。
    int c = a / b;
    System.out.println(a + "/" + b + "=" + c);
​
    // 此处运行也会创立一个:ArithmeticException类型的反常目标。
    //System.out.println(100 / 0);
​
    // 我观察到反常信息之后,对程序进行修改,愈加强健。
    /*
    int a = 10;
    int b = 2;
    if(b == 0) {
      System.out.println("除数不能为0");
      return;
    }
    // 程序履行到此处表示除数必定不是0
    int c = a / b;
    System.out.println(a + "/" + b + "=" + c);
     */
   }
}

反常的存在办法

1、反常在java中以类的办法存在,每一个反常类都能够创立反常目标。
​
2、反常对应的现实生活中是怎样的?
火灾(反常类):
2008年8月8日,小明家着火了(反常目标)
2008年8月9日,小刚家着火了(反常目标)
2008年9月8日,小红家着火了(反常目标)
​
类是:模板。
目标是:实践存在的个别。
​
钱包丢了(反常类):
2008年1月8日,小明的钱包丢了(反常目标)
2008年1月9日,小芳的钱包丢了(反常目标)

栗子

public class ExceptionTest02 {
  public static void main(String[] args) {
​
    // 经过“反常类”实例化“反常目标”
    NumberFormatException nfe = new NumberFormatException("数字格式化反常!");
​
    // java.lang.NumberFormatException: 数字格式化反常!
    System.out.println(nfe);
​
    // 经过“反常类”创立“反常目标”
    NullPointerException npe = new NullPointerException("空指针反常发生了!");
​
    //java.lang.NullPointerException: 空指针反常发生了!
    System.out.println(npe);
   }
}

运行时反常实例

public class ExceptionTest03 {
  public static void main(String[] args) {
    /*
    程序履行到此处发生了ArithmeticException反常,
    底层new了一个ArithmeticException反常目标,
    然后抛出了,由于是main办法调用了100 / 0,
    所以这个反常ArithmeticException抛给了main办法,
    main办法没有处理,将这个反常主动抛给了JVM。
    JVM终究终止程序的履行。
​
    ArithmeticException 承继 RuntimeException,归于运行时反常。
    在编写程序阶段不需求对这种反常进行预先的处理。
     */
    System.out.println(100 / 0);
​
    // 这儿的HelloWorld没有输出,没有履行。
    System.out.println("Hello World!");
   }
}
​

编译时反常的实例

/*
以下代码报错的原因是什么?
  由于doSome()办法声明方位上运用了:throws ClassNotFoundException
  而ClassNotFoundException是编译时反常。有必要编写代码时处理,没有处理
  编译器报错。
 */
public class ExceptionTest04 {
  public static void main(String[] args) {
    // main办法中调用doSome()办法
    // 由于doSome()办法声明方位上有:throws ClassNotFoundException
    // 咱们在调用doSome()办法的时分有必要对这种反常进行预先的处理。
    // 假如不处理,编译器就报错。
    //编译器报错信息: Unhandled exception: java.lang.ClassNotFoundException
    //doSome();
   }
​
  /**
   * doSome办法在办法声明的方位上运用了:throws ClassNotFoundException
   * 这个代码表示doSome()办法在履行进程中,有或许会呈现ClassNotFoundException反常。
   * 叫做类没找到反常。这个反常直接父类是:Exception,所以ClassNotFoundException归于编译时反常。
   * @throws ClassNotFoundException
   */
  public static void doSome() throws ClassNotFoundException{
    System.out.println("doSome!!!!");
   }
​
}
​

反常的处理办法

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
​
/*
处理反常的榜首种办法:
  在办法声明的方位上运用throws关键字抛出,谁调用我这个办法,我就抛给谁。抛给调用者来处理。
  这种处理反常的态度:上报。
​
处理反常的第二种办法:
  运用try..catch句子对反常进行捕捉。
  这个反常不会上报,自己把这个事儿处理了。
  反常抛到此处为止,不再上抛了。
​
留意:
  只需反常没有捕捉,选用上报的办法,此办法的后续代码不会履行。
  其他需求留意,try句子块中的某一行呈现反常,该行后边的代码不会履行。
  try..catch捕捉反常之后,后续代码能够履行。 */
​
"在今后的开发中,处理编译时反常,应该上报仍是捕捉呢,怎样选?
  假如希望调用者来处理,选择throws上报。
  其它状况运用捕捉的办法。
​
public class ExceptionTest06 {
  " 一般不主张在main办法上运用throws,由于这个反常假如真实的发生了,必定会抛给JVM。JVM只有终止。
  // 反常处理机制的效果便是增强程序的强健性。怎样能做到,反常发生了也不影响程序的履行。所以
  // 一般main办法中的反常主张运用try..catch进行捕捉。main就不要持续上抛了。
  /*
  public static void main(String[] args) throws FileNotFoundException {
    System.out.println("main begin");
    m1();
    System.out.println("main over");
  }
   */
  public static void main(String[] args) {
​
    // 100 / 0这是算术反常,这个反常是运行时反常,你在编译阶段,能够处理,也能够不处理。编译器不论。
    //System.out.println(100 / 0); // 不处理编译器也不论
    // 你处理也能够。
    /*
    try {
      System.out.println(100 / 0);
    } catch(ArithmeticException e){
      System.out.println("算术反常了!!!!");
    }
     */
​
    System.out.println("main begin");
    try {
      // try测验
      m1();
      // 以上代码呈现反常,直接进入catch句子块中履行。
      System.out.println("hello world!");
     } catch (FileNotFoundException e){ // catch后边的如同一个办法的形参。
      // 这个分支中能够运用e引证,e引证保存的内存地址是那个new出来反常目标的内存地址。
      // catch是捕捉反常之后走的分支。
      // 在catch分支中干什么?处理反常。
      System.out.println("文件不存在,或许路径过错,也或许该文件被删去了!");
      System.out.println(e); //java.io.FileNotFoundException: D:course1-课学习办法.txt (体系找不到指定的路径。)
     }
​
    // try..catch把反常捉住之后,这儿的代码会持续履行。
    System.out.println("main over");
   }
​
  private static void m1() throws FileNotFoundException {
    System.out.println("m1 begin");
    m2();
    // 以上代码出反常,这儿是无法履行的。
    System.out.println("m1 over");
   }
​
  // 抛其他不行,抛ClassCastException阐明你仍是没有对FileNotFoundException进行处理
  //private static void m2() throws ClassCastException{
  // 抛FileNotFoundException的父目标IOException,这样是能够的。由于IOException包含FileNotFoundException
  //private static void m2() throws IOException {
  // 这样也能够,由于Exception包含所有的反常。
  //private static void m2() throws Exception{
  // throws后边也能够写多个反常,能够运用逗号离隔。
  //private static void m2() throws ClassCastException, FileNotFoundException{
  private static void m2() throws FileNotFoundException {
    System.out.println("m2 begin");
    // 编译器报错原因是:m3()办法声明方位上有:throws FileNotFoundException
    // 咱们在这儿调用m3()没有对反常进行预处理,所以编译报错。
    // m3();
​
    m3();
    // 以上假如呈现反常,这儿是无法履行的!
    System.out.println("m2 over");
   }
​
  private static void m3() throws FileNotFoundException {
    // 调用SUN jdk中某个类的结构办法。
    // 这个类还没有接触过,后期IO流的时分就知道了。
    // 咱们仅仅凭借这个类学习一下反常处理机制。
    // 创立一个输入流目标,该流指向一个文件。
    /*
    编译报错的原因是什么?
      榜首:这儿调用了一个结构办法:FileInputStream(String name)
      第二:这个结构办法的声明方位上有:throws FileNotFoundException
      第三:经过类的承继结构看到:FileNotFoundException父类是IOException,IOException的父类是Exception,
      终究得知,FileNotFoundException是编译时反常。
​
      过错原因?编译时反常要求程序员编写程序阶段有必要对它进行处理,不处理编译器就报错。
     */
    //new FileInputStream("D:course1-开课学习办法.txt");
​
    // 咱们选用榜首种处理办法:在办法声明的方位上运用throws持续上抛。
    // 一个办法体傍边的代码呈现反常之后,假如上报的话,此办法完毕。
    new FileInputStream("D:course1-课学习办法.txt");
​
    System.out.println("假如以上代码出反常,这儿会履行吗??????????????????不会!!!");
   }
}
​
​
public class ExceptionTest05 {
  // 榜首种处理办法:在办法声明的方位上持续运用:throws,来完结反常的持续上抛。抛给调用者。
  // 上抛类似于推卸责任。(持续把反常传递给调用者。)
  /*
  public static void main(String[] args) throws ClassNotFoundException {
    doSome();
  }
   */
​
  // 第二种处理办法:try..catch进行捕捉。
  // 捕捉等于把反常拦下了,反常真实的处理了。(调用者是不知道的。)
  public static void main(String[] args) {
    try {
      doSome();
     } catch (ClassNotFoundException e) {
      e.printStackTrace();
     }
   }
​
  public static void doSome() throws ClassNotFoundException{
    System.out.println("doSome!!!!");
   }
​
}
​

About Try-catch

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
​
​
深入try..catch
  "1、catch后边的小括号中的类型能够是具体的反常类型,也能够是该反常类型的父类型。
  "2、catch能够写多个。主张catch的时分,精确的一个一个处理。这样有利于程序的调试。
  "3、catch写多个的时分,从上到下,有必要恪守从小到大。
​
public class ExceptionTest07 {
  /*
  public static void main(String[] args) throws Exception, FileNotFoundException, NullPointerException {
​
  }
   */
​
  /*public static void main(String[] args) throws Exception {
​
  }*/
​
  public static void main(String[] args) {
​
    //编译报错
    /*try {
      FileInputStream fis = new FileInputStream("D:course2-JavaSEdocumentJavaSE进阶讲义JavaSE进阶-01-面向目标.pdf");
    } catch(NullPointerException e) {
​
    }*/
​
    /*try {
      FileInputStream fis = new FileInputStream("D:curse2-JavaSEdocumentJavaSE进阶讲义JavaSE进阶-01-面向目标.pdf");
      System.out.println("以上呈现反常,这儿无法履行!");
    } catch(FileNotFoundException e) {
      System.out.println("文件不存在!");
    }
​
    System.out.println("hello world!");*/
​
    /*try {
      FileInputStream fis = new FileInputStream("D:curse2-JavaSEdocumentJavaSE进阶讲义JavaSE进阶-01-面向目标.pdf");
    } catch(IOException e) { // 多态:IOException e = new FileNotFoundException();
      System.out.println("文件不存在!");
    }*/
​
    /*try {
      FileInputStream fis = new FileInputStream("D:curse2-JavaSEdocumentJavaSE进阶讲义JavaSE进阶-01-面向目标.pdf");
    } catch(Exception e) { // 多态:Exception e = new FileNotFoundException();
      System.out.println("文件不存在!");
    }*/
​
    /*try {
      //创立输入流
      FileInputStream fis = new FileInputStream("D:curse2-JavaSEdocumentJavaSE进阶讲义JavaSE进阶-01-面向目标.pdf");
      //读文件
      fis.read();
    } catch(Exception e) { //所有的反常都走这个分支。
      System.out.println("文件不存在!");
    }*/
​
    /*try {
      //创立输入流
      FileInputStream fis = new FileInputStream("D:curse2-JavaSEdocumentJavaSE进阶讲义JavaSE进阶-01-面向目标.pdf");
      //读文件
      fis.read();
    } catch(FileNotFoundException e) {
      System.out.println("文件不存在!");
    } catch(IOException e){
      System.out.println("读文件报错了!");
    }*/
​
    // 编译报错。
    /*
    try {
      //创立输入流
      FileInputStream fis = new FileInputStream("D:curse2-JavaSEdocumentJavaSE进阶讲义JavaSE进阶-01-面向目标.pdf");
      //读文件
      fis.read();
    } catch(IOException e){
      System.out.println("读文件报错了!");
    } catch(FileNotFoundException e) {
      System.out.println("文件不存在!");
    }
     */
​
    // JDK8的新特性!
    try {
      //创立输入流
      FileInputStream fis = new FileInputStream("D:curse2-JavaSEdocumentJavaSE进阶讲义JavaSE进阶-01-面向目标.pdf");
      // 进行数学运算
      System.out.println(100 / 0); // 这个反常是运行时反常,编写程序时能够处理,也能够不处理。
     } catch(FileNotFoundException | ArithmeticException | NullPointerException e) {
      System.out.println("文件不存在?数学反常?空指针反常?都有或许!");
     }
   }
}

反常目初始化英文标的两个重要办法

  获取反常简略的描述信息:
    String msg = exception.getMessage();
​
  打印反常追踪的堆栈信息:
    exception.printStackTrace();

实例

public class ExceptionTest08 {
  public static void main(String[] args) {
    // 这儿仅仅为了测验getMessage()办法和printStackTrace()办法。
    // 这儿仅仅new了反常目标,可是没有将反常目标抛出。JVM会以为这是一个普通的java目标。
    NullPointerException e = new NullPointerException("空指针反常fdsafdsafdsafds");
​
    // 获取反常简略描述信息:这个信息实践上便是结构办法上面String参数。
    String msg = e.getMessage(); //空指针反常fdsafdsafdsafds
    System.out.println(msg);
​
    // 打印反常堆栈信息
    // java后台打印反常堆栈追踪信息的时分,选用了异步线程的办法打印的。
    e.printStackTrace();
​
    for(int i = 0; i < 1000; i++){
      System.out.println("i = " + i);
     }
​
    System.out.println("Hello World!");
   }
}
运行成果如下:

怎样检查反常并做处理

import java.io.FileInputStream;
import java.io.FileNotFoundException;
​
/*
咱们今后检查反常的追踪信息,咱们应该怎样看,能够快速的调试程序呢?
  反常信息追踪信息,从上往下一行一行看。
  可是需求留意的是:SUN写的代码就不用看了(看包名就知道是自己的仍是SUN的。)。
  首要的问题是呈现在自己编写的代码上。
 */
public class ExceptionTest09 {
  public static void main(String[] args) {
    try {
      m1();
     } catch (FileNotFoundException e) {
      // 获取反常的简略描述信息
      String msg = e.getMessage();
      System.out.println(msg); //C:jetns-agent.jar (体系找不到指定的文件。)
​
      //打印反常堆栈追踪信息!!!
      //在实践的开发中,主张运用这个。养成好习惯!
      // 这行代码要写上,不然出问题你也不知道!
      //e.printStackTrace();
      /*
      java.io.FileNotFoundException: C:jetns-agent.jar (体系找不到指定的文件。)
        at java.base/java.io.FileInputStream.open0(Native Method)
        at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:155)
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:110)
        at com.bjpowernode.javase.exception.ExceptionTest09.m3(ExceptionTest09.java:31)
        at com.bjpowernode.javase.exception.ExceptionTest09.m2(ExceptionTest09.java:27)
        at com.bjpowernode.javase.exception.ExceptionTest09.m1(ExceptionTest09.java:23)
        at com.bjpowernode.javase.exception.ExceptionTest09.main(ExceptionTest09.java:14)
        由于31行出问题导致了27行
        27行出问题导致23行
        23行出问题导致14行。
        应该先检查31行的代码。31行是代码过错的根源。
       */
     }
​
    // 这儿程序不耽误履行,很强健。《服务器不会由于遇到反常而宕机。》
    System.out.println("Hello World!");
   }
​
  private static void m1() throws FileNotFoundException {
    m2();
   }
​
  private static void m2() throws FileNotFoundException {
    m3();
   }
​
  private static void m3() throws FileNotFoundException {
    new FileInputStream("C:jetns-agent.jar");
   }
}
​

Finally句子

  "1、在finally子句中的代码是终究履行的,并且是必定会履行的,即便try句子块中的代码呈现了反常。
    finally子句有必要和try一同呈现,不能独自编写。
​
  "2、finally句子通常运用在哪些状况下呢?
    通常在finally句子块中完结资源的释放/封闭。
    由于finally中的代码比较有保证。
    即便try句子块中的代码呈现反常,finally中代码也会正常履行。

实例

public class ExceptionTest10 {
  public static void main(String[] args) {
    FileInputStream fis = null; // 声明方位放到try外面。这样在finally中才能用。
    try {
      // 创立输入流目标
      fis = new FileInputStream("D:course2-JavaSEdocumentJavaSE进阶讲义JavaSE进阶-01-面向目标.pdf");
      // 开端读文件....
​
      String s = null;
      // 这儿必定会呈现空指针反常!
      s.toString();
      System.out.println("hello world!");
​
      // 流运用完需求封闭,由于流是占用资源的。
      // 即便以上程序呈现反常,流也有必要要封闭!
      // 放在这儿有或许流关不了。
      //fis.close();
     } catch (FileNotFoundException e) {
      e.printStackTrace();
     } catch(IOException e){
      e.printStackTrace();
     } catch(NullPointerException e) {
      e.printStackTrace();
     } finally {
      System.out.println("hello 浩克!");
      // 流的封闭放在这儿比较稳妥。
      // finally中的代码是必定会履行的。
      // 即便try中呈现了反常!
      if (fis != null) { // 防止空指针反常!
        try {
          // close()办法有反常,选用捕捉的办法。
          fis.close();
         } catch (IOException e) {
          e.printStackTrace();
         }
       }
     }
​
    System.out.println("hello kitty!");
​
   }
}

Try和finally的连用

验证finally句子必定是终究履行的

public class ExceptionTest11 {
  public static void main(String[] args) {
    /*
    try和finally,没有catch能够吗?能够。
      try不能独自运用。
      try finally能够联合运用。
    以下代码的履行次序:
      先履行try...
      再履行finally...
      终究履行 return (return句子只需履行办法必定完毕。)
     */
    try {
      System.out.println("try...");
      return;
     } finally {
      // finally中的句子会履行。能履行到。
      System.out.println("finally...");
     }
​
    // 这儿不能写句子,由于这个代码是无法履行到的。
    //System.out.println("Hello World!");
   }
}

关于Finally中的特例

"留意,当退出JVM的时分,Finally句子就不会履行了"
​
public class ExceptionTest12 {
  public static void main(String[] args) {
    try {
      System.out.println("try...");
      // 退出JVM
      System.exit(0); // 退出JVM之后,finally句子中的代码就不履行了!
     } finally {
      System.out.println("finally...");
     }
   }
}
​

Finally易错点

"试推测下列result的返回值
public class Test {
    public static void main(String[] args) {
      int result = m();
      System.out.println(result); //猜测成果
     }
    public static int m(){
      int i = 100;
      try {
        return i;
       } finally {
        i++;
        System.out.println("Finally中的i"+i);
       }
     }
   }

分析

 "java语法规矩(有一些规矩是不能破坏的,一旦这么说了,就有必要这么做!):
    java中有一条这样的规矩:
      办法体中的代码有必要遵循自上而下次序依次逐行履行(亘古不变的语法!)
    java中还有一条语法规矩:
      return句子一旦履行,整个办法有必要完毕(亘古不变的语法!)

运行成果

能够看出,虽然finally句子块履行了,可是,return的值仍是100!!
    为什么?看看反编译的成果
      public static int m(){
        int i = 100;
        int j = i;
        i++;
        return j;
       }
"不难看出,在反编译的时分,引入了一个新的中心变量,来处理咱们的疑问

Final 、Finally、 Finalize的差异

  "final 关键字
    final润饰的类无法承继
    final润饰的办法无法掩盖
    final润饰的变量不能从头赋值。
​
  "finally 关键字
    和try一同联合运用。
    finally句子块中的代码是有必要履行的。
​
  "finalize 标识符
    是一个Object类中的办法名。
    这个办法是由废物收回器GC担任调用的。

实例

public class ExceptionTest14 {
  public static void main(String[] args) {
​
    // final是一个关键字。表示终究的。不变的。
    final int i = 100;
    //i = 200;
​
    // finally也是一个关键字,和try联合运用,运用在反常处理机制中
    // 在fianlly句子块中的代码是必定会履行的。
    try {
​
     } finally {
      System.out.println("finally....");
     }
​
    // finalize()是Object类中的一个办法。作为办法名呈现。
    // 所以finalize是标识符。
    // finalize()办法是JVM的GC废物收回器担任调用。
    Object obj;
   }
}
​
// final润饰的类无法承继
final class A {
  // 常量。
  public static final double MATH_PI = 3.1415926;
}
​
class B {
  // final润饰的办法无法掩盖
  public final void doSome(){
​
   }
}
​

自定义的生产异常处理反常类

Java中怎样自定义反常呢?
  两步:
    "榜首步:编写一个类承继Exception或许RuntimeException.
    "第二步:供给两个结构办法,一个无参数的,一个带有String参数的。

实例

public class MyException extends Exception{ // 编译时反常
  public MyException(){
​
   }
  public MyException(String s){
    super(s);
   }
}

反常的归纳实例

    编写程序,运用一维数组,模仿栈数据结构。
    要求:
        1、这个栈能够存储java中的任何引证类型的数据。
        2、在栈中供给push办法模仿压栈。(栈满了,要有提示信息。)
        3、在栈中供给pop办法模仿弹栈。(栈空了,也有有提示信息。)
        4、编写测验程序,new栈目标,调用push pop办法来模仿压栈弹栈的动作。
        5、假设栈的默认初始化容量是10.(请留意无参数结构办法的编写办法。)

自定义反常办法

public class StackException extends Exception{
  public StackException(){
​
   }
  public StackException(String s){
    super(s);
   }
}
​

测验类

import java.util.ArrayList;
import java.util.List;
​
public class Test {
  static int temp = 0;
  public static void main(String[] args) {
//     创立一个调集目标并指定其初始容量
    List<Object> mylist = new ArrayList<Object>(10);//自定义栈的初始容量为10
//     创立一个本类的目标,便利来调用下面的实例办法
    Test test = new Test();
//     模仿压栈
    test.push(mylist,1);
    test.push(mylist,2);
    test.push(mylist,3);
    test.push(mylist,4);
​
//     模仿弹栈
    test.pop(mylist);
    test.pop(mylist);
    test.pop(mylist);
    test.pop(mylist);
​
   }
​
​
//压栈办法
  public void push(List list,Object object){
    if(temp <= 9){
      list.add(temp,object);
      System.out.println("添加元素"+object+"成功!");
      temp++;
     }else{
      try {
        throw new StackException("添加元素"+object+"失利,栈已满!!!");
       } catch (StackException e) {
        e.printStackTrace();
       }
     }
​
   }
//   弹栈办法
​
​
  public void pop (List list){
    if(temp > 0){
      Object o1 = list.get(temp-1);
      list.remove(temp-1);
      System.out.println("删去元素"+o1+"成功!!!");
      temp--;
     }else{
      try {
        throw new StackException("弹栈失利,栈已空!!!");
       } catch (StackException e) {
        e.printStackTrace();
       }
     }
   }
}
​