Java学习笔记 --异常处理

一、异常概述
  • 异常:程序中发生的不正常情况('开发过程中的语法错误逻辑错误不是异常)
  • 异常可分为两类
    Error: Java虚拟机无法解决的严重问题,不编写代码进行处理
                 Eg. JVM内部错误、资源损耗严重、内存溢出…(StackOverFlowError、OOM…)
    Exception: 其他因为编程错误或偶然的外在因素导致的一般问题,可以使用针对性代码进行处理
                 - 编译时异常 (受检异常):执行javac.exe命名时,可能出现的异常
                    IOExecption (FileNotFoundException)
                    ClassNotFoundException
                 - 运行时异常 (非受检异常) RuntimeException执行java.exe命名时,可能出现的异常
                    NullPointerException 空指针
                    ArrayIndexOutOfBoundsException 角标越界
                    ClassCastException 类型转换异常
                    NumberFormatException 数值转换异常
                    InputMismatchException 输入不匹配
                    ArithmaticException…
public class ErrorTest1 {
	public static void main(String[] args) {
		// java.lang.StackOverflowError
		main(args);
		
		// java.lang.OutOfMemoryError: Java heap space
		Integer[] arr = new Integer[1024 * 1024 * 1024];
	}
}
public class ExeptionTest1 {
	//编译时异常 --> 执行后不会生成字节码
	public void test() {
		File file = new File("hello.txt");
		FileInputStream fis = new FileInputStream(file); //Unhandled exception type FileNotFoundException
		int data = fis.read();
		while(data != -1) {
			System.out.print((char)data);
			data = fis.read();
		}
		fis.close();
	}

	//运行时异常 --> 编译会正常编译、生成字节码文件
	public void classCastExceptionTest() {
		String str1 = new Date(); // 编译时报错

		Object obj = new Date(); // 运行时异常
		String str2 = (String) obj;
	}

	public void numberFormatExceptionTest() {
		String str1 = "123";
		Integer.parseInt(str1); // OK

		String str2 = "abc";
		Integer.parseInt(str2); // NumberFormatException
	}

	public void inputMismatchExceptionTest() {
		Scanner scanner = new Scanner(System.in);
		int score = scanner.nextInt(); // 输入abc InputMismatchException
	}

	public void arithmeticExceptionTest() {
		int a = 10;
		int b = 0;
		System.out.println(a / b);
	}
}

在这里插入图片描述在这里插入图片描述

二、异常处理机制
  • 过程一:抛出异常
    程序在正常执行过程中,一旦出现异常,就会在异常代码处生成对应异常类的对象,并将此对象抛出,一旦抛出异常,其后的代码不再执行
          关于异常的产生: 1. 系统自动生成的异常对象
                                     2. 手动生成异常对象,并抛出(throw)
  • 过程二:抓异常(处理机制: try-catch-finally 、throws
  • try-catch-finally 真正的将异常处理了
    throws的方式只是将异常抛给了方法的调入者,并没有真正将异常处理掉
    // throw 表示抛出一个异常类的对象,生成异常对象的过程,声明在方法体内
    // throws 属于异常处理的一种方式,声明在方法的声明处
    在这里插入图片描述
(一)try-catch-finally
   try-catch
  • 使用try将可能出现异常的代码包起来,在执行过程中出现异常,会生成异常类的对象,根据异常对象的类型,去catch中进行匹配
  • 一旦try中对象匹配到某一个catch时,就进入catch进行处理,一旦处理完成便退出当前try-catch结构,继续执行其后的代码。
  • 如果catch中的异常类型没有父子关系,则位置先后无所谓
    如果carch中的异常类型存在父子关系,则子类catch必须写在父类之前
try {
		// 可能出现异常的代码
} catch(异常类型1 变量1) {
		//处理异常1的代码
} catch (异常类型1 变量2) {
		//处理异常2的代码
}
...
finally {
		//一定会去执行的代码 (选择性写finally)
}
	public void numberFormatExceptionTest() {
		String str1 = "123";
		Integer.parseInt(str1); // OK

		String str2 = "abc";
		try {
			Integer.parseInt(str2); // NumberFormatException
			System.out.println("inTry"); // 不会执行
		} catch (NumberFormatException e) {
			System.out.println("NumberFormatException");
		}
		System.out.println("out of try-catch"); //会执行
	}
  • 常用异常对象的处理方式:
    1)String getMessage()
    2)printStackTrace()
