跳到主要内容

第7章:异常处理

作为 Java 开发者,你已经熟悉了 Java 的异常处理机制。Python 的异常处理与 Java 有很多相似之处,但也有一些重要的区别。本章将详细对比两种语言的异常处理。

7.1 异常处理基础

Java 异常处理

try {
// 可能抛出异常的代码
} catch (ExceptionType1 e) {
// 处理异常类型1
} catch (ExceptionType2 e) {
// 处理异常类型2
} finally {
// 无论是否异常都会执行的代码
}

Python 异常处理

try:
# 可能抛出异常的代码
except ExceptionType1 as e:
# 处理异常类型1
except ExceptionType2 as e:
# 处理异常类型2
finally:
# 无论是否异常都会执行的代码

对比

特性JavaPython
try 块相同相同
catch 块catch (ExceptionType e)except ExceptionType as e
finally 块相同相同
多个异常多个 catch 块多个 except 块
异常类型检查型和非检查型都是运行时异常
throw 异常throw new Exception()raise Exception()

Python 异常处理示例

# 基本异常处理
try:
x = int(input("Enter a number: "))
y = 10 / x
print(f"Result: {y}")
except ValueError:
print("Please enter a valid number")
except ZeroDivisionError:
print("Cannot divide by zero")
except Exception as e:
print(f"An error occurred: {e}")
finally:
print("Execution completed")

# 多个异常在一个 except 块
try:
x = int(input("Enter a number: "))
y = 10 / x
except (ValueError, ZeroDivisionError) as e:
print(f"Error: {e}")

# 异常的层次结构
try:
# 代码
pass
except Exception as e: # 捕获所有异常
print(f"Error: {e}")

7.2 异常类型

