异常
in Java with 0 comment

异常

in Java with 0 comment

异常(Exception)的概述

在项目开发中,即是程序员想要把代码尽善尽美,在系统的运行过程中仍会遇到一些问题,因为很多问题不是靠代码能避免的,比如:客户输入的数据格式,读取文件是否存在,网络是否通畅等。

Java异常事件可以分为两类:

异常(Exception)分类

JVM运行过程:

20200827101725

  1. 编译时异常:执行javac.exe可能会出现的异常;
  2. 运行时异常(java.lang.RuntimeException):执行java.exe可能出现的异常。

异常体系结构

java.lang.Throwable
|----java.lang.Error:一般不编写针对性的代码进行处理
|----java.lang.Exception:可以进行异常的处理
|----编译时异常(checked)
|----IOException
|----FileNotFoundException
|----ClassNotFoundException
|----运行时异常(unchecked,RuntimeException)
|----NullPointerException
|----IndexOutOfBoundsException
|----ArrayIndexOutOfBoundsException
|----StringIndexOutOfBoundsException
|----ClassCastException
|----NumberFormatException
|----InputMismatchException
|----ArithmeticException

20200827101901

常见的编译时异常

  1. FileNotFoundException
  2. IOException
	@Test
	public void test6() {
		File file = new File("http.txt");
		FileInputStream s = new FileInputStream(file);
		
		int read = s.read();
	}

20200826102325

20200826102348

常见的运行时异常

  1. NullPointerException
  2. IndexOutOfBoundsException
    1. ArrayIndexOutOfBoundsException
    2. StringIndexOutOfBoundsException
  3. NumberFormatException
  4. InputMismatchException
  5. ClassCastException
  6. ArithmeticException
	// ClassCastException
	@Test
	public void test5() {
		Object ob = new Date();
		String s = (String) ob;
		System.out.println(s);
	}

	// InputMismatchException
	@Test
	public void test4() {
		Scanner sc = new Scanner(System.in);
		int i = sc.nextInt();
		System.out.println(i);
	}

	// NumberFormatException
	@Test
	public void test3() {
		String a = "123a";
		int i = Integer.valueOf(a);
		System.out.println(i);
	}

	// IndexOutOfBoundsException
	@Test
	public void test2() {
		// ArrayIndexOutOfBoundsException
		String[] arr = new String[3];
		System.out.println(arr[3]);

		// StringIndexOutOfBoundsException
		String a = "aaa";
		System.out.println(a.charAt(44));
	}

	// NullPointerException
	@Test
	public void test1() {
		String[] arr = null;
		System.out.println(arr[2]);
	}

异常(Exception)处理机制

抓抛模型

  1. "抛":程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出。
    关于异常对象的产生:①系统自动产生的异常对象;②手动生成异常得想,并抛出(throw)

  2. “抓”:可以理解为异常的处理方法

    • try_catch_finally
    • throws + 异常类型

try_catch_finally

  1. 格式:
try{
  //可能存在异常的代码块
}catch(异常类型1 异常名1){
  //处理异常类型1的解决方法
}catch(异常类型2 异常名2){
  //处理异常类型2的解决方法
}catch(异常类型3 异常名3){
  //处理异常类型3的解决方法
}
...
finally{
  //最后一定要执行的内容
}
  1. finally是可选的
  2. 使用try将可能出现的代码包装起来,一旦出现异常,就会生成一个对应异常类型的对象,根据此类型的对象去catch中匹配
  3. 一旦异常对象匹配到某一个catch中,就会进行异常的处理。处理完成后就会跳出try-catch结构(没有finally结构的情况下),继续执行其后的代码。
	// NullPointerException
	@Test
	public void test1() {
		try {
			String[] arr = null;
			System.out.println(arr[2]);

			System.out.println("hello1");
		} catch (NullPointerException e) {
			// 出现异常
			System.out.println("出现空指针异常");
		}

		System.out.println("hello2");
	}

20200826103210

  1. catch中的异常类型没有父子类关系的,谁声明在上,谁声明在下都没有关系。

  2. catch中的异常类型有父子类关系的,子类声明在上,父类在下。
    20200826103518

  3. Exception中的常用方法:

    • String getMessage():打印错误内容
    • void printStackTrace():打印详细的错误内容(包括错误行数等)
      20200826103907
  4. try-catch结构可以嵌套

  5. 使用try-catch中声明的变量,出了try结构以后,就不能被调用。

  6. finally中声明的代码一定会被执行,即使是catch中出异常,try中有return语句,catch中有return语句等情况。

  7. 向数据库链接、输入输出流、网络编程socket等资源,JVM是不能自动回收的,我们需要手动的进行关闭,所以资源的释放放到finally中。