try {
...
} catch (Exception e) {
	System.out.println(e.getMessage());
		//or
	e.printStackTrace();
}
  • 在try{…}中定义的变量,在try{…}结构外不能被调用
	public void test() {
		try {
			int num = 20;
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		System.out.println(num);// num cannot be resolved to a variable
	}
	public void test() {
		int num = 10;
		try {
			num = 20;
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		System.out.println(num); // 20
	}
    finally
  • finally结构时可选的
  • finally中声明的是一定会被执行的代码,即使catch中出现了异常、try中出现return语句、catch中有return语句等情况下一定会被执行。
	public void test1() {
		try {
			int a = 10;
			int b = 0;
			System.out.println(a / b);
		} catch (ArithmeticException e) {
			e.printStackTrace();
			Object obj = new Date();
			String str2 = (String) obj;
		} finally {
			System.out.println("抓住了!"); // 执行
		}
		System.out.println("抓住了!!"); // 未执行
	}
	public int test2() {
		try {
			int[] arr = new int[10];
			System.out.println(arr[10]);
			return 1;
		} catch (ArrayIndexOutOfBoundsException e) {
			e.printStackTrace();
			return 2;
		} finally {
			System.out.println("一定会被执行");
		}
	}
  • 像数据库连接、输入输出流、网络编程Socket资源,JVM是不能自动回收的,需要我们自己手动的进行资源释放,资源释放操作需要声明在finally中。
  • try-catch结构可以嵌套
	public void test() {
		FileInputStream fis = null;
		try {
			File file = new File("hello.txt");
			fis = new FileInputStream(file); // Unhandled exception type FileNotFoundException
			int data = fis.read();
			while (data != -1) {
				System.out.print((char) data);
				data = fis.read();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (fis != null)
					fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
  • 使用try-catch处理编译时异常,使得程序在编译时不报错,在运行时仍可能报错
    -> 将编译时异常延迟到运行时出现
  • 运行时异常一般不用try-catch处理,编译时异常需考虑try-catch异常处理
(二)throws + 异常类型
  • “thorws +异常类型” 写在方法声明处,指明此方法执行时,可能会抛出的异常类型
    一旦方法执行时出现异常,会在出现异常的代码处生成一个异常类的对象,此对象满足throws后的异常类型时,就会被抛出
public class ExceptionTest2 {
	
	public static void main(String[] args) {
		ExceptionTest2 test = new ExceptionTest2();
		try {
			test.method2();
		} catch (FileNotFoundException e) {
			System.out.println("处理FileNotFoundException的方法");
		} catch (IOException e) {
			System.out.println("处理IOException的方法");
		}
		
		test.method3();
	}

	public void method3() {
		try {
			method2();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} 
	}
	public void method2() throws FileNotFoundException, IOException {
		method1();
	}
	
	public void method1() throws FileNotFoundException, IOException {
		File file = new File("hello.txt");
		FileInputStream fis = new FileInputStream(file); // Unhandled exception type FileNotFoundException
		
		int data = fis.read();
		while (data != -1) {
			System.out.print((char) data);
			data = fis.read();
		}
		fis.close();
	}
}
(三)throw 手动生成一个异常对象
public class ThrowTest {
	public static void main(String[] args) {
		Student s = new Student();
		s.register(-1001);
		System.out.println(s);
	}
}

class Student {
	private int id;

	public void register(int id) {
		if (id > 0) {
			this.id = id;
		} else {
			throw new RuntimeException("输入的学号非法");
		}
	}
}
public class ThrowTest {
	public static void main(String[] args) {
		try {
			Student s = new Student();
			s.register(-1001);
			System.out.println(s);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}
}

class Student {
	private int id;

	public void register(int id) throws Exception { // throws体现了异常处理
		if (id > 0) {
			this.id = id;
		} else {
			throw new Exception("输入的学号非法"); // throw体现了生成一个异常对象
		}
	}
}
三、使用场景
  • 在方法的重写中,子类重写方法抛出异常类型不能大于父类的方法抛出异常的类型
    开发中如果父类中方法没有throws方法抛出异常,则子类方法中不能使用throws
    如果子类重写方法有异常,只能使用try-catch-finally方式处理异常
  • 执行的方法a中,先后又调用了几个有递进关系的代码,我们建议这几个有递进关系的代码中使用throws,而在方法a中使用try-catch结构进行处理
public class OverrideTest {
	public static void main(String[] args) {
		OverrideTest test = new OverrideTest();
		test.display(new SubClass());
	}

	public void display(SuperClass s) {
		try {
			s.method();
		} catch (IOException e) { //多态时若子类对象抛出异常大于父类异常,则无法被catch,造成错误
			e.printStackTrace();
		}
	}
}

class SuperClass {
	public void method() throws IOException {

	}
}

class SubClass extends SuperClass {
	public void method() throws FileNotFoundException {

	}
}
四、用户自定义异常类
  1. 继承现有的异常结构:RuntimeException、Exception…
  2. 提供全局常量:serialVersionUID
  3. 提供重载构造器
public class MyException extends RuntimeException {
	static final long serialVersionUID = -7034897190745766939L;

	public MyException() {

	}

	public MyException(String msg) {
		super(msg);
	}
}
class Student {
	private int id;

	public void register(int id) { //若MyException继承自Exception类 需在此处throws Exception
		if (id > 0) {
			this.id = id;
		} else {
			throw new MyException("输入的学号非法");
		}
	}
}
五、练习
public class RuntimeExceptionDemo {
	public static void main(String[] args) {
		try {
			methodA();
		} catch (RuntimeException e) {
			System.out.println(e.getMessage());
		}
		methodB();
	}

	static void methodA() {
		try {
			System.out.println("进入方法A");
			throw new RuntimeException("制造异常");
		} finally {
			System.out.println("使用方法A的final");
		}
	}

	static void methodB() {
		try {
			System.out.println("进入方法B");
			return;
		} finally {
			System.out.println("使用方法B的final");
		}
	}
}
进入方法A
使用方法A的final
制造异常
进入方法B
使用方法B的final
  • 接受命令行参数:右键 -> RunConfigurations -> Arguments -> 在Program arguments中填入参数
public class EcmDef {
	public static void main(String[] args) {
		try {
			int a = Integer.parseInt(args[0]);
			int b = Integer.parseInt(args[1]);
			int result = ecm(a, b);
			System.out.println(result);
		} catch (NumberFormatException e) {
			System.out.println("数据类型错误");
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("缺少命令行参数");
		} catch (ArithmeticException e) {
			System.out.println("分母为0异常");
		} catch (EcDef e) {
			System.out.println(e.getMessage());
		}
	}

	public static int ecm(int a, int b) throws EcDef {
		if (a < 0 || b < 0) {
			throw new EcDef("分子or分母为负数");
		}
		return a / b;
	}
}

class EcDef extends Exception {
	static final long serialVersionUID = -7034897190745766939L;

	public EcDef() {
	}

	public EcDef(String msg) {
		super(msg);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值