4、异常机制

4. 异常机制

1. 异常Exception的概念

1、导引问题

   实际工作中,遇到的情况不可能是非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求、你的程序要打开某个文件,这个文件可能不存在或者文件格式不对,你要读取数据库的数据,数据可能是空的等。我们的程序再跑着,内存或硬盘可能满了。等等。

   软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们叫异常,英文是:Exception,意思是例外。这些,例外情况,或者叫异常,怎么让我们写的程序做出合理的处理。而不至于程序崩溃。

2、常见的错误:

(1)用户输入错误

(2)设备错误:硬件问题,比如打印机关掉、服务器问题

(3)物理限制:磁盘满了。

(4)代码限制:数组下标越界等

(5)设计良好的程序应该在异常发生时提供处理这些错误的方法,使得程序不会因为异常的发生而终断或产生不可预见的结果。

3、如果没有异常处理机制,那么:

//将d:/a.txt复制到e:/a.txt

if("d:/a.txt"这个文件存在){

      if(e盘的空间大于a.txt文件长度){

           if(文件复制一半IO流断掉){

                 停止copy,输出:IO流出问题!

           }else{

                 copyFile("d:/a.txt","e:/a.txt");

           }

      }else{

           输出:e盘空间不够存放a.txt!

      }

}else{

      输出:a.txt不存在!

}    

两个坏处:

(1)逻辑代码和错误处理代码放一起!

(2)程序员本身需要考虑的例外情况较复杂,对程序员本身要求较高!

4、异常(Exception)的概念          

JAVA是采用面向对象的方式来处理异常的。处理过程:

(1)抛出异常:在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径,并把异常对象提交给JRE。

(2)捕获异常:JRE得到该异常后,寻找相应的代码来处理该异常。JRE在方法的调用栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止。

异常分类

   JDK 中定义了很多异常类,这些类对应了各种各样可能出现的异常事件,所有异常对象都是派生于Throwable类的一个实例。如果内置的异常类不能够满足需要,还可以创建自己的异常类。

5、Error

(1)Error类层次描述了Java运行时系统内部错误和资源耗尽错误。这类错误是我们无法控制的,同时也是非常罕见的错误。所以在编程中,不去处理这类错误。

(2)Error表明系统JVM已经处于不可恢复的崩溃状态中。我们不需要管他。

(3)Error和Exception的区别:

    我开着车走在路上,一头猪冲在路中间,我刹车。这叫一个异常。

    我开着车在路上,发动机坏了,我停车,这叫错误。系统处于不可恢复的崩溃状态。发动机什么时候坏?我们普通司机能管吗?不能。发动机什么时候坏是汽车厂发动机制造商的事。

(4)Exception

      所有异常类的父类,其子类对应了各种各样可能出现的异常事件。

6Runtime Exception(unchecked Exception)

   出现RuntimeException就一定是你的问题可以不捕获因为小心点这些异常是可以避免的。 派生于RuntimeException的异常。是一类特殊的异常,如被 0 除、数组下标超范围等,其产生比较频繁,处理麻烦,如果显式的声明或捕获将会对程序可读性和运行效率影响很大。因此由系统自动检测并将它们交给缺省的异常处理程序(用户可不必对其处理)。这类异常通常是由编程错误导致的,因为只有小心点,这些异常都是可以避免的,所以在编写程序时,并不要求必须使用异常处理机制来处理这类异常,所有这类异常都继承自java.lang.RuntimeException。

   注意:RuntimeException这个名字很容易让人产生错误影响。事实上,我们这里讨论的所有Error、Exception都是运行时发生的。

常见的有:

(1)ArithmeticException

如试图除以0

           if(b!=0){

                 int i = 1/b;

           }else{

                 System.out.println("不能用0做除数!!");

           }   

(2)NullPointerException

当程序访问一个空对象的成员变量或方法,访问一个空数组的成员时发生。怎么处理?

//        TestException te = null;

           TestException te = new TestException();

           if(te!=null){

                 te.test1(2);

           }   

(3)ClassCastException对象类型不匹配

      void test2(Object obj){

           if(obj instanceof Man){

                 Man man = (Man) obj;

           }

      }   

(4)ArrayIndexOutOfBoundsException

