异常处理方式一:捕获异常(抓抛模型)(try-catch-finally的使用)
过程①:“抛”
程序在执行的过程中,一旦出现异常,就会在出现异常的代码处,生成对应异常类的对象,并将此对象抛出。
一旦抛出,此程序就不执行其后的代码了。
过程②:“抓” 针对于过程1抛出的异常对象,进行捕获处理。此捕获处理的过程,就称为抓。 一旦将异常进行了处理,代码就可以继续执行。
基本结构:
try{
……//可能产生异常的代码
}
catch(异常类型1 e){
……//当异常类型1型异常时的处理措施
}
catch(异常类型2 e){
……//当异常类型2型异常时的处理措施
}
……
……
finally{
//无论是否发生异常,都无条件执行的语句
}
使用细节:
将可能出现异常的代码声明在try语句中。一旦代码出现异常,就会自动生成一个对应异常类的对象。并将此对象抛出。
针对于try中抛出的异常类的对象,使用之后的catch语句进行匹配。一旦匹配上,就会进入catch语句进行处理。
一旦处理结束,代码就可以继续向下执行。
如果声明了多个catch的结构,如果不同的异常类型存在子父类的关系,则必须将子类声明在父类结构的上面。否则会报错(因为多态性,父类的声明在上面的话,会先被有父类声明的catch捕获,下面的有子类的声明的catch就没有机会捕获到异常类型的对象)
catch中异常处理的方式:
①自己编写输出的语句。
②printStackTrace( ) : 打印异常的详细信息。(推荐)
③getMessage( ) : 获取发生异常的原因。
try中声明的变量,出了try结构之后,就不可以调用了。
开发时:
对于运行时的异常:
开发时,通常不进行显式的处理。一旦在程序执行中,出现了运行时异常,那么就根据异常的提示信息修改代码即可。
对于编译时异常:
一定要处理,否则编译报错。
finally关键字的使用:
我们将一定要被执行的代码声明在finally结构中。
无论try中或catch中是否存在未被处理的异常,无论try中或catch中是否存在return语句等,finally中声明的语句一定要被执行。
如以下代码:
public class FinallyTest
{
public static void main(String[] args)
{
FinallyTest ff = new FinallyTest();
ff.test2();
ff.test1();
}
public void test1()
{
try{
String str = "aaa";
int i = Integer.parseInt(str); //NumberFormatException
System.out.println(i); //try中出翔异常,会进入catch语句
}
catch (NumberFormatException e)
{
e.printStackTrace();//输出异常信息
System.out.println(10/0);//若catch中出现异常,就不会继续向下执行,就不会继续输出“最后一个语句”.
}
System.out.println("最后一个语句");
}
public void test2()
{
try{
String str = "aaa";
int i = Integer.parseInt(str); //NumberFormatException
System.out.println(i); //try中出翔异常,会进入catch语句
}
catch (NumberFormatException e)
{
e.printStackTrace();//输出异常信息
System.out.println(10/0);//算术异常
}
finally
{
System.out.println("最后一个语句");//放在finally中,一定会执行。
}
}
}
public class FinallyTest1
{
public static void main(String[] args)
{
int result = test("123");
System.out.println(result);
int result2 = test("abc");
System.out.println(result2);
}
public static int test(String str)
{
try{
Integer.parseInt(str);
return 1;
}
catch (NumberFormatException e)
{
return -1;
}
finally{
System.out.println("test结束");
//如果加入语句:
//return 0;
//由于finally语句的运行的必然性,最后运行的语句一定是finally中的语句,所以此情况下,最后放回的是0.
}
//体现了finally中的语句的运行的必然性
}
}
要声明在finally中的代码:
比如:输入流、输出流,数据库连接、Socket连接等资源,在使用完后,必须显式的进行关闭操作,否则,GC不会自动的回收这些资源。进而导致内存的泄露。为了保证这些资源在使用完后,不管是否出现了未被处理的异常的情况下,这些资源能被关闭,必须将这些操作声明在finally中。
异常处理方式二:throws 格式:
在方法的声明处,使用“throws 异常类型1,异常类型2,异常类型3,……”的格式声明。
public void test( ) throws 异常类型1,异常类型2,…… {
//可能存在编译时异常的代码
}
从编译是否能通过的角度看,看成是给出了异常万一要是出现时候的解决方案,就是继续向上抛出(throws)。但是,此throws的方式,仅仅是将出现的异常抛给了此方法的调用者。此调用着依然要考虑如何处理相关异常。所以throws的方式不算是真正意义上处理了异常。
方法重写的要求:(针对于编译时异常)
①子类重写的方法中可以抛出父类中被重写的方法的抛出的异常类型的子类的异常类型,但是子类中的方法不能抛出不是父类中被重写的方法的抛出的异常类型的子类的异常类型。
②父类中的方法没有抛出异常,子类也不能抛出异常类型(运行时异常除外)。
③实现接口中的方法也是同理。
import java.io.FileNotFoundException;
import java.io.IOException;
public class OverrideTest
{
public static void main(String[] args)
{
Father f = new Son();
try{
f.method1();
}
catch (IOException e) //抛出的时候利用了异常类型的多态性
{
e.printStackTrace();
}
}
}
class Father{
public void method1() throws IOException
{
}
public void method2()
{
}
}
class Son extends Father{
//子类重写的方法中可以抛出父类中被重写的方法的抛出的异常类型的子类的异常类型,但是子类中的方法不能抛出不是父类中被重写的方法的抛出的异常类型的子类的异常类型
public void method1() throws FileNotFoundException //FileNotFoundException是IOException的子类,二者不能交换
{
}
public void method2() //throws IOException
{
//父类中的方法没有抛出异常,子类也不能抛出异常类型(运行时异常除外),
}
}
使用场景:
try-catch-finally:
资源一定要执行、重写。
throws:
方法之间有递进关系,一般以终点的调用者使用try-catch-finally。
手动抛出异常:
实际开发中,如果不满足具体场景的代码问题,就要手动抛出指定类型的异常对象。
比如:学生的学号输入为负数。
程序在执行的过程中,不满足指定条件的情况下,我们主动地使用“throw + 异常类的对象” 的方式抛出异常对象。
注:throw之后的语句不能被执行。
public class ThrowTest
{
public static void main(String[] args)
{
Student ss = new Student();
//ss.register(-3);
try{
ss.register2(-2);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
class Student
{
int id;
public void register(int id)
{
if(id > 0)
{
this.id = id;
}
else {
//手动抛出异常类的对象
throw new RuntimeException("输入的id非法");
//throw之后的语句不能被执行
//System.out.println("此语句不能被执行");
}
}
public void register2(int id) throws Exception
{
if(id > 0)
{
this.id = id;
}
else {
//手动抛出异常类的对象
throw new Exception("输入的id非法");
}
}
}
throws和throw的区别:
throws是声明在方法处,将异常向上抛出,抛出给方法的调用者。
throw是手动将异常对象抛出时使用的。
自定义异常类:
步骤:
①继承于现有的异常体系。通常继承于RuntimeException异常类或者Exception异常类。
②通常提供几个重载的构造器。
③提供一个全局常量,声明为:static final long serialVersionUID
如何使用自定义异常类:
在具体的代码中,满足指定条件的情况下,需要手动地使用“throw + 自定义异常类的对象”的方式,将异常对象抛出。
如果自定义异常类是非运行时异常,则必须考虑如何处理此异常类的对象。(两种异常处理方式)
public class ThrowTest
{
public static void main(String[] args)
{
Student ss = new Student();
try{
ss.register2(-2);
}
catch (BelowZeroExpection e)
{
e.printStackTrace();
}
}
}
class Student
{
int id;
public void register2(int id) throws BelowZeroExpection
{
if(id > 0)
{
this.id = id;
}
else {
//手动抛出异常类的对象
throw new BelowZeroExpection("输入的id非法");
}
}
}
为什么需要自定义异常类?
为了在异常出现时,通过异常的名称,就能判断出具体出现的问题。在实际开发中,不满足我们指定的条件时,指明我们特有的自定义的异常类。通过此异常类的名称,就能判断出具体出现的问题。