异常

错误与异常

java中对于程序出现的异常情况分为两种类别:

  1. 错误(Error)
  2. 异常(Exception)

**Error:**错误通常是系统级别的问题,比如说JVM内存溢出(StackOverflowError),JVM系统错误等,这些问题是程序员无法修复的问题,程序运行时出现的无法被程序员从业务上解决的问题,这些问题一般是系统级别的。错误不是我们关注的范畴

**Exception:**异常通常是程序再运行期间,或者编译期间由编译器抛出的一些,可以被程序员处理的代码上的问题,比如(NullPointerExcepotion/ArrayIndexOutOfBoundsException),异常是程序员开发中需要解决的问题

Throwable

Throwable是Java中错误和异常的顶级父类,以下是Throwable和Error,Exception之间的关系

Java中的所有错误从Error类继承,并且绝大多数类名称后缀以Error结尾

Java中的所有异常从Exception类继承,都是以Exception作为后缀结尾

异常概述

Exception:异常,一般在程序运行期间,或者编译期间由编译器抛出的异常信息,这些异常情况可以由程序员进行处理(抛出,捕获);java中的异常根据类型划分又分为两种类型:

  • 运行时异常(RuntimeException)
  • 检查异常(一般异常)

运行时异常

运行时异常一般在程序运行期间,出现了对应异常情况之后由JVM抛出,并且将异常的堆栈信息输出到控制台(或日志文件),java中的所有运行时异常都是从java.lang.RuntimeException继承而来。常见的运行时异常:

异常类型说明
java.lang.ArithmeticException算术异常(比如被零除)
java.lang.NullPointerException空指针异常(调用方法,属性的对象为null时)
java.lang.ArrayIndexOutOfBoundsException数组索引越界
java.lang.ClassCastException类型转换异常
java.util.InputMismatchException输入的数据类型不匹配读取的类型

运行时异常即程序运行时才会产生的异常

检查异常

检查异常也称之为一般异常,或者编译期异常,这种类型异常通常在编译期间由编译器提示需要进行显式的处理:

常见的检查异常:

异常类型说明
java.lang.ClassNotFoundException类未找到异常
java.io.FileNotFoundException文件未找到异常
java.io.IOExceptionIO异常(输入输出异常)
java.sql.SQLException访问数据库的异常
java.text.ParseException解析异常

检查异常是在程序编译时产生的

异常处理

异常既然产生则有必要进行合理的处理,Java中对于异常的处理分为两种方式:

  1. 异常抛出(throw/throws)
  2. 异常捕获(try/catch/finally)

Java程序中一旦出现异常,则出现异常问题的所在代码行之后的代码无法再执行

异常抛出

异常的抛出指的是将有可能出现的异常通过方法的结构向外抛出,交给下一级的调用者处理

/**
	 * 	抛出异常
	 * @throws ClassNotFoundException
	 */
public static void e1() throws ClassNotFoundException{	
    Class.forName("java.lang.Strin");		
}

抛出异常常见的关键字:

  • throws:用于方法的声明中,抛出有可能出现的异常
  • throw:用于语句块中,抛出指定类型的异常对象,throw一旦执行,则一定会出现该类型异常

语法区别:

  • throws

    【修饰符】 返回值类型 方法名(【参数列表】) throws 异常类型名称{
        //方法体
    }
    
    public static void e1() throws ClassNotFoundException{	
    	Class.forName("java.lang.String");		
    }
    
  • throw

    方法体{
        throw  异常类型对象
    }
    
    public static void main(String[] args) throws IOException  {	
        int i = 0;
        if(i == 0) {	
            //抛出异常对象
            throw new IOException();
        }
        System.out.println("hello");
    }
    
对于存在继承关系的异常抛出问题

父类的结构:

public class Animal {
	
	public void eat(){
		System.out.println("吃东西");
	}
}

子类结构:

public class Dog extends Animal{

    //编译错误
	public void eat() throws ClassNotFoundException{
		Class.forName("");
	}
}

对于以上程序:

子类Dog对父类Animal中的方法eat()方法进行了重写,但是由于父类方法没有抛出任何的异常,此时子类无法进行任何检查的抛出,否则会不兼容父类方法定义,因此以上程序在子类中会出现编译错误

解决方案有两种:

  1. 子类方法中对异常捕获

  2. 在父类方法的声明上加上对应的异常类型抛出定义:throws ClassNotFoundException

父类方法可以抛出比子类方法抛出的范围更大的异常,比如直接throws Exception

注意事项:

父类方法未抛出任何异常情况下,子类只能抛出运行时异常。

游离快和静态语句块中不能抛出任何异常,因为外界无法直接调用这两种语句块

异常捕获

异常的捕获即,将有可能出现异常的代码片段使用try语句块进行包裹,然后使用catch语句块将有可能产生的异常类型进行捕获,并作出处理。

异常捕获常见的关键字:

  • try
  • catch
  • finally

语法结构:

try{
    //有可能出现异常的代码片段
}catch(异常类型 变量名){
    //处理异常
}finally{
    //不论是否出现异常,始终执行
}
try {
    m1();
    PrintStream ps = new PrintStream("a/test/details.log");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (FileNotFoundException e) {
    System.out.println("fileNotFound");
    e.printStackTrace();
} catch (IOException e) {
    System.out.println("IO");
    e.printStackTrace();
} 

程序执行到try语句块,在执行期间如果出现了对应catch的异常类型,则直接进入catch语句块,如果catch语句块中没有合适的异常解决方案,则由JVM进行统一处理(打印异常的堆栈信息)

finally

finally一般用于异常捕获之后执行最终的处理工作,比如,清理资源,关闭流,关闭连接;finally中的代码无论是否出现异常,始终会执行。

try {
    //打开资源
    System.out.println("打开文件");
    System.out.println(10/2); 
}catch(Exception e) {
    e.printStackTrace();
}finally {
    //无论是否异常始终执行
    System.out.println("关闭文件");			
}

try,catch,finally的组织方式可以有多种:

//方法一:
try {

}finally {

}

//方法二:
try {

}catch(Exception e) {

}

//方法三:
try {

}catch(RuntimeException re) {

}catch(Exception e) {

}

//方法四:
try {

}catch(Exception e) {

}finally {

}

关于异常的常见面试题

  1. 请你说出 final、finalize和finally的区别?

final是一个关键字用于修饰类,属性,方法

finalize是Object类中提供的一个方法,用于在jvm对对象清理时,对于当前对象执行一些最终的处理工作的

finally是异常中的语句块

  1. java中是否会存在内存溢出的问题?(指针)

理论上java不会存在内存泄漏问题,因为jvm提供了GC(垃圾回收:garbage collection)机制,会在适当的时候自动回收内存空间,不需要由程序员手动处理;但是如果使用第三方资源(比如:打开一个文件,打开了网络通道,打开数据库连接等)并且未及时的清理以及回收,将会导致内存泄漏。

  1. 异常处理中finally和return的结合使用?

如果try语句块中有使用return,并且try语句块中没有任何异常时,程序首先会执行finally然后再执行return;但是对于基本类型的数据,finally的赋值是不会生效的,但是finally中操作引用类型的属性可以生效

//程序正常执行,返回 20;finally中的赋值无效
public static int m2() {
    int i = 10;
    try {
        i = 20;
        return i;		
    }catch(Exception e){
        e.printStackTrace();
    }finally {
        i = 30;
        System.out.println("finally");
    }
    return i;
}

//程序正常执行,返回对象中的name属性值被修改为“李四”;finally中的赋值生效
public static User m3() {
    User u = new User();
    try {
        u.name = "张三";
        return u;
    }catch(Exception e) {
        e.printStackTrace();
    } finally {
        u.name = "李四";
    }
    return u;
}

自定义异常

概述

​ 以上我们已经熟悉了java中的异常分类以及处理方式,其中异常分类主要包含检查异常和运行时异常,但是以上所有异常都是有JDK预定义好的异常类型,比如:空指针,索引越界,类型转换失败等代码语法方面的异常,并没有与实际项目相关一些业务方面的异常,比如:订单创建失败,用户权限不足,余额不足等异常情况;

​ 因此,针对以上的需求,当预定的异常无法满足所有需要时,我们可以通过对JDK的异常进行扩展,自定义异常,以满足实际项目的需求。

自定义异常的使用

​ java中创建自定义异常十分简单,只需要对现有的异常类型,扩展即可,比如常见的方式为:继承Exception,声明一个无参的以及一个包含字符串类型参数的构造器即可。异常的定义通常用于标记程序运行时的异常情况,并不需要在异常中进行任何的业务逻辑处理,因此自定义异常中也无需定义任何的方法。

案例:

public class MyException extends Exception{

	public MyException() {
		super();
	}
	
	public MyException(String msg) {
		super(msg);
	}
}

自定义异常综合案例:

一有个银行账户A和账户B,现在需要从账户A转账到账户B,转账需要检查账户的余额是否足够,如果余额不足,则抛出一个 MoneyLessException,请实现!

账户类(Account.java)

public class Account {

	private int id;
	private String name;
	private double money;
	
    //构造器(略)
	//setter/getter(略)
    //toString(略)
}

账户管理类(AccountManager.java)

public class AccountManager {

	/**
	 * 	将指定账户中的余额转移指定数目到另一个账户中
	 * @param a1  	账户A
	 * @param a2 	账户B
	 * @param money 需要转账的金额
	 * @throws MoneyLessException
	 */
	public void transfer(Account a1,Account a2,double money) throws MoneyLessException {
		
		if(a1.getMoney() < money) {
			//余额不足
			throw new MoneyLessException("余额不足:"+(a1.getMoney() - money));
		}
		a1.setMoney(a1.getMoney() - money);
		a2.setMoney(a2.getMoney() + money);
		
		System.out.println(a1);
		System.out.println(a2);
	}
}

自定义异常类(MoneyLessException.java)

/**
 * 	余额不足异常
 * @author mrchai
 *
 */
public class MoneyLessException extends Exception {

	public MoneyLessException() {
		super();
	}
	
	public MoneyLessException(String msg) {
		super(msg);
	}
}

测试类(Test.java)

public class Test {

	public static void main(String[] args) throws MoneyLessException {
		
		Account a1 = new Account(1, "A", 500);
		Account a2 = new Account(2, "B", 100);
		
		AccountManager am = new AccountManager();
		am.transfer(a1, a2, 100);
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值