访问的元素下表超过数组长度

           int[] a = {1,2,3};

           int idx = 3;         

           if(idx<=a.length-1){

                 System.out.println(a[idx]);

           }   

(5)NumberFormatException

数字格式异常

           String str = "1234abcf";

           Integer i = new Integer(str);

//        Integer i2 = Integer.parseInt(str); 

7Checked Exception:

   所有不是Runtime Exception的异常统称为Checked Exception又被称为已检查异常 这类异常的产生不是程序本身的问题通常由外界因素造成的。为了预防这些异常产生时造成程序的中断或得到不正确的结果,Java要求编写可能产生这类异常的程序代码时一定要去做异常的处理。

为什么叫已检查异常?

      Checked,运行前已经检查过。就是编译器会检查是否对这些异常做了处理。

          unchecked异常指的是编译器不会做检查。error是unchecked的,因为我们无法控制他;RuntimeException太频繁了,并且编译器也无法检查。

2. 异常Exception

/*

 * 异常:程序出现了不正常的情况。

 *

 * 举例:今天天气很好,班长出去旅游。骑着自行车,去山里面呼吸新鲜空气。

 *       问题1:山路塌陷了,班长及时停住了,但是过不去了。严重的问题。

 *       问题2:班长出门推自行车,发现气没了,把气吹起来。出发前就应该检查的问题。

 *       问题3:班长骑着车在山路上惬意的行驶着,山路两边是有小石子的,中间是平坦的水泥路。

 *         一直在平坦的水泥路上行驶是没有任何问题的,但是呢,他偏偏喜欢骑到小石子上,结果爆胎了。旅游的过程中出现的问题。

 *         no zuo no die。

 *

 * 程序的异常:Throwable

 *       严重问题:Error 我们不处理。这种问题一般都是很严重的,比如说内存溢出。

 *       问题:Exception

 *         编译期问题:不是RuntimeException的异常 必须进行处理的,因为你不处理,编译就不能通过。

 *         运行期问题:RuntimeException  这种问题我们也不处理,因为是你的问题,而且这个问题出现肯定是我们的代码不够严谨,需要修正代码的。

 *

 * 如何程序出现了问题,我们没有做任何处理,最终jvm会做出默认的处理。

 * 把异常的名称,原因及出现的问题等信息输出在控制台。

 * 同时会结束程序。

 */

public class ExceptionDemo {

   public static void main(String[] args) {

      //第一阶段

      int a = 10;

      // int b = 2;

      int b = 0;

      System.out.println(a / b);

     

      //第二阶段

      System.out.println("over");

   }

}
/*

 * 我们自己如何处理异常呢?

 * A:try...catch...finally

 * B:throws 抛出

 *

 * try...catch...finally的处理格式:

 *       try {

 *         可能出现问题的代码;

 *       }catch(异常名 变量) {

 *         针对问题的处理;

 *       }finally {

 *         释放资源;

 *       }

 *

 * 变形格式:

 *       try {

 *         可能出现问题的代码;

 *       }catch(异常名 变量) {

 *         针对问题的处理;

 *       }

 *

 * 注意:

 *       A:try里面的代码越少越好

 *       B:catch里面必须有内容,哪怕是给出一个简单的提示

 */

public class ExceptionDemo {

   public static void main(String[] args) {

      // 第一阶段

      int a = 10;

      // int b = 2;

      int b = 0;


      try {

        System.out.println(a / b);

      } catch (ArithmeticException ae) {

        System.out.println("除数不能为0");

      }


      // 第二阶段

      System.out.println("over");

   }

}
/*

 * A:一个异常

 * B:二个异常的处理

 *       a:每一个写一个try...catch

 *       b:写一个try,多个catch

 *         try{

 *            ...

 *         }catch(异常类名 变量名) {

 *            ...

 *         }

 *         catch(异常类名 变量名) {

 *            ...

 *         }

 *         ...

 *

 *         注意事项:

 *            1:能明确的尽量明确,不要用大的来处理。

 *            2:平级关系的异常谁前谁后无所谓,如果出现了子父关系,父必须在后面。

 *

 * 注意:

 *       一旦try里面出了问题,就会在这里把问题给抛出去,然后和catch里面的问题进行匹配,

 *       一旦有匹配的,就执行catch里面的处理,然后结束了try...catch

 *       继续执行后面的语句。

 */