public class FinallyTest {
	@Test
	public void test1() {
		FileInputStream s = null;
		try {
			File file = new File("http1.txt");
			s = new FileInputStream(file);

			int read = s.read();
			while (read != -1) {
				System.out.print((char) read);
				read = s.read();
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 释放资源
			try {
				if (s != null)
					s.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
  1. 体会
    • 体会1:我们使用try_catch_finally处理编译时异常,是使得程序在编译时不再报错,但是在运行时仍可能会报错。相当与我们使用try_catch_finally一个编译时可能出现的异常,延迟到运行时出现。
    • 体会2:开发室,由于运行时异常比较常见,所以我们通常不针对运行时异常进行使用try_catch_finally针对编译时异常,我们一定考虑异常的处理。

面试题

问:final\finally\finalize三者的区别。
答:

  1. final:最终的,可以修饰方法(最终的方法,子类不可以重写)、类(不能被继承)、属性(常量)
  2. finally:用在try-catch-finally结构中,最后一定会执行的代码块。
  3. finalize:方法名

throws + 异常类型

  1. throws + 异常类型写在方法的声明处,指明此方法在执行时,可能会抛出的异常;一旦方法体执行时,出现异常,仍会在代码异常处生成一个异常类的对象,此对象满足throws后异常的类型时,就会被抛出。
  2. 异常后续的代码不会再被执行。
public class ThrowsTest {
	public static void main(String[] args) {
		try {
			method2();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void method2() throws IOException {
		//调用有throws异常的方法
		//处理方式有两种,一种是向上抛,一种是try-catch解决
		//调用异常方法的方法抛出的异常类型要大于等于异常方法
		method1();
	}

	// 往上抛出异常
	public static void method1() throws FileNotFoundException, IOException {
		File file = new File("http1.txt");
		FileInputStream s = new FileInputStream(file);
		int read = s.read();
		System.out.println("hello");
	}

}
  1. 体会:try-catch真正的将异常处理了,throws方法只是将异常抛给了调用者,没有真正的处理掉。

重写方法异常处理

public class ThrowsTest2 extends SuperClass {
	//重写方法异常等于被重写方法的异常,可以的
	@Override
	public void method1() throws IOException {
	}
	
	//重写方法异常小于被重写方法的异常,可以的
	@Override
	public void method2() throws FileNotFoundException {
	}
	
	//重写方法异常大于被重写方法的异常,错误的
	@Override
	public void method3() throws Exception {
	}
}

class SuperClass {
	public void method1() throws IOException {
		File file = new File("http1.txt");
		FileInputStream s = new FileInputStream(file);
		int read = s.read();
		System.out.println("hello");
	}
	public void method2() throws IOException {
		File file = new File("http1.txt");
		FileInputStream s = new FileInputStream(file);
		int read = s.read();
		System.out.println("hello");
	}
	public void method3() throws IOException {
		File file = new File("http1.txt");
		FileInputStream s = new FileInputStream(file);
		int read = s.read();
		System.out.println("hello");
	}
}

处理方式的选择

  1. 如果父类的方法没有抛出异常,那么子类重写的方法必须不能使用throws。如果要处理异常必须使用try-catch
  2. 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。

手动抛出异常

  1. 格式:
    • throw new 异常类型();
  2. 常用抛出异常:
    • RuntimeException()
    • Exception()
public class ThrowTest {
	public static void main(String[] args) {
		User user = new User();
		try {
			user.register(-1);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		System.out.println(user);
	}

}

class User {
	// id必须大于0
	private int id;

	public void register(int id) throws Exception {
		if (id > 0) {
			this.id = id;
		} else {
			// 如果在这里不处理小于0的数
			// 那么id都是默认值0,没有变
			// 所以小于0的时候我们就手动抛出异常
			throw new RuntimeException("输入数据非法");
		}
	}

	@Override
	public String toString() {
		return "User [id=" + id + "]";
	}

}

throws和throw区别

  1. throws是抛出异常;声明在方法名后;是异常处理的一种方式;
  2. throw是声明异常;声明在方法体内;是生成异常对象的一种方式。

自定义异常类

  1. 步骤:
    1. 继承Exception结构(一般继承ExceptionRuntimeException)
    2. 定义全局常量serialVersionUID
    3. 定义构造器
public class MyException extends Exception{
	//定义全局常量serialVersionUID
	static final long serialVersionUID = -3311516993124229948L;
	//定义构造器
    public MyException() {
        super();
    }

    public MyException(String message) {
        super(message);
    }
    
}

public class MyExceptionTest {
	public static void main(String[] args) {
		try {
			method();
		} catch (MyException e) {
			e.printStackTrace();
		}
	}

	private static void method() throws MyException{
	}
}


练习

public class EcmDef {
	public static void main(String[] args) {
		double result;
		try {
			result = ecm(args);
			System.out.println(result);
		} catch (ArrayIndexOutOfBoundsException e) {
			e.printStackTrace();
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (ArithmeticException e) {
			e.printStackTrace();
		} catch (EcDef e) {
			System.out.println(e.getMessage());
		}

	}

	private static double ecm(String[] args)
			throws ArrayIndexOutOfBoundsException, NumberFormatException, ArithmeticException, EcDef {

		int a = Integer.parseInt(args[0]);
		int b = Integer.parseInt(args[1]);
		if (a < 0 || b < 0) {
			throw new EcDef("输入数据不能为小数");
		}

		return a / b;
	}
}

class EcDef extends RuntimeException {
	static final long serialVersionUID = -1387516994229948L;

	public EcDef() {
		super();
	}

	public EcDef(String message) {
		super(message);
	}

}