java基础:8.1 异常

本文深入探讨Java异常处理机制,包括异常的基本概念、抛出与捕获异常的方法、再次抛出异常的技巧及finally块的使用。同时,文章还介绍了如何自定义异常类,并通过实例演示了异常处理的实践应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 基本概念

异常的根类是 java.lang.Throwable。可以通过继承Exception或者Exception的子类来创建自己的异常类。

异常处理的优点:它能使方法抛出一个异常给它的调用者,由调用者处理该异常。

key word : try、 catch 、 finally、 throws
在这里插入图片描述
Error类层次描述的是JAVA运行时系统内部错误和资源耗尽错误。应用程序不应该会抛出此类错误!!

我们主要关注的是Expection(异常)。分两类:RuntimeException、IOException。
由程序运行导致的异常是RuntimeException;而程序本身没有问题,但由于像I/O错误这类问题导致的异常属于其他异常。

派生于Error类或者RuntimeException的所有异常称为“未检查异常”,所有其他异常未已检查异常。


throw 和 throws的区别

throws与throw这两个关键字接近,不过意义不一样,有如下区别:

  • throws 出现在方法声明上,而throw通常都出现在方法体内。
  • throws 表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某个异常对象。

异常类有三种主要类型:

异常 (Exception) 又叫可查异常: CheckedException,是必须进行处理的异常,要么try catch,要么往外抛,谁调用,谁处理,如果不处理,编译器就不让你通过。

错误 (Error)是由java虚拟机抛出的,用Error类表示,不要求强制捕获
Error类描述的是内部系统错误,这样的错误很少发生。如果发生,除了通知用户以及尽量稳妥地终止程序外,几乎什么也不能做。常见的有:内存用光了,抛出OutOfMemoryError

运行时异常 (Runtime Exception)是用RuntimeException类表示的,它描述的是程序设计错误, 不是必须进行try catch。在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,即便不进行try catch,也不会有编译错误。运行时异常通常是由Java 虚拟机抛出的。

一个方法必须声明所有可能抛出的已检查异常!
如果一个超类没有抛出任何异常,子类也不能抛出任何异常。


常见的RuntimeException(运行时异常):

IndexOutOfBoundsException(下标越界异常)
NullPointerException(空指针异常)
NumberFormatException (String转换为指定的数字类型异常)
ArithmeticException -(算术运算异常 如除数为0)
ArrayStoreException - (向数组中存放与声明类型不兼容对象异常)
SecurityException -(安全异常)
IOException(其他异常)
FileNotFoundException(文件未找到异常。)
IOException(操作输入流和输出流时可能出现的异常。)
EOFException (文件已结束异常)


2. 抛出异常

String ClassName(...) throws ArithmeticException
{
	...
	if(....)
		throw new xxException();
	...
}      
//  java.lang.ArithmeticException,在方法中使用。当异常被抛出,正常的执行流程被中断

当标准异常类不能完全说明异常问题时,我们可以自定义一个异常类。

派生于Exception的类,或派生于Exception子类的类

public class DefineException extends Exception{
...
}

如果方法没有在父类中声明异常,那么就不能再子类中对其进行继承来声明异常。

注意:在catch块中异常被指定的顺序是非常重要的,如果父类的catch块出现在唉子类的catch块之前,就会导致编译错误。
 

3. 捕获异常

知道异常怎么抛出后,要知道怎么捕获异常。语法如下

try  {     
	code;
	more code;
	more code; 
 }

catch(ExceptionType e){    
	handle for this type;
}    
catch(ExceptionType e){    
	handle for this type;
}    
catch(ExceptionType e){    
	handle for this type;
}    
// 可以存在多个catch块,或者使用 | 在一个块内声明

finally{ 
	此处的语一定会被执行 
} 

注意,不要用try-catch块处理简单、可预料的情况!!!因为捕获异常所花费的时间远远超过执行简单测试程序

可用 e.getMessage()得到详细的错误信息;用 e.getClass().getName()得到异常对象的实际类型。

刚才说过,如果一个超类没有抛出任何异常,子类也不能抛出任何异常。那么说明,在子类中必须捕获本方法中可能发生的所有可检查异常,不允许在子类的throws说明符中出现超过超类方法所列出的异常类范围。
 