public class ExceptionDemo2 {

   public static void main(String[] args) {

      // method1();


      // method2();


      // method3();


      method4();

   }


   public static void method4() {

      int a = 10;

      int b = 0;

      int[] arr = { 1, 2, 3 };


      // 爷爷在最后

      try {

        System.out.println(a / b);

        System.out.println(arr[3]);

        System.out.println("这里出现了一个异常,你不太清楚是谁,该怎么办呢?");

      } catch (ArithmeticException e) {

        System.out.println("除数不能为0");

      } catch (ArrayIndexOutOfBoundsException e) {

        System.out.println("你访问了不该的访问的索引");

      } catch (Exception e) {

        System.out.println("出问题了");

      }


      // 爷爷在前面是不可以的

      // try {

      // System.out.println(a / b);

      // System.out.println(arr[3]);

      // System.out.println("这里出现了一个异常,你不太清楚是谁,该怎么办呢?");

      // } catch (Exception e) {

      // System.out.println("出问题了");

      // } catch (ArithmeticException e) {

      // System.out.println("除数不能为0");

      // } catch (ArrayIndexOutOfBoundsException e) {

      // System.out.println("你访问了不该的访问的索引");

      // }


      System.out.println("over");

   }


   // 两个异常的处理

   public static void method3() {

      int a = 10;

      int b = 0;

      int[] arr = { 1, 2, 3 };


      try {

        System.out.println(arr[3]);

        System.out.println(a / b);

        // System.out.println(arr[3]);

      } catch (ArithmeticException e) {

        System.out.println("除数不能为0");

      } catch (ArrayIndexOutOfBoundsException e) {

        System.out.println("你访问了不该的访问的索引");

      }


      System.out.println("over");

   }


   // 两个异常

   public static void method2() {

      int a = 10;

      int b = 0;

      try {

        System.out.println(a / b);

      } catch (ArithmeticException e) {

        System.out.println("除数不能为0");

      }


      int[] arr = { 1, 2, 3 };

      try {

        System.out.println(arr[3]);

      } catch (ArrayIndexOutOfBoundsException e) {

        System.out.println("你访问了不该的访问的索引");

      }


      System.out.println("over");

   }


   // 一个异常

   public static void method1() {

      // 第一阶段

      int a = 10;

      // int b = 2;

      int b = 0;


      try {

        System.out.println(a / b);

      } catch (ArithmeticException ae) {

        System.out.println("除数不能为0");

      }


      // 第二阶段

      System.out.println("over");

   }

}

 

 

/*

 * 编译时异常和运行时异常的区别

 * 编译期异常:Java程序必须显示处理,否则程序就会发生错误,无法通过编译

 * 运行期异常:无需显示处理,也可以和编译时异常一样处理

 */

public class ExceptionDemo {

   public static void main(String[] args) {

      // int a = 10;

      // int b = 0;

      // if (b != 0) {

      // System.out.println(a / b);

      // }

 

      String s = "2014-11-20";

      // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

      // Date d = sdf.parse(s);

      try {

        Date d = sdf.parse(s);

        System.out.println(d);

      } catch (ParseException e) {

        // e.printStackTrace();

        System.out.println("解析日期出问题了");

      }

   }

}

/*

 * JDK7出现了一个新的异常处理方案:

 *       try{

 *

 *       }catch(异常名1 | 异常名2 | ...  变量 ) {

 *         ...

 *       }

 *

 *       注意:这个方法虽然简洁,但是也不够好。

 *         A:处理方式是一致的。(实际开发中,好多时候可能就是针对同类型的问题,给出同一个处理)

 *       B:多个异常间必须是平级关系。

 */

public class ExceptionDemo3 {

   public static void main(String[] args) {

      method();

   }


   public static void method() {

      int a = 10;

      int b = 0;

      int[] arr = { 1, 2, 3 };


      // try {

      // System.out.println(a / b);

      // System.out.println(arr[3]);

      // System.out.println("这里出现了一个异常,你不太清楚是谁,该怎么办呢?");

      // } catch (ArithmeticException e) {

      // System.out.println("除数不能为0");

      // } catch (ArrayIndexOutOfBoundsException e) {

      // System.out.println("你访问了不该的访问的索引");

      // } catch (Exception e) {

      // System.out.println("出问题了");

      // }


      // JDK7的处理方案

      try {

        System.out.println(a / b);

        System.out.println(arr[3]);

      } catch (ArithmeticException | ArrayIndexOutOfBoundsException e) {

        System.out.println("出问题了");

      }


      System.out.println("over");

   }


}

