疯狂Java讲义_Chapter10异常处理

1.异常概述

  • Java的异常机制主要依赖于try, catch, finally, throw, throws五个关键字;
  • try块中放置可能引发异常的代码;
  • catch块中放置处理异常的代码;
  • finally块用于回收在try块中打开的物理资源,异常机制保证finally块总被执行;
  • throws用于声明该方法可能抛出的异常;
  • throw用于抛出一个实际异常;

2.异常处理机制

1.使用try-catch捕获异常

  • 异常处理过程:
  1. 抛出异常:try块中的代码出现异常,系统生成异常对象;
  2. 捕获异常:Java环境收到异常对象,在catch中寻找对应对象的解决办法;

import java.io.*;

public class Gobang
{
	// 定义一个二维数组来充当棋盘
	private String[][] board;
	// 定义棋盘的大小
	private static int BOARD_SIZE = 15;
	public void initBoard()
	{
		// 初始化棋盘数组
		board = new String[BOARD_SIZE][BOARD_SIZE];
		// 把每个元素赋为"╋",用于在控制台画出棋盘
		for (var i = 0; i < BOARD_SIZE; i++)
		{
			for (var j = 0; j < BOARD_SIZE; j++)
			{
				board[i][j] = "╋";
			}
		}
	}
	// 在控制台输出棋盘的方法
	public void printBoard()
	{
		// 打印每个数组元素
		for (var i = 0; i < BOARD_SIZE; i++)
		{
			for (var j = 0; j < BOARD_SIZE; j++)
			{
				// 打印数组元素后不换行
				System.out.print(board[i][j]);
			}
			// 每打印完一行数组元素后输出一个换行符
			System.out.print("\n");
		}
	}
	public static void main(String[] args) throws Exception
	{
		var gb = new Gobang();
		gb.initBoard();
		gb.printBoard();
		// 这是用于获取键盘输入的方法
		var br = new BufferedReader(
			new InputStreamReader(System.in));
		String inputStr = null;
		// br.readLine():每当在键盘上输入一行内容按回车,
		// 用户刚刚输入的内容将被br读取到。
		while ((inputStr = br.readLine()) != null)
		{
			try
			{
				// 将用户输入的字符串以逗号作为分隔符,分解成2个字符串
				String[] posStrArr = inputStr.split(",");
				// 将2个字符串转换成用户下棋的坐标
				var xPos = Integer.parseInt(posStrArr[0]);
				var yPos = Integer.parseInt(posStrArr[1]);
				// 把对应的数组元素赋为"●"。
				if (!gb.board[xPos - 1][yPos - 1].equals("╋"))
				{
					System.out.println("您输入的坐标点已有棋子了,"
						+ "请重新输入");
					continue;
				}
				gb.board[xPos - 1][yPos - 1] = "●";
			}
			catch (Exception e)
			{
				System.out.println("您输入的坐标不合法,请重新输入,"
					+ "下棋坐标应以x,y的格式");
				continue;
			}

			gb.printBoard();
			System.out.println("请输入您下棋的坐标,应以x,y的格式:");
		}
	}
}
  •  try块一般放在方法体里面,保证方法执行过程中的容错性;

2.异常类的继承体系

  • try-catch块的{}不可省略;
  • Java的异常类的总接口为Throwable,Error类和Exception类实现了这一接口;
  • error一般是虚拟机错误、系统崩溃等问题,不需要程序捕获;
  • 不同exception子类对应的try-catch
public class DivTest
{
	public static void main(String[] args)
	{
		try
		{
			var a = Integer.parseInt(args[0]);
			var b = Integer.parseInt(args[1]);
			var c = a / b;
			System.out.println("您输入的两个数相除的结果是:" + c );
		}
		catch (IndexOutOfBoundsException ie)
		{
			System.out.println("数组越界:运行程序时输入的参数个数不够");
		}
		catch (NumberFormatException ne)
		{
			System.out.println("数字格式异常:程序只能接受整数参数");
		}
		catch (ArithmeticException ae)
		{
			System.out.println("算术异常");
		}
		catch (Exception e)
		{
			System.out.println("未知异常");
		}
	}
}

import java.util.*;

