在学习其他语言的时候,我会常忽略异常,但是真的面对自己的项目时,又异常是一个非常有用的存在,所以今天说说Java异常(结合多个up主内容总结)
一、异常的介绍
- 将程序执行中发生的不正常情况称为“异常”(程序逻辑错误或者语法的错误不叫异常),例如空指针访问,试图读取不存在的文件,网络连接中断等等
- 基本异常类型Throwable:所有错误和异常的超类;
Error:表示编译时和系统错误(外部错误)。如OutOfMemoryError、StackOverflowError等。这些错误通常是严重的,应用程序不应该尝试捕获。
Exception:表示需要应用程序来捕获的条件。进一步异常分为2大类:运行时异常和编译时异常
二、运行时异常和编译时异常
下面简要的介绍一下运行时异常和编译时异常几个常会遇到的种类
运行时异常:算数异常(ArithmeticException)
解释:
算术异常在Java中是通过ArithmeticException
类表示的,它是RuntimeException
的一个子类。这意味着ArithmeticException
是一个运行时异常,Java编译器不要求程序员显式地处理(即捕获或声明抛出)这种类型的异常。最常见的就是除以零。
例如:
package exception;
public class Exception_Introduction {
public static void main(String[] args) {
int n1=100;
int n2=0;
int temp=n1/n2;
System.out.println("除数不能为0");
}
}
运行时异常:空指针异常(NullPointerException)
解释:
空指针异常(NullPointerException
)在Java中是一种运行时异常(RuntimeException
的子类),它发生在尝试使用null
值调用对象实例方法或访问对象的属性时。因为null
表示没有任何对象引用,所以尝试执行任何操作都会导致NullPointerException
。
例如:
package exception;
public class NullPointerException_ {
public static void main(String[] args) {
String name= null;
System.out.println(name.length);
}
}
运行时异常:数组索引越界异常(ArrayIndexOutOfBoundsException)
解释:
数组索引越界异常在Java中通过ArrayIndexOutOfBoundsException
类表示,它是RuntimeException
的一个子类。这种异常发生在尝试访问数组的一个不存在的索引时,即索引值小于0或大于等于数组的长度。
例如:
package exception;
public class ArrayIndexOutOfBoundsException_ {
public static void main(String[] args) {
int[] arr={1,2,3,4,5};
for (int i=0;i<=arr.length;i++){
System.out.println(arr[i]);
}
}
}
运行时异常:数字格式不正确异常(NumberFormatException)
解释:
NumberFormatException
是Java中的一个运行时异常(RuntimeException
的子类),它发生在尝试将字符串转换为数字格式,但字符串的格式不适合转换为数字时。这种异常通常是由Integer.parseInt()
、Double.parseDouble()
等包装类的解析方法抛出的
例如:
package exception;
public class NumberFormatException_ {
public static void main(String[] args) {
String name="Tom";
int num=Integer.parseInt(name);
System.out.println(num);
}
}
编译时异常
解释:
在编译期间,就必须处理的异常,否则代码不能通过编译
常见的编译异常:
1、SQLException
:操作数据库时,查询表可能发生的异常
2、IOException
:操作文件时发生的异常
3、FileNotFoundException
:当操作一个不存在的文件时,发生的异常
4、ClassNotFoundException
:加载类,而该类不存在时,发生异常
5、EOFException
:操作文件,到文件末尾,发生异常
6、IllegalArguementException
:参数异常
例如:
package exception;
import java.io.FileInputStream;
import java.io.IOException;
public class FileNotFoundException_ {
public static void main(String[] args) {
//IOException 当操作一个不存在的文件时,发生的异常
FileInputStream file;
file = new FileInputStream("./niha.s");
int len;
while((len=file.read())!=-1){
System.out.println(len);
}
file.close();
}
}
三、异常处理机制
解释:
- 为啥要有异常处理机制?之前我很不理解,我都已经知道这个代码有问题,用
if-else
就可以将问题排除外呀,还要用异常处理机制吗?很早之前当我有一次用python写一个关于连接串口程序时,我才知道异常处理机制真的很有用,因为无论程序出现异常与否,都需要关闭串口,其中的try-except-finally
发挥了重要作用。 - 继续拿之间的代码举例(如下),程序执行到
int temp=n1/n2;
时,就会抛出异常(ArithmeticException
)后面的代码就不会执行,但是当想执行后面语句时,就需要使用异常处理机制来解决该问题 - 处理方式(IDEA快捷键):
电脑快捷键command+option+t
,就会跳出选择框,选择try-catch
win电脑快捷键ctrl+alt+t
,就会跳出选择框,选择try-catch
package exception;
public class Exception_Introduction {
public static void main(String[] args) {
int n1=100;
int n2=0;
try {
//运行时异常
int temp=n1/n2;
} catch (Exception e) {
// throw new RuntimeException(e);
System.out.println("除数不能为0");
}
System.out.println("程序运行");
}
}
基本介绍:
- 异常处理就是当异常发生时,对异常处理的方式
- 异常处理的方式:
try-catch-fially:
捕获代码异常,自行处理
throws
:将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM
try-catch-fially
用法:
try{
可能有异常的代码
}catch(Exception e){
捕获异常
当异常发生时,系统将异常封装成Exception对象e,传递给catch
得到异常对象后,可以自行处理,另外如果没有发生异常,catch就不会执行
可以有多个catch语句,捕获不同的异常(进行不同业务处理,类似与if-else语句),要求父类异常在后。
}finally{
没有finally,语法也是没有问题的
如果有finally,不管try代码块是否有异常发生,始终要执行finally
所以一般情况下将释放资源的代码,放在finally
另外也可以只写try- finally配合使用,这种用法相当于没有捕获异常,程序执行完finally代码块之后还是会崩溃。
}
例如
/*
程序异常
*/
package exception;
public class TryCatch_ {
public static void main(String[] args) {
try {
String str="Tom";
int num=Integer.parseInt(str);
System.out.println(num);
} catch (NumberFormatException e) {
System.out.println("异常信息:"+e);
}finally {
System.out.println("程序还是会继续");
}
}
}
输出结果:
异常信息:java.lang.NumberFormatException: For input string: "Tom"
程序还是会继续
/*
程序正常
*/
package exception;
public class TryCatch_ {
public static void main(String[] args) {
try {
String str="231";
int num=Integer.parseInt(str);
System.out.println(num);
} catch (NumberFormatException e) {
System.out.println("异常信息:"+e);
}finally {
System.out.println("程序还是会继续");
}
}
}
输出结果:
231
程序还是会继续
package exception;
public class TryCatchFinally_01 {
public static void main(String[] args) {
try {
Person person = new Person();
person = null;
// NullPointerException
System.out.println(person.getName());
int num1 = 10;
int num2 = 0;
// ArithmeticException
int temp = num1 / num2;
}catch (ArithmeticException e){
System.out.println("Exception:"+e);
} catch (NullPointerException e) {
System.out.println("Exception:"+e);
} catch (Exception e){
System.out.println("Exception:"+e);
}finally {
}
}
}
class Person{
private String name;
public String getName(){
return name;
}
}
输出结果:
Exception:java.lang.NullPointerException
package exception;
public class TryCatchFinally_02 {
public static void main(String[] args) {
try {
int num1=100;
int num2=0;
int temp=num1/num2;
System.out.println(temp);
} finally {
System.out.println("finally代码块执行");
}
System.out.println("继续执行");
}
}
输出结果:
finally代码块执行
Exception in thread "main" java.lang.ArithmeticException: / by zero
at exception.TryCatchFinally_02.main(TryCatchFinally_02.java:9)
下面是一个较难的例子:
package exception;
public class TryCatchFinally_05 {
public static void main(String[] args) {
Exercise_03 exercise03 = new Exercise_03();
int num=exercise03.method();
System.out.println(num);
}
}
class Exercise_03{
private static int i=1;
public static int method(){
try {
i++;
String[] names =new String[3];
if (names[1].equals("Tom")){
System.out.println(names[1]);
}else {
names[3]="sxp";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
} catch (NullPointerException e){
return ++i; //保存临时变量temp=3
}finally { //finally肯定会执行
++i;
System.out.println("i="+i);
}
}
}
输出结果:
i=4
3
throws
throws单词就有抛的意思,如果一个方法在执行过程中,可能生成某个异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常处理,而是由方法的调用者处理。在方法声明中用throws语句,可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
解释:
1、使用throws,抛出异常,让调用方法的调用者处理
2、throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类
3、 throws关键字后也可以是异常列表,可以抛出多个异常
package exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Throws_01 {
public static void main(String[] args) {
}
//下面也可以写成 public void func1() throws Exception
// 创建一个文件流对象
// 异常处理,这里是一个FileNotFoundException,编译异常
// 可以使用 try-catch-finally
// 也可以使用throws,抛出异常,让调用func1方法的调用者处理
public static void func1() throws FileNotFoundException,NullPointerException{
FileInputStream file = new FileInputStream("./dir/test.txt");
}
}
解释:
1、对于编译异常,程序中必须处理,比如用try-catch或者throws
2、对于运行异常。程序中如果没有处理,默认就是throws的方式处理
3、子类重写父类的方法时,对抛出异常的规定:子类重写方法,所抛出的异常类型要么和父类一致,要么为父类抛出的异常的类型的子类型,否则语法不能通过
例如:
package exception;
public class Throws_02 {
public static void main(String[] args) {
func2();
}
public static void func2() /*throws ArithmeticException*/{
// 则该方法相当于public void func2 throw ArithmeticException
int num1=1000;
int num2=0;
int temp=num1/num2;
}
}
class Father{
public void method() throws RuntimeException{
}
}
class Son extends Father{
@Override
public void method() throws NullPointerException {
}
}
解释:
java中不要求在程序中显示的处理运行异常,因为有默认的处理机制
例如:
package exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Throws_03 {
public static void main(String[] args) {
func3();
}
public static void func3() /*throws ArithmeticException*/{
// 则该方法相当于public void func2 throw ArithmeticException
int num1=1000;
int num2=0;
int temp=num1/num2;
}
// func5方法抛出了一个编译异常
// 这就要求func4方法处理这种异常
// 在func4中要么try-catch-finally或者继续使用throws抛出异常
public static void func4() /* throws Exception */{
//这里直接调用func6方法不会出现语法问题,因为是func6方法抛出的是运行异常
//而java中不要求在程序中显示的处理,因为有默认的处理机制
func6();
try {
func5();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} finally {
System.out.println("继续执行");
}
}
public static void func5() throws FileNotFoundException {
FileInputStream file = new FileInputStream("./dir/test.txt");
}
public static void func6() throws ArithmeticException{
}
}
自定义异常
- 定义类:自定义异常类继承Exception或者RuntimeException
- 如果继承Exception,属于编译异常
- 如果继承RuntimeException,属于运行异常
- 一般来说,自定义异常是继承RuntimeException,把自定义异常做成运行时异常,好处是我们可以使用默认的处理机制
例如:
package exception;
public class CustomException_01 {
public static void main(String[] args) {
int age=10;
//要求范围在18-120之间,否则抛出一个自定义异常
if (!(age>=18 && age<=120)){
throw new AgeException("年龄需要在18~120之间");
}
System.out.println("你的年龄是正确的");
}
}
//自定义异常
class AgeException extends RuntimeException{
public AgeException(String message){
super(message);
}
}
throw和throws的区别
两者 | 意义 | 位置 | 后面跟的内容 |
---|---|---|---|
throws | 异常处理的一种方式 | 方法声明处 | 异常类型 |
throw | 手动生成异常对象的关键字 | 方法体中 | 异常对象 |
下面是一个较难的例子: |
package exception;
public class ThrowException_02 {
public static void main(String[] args) {
try {
ReturnExceptionDemo.method_1();
} catch (Exception e) {
System.out.println(e.getMessage());
}
ReturnExceptionDemo.method_2();
}
}
class ReturnExceptionDemo{
static void method_1(){
try {
System.out.println("进入方法 1");
throw new RuntimeException("运行时异常");
} finally {
System.out.println("方法 1 finally 仍然执行");
}
}
static void method_2(){
try {
System.out.println("进入方法 2");
return ;
} finally {
System.out.println("方法 2 finally 仍然执行");
}
}
}
输出结果:
进入方法 1
方法 1 finally 仍然执行
运行时异常
进入方法 2
方法 2 finally 仍然执行