/*

 * 有些时候,我们是可以对异常进行处理的,但是又有些时候,我们根本就没有权限去处理某个异常。

 * 或者说,我处理不了,我就不处理了。

 * 为了解决出错问题,Java针对这种情况,就提供了另一种处理方案:抛出。

 *

 * 格式:

 *       throws 异常类名

 *       注意:这个格式必须跟在方法的括号后面。

 *

 * 注意:

 *       尽量不要在main方法上抛出异常。

 *       但是我讲课为了方便我就这样做了。

 *

 * 小结:

 *       编译期异常抛出,将来调用者必须处理。

 *       运行期异常抛出,将来调用可以不用处理。

 */

public class ExceptionDemo {

   public static void main(String[] args) {

      System.out.println("今天天气很好");

      try {

        method();

      } catch (ParseException e) {

        e.printStackTrace();

      }

      System.out.println("但是就是不该有雾霾");


      method2();

   }


   // 运行期异常的抛出

   public static void method2() throws ArithmeticException {

      int a = 10;

      int b = 0;

      System.out.println(a / b);

   }


   // 编译期异常的抛出

   // 在方法声明上抛出,是为了告诉调用者,你注意了,我有问题。

   public static void method() throws ParseException {

      String s = "2014-11-20";

      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

      Date d = sdf.parse(s);

      System.out.println(d);

   }

}
/*

 * throw:如果出现了异常情况,我们可以把该异常抛出,这个时候的抛出的应该是异常的对象。

 *

 * throws和throw的区别(面试题)

   throws

      用在方法声明后面,跟的是异常类名

      可以跟多个异常类名,用逗号隔开

      表示抛出异常,由该方法的调用者来处理

      throws表示出现异常的一种可能性,并不一定会发生这些异常

   throw

      用在方法体内,跟的是异常对象名

      只能抛出一个异常对象名

      表示抛出异常,由方法体内的语句处理

      throw则是抛出了异常,执行throw则一定抛出了某种异常

 */

public class ExceptionDemo {

   public static void main(String[] args) {

      // method();

     

      try {

        method2();

      } catch (Exception e) {

        e.printStackTrace();

      }

   }


   public static void method() {

      int a = 10;

      int b = 0;

      if (b == 0) {

        throw new ArithmeticException();

      } else {

        System.out.println(a / b);

      }

   }


   public static void method2() throws Exception {

      int a = 10;

      int b = 0;

      if (b == 0) {

        throw new Exception();

      } else {

        System.out.println(a / b);

      }

   }

}
/*

 * finally:被finally控制的语句体一定会执行

 * 注意:如果在执行到finally之前jvm退出了,就不能执行了。

 *

 * A:格式

 *       try...catch...finally...

 * B:用于释放资源,在IO流操作和数据库操作中会见到

 */

public class FinallyDemo {

   public static void main(String[] args) {

      String s = "2014-11-20";

      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");


      Date d = null;

      try {

        // System.out.println(10 / 0);

        d = sdf.parse(s);

      } catch (ParseException e) {

        e.printStackTrace();

        System.exit(0);

      } finally {

        System.out.println("这里的代码是可以执行的");

      }


      System.out.println(d);

   }

}
/*

 * 1:final,finally和finalize的区别

 * final:最终的意思,可以修饰类,成员变量,成员方法

 *       修饰类,类不能被继承

 *       修饰变量,变量是常量

 *       修饰方法,方法不能被重写

 * finally:是异常处理的一部分,用于释放资源。

 *       一般来说,代码肯定会执行,特殊情况:在执行到finally之前jvm退出了

 * finalize:是Object类的一个方法,用于垃圾回收

 *

 * 2:如果catch里面有return语句,请问finally里面的代码还会执行吗?

 *   如果会,请问是在return前,还是return后。

 *       会。前。

 *

 *     准确的说,应该是在中间。

 *

 * 3:try...catch...finally的格式变形

 *       A:try...catch...finally

 *       B:try...catch

 *       C:try...catch...catch...

 *       D:try...catch...catch...finally

 *       E:try...finally

 *         这种做法的目前是为了释放资源。

 */