public class NullTest
{
	public static void main(String[] args)
	{
		Date d = null;
		try
		{
			System.out.println(d.after(new Date()));
		}
		catch (NullPointerException ne)
		{
			System.out.println("空指针异常");
		}
		catch (Exception e)
		{
			System.out.println("未知异常");
		}
	}
}
  •  一般要把小异常放在前面,exception异常放在最后,这是因为程序要先处理对应的小异常,没有办法了才用exception对象处理;
try
{....}
catch
{IOException e1}
catch
{Exception e2}

3.多异常捕获

  • 从Java7开始,一个catch块可以捕获多种类型的异常;

public class MultiExceptionTest
{
	public static void main(String[] args)
	{
		try
		{
			var a = Integer.parseInt(args[0]);
			var b = Integer.parseInt(args[1]);
			var c = a / b;
			System.out.println("您输入的两个数相除的结果是:" + c );
		}
		catch (IndexOutOfBoundsException|NumberFormatException
			|ArithmeticException ie)
		{
			System.out.println("程序发生了数组越界、数字格式异常、算术异常之一");
			// 捕捉多异常时,异常变量默认有final修饰,
			// 所以下面代码有错:
//			ie = new ArithmeticException("test");  // ①
		}
		catch (Exception e)
		{
			System.out.println("未知异常");
			// 捕捉一个类型的异常时,异常变量没有final修饰
			// 所以下面代码完全正确。
			e = new RuntimeException("test");    // ②
		}
	}
}

4.访问异常信息

  • 访问catch块中的异常信息的方法:
  1. getMessage():返回该异常的详细描述字符串;
  2. printStackTrace():将该异常的跟踪栈信息输出到标准错误输出;
  3. printStackTrace(PrintStream p):将异常跟踪信息输出到指定流;
  4. getStackTrace():返回该异常的跟踪栈信息;

import java.io.*;

public class AccessExceptionMsg
{
	public static void main(String[] args)
	{
		try
		{
			var fis = new FileInputStream("a.txt");
		}
		catch (IOException ioe)
		{
			System.out.println(ioe.getMessage());
			ioe.printStackTrace();
		}
	}
}

5.使用finally回收资源

  • Java的垃圾回收机制不会回收任何物理资源,垃圾回收机制只能回收堆内存中对象所占用的内存;
  • finally块的作用就是回收物理资源的,不论try块中的代码是否出错,finally块总会执行;
  • catch块和finally块至少出现其一;

import java.io.*;

public class FinallyTest
{
	public static void main(String[] args)
	{
		FileInputStream fis = null;
		try
		{
			fis = new FileInputStream("a.txt");
		}
		catch (IOException ioe)
		{
			System.out.println(ioe.getMessage());
			// return语句强制方法返回
			return;       // ①
			// 使用exit来退出虚拟机
			// System.exit(1);     // ②
		}
		finally
		{
			// 关闭磁盘文件,回收资源
			if (fis != null)
			{
				try
				{
					fis.close();
				}
				catch (IOException ioe)
				{
					ioe.printStackTrace();
				}
			}
			System.out.println("执行finally块里的资源回收!");
		}
	}
}

 

6.异常处理的嵌套

7.Java9增强的自动关闭资源的try块

  • java7增强了try语句的功能——它允许在try关键字后紧跟一对圆括号,圆括号内可以初始化一个或多个资源(物理资源),try语句在该语句结束时自动关闭资源;
  • 自动关闭资源的try语句相当于包含了隐式的finally块,因此这个语句可以没有catch和finally;
  • Java7把所有的资源类都进行了改写,改写后的资源类都实现了AutoCloseable或者Closeable接口;
import java.io.*;

public class AutoCloseTest2
{
	public static void main(String[] args)
		throws IOException
	{
		// 有final修饰的资源
		final var br = new BufferedReader(
			new FileReader("AutoCloseTest.java"));
		// 没有显式使用final修饰,但只要不对该变量重新赋值,按该变量就是有效的final
		var ps = new PrintStream(new
			FileOutputStream("a.txt"));
		// 只要将两个资源放在try后的圆括号内即可
		try (br; ps)
		{
			// 使用两个资源
			System.out.println(br.readLine());
			ps.println("庄生晓梦迷蝴蝶");
		}
	}
}

 