4. 再次抛出异常

在catch子句中可以抛出一个异常,目的是改变异常的类型。

 

5. finally

在任何情况下,finally块中的代码都会被执行,不论try块中是否出现异常或者是否被捕获。考虑下面三种可能出现的情况:

	try  {     
		code;
	 }
	
	catch(ExceptionType e){    
		handle for this type;
	}    
	
	finally{ 
		此处的语一定会被执行 
	} 

如果try块中没有出现异常,执行finalStatements,然后执行try语句的下一条语句。

如果try块中有一条语句引起异常,并被catch捕获,然后跳过try块的其他语句,执行catch块和finally子句。执行try语句之后的下一条语句。

如果try块中有一条语句引起异常,但是没有被任何catch块捕获,就会跳过try块中的其他语句,执行finally子句,并且将异常传递给这个方法的调用者。

即使在到达finally块之前有一个return语句,finally块还是会执行。
 

实践:

修改我们之前的类Circle

// Circle.java
public class Circle {
    private double radius;
    private static int circleNumber =0;
    
    /*	set radius */
    Circle(){
        circleNumber++;
        radius = 1.0;
    }
    Circle(double newRadius){
        setRadius(newRadius);
        circleNumber++;
    }
    /* return radius */
    public double getRadius() {
    	return radius;
    }
    /* set radius 
    public void setRadius(double radius) {	
    	this.radius = (radius >=0) ? radius : 0;
    } */   
    public void setRadius(double radius) throws IllegalArgumentException{	
/* 即使在方法声明中删除throws IllegalArgumentException 子句,
Circle类也仍然会编译,因为该异常是RuntimeException 的子类,
而且不管是否在方法头中声明,每个方法都能抛出RuntimeException 异常(免检异常)。*/
    	if (radius>=0)  
    		this.radius=radius;
    	else throw new IllegalArgumentException("radius cannot be negative");
    } 
    /* get the number of all circles */
    public static int getNumberOfCircle() {
    	return circleNumber;
    }
    public double getArea(){
        return radius*radius*Math.PI;
    }
}

测试程序

//  testCircle.java
public class testCircle {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
		Circle c1 = new Circle(-2);
		Circle c2 = new Circle(5);
		Circle c3 = new Circle();
		Circle c4 = new Circle(0);
		}
		catch (IllegalArgumentException ex) {
			System.out.println(ex);
		}
		System.out.println("the number of circle is : " + Circle.getNumberOfCircle());
	}
}

观察程序的运行结果,我们发现:当c1出现异常,成功catch后,不会再返回执行Circle c2 = new Circle(5)以及后续语句;程序会顺着catch块后继续执行。

basic knowledge:
在执行完catch 块之后,程序控制不返回到throw 语句;而是执行catch 块后的下一条语句
 

自定义异常代码

//  DefineException.java
public class DefineException extends Exception {
	private double radius;
	public DefineException(double radius) {
		super("Invaild radius "+radius);
		this.radius= radius;
	}
	public double getRadius() {
		return radius;
	}
}

// Circle.java
public class Circle {
    private double radius;
    private static int circleNumber =0;
    
    /*	set radius */
    Circle() throws DefineException {
        circleNumber++;
        radius = 1.0;
    }
    Circle(double newRadius) throws DefineException{
        setRadius(newRadius);
        circleNumber++;
    }

    /* return radius */
    public double getRadius() {
    	return radius;
    }
    public void setRadius(double radius) throws DefineException{	
    	if (radius>=0)  
    		this.radius=radius;
    	else throw new DefineException(radius);
    }
    
    /* get the number of all circles */
    public static int getNumberOfCircle() {
    	return circleNumber;
    }
    
    public double getArea(){
        return radius*radius*Math.PI;
    }
}
//  testCircle.java
public class testCircle {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
		Circle c1 = new Circle(-2);
		Circle c2 = new Circle(5);
		Circle c3 = new Circle(6);
		Circle c4 = new Circle(0);
		}
		catch (DefineException ex) {
			System.out.println(ex);
		}
		finally {
			System.out.println("finally");
		}

		System.out.println("the number of circle is : " + Circle.getNumberOfCircle());
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值