public class FinallyDemo2 {

   public static void main(String[] args) {

      System.out.println(getInt());

   }


   public static int getInt() {

      int a = 10;

      try {

        System.out.println(a / 0);

        a = 20;

      } catch (ArithmeticException e) {

        a = 30;

        return a;

        /*

         * return a在程序执行到这一步的时候,这里不是return a而是return 30;这个返回路径就形成了。

         * 但是呢,它发现后面还有finally,所以继续执行finally的内容,a=40

         * 再次回到以前的返回路径,继续走return 30;

         */

      } finally {

        a = 40;

        return a;//如果这样结果就是40了。

      }

      // return a;

   }

}

/*

 * java不可能对所有的情况都考虑到,所以,在实际的开发中,我们可能需要自己定义异常。

 * 而我们自己随意的写一个类,是不能作为异常类来看的,要想你的类是一个异常类,就必须继承自Exception或者RuntimeException

 *

 * 两种方式:

 * A:继承Exception

 * B:继承RuntimeException

 */

public class MyException extends Exception {

   public MyException() {

   }


   public MyException(String message) {

      super(message);

   }

}


// public class MyException extends RuntimeException {

//

// }
/*

 * 自定义异常测试类

 */

public class StudentDemo {

   public static void main(String[] args) {

      Scanner sc = new Scanner(System.in);

      System.out.println("请输入学生成绩:");

      int score = sc.nextInt();


      Teacher t = new Teacher();

      try {

        t.check(score);

      } catch (MyException e) {

        e.printStackTrace();

      }

   }

}

/

*

 * 异常注意事项:

 * A:子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。(父亲坏了,儿子不能比父亲更坏)

 * B:如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常

 * C:如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws

 */

public class ExceptionDemo {


}


class Fu {

   public void show() throws Exception {

   }


   public void method() {

   }

}


class Zi extends Fu {

   @Override

   public void show() throws ArithmeticException {


   }


   @Override

   public void method() {

      // String s = "2014-11-20";

      // SimpleDateFormat sdf = new SimpleDateFormat();

      // Date d = sdf.parse(s);

      // System.out.println(d);

   }

}

/

*

   封装和private的应用:

      A:把成员变量用private修饰

      B:提高对应的getXxx()和setXxx()方法

*/

//定义学生类

class Student {

   //姓名

   private String name;

   //年龄

   private int age;

  

   //姓名获取值

   public String getName() {

      return name;

   }

  

   //姓名设置值

   public void setName(String n) {

      name = n;

   }

  

   //年龄获取值

   public int getAge() {

      return age;

   }

  

   //年龄赋值

   public void setAge(int a) {

      age = a;

   }

}


//测试类

class StudentTest {

   public static void main(String[] args) {

      //创建学生对象

      Student s = new Student();

     

      //使用成员变量

      //错误:被私有修饰了,外界不能直接访问了

      //System.out.println(s.name+"---"+s.age);

      System.out.println(s.getName()+"---"+s.getAge());

     

      //给成员变量赋值

      //s.name = "林青霞";

      //s.age = 27;

      //通过方法给赋值

      s.setName("林青霞");

      s.setAge(27);

      System.out.println(s.getName()+"---"+s.getAge());

   }

}
/*

   private:

      是一个权限修饰符

      可以修饰成员变量和成员方法

      被其修饰的成员只能在本类中被访问

*/

class Demo {

   //int num = 10;

   //用private修饰

   private int num = 10;

  

   public void show() {

      System.out.println(num);

   }

  

   private void method() {

      System.out.println("method");

   }

  

   public void function() {

      method();

   }

}


class PrivateDemo {

   public static void main(String[] args) {

      Demo d = new Demo();

      //不能方法私有的成员变量

      //System.out.println(d.num);

      d.show();

      //不能访问私有的成员方法

      //d.method();

      d.function();

   }

}

猜你喜欢

转载自blog.csdn.net/qq_35234765/article/details/84967440