Java 异常类型

  • 检查型异常:必须处理或声明的异常(如 IOException
  • 非检查型异常:不需要强制处理的异常(如 RuntimeException
  • 所有异常都是 Throwable 的子类

Python 异常类型

  • 所有异常都是 BaseException 的子类
  • 常用异常类型:
    • Exception:所有非系统退出异常的基类
    • ValueError:值错误
    • TypeError:类型错误
    • ZeroDivisionError:除零错误
    • FileNotFoundError:文件未找到错误
    • ImportError:导入错误
    • KeyError:字典键错误
    • IndexError:索引错误

对比

Java 异常Python 异常描述
NullPointerExceptionAttributeError访问不存在的属性
ArrayIndexOutOfBoundsExceptionIndexError索引越界
IllegalArgumentExceptionValueError参数值错误
ClassCastExceptionTypeError类型转换错误
FileNotFoundExceptionFileNotFoundError文件未找到
IOException多种 IO 相关异常IO 操作错误

Python 异常层次结构

BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
└── Exception
├── ArithmeticError
│ ├── FloatingPointError
│ ├── OverflowError
│ └── ZeroDivisionError
├── AssertionError
├── AttributeError
├── EOFError
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── NameError
├── OSError
│ ├── FileExistsError
│ └── FileNotFoundError
├── SyntaxError
├── TypeError
└── ValueError

7.3 抛出异常

Java 抛出异常

// 抛出已定义的异常
throw new Exception("Error message");

// 在方法声明中声明异常
public void method() throws Exception {
// 代码
}

Python 抛出异常

# 抛出已定义的异常
raise Exception("Error message")

# 抛出特定异常
raise ValueError("Invalid value")

# 重新抛出异常
try:
# 代码
except Exception as e:
# 处理
raise # 重新抛出原异常

对比

操作JavaPython
抛出异常throw new Exception()raise Exception()
声明异常throws Exception不需要声明
重新抛出throw eraise
异常链throw new Exception("Message", cause)raise NewException("Message") from original_exception

Python 抛出异常示例

# 抛出异常
def divide(a, b):
if b == 0:
raise ZeroDivisionError("Cannot divide by zero")
return a / b

try:
result = divide(10, 0)
except ZeroDivisionError as e:
print(f"Error: {e}")

# 异常链
try:
x = int("abc")
except ValueError as e:
raise RuntimeError("Failed to process input") from e

# 自定义异常
class CustomError(Exception):
"""自定义异常"""
pass

def validate_age(age):
if age < 0:
raise CustomError("Age cannot be negative")

try:
validate_age(-5)
except CustomError as e:
print(f"Custom error: {e}")

7.4 自定义异常

Java 自定义异常

// 自定义检查型异常
public class CustomException extends Exception {
public CustomException(String message) {
super(message);
}
}

// 自定义非检查型异常
public class CustomRuntimeException extends RuntimeException {
public CustomRuntimeException(String message) {
super(message);
}
}

Python 自定义异常

# 自定义异常
class CustomError(Exception):
"""自定义异常"""
pass

class ValidationError(CustomError):
"""验证错误"""
pass

# 使用自定义异常
def validate_email(email):
if "@" not in email:
raise ValidationError("Invalid email format")

try:
validate_email("user.example.com")
except ValidationError as e:
print(f"Validation error: {e}")
except CustomError as e:
print(f"Custom error: {e}")
except Exception as e:
print(f"General error: {e}")

对比

特性JavaPython
继承继承自 ExceptionRuntimeException继承自 Exception
构造方法需要显式调用父类构造方法自动调用父类构造方法
异常类型检查型和非检查型都是运行时异常

7.5 异常处理最佳实践

Java 最佳实践

  1. 只捕获你能处理的异常
  2. 捕获具体的异常类型,而不是通用的 Exception
  3. 使用 finally 块释放资源
  4. 不要忽略异常
  5. 记录异常信息

Python 最佳实践

  1. 只捕获你能处理的异常
  2. 捕获具体的异常类型,而不是通用的 Exception
  3. 使用 finally 块释放资源
  4. 不要忽略异常
  5. 记录异常信息
  6. 使用 with 语句管理资源(自动处理异常)

Python with 语句示例

# 使用 with 语句管理文件
with open("example.txt", "r") as file:
content = file.read()
print(content)
# 文件自动关闭,即使发生异常

# 自定义上下文管理器
class Timer:
def __enter__(self):
import time
self.start = time.time()
return self

def __exit__(self, exc_type, exc_val, exc_tb):
import time
self.end = time.time()
print(f"Elapsed time: {self.end - self.start:.2f} seconds")
# 如果返回 True,异常会被抑制
return False

with Timer():
# 执行一些操作
import time
time.sleep(1)

7.6 异常处理对比

特性JavaPython
异常类型检查型和非检查型都是运行时异常
异常声明方法必须声明可能抛出的检查型异常不需要声明异常
异常捕获catch (Exception e)except Exception as e
异常抛出throw new Exception()raise Exception()
异常链throw new Exception("Message", cause)raise NewException() from original
资源管理try-with-resourceswith 语句
异常层次基于 Throwable基于 BaseException

7.7 练习

  1. 异常处理练习:编写程序,处理用户输入的非数字情况
  2. 自定义异常练习:创建一个 AgeValidationError 异常,用于验证年龄是否在合理范围内
  3. 资源管理练习:使用 with 语句读取文件内容并处理可能的异常
  4. 异常链练习:创建异常链,展示底层异常和上层异常
  5. 综合练习:编写一个函数,读取文件并处理所有可能的异常

7.8 小结

  • Python 的异常处理语法与 Java 类似,但更简洁
  • Python 没有检查型异常,所有异常都是运行时异常
  • Python 使用 as 关键字捕获异常对象
  • Python 支持异常链,使用 from 关键字
  • Python 的 with 语句提供了更好的资源管理
  • Python 的异常层次结构与 Java 类似,但有一些差异
  • Python 的自定义异常更容易创建和使用

通过本章的学习,你已经了解了 Java 和 Python 在异常处理上的主要区别。接下来,我们将学习 Python 的 IO 操作。