1、什么是异常?
程序在执行的过程中出现的非正常情况,如果不处理最终会导致JVM的非正常停止。
对于语法错误(导致编译不能通过)和逻辑错误(导致无法达到预期效果)不属于异常
2、异常的抛出机制
Java中把不同的异常用不同类表示(异常属于一个对象),一旦发生某种异常,就创建该异常类型的对象,并且抛出(throw)。然后程序员可以捕获(catch)到这个异常对象,并处理;相反如果没有捕获到这个异常对象,它就会导致程序终止。
3、如何对待异常?
(1)遇到错误就终止程序运行
(2)添加异常检测与异常处理来保证程序的健壮性(预处理)
java.lang.Throwable 类是Java异常类根类
Throwable 类具有两个子类 java.lang.Error 和 java.lang.Exception
异常根据程序可能出现的阶段分为:
1、 Error 常见错误:
package com.zwh.shangguigu;/*** @author Bonbons* @version 1.0*/
public class Main {public static void main(String[] args) {fun();}//StackOverFlowErrorpublic static void fun(){fun();}
}
package com.zwh.shangguigu;import java.util.HashSet;
import java.util.Set;/*** @author Bonbons* @version 1.0*/
public class Main {public static void main(String[] args) {//OutOfMemoryError//方法一:直接创建一个超大内存的数组int [] arr = new int[Integer.MAX_VALUE];//方法二:创建一个动态的容器,然后无限添加元素StringBuilder s = new StringBuilder();while(true){s.append("atguigu");}}}
2、 常见的运行时异常:
package com.zwh.shangguigu;import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;/*** @author Bonbons* @version 1.0*/
public class Main {public static void main(String[] args) {//NullPointerExceptionString s = null;System.out.println(s.length());//ClassCastException[类型转换异常] >> 为什么Integer不能转换成String类型//因为存在父子关系的类才能进行强转,Integer的父类是Number,String的父类是ObjectObject o = new Integer(6); //表面编译类型是Object,但是运行类型是IntegerString str = (String)o;//ArrayIndexOutOfBoundsExceptionint [] arr = {1, 3, 4};System.out.println(arr[3]);//InputMismatchExceptionScanner sc = new Scanner(System.in);int x = sc.nextInt(); //输入的时候故意输入一个其他类型的数据//ArithmeticExceptionint y = 1 / 0;}
}
3、常见的编译时异常
package com.atguigu.exception;import org.junit.Test;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class TestCheckedException {@Testpublic void test06() {Thread.sleep(1000);//休眠1秒 InterruptedException}@Testpublic void test07(){Class c = Class.forName("java.lang.String");//ClassNotFoundException}@Testpublic void test08() {Connection conn = DriverManager.getConnection("...."); //SQLException}@Testpublic void test09() {FileInputStream fis = new FileInputStream("尚硅谷Java秘籍.txt"); //FileNotFoundException}@Testpublic void test10() {File file = new File("尚硅谷Java秘籍.txt");FileInputStream fis = new FileInputStream(file);//FileNotFoundExceptionint b = fis.read();//IOExceptionwhile(b != -1){System.out.print((char)b);b = fis.read();//IOException}fis.close();//IOException}
}
(1)利用 try-catch-finally 代码块捕获异常
try{...... //可能产生异常的代码
}
catch( 异常类型1 e ){...... //当产生异常类型1型异常时的处置措施
}
catch( 异常类型2 e ){...... //当产生异常类型2型异常时的处置措施
}
finally{...... //无论是否发生异常,都无条件执行的语句
}
当某段代码可能发生异常,不管这个异常是编译时异常(受检异常)还是运行时异常(非受检异常),我们都可以用try块将它括起来,并在try块下面编写catch分支尝试捕获对应的异常对象。
执行规律:
举例:
@Test
public void test1(){try{String str1 = "atguigu.com";str1 = null;System.out.println(str1.charAt(0));}catch(NullPointerException e){//异常的处理方式1System.out.println("不好意思,亲~出现了小问题,正在加紧解决..."); }catch(ClassCastException e){//异常的处理方式2System.out.println("出现了类型转换的异常");}catch(RuntimeException e){//异常的处理方式3System.out.println("出现了运行时异常");}//此处的代码,在异常被处理了以后,是可以正常执行的System.out.println("hello");
}
插入一个经典笔试题: final、finally、finalize 之间得区别如下:
1、final可以修饰类,变量,方法,修饰的类不能被继承,修饰的变量不能重新赋值,修饰的方法不能被重写。
2、finally用于抛异常,finally代码块内语句无论是否发生异常,都会在执行finally,常用于一些流的关闭。
3、finalize方法用于垃圾回收。一般情况下不需要我们实现finalize,当对象被回收的时候需要释放一些资源,比如socket链接,在对象初始化时创建,整个生命周期内有效,那么需要实现finalize方法,关闭这个链接。但是当调用finalize方法后,并不意味着GC会立即回收该对象,所以有可能真正调用的时候,对象又不需要回收了,然后到了真正要回收的时候,因为之前调用过一次,这次又不会调用了,产生问题。所以不推荐使用finalize方法。
(2)采用声明抛出异常类型(throws)
在编写方法体的代码时,某句代码可能发生某个编译时异常,不处理编译不通过,但是在当前方法中可能不适合处理 或 无法给出合理的处理方法,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
throws 异常列表
public void readFile(String file) throws FileNotFoundException, IOException{FileInputStream fis = new FileInputStream(file);
}
编译时异常举例:
public class TestThrowsCheckedException{public static void main(String [] args){System.out.println("上课......");try{afterClass();}catch(InterruptedException e){e.printStackTrace();System.out.println("准备提前上课");}System.out.println("上课......");}public static void afterClass() throws InterruptedException{for(int i = 10; i >= 1; i--){Thread.sleep(1000);System.out.println("距离上课还有:" + i + "分钟");}}
}
运行时异常举例
import java.util.InputMismatchException;
import java.util.Scanner;public class TestThrowsRuntimeException {public static void main(String[] args) {Scanner input = new Scanner(System.in);try {System.out.print("请输入第一个整数:");int a = input.nextInt();System.out.print("请输入第二个整数:");int b = input.nextInt();int result = divide(a,b);System.out.println(a + "/" + b +"=" + result);} catch (ArithmeticException | InputMismatchException e) {e.printStackTrace();} finally {input.close();}}public static int divide(int a, int b)throws ArithmeticException{return a/b;}
}
(1)方法名必须相同
(2)形参列表必须相同
(3)返回值类型
- 基本数据类型和void:必须相同
- 引用数据类型:<=
(4)权限修饰符:>=,而且要求父类被重写方法在子类中是可见的
(5)不能是static,final修饰的方法
import java.io.IOException;class Father{public void method()throws Exception{System.out.println("Father.method");}
}
class Son extends Father{@Overridepublic void method() throws IOException,ClassCastException {System.out.println("Son.method");}
}
Java中异常对象的生成方式有两种:
throw语句抛出的异常对象和JVM自动创建并抛出的异常对象相同:
package com.atguigu.keyword;public class TestThrow {public static void main(String[] args) {try {System.out.println(max(4,2,31,1));} catch (Exception e) {e.printStackTrace();}try {System.out.println(max(4));} catch (Exception e) {e.printStackTrace();}try {System.out.println(max());} catch (Exception e) {e.printStackTrace();}}public static int max(int... nums){if(nums == null || nums.length==0){throw new IllegalArgumentException("没有传入任何整数,无法获取最大值");}int max = nums[0];for (int i = 1; i < nums.length; i++) {if(nums[i] > max){max = nums[i];}}return max;}
}
(1)为什么需要自定义异常类?
Java总不同的异常类,分别表示着某一种具体的异常情况。那么在开发中总有一些异常是核心类库中没有的,所以要想处理这些异常就需要我们自定义。
(2)如何自定义异常类?
(3)注意事项:
package com.zwh.shangguigu;
public class Main{public static void main(String[] args) {Triangle t = null;//在创建三角形的时候可能会发生异常try{t = new Triangle(2, 2, 3);System.out.println("三角形创建成功");System.out.println(t);}catch (NotTriangleException e){System.out.println("三角形创建失败");e.printStackTrace();}//在修改边的时候也可能会发生异常try{if(t != null){t.setA(1);}System.out.println("a边修改成功");}catch (NotTriangleException e){System.out.println("a边修改失败");e.printStackTrace();}}
}
//自定义异常类
class NotTriangleException extends Exception{//两个构造方法public NotTriangleException(){}public NotTriangleException(String message){super(message);}
}//定义一个三角形类
class Triangle{//三条边private double a, b, c;public Triangle(){}public Triangle(double a, double b, double c) throws NotTriangleException{if(a <= 0 || b <= 0 || c <= 0){throw new NotTriangleException("三角形的三条边必须都是正数");}if(a + b <= c || a + c <= b || b + c <= a){throw new NotTriangleException("不满足三角形的任意两边之和大于第三边");}//数据符合规定this.a = a;this.b = b;this.c = c;}//为私有成员变量提供get和set方法public double getA() {return a;}public double getB() {return b;}public double getC() {return c;}public void setA(double a) throws NotTriangleException{if(a <= 0){throw new NotTriangleException("三角形的任意边都为正数");}if(a + b <= c || a + c <= b || b + c <= a){throw new NotTriangleException("不满足三角形的任意两边之和大于第三边");}this.a = a;}public void setB(double b) throws NotTriangleException{if(b <= 0){throw new NotTriangleException("三角形的任意边都为正数");}if(a + b <= c || a + c <= b || b + c <= a){throw new NotTriangleException("不满足三角形的任意两边之和大于第三边");}this.b = b;}public void setC(double c) throws NotTriangleException{if(c <= 0){throw new NotTriangleException("三角形的任意边都为正数");}if(a + b <= c || a + c <= b || b + c <= a){throw new NotTriangleException("不满足三角形的任意两边之和大于第三边");}this.c = c;}@Overridepublic String toString() {return "Triangle:{" +"a=" + a +"b=" + b +"c=" + c +"}";}
}
补充案例:
package com.zwh.shangguigu.exception_;import java.util.Scanner;/*** @author Bonbons* @version 1.0* 编写应用程序EcmDef.java,接收命令行的两个参数,要求不能输入负数,计算两数相除。* 对数据类型不一致(NumberFormatException)、缺少命令行参数(ArrayIndexOutOfBoundsException、* 除0(ArithmeticException)及输入负数(EcDef 自定义的异常)进行异常处理。* 提示:* (1)在主类(EcmDef)中定义异常方法(ecm)完成两数相除功能。* (2)在main()方法中使用异常处理语句进行异常处理。* (3)在程序中,自定义对应输入负数的异常类(EcDef)。* (4)运行时接受参数 java EcmDef 20 10 //args[0]=“20” args[1]=“10”* (5)Interger类的static方法parseInt(String s)将s转换成对应的int值。* 如:int a=Interger.parseInt(“314”); //a=314;*//*** 代码存在的问题:没有通过控制台的args[]数组输入两个参数,而是采用Scanner输入的 >> 已解决*/
public class EcmDef {public static double ecm(double a, double b){if(b == 0.0){throw new ArithmeticException();}return a / b;}public static void main(String[] args) {
// Scanner sc = new Scanner(System.in);try{
// String num1 = sc.next();
// String num2 = sc.next();double a = Integer.parseInt(args[0]);double b = Integer.parseInt(args[1]);if(a < 0 || b < 0){throw new EcDef("数据为非负数");}//NumberFormatException异常出现在数据类型转换的时候double res = ecm(a, b);System.out.println(a + " / " + b + " = " + res);}catch (EcDef | ArrayIndexOutOfBoundsException | NumberFormatException | ArithmeticException e){e.printStackTrace();}}
}class EcDef extends Exception{public EcDef(){}public EcDef(String message){super(message);}
}