3.Checked异常和Runtime异常

  • java的异常分为checked异常和Runtime异常,其中checked异常必须显式指定解决办法;
  • checked异常的解决方法:
  1. 使用try-catch块解决;
  2. 如果不知道如何解决,就只能声明抛出异常;
  • throws将异常抛出给上一级处理,上一级无法处理的话交给JVM处理,一般出现要抛出的异常,程序就会终止,并在JVM中打印出异常信息;

4.使用throw抛出异常

1.抛出异常

  • 如果程序中的数据、执行与既定的业务需求不符,这就是一种异常;由于业务抛出的异常,必须由程序员自行决定是否抛出;
  • throw抛出的不是异常类,是异常实例
public class ThrowTest
{
	public static void main(String[] args)
	{
		try
		{
			// 调用声明抛出Checked异常的方法,要么显式捕获该异常
			// 要么在main方法中再次声明抛出
			throwChecked(-3);
		}
		catch (Exception e)
		{
			System.out.println(e.getMessage());
		}
		// 调用声明抛出Runtime异常的方法既可以显式捕获该异常,
		// 也可不理会该异常
		throwRuntime(3);
	}
	public static void throwChecked(int a) throws Exception
	{
		if (a > 0)
		{
			// 自行抛出Exception异常
			// 该代码必须处于try块里,或处于带throws声明的方法中
			throw new Exception("a的值大于0,不符合要求");
		}
	}
	public static void throwRuntime(int a)
	{
		if (a > 0)
		{
			// 自行抛出RuntimeException异常,既可以显式捕获该异常
			// 也可完全不理会该异常,把该异常交给该方法调用者处理
			throw new RuntimeException("a的值大于0,不符合要求");
		}
	}
}

 

2.自定义异常类

  • 用户自定义异常都应该继承Exception基类;定义异常类要提供两个构造器:1是无参构造器;2是带一个字符串参数的构造器,该字符串将作为该异常对象的描述信息:

public class AuctionException extends Exception
{
	// 无参数的构造器
	public AuctionException(){}       // ①
	// 带一个字符串参数的构造器
	public AuctionException(String msg)    // ②
	{
		super(msg);
	}
}

3.catch和throw同时使用

  • catch和throw同时使用程序:
public class AuctionTest
{
	private double initPrice = 30.0;
	// 因为该方法中显式抛出了AuctionException异常,
	// 所以此处需要声明抛出AuctionException异常
	public void bid(String bidPrice)
		throws AuctionException
	{
		var d = 0.0;
		try
		{
			d = Double.parseDouble(bidPrice);
		}
		catch (Exception e)
		{
			// 此处完成本方法中可以对异常执行的修复处理,
			// 此处仅仅是在控制台打印异常跟踪栈信息。
			e.printStackTrace();
			// 再次抛出自定义异常
			throw new AuctionException("竞拍价必须是数值,"
				+ "不能包含其他字符!");
		}
		if (initPrice > d)
		{
			throw new AuctionException("竞拍价比起拍价低,"
				+ "不允许竞拍!");
		}
		initPrice = d;
	}
	public static void main(String[] args)
	{
		var at = new AuctionTest();
		try
		{
			at.bid("df");
		}
		catch (AuctionException ae)
		{
			// 再次捕捉到bid方法中的异常。并对该异常进行处理
			System.err.println(ae.getMessage());
		}
	}
}

 

4.使用throw语句抛出异常​​​​​​​(387)

5.异常链

  • 异常转译:程序捕获原始异常,抛出一个业务异常,其中包含了对异常的提示信息;这种行为也被称为异常链:
public static void main(String[] args)
{
    try
     {
        ...
         }
    catch
     {
        ...//原始异常信息
        throw new SalException("自定义异常");    //向客户展示的是无关于系统的异常
        }
  }
public class SalException extends Exception
{
	public SalException(){}
	public SalException(String msg)
	{
		super(msg);
	}
	
}

 

5.Java的异常跟踪栈

  • 异常对象的printStackTrace()用于打印异常的跟踪信息

6.异常处理规则​​​​​​​(391)

  1. 不要使用过于庞大的try块;
  2. 不要忽略捕获的异常;
  3. 不要过度使用异常;

​​​​​​​

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值