概述
/*
* 如果程序报错,会终止程序生命周期执行(错误代码后代码不再执行)
* 1 异常机制
* 异常时错误的一种说法
* 在Java中有一个模拟所有异常和错误的类(Throwable),所有异常都必须继承这个类
* 目的:增强程序的鲁棒性、健壮性
*
* 异常对于程序员在编程时,是对于某些高风险的操作规定了一个提醒机制,对于系统来说
* 就是处理异常的触发机制
*
* 2 异常的处理形式
* try...catch... : 解决异常,一般用在客户端
* throws : 抛出异常,一般用在类库端(服务端)
* throw : 制造异常,异常源点,创建一个异常对象
* throw new 异常类(错误信息);
*
* if..else.. 进行判断,可以解决大多数预知错误,对于无法预料情况,使用try...catch...解决
*
* 3 不同异常机制的选择
* 有些异常不想处理,或无法处理,或不知道如何处理,一般使用throws把异常抛出给调用处
* 知道怎么处理,直接使用try...catch...处理异常,一般在main方法中
*
* 4 finally语句块
* 必须执行的语句块
*
* 5 异常机制继承体系
* 最大异常类:Throwable
* 直接两个子类:Error(系统内部错误,一般无法解决,比如栈溢出)和
* Exception(所有子类除了RunTimeException,其它全是编译时异常)
* 常见运行时异常: 空指针、下标越界、类型转换、栈内存溢出
*
* 6 语法:
* try{
* 高风险代码
* }catch(异常类 变量){
* 处理措施
* }
* */
public class _01_ExceptionB {
public static void main(String[] args) {
int a = 10;
int b = 0;
// java.lang.ArithmeticException: / by zero 除0异常 "divide by zero"
if (b == 0){
System.out.println("除数不能为0");
}else {
int c = a/b;
System.out.println(c);
}
System.out.println("执行成功");
}
}
/*
* try...catch...使用
* */
public class _02_ExceptionM1 {
public static void main(String[] args) {
test1();
test2();
}
public static void test1(){
// 简单异常。if也能解决
int a = 10;
int b = 0;
// 如果try中出现异常,异常语句后剩余部分不再执行,执行catch
try {
int c = a/b;
System.out.println(c);
}catch (Exception e){ // 捕捉异常,需要使用异常对应的异常或其父类(多态)
// 如果try中代码没有异常,则正常执行try,不会执行catch
// 打印追踪栈帧
e.printStackTrace(); // 编译能通过,属于运行时异常
System.out.println("除数不能为0");
}
System.out.println("-------");
}
public static void test2(){
// 检查型异常。if无法处理
try {
FileInputStream fileInputStream = new FileInputStream("xxxxxx");
}catch (FileNotFoundException e){
// 适合返回给编程人员
e.printStackTrace();
// 获取异常信息,适合返回给客户
String message = e.getMessage();
// xxxxxx (系统找不到指定的文件。)
System.out.println(message);
// 获取栈内存地址,不常用
// [Ljava.lang.StackTraceElement;@74a14482
System.out.println(e.getStackTrace());
}
}
}
throws
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
* throws:不处理异常,而是一种提醒,告诉调用处,这里有一个异常/可能有异常
* 异常而没有解决,需要调用处自行处理(抛出异常)
* 如果调用处收到提醒,要么这里也要提醒调用这里的地方,或者try处理掉
*
* throws 也可以同时抛出多个异常,使用逗号隔开
* throws只是提醒,无所谓继承与顺序关系
* */
public class _02_ExceptionM2 {
public static void main(String[] args) {
try {
test1();
// 可使用FileNotFoundException进行异常捕捉,也可以使用FileNotFoundException的父类
// IOException、Exception
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
}
// 抛出多个异常
public static void test1() throws FileNotFoundException,
Exception,IOException,FileNotFoundException{
test2();
}
public static void test2() throws FileNotFoundException{
test3();
}
public static void test3() throws FileNotFoundException {
FileInputStream fileInputStream = new FileInputStream("F:/123.txt");
}
}
catch语句块
/*
* try{
* 高风险代码
* }catch(异常类 变量){
* 处理措施
* }catch(异常类 变量){
* 处理措施
* }
* 1 catch语句块可以根据代码返回的异常编写多个
* 2 从上往下,必须是子类到父类,否则根据多态特性,父类会把子类异常对象捕获
* 3 如果从上往下没有继承关系,则不用考虑顺序问题
* 4 多个异常,只会有一个执行,在产生异常代码语句只后,try中往后代码不会执行
*
* 如果 多个异常中,处理措施一样,可以直接写Exception,否则分开捕获
* */
public class _02_ExceptionM3 {
public static void main(String[] args) {
try {
// 可能存在FileNotFoundException
new FileInputStream("xxx");
// 可能空指针异常
String s = null;
s.equals("");
}catch (FileNotFoundException e){
System.out.println("找不到指定文件");
}catch (NullPointerException e1){
System.out.println("空指针");
}catch (Exception e3){
// 不能把这个异常放第一个catch块,Exception可以捕获大部分异常,包括空指针和FileNotFoundException等
// 若其在第一个catch,可能导致另外两个异常永远捕获不到(多态),父类把异常都捕获了
System.out.println("其他异常");
}
}
}
异常改进
/*
* java1.7开始,异常改进
* 1 可以一次捕获多个异常
* 但异常直接不能有继承关系,若存在继承关系,直接写父类
* 多个异常使用 | 隔开
* 异常类型 | 异常类型
* 2 自动关闭资源,多条开启资源语句以分号分割
* try(开启资源语句){
* 高风险代码;
* }catch(异常类 变量){
* 处理措施;
* }
* */
public class _02_ExceptionM4 {
public static void main(String[] args) {
test_1();
test_2();
}
public static void test_1(){
try {
// 可能存在FileNotFoundException
new FileInputStream("xxx");
// 可能空指针异常
String s = null;
s.equals("");
// 不能有继承关系,否 写父类异常
}catch (FileNotFoundException | NullPointerException e){
System.out.println("找不到文件");
}
}
public static void test_2(){
// 保存打开的资源对象,测试是否关闭
FileInputStream fisc = null;
// 可自动关闭资源
try (
FileInputStream fis1 = new FileInputStream("D:/111.txt");
FileInputStream fis2 = new FileInputStream("D:/111.txt");
){
// 操作,读取文件中数据
System.out.println(fis1.read());
System.out.println(fis2.read());
// fis2对象赋值非测试对象fisc
fisc = fis2;
} catch (FileNotFoundException e) {
System.out.println("找不到");
} catch (IOException e) {
e.printStackTrace();
}
// 测试
try {
// java.io.IOException: Stream Closed 文件流关闭
System.out.println(fisc.read());
} catch (IOException e) {
e.printStackTrace();
}
}
}
finally
/*
* finally: 必须执行的语句块
* 在编程中,会有出错情况,但某些情况下,尽管出错,一些代码任然需要执行
* 比如资源关闭语句
* 1 finally不能单独使用
* 2 finally可以直接和 try 一起使用:try...finally...
* 3 finally可以直接和 try...catch... 一起使用:try...catch...finally...
* 4 finally不执行情况
* System.exit():关闭虚拟机
* 5 finally 中return语句情况
*
* 应用场景
* 由于finally必须执行,所以一般用于关闭资源
*
* 面试题:谈谈对final的理解
* 1 final是什么,怎么用?
* 2 finally是什么,怎么用?
* 3 finalize是什么,怎么用?
* */
public class _02_ExceptionM5 {
int i;
public static void main(String[] args) {
_02_ExceptionM5 exceptionM5 = new _02_ExceptionM5();
System.out.println(exceptionM5.i); // 成员变量有默认初始值
testFinally_01();
System.out.println("--------分割线");
int i = testFinally_02();
System.out.println("返回值:"+i);
System.out.println("--------分割线");
testFinally_03();
}
// finally基本情况
public static void testFinally_01(){
int a = 10;
int b = 0;
try {
int c = a/b; // 终止生命周期
System.out.println(c);
}catch (Exception e){
e.printStackTrace(); // 编译能通过,属于运行时异常
System.out.println("除数不能为0");
}finally {
// 依然执行
System.out.println("必须执行");
}
// 如果没有catch,没办法捕获并处理异常,依旧导致终止程序生命周期,故不会执行下面输出语句
// 如果catch处理了异常,下面输出语句正常执行
System.out.println("main方法执行");
}
// finally中return语句情况
public static int testFinally_02(){
int i = 10;
try {
// 这里return语句也会执行,后面finally语句块优先级高,把它覆盖了
// 若返回数据,需要先执行运算操作(i++)。再返回
// 当运算操作执行完,要执行return语句,发现finally优先级更高,放弃return,执行finally
return i++;
}finally {
System.out.println("我先执行");
// 当执行到这里,try里面i++已经执行,i=11,return i=11
return i;
}
}
// 应用场景
public static void testFinally_03(){
// 局部变量没有默认初始值,必须赋值
FileInputStream fileInputStream = null;
int i; // 必须赋值
// 输出报错
// System.out.println(i);
try {
fileInputStream = new FileInputStream("F:111.txt");
}catch (FileNotFoundException e){
System.out.println("找不到指定文件");
}finally {
// fileInputStream.close();可能会抛出异常
try {
// 不确保 fileInputStream != null 的话,会出现空指针异常
// 如果try中文件没有找到,说明fileInputStream没有创建成功,则fileInputStream为空指针
if (fileInputStream != null){
fileInputStream.close(); // 文件流关闭
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注意
/*
* 子类重写父类方法,不能有更宽泛的异常
* 子类方法中抛出的异常,必须是父类方法抛出的异常类,或者其子类
* 子类覆写后异常 <= 父类方法异常
* */
public class _03_ExceptionA {
public static void main(String[] args) {
B b = new B();
try {
b.test();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class A{
public void test() throws IOException{
System.out.println("A方法");
}
}
class B extends A{
@Override
// public void test() throws Exception { // 错误,更高异常
// public void test() throws IOException { // 正确
public void test() throws FileNotFoundException{ // 正确
System.out.println("B方法");
}
}
自定义异常类
/*
* 自定义异常类
* 1 继承一个已有的异常,基本都是继承Exception类,若是运行时异常,则继承RunTimeException
* 2 无参构造
* 3 有参构造,传入错误信息,并把信息传递给父类 super(xxx)
* */
public class UserException extends Exception{
public UserException() {
}
public UserException(String message) {
super(message);
}
}
/*
* 业务类
* */
public class UserService {
public void login(User user) throws UserException {
if ("admin".equals(user.getUsername())){
// 若用户名数admin,则比较密码
if ("root".equals(user.getPassword())){
// 若密码正确
System.out.println("登录成功");
}else {
// 密码不正确,抛出异常 并提示 密码错误
throw new UserException("密码错误");
}
}else {
// 用户名不存在,抛出异常 并提示 用户名不存在
// throw : 制造异常,异常源点,创建一个异常对象
throw new UserException("用户名不存在");
}
}
}
/*
* 用户类
* */
public class User {
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
/*
* 需求:完成用户登录
* 1 如果用户名不是admin 则提示用户不存在
* 2如果密码不是root,提示密码错误
* 客户端类
* */
public class Client {
public static void main(String[] args) {
System.out.println("请输入用户名和密码:");
Scanner scanner = new Scanner(System.in);
// 获取用户输入
String username = scanner.nextLine();
String password = scanner.nextLine();
// 封装对象
User user = new User(username,password);
// 创建业务类对象
UserService userService = new UserService();
// 登录
try {
userService.login(user);
System.out.println("跳转到主页面");
} catch (UserException e) { // 处理自定义异常
// com.tianl.Exception.ThrowException.UserException: 密码错误
// 不必打印追踪栈帧,只需把错误提醒信息 展示给页面即可
// 因为编程人员知道异常如何发生
// e.printStackTrace();
// 获取错误信息
String msg = e.getMessage();
// 错误信息传递到页面
System.out.println(msg);
}
}
}