Java 运行时异常和编译时异常之间的区别是什么?

📅 发布时间:2026/7/14 16:02:12 👁️ 浏览次数:
Java 运行时异常和编译时异常之间的区别是什么?
Java 中的异常分为两大类运行时异常和编译时异常也称为检查型异常。它们在处理方式、继承层次和使用场景上有重要区别。异常层次结构Throwable ├── Error错误不处理 └── Exception异常 ├── RuntimeException运行时异常非检查型 │ ├── NullPointerException │ ├── ArrayIndexOutOfBoundsException │ ├── ArithmeticException │ ├── ClassCastException │ └── ... └── 其他异常编译时异常检查型 ├── IOException ├── SQLException ├── FileNotFoundException └── ...主要区别对比特性运行时异常编译时异常继承类RuntimeExceptionException非RuntimeException检查时机运行时编译时必须处理不强制必须处理声明要求不需要声明必须声明或捕获常见例子NullPointerException, ArrayIndexOutOfBoundsExceptionIOException, SQLException处理方式可选强制运行时异常特点继承自RuntimeException编译器不强制要求处理通常表示编程错误可以在代码中避免常见运行时异常// 1. NullPointerException - 空指针异常Stringstrnull;intlengthstr.length();// NullPointerException// 2. ArrayIndexOutOfBoundsException - 数组越界int[]arrnewint[5];intvaluearr[10];// ArrayIndexOutOfBoundsException// 3. ArithmeticException - 算术异常intresult10/0;// ArithmeticException// 4. ClassCastException - 类型转换异常ObjectobjHello;Integernum(Integer)obj;// ClassCastException// 5. IllegalArgumentException - 非法参数异常publicvoidsetAge(intage){if(age0||age150){thrownewIllegalArgumentException(年龄必须在0-150之间);}}// 6. NumberFormatException - 数字格式异常intnumInteger.parseInt(abc);// NumberFormatException// 7. IndexOutOfBoundsException - 索引越界ListStringlistnewArrayList();Stringitemlist.get(0);// IndexOutOfBoundsException运行时异常处理示例publicclassRuntimeExceptionExample{// 不需要声明运行时异常publicintdivide(inta,intb){returna/b;// 可能抛出 ArithmeticException}// 可选处理运行时异常publicvoidprocessString(Stringstr){// 方式1不处理让调用者处理intlengthstr.length();// 方式2主动检查避免异常if(str!null){intlength2str.length();}// 方式3捕获处理try{intlength3str.length();}catch(NullPointerExceptione){System.out.println(字符串为空);}}// 抛出自定义运行时异常publicvoidsetAge(intage){if(age0||age150){thrownewIllegalArgumentException(年龄必须在0-150之间);}}}编译时异常特点继承自Exception但不是RuntimeException编译器强制要求处理通常表示外部环境问题必须声明或捕获常见编译时异常// 1. IOException - 输入输出异常importjava.io.*;publicvoidreadFile(Stringfilename)throwsIOException{FileReaderreadernewFileReader(filename);// 必须声明 throws IOException}// 2. FileNotFoundException - 文件未找到异常publicvoidopenFile(Stringpath)throwsFileNotFoundException{FileInputStreamfisnewFileInputStream(path);// 必须声明 throws FileNotFoundException}// 3. SQLException - 数据库异常importjava.sql.*;publicvoidqueryData()throwsSQLException{ConnectionconnDriverManager.getConnection(jdbc:mysql://localhost/test);Statementstmtconn.createStatement();ResultSetrsstmt.executeQuery(SELECT * FROM users);// 必须声明 throws SQLException}// 4. ClassNotFoundException - 类未找到异常publicvoidloadClass(StringclassName)throwsClassNotFoundException{Class.forName(className);// 必须声明 throws ClassNotFoundException}// 5. InterruptedException - 中断异常publicvoidsleep()throwsInterruptedException{Thread.sleep(1000);// 必须声明 throws InterruptedException}// 6. ParseException - 解析异常importjava.text.*;publicvoidparseDate(StringdateStr)throwsParseException{SimpleDateFormatsdfnewSimpleDateFormat(yyyy-MM-dd);Datedatesdf.parse(dateStr);// 必须声明 throws ParseException}编译时异常处理示例importjava.io.*;importjava.sql.*;publicclassCheckedExceptionExample{// 方式1声明异常让调用者处理publicvoidreadFile(Stringfilename)throwsIOException{FileReaderreadernewFileReader(filename);BufferedReaderbrnewBufferedReader(reader);Stringlinebr.readLine();System.out.println(line);br.close();}// 方式2捕获异常publicvoidreadFileSafe(Stringfilename){try{FileReaderreadernewFileReader(filename);BufferedReaderbrnewBufferedReader(reader);Stringlinebr.readLine();System.out.println(line);br.close();}catch(IOExceptione){System.out.println(读取文件失败: e.getMessage());}}// 方式3捕获后重新抛出publicvoidreadFileAndRethrow(Stringfilename)throwsCustomException{try{FileReaderreadernewFileReader(filename);BufferedReaderbrnewBufferedReader(reader);Stringlinebr.readLine();System.out.println(line);br.close();}catch(IOExceptione){thrownewCustomException(文件处理失败,e);}}// 方式4使用 try-with-resources推荐publicvoidreadFileWithResources(Stringfilename){try(FileReaderreadernewFileReader(filename);BufferedReaderbrnewBufferedReader(reader)){Stringlinebr.readLine();System.out.println(line);}catch(IOExceptione){System.out.println(读取文件失败: e.getMessage());}}// 多个异常的处理publicvoidprocessFile(Stringfilename)throwsIOException,ClassNotFoundException{// 可以声明多个异常FileReaderreadernewFileReader(filename);Class.forName(com.example.SomeClass);}// 多个异常的捕获publicvoidhandleMultipleExceptions(Stringfilename){try{FileReaderreadernewFileReader(filename);Class.forName(com.example.SomeClass);}catch(IOException|ClassNotFoundExceptione){// Java 7 可以捕获多个异常System.out.println(处理异常: e.getMessage());}}}实际应用场景对比1.文件操作编译时异常importjava.io.*;publicclassFileProcessor{// 编译时异常 - 必须处理publicvoidprocessFile(StringinputPath,StringoutputPath)throwsIOException{try(BufferedReaderreadernewBufferedReader(newFileReader(inputPath));BufferedWriterwriternewBufferedWriter(newFileWriter(outputPath))){Stringline;while((linereader.readLine())!null){// 处理每一行StringprocessedprocessLine(line);writer.write(processed);writer.newLine();}}// IOException 是编译时异常必须处理}privateStringprocessLine(Stringline){// 这里可能抛出运行时异常if(linenull){thrownewIllegalArgumentException(行不能为空);}returnline.toUpperCase();}}2.数据库操作编译时异常importjava.sql.*;publicclassDatabaseManager{publicUsergetUserById(intuserId)throwsSQLException{Connectionconnnull;PreparedStatementstmtnull;ResultSetrsnull;try{connDriverManager.getConnection(jdbc:mysql://localhost/mydb);StringsqlSELECT * FROM users WHERE id ?;stmtconn.prepareStatement(sql);stmt.setInt(1,userId);rsstmt.executeQuery();if(rs.next()){returnnewUser(rs.getInt(id),rs.getString(name));}returnnull;}finally{// 清理资源if(rs!null)rs.close();if(stmt!null)stmt.close();if(conn!null)conn.close();}// SQLException 是编译时异常必须处理}}3.业务逻辑运行时异常publicclassUserService{// 运行时异常 - 不需要声明publicvoidregisterUser(Stringusername,Stringpassword){// 参数验证 - 抛出运行时异常if(usernamenull||username.trim().isEmpty()){thrownewIllegalArgumentException(用户名不能为空);}if(passwordnull||password.length()6){thrownewIllegalArgumentException(密码长度不能少于6位);}// 检查用户名是否已存在if(userExists(username)){thrownewIllegalStateException(用户名已存在);}// 保存用户saveUser(username,password);}// 运行时异常 - 不需要声明publicUsergetUser(intuserId){UseruseruserRepository.findById(userId);if(usernull){thrownewUserNotFoundException(用户不存在: userId);}returnuser;}// 自定义运行时异常publicstaticclassUserNotFoundExceptionextendsRuntimeException{publicUserNotFoundException(Stringmessage){super(message);}}}异常处理最佳实践1.何时使用运行时异常// ✅ 适合使用运行时异常的情况publicclassBankAccount{privatedoublebalance;publicvoidwithdraw(doubleamount){// 编程错误 - 应该在调用前检查if(amount0){thrownewIllegalArgumentException(取款金额不能为负数);}// 业务规则违反 - 运行时异常if(amountbalance){thrownewIllegalStateException(余额不足);}balance-amount;}publicvoiddeposit(doubleamount){// 参数验证if(amount0){thrownewIllegalArgumentException(存款金额必须大于0);}balanceamount;}}2.何时使用编译时异常// ✅ 适合使用编译时异常的情况publicclassFileDownloader{publicvoiddownloadFile(Stringurl,StringsavePath)throwsIOException{// 文件操作 - 外部环境问题使用编译时异常URLwebsitenewURL(url);ReadableByteChannelrbcChannels.newChannel(website.openStream());FileOutputStreamfosnewFileOutputStream(savePath);fos.getChannel().transferFrom(rbc,0,Long.MAX_VALUE);}publicvoidprocessDownloadedFile(StringfilePath)throwsIOException,ParseException{// 多个可能的编译时异常BufferedReaderreadernewBufferedReader(newFileReader(filePath));Stringcontentreader.readLine();// 解析日期SimpleDateFormatsdfnewSimpleDateFormat(yyyy-MM-dd);Datedatesdf.parse(content);}}3.异常处理策略publicclassExceptionHandlingStrategy{// 策略1恢复并继续publicvoidprocessMultipleFiles(ListStringfilePaths){for(StringfilePath:filePaths){try{processFile(filePath);}catch(IOExceptione){// 记录错误但继续处理其他文件System.err.println(处理文件失败: filePath, 错误: e.getMessage());}}}// 策略2转换异常类型publicUserloadUser(intuserId)throwsUserLoadException{try{returnuserRepository.findById(userId);}catch(SQLExceptione){// 将底层异常转换为业务异常thrownewUserLoadException(加载用户失败,e);}}// 策略3提供默认值publicStringgetConfigValue(Stringkey){try{returnconfigLoader.load(key);}catch(IOExceptione){// 返回默认值而不是抛出异常returndefault;}}// 自定义业务异常publicstaticclassUserLoadExceptionextendsException{publicUserLoadException(Stringmessage,Throwablecause){super(message,cause);}}}异常处理注意事项✅ 推荐做法// 1. 具体异常处理try{// 代码}catch(FileNotFoundExceptione){// 处理文件未找到}catch(IOExceptione){// 处理其他IO异常}// 2. 提供有意义的错误信息thrownewIllegalArgumentException(年龄必须在0-150之间当前值: age);// 3. 保留原始异常信息try{// 代码}catch(IOExceptione){thrownewBusinessException(处理失败,e);}// 4. 清理资源try(FileReaderreadernewFileReader(file.txt)){// 使用资源}catch(IOExceptione){// 处理异常}❌ 避免的做法// 1. 捕获所有异常try{// 代码}catch(Exceptione){// 太宽泛难以处理}// 2. 吞掉异常try{// 代码}catch(IOExceptione){// 什么都不做}// 3. 捕获后立即抛出try{// 代码}catch(IOExceptione){throwe;// 没有意义}// 4. 不恰当的使用运行时异常publicvoidreadFile(Stringpath){// 文件操作应该使用编译时异常thrownewRuntimeException(文件读取失败);}总结运行时异常用途表示编程错误和逻辑错误处理可选通常通过代码检查避免例子NullPointerException, IllegalArgumentException原则快速失败暴露问题编译时异常用途表示外部环境问题和可恢复错误处理必须处理强制调用者关注例子IOException, SQLException原则优雅处理提供恢复机制理解这两种异常的区别有助于编写更健壮、更易维护的 Java 代码。运行时异常用于快速发现编程错误编译时异常用于处理外部环境的不确定性。