Java异常

简介

       Java异常是Java提供的一种识别及响应错误的一致性机制。
在这里插入图片描述
在这里插入图片描述

Throwable

       Throwable 是 Java 中所有错误与异常的超类。

       Throwable 包含两个子类:Error(错误)和 Exception(异常),通常用于指明异常情况。

       Throwable 包含了其线程创建时线程执行堆栈的快照,它提供了printStackTrace()等接口用于获取堆栈跟踪数据等信息。

Error(错误)

       Error是程序中无法处理的错误。

       此类错误一般表示代码运行时JVM出现问题。通常有Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)。比如 OutOfMemoryError:内存溢出错误;StackOverflflowError:栈溢出错误。此类错误发生时,JVM 将终止线程。这些错误是不受检异常(非受检异常),非代码性错误。

OOM(OutOfMemoryError)

为什么会没有内存了呢?原因不外乎有两点:

       1)分配的少了:比如虚拟机本身可使用的内存(一般通过启动时的VM参数指定)太少。

       2)应用用的太多,并且用完没释放,浪费了。此时就会造成内存泄露或者内存溢出。

引起OOM主要有2个原因:

       内存泄露:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。(堆溢出)

       内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢出。(栈溢出)

最常见的OOM情况有以下三种:

       java.lang.OutOfMemoryError: Java heap Space; :java堆内存溢出,这种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms,-Xmx等修改。

       java.lang.OutOfMemoryError: PermGen Space;:java永久代溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。此种情况可以通过更改方法区的大小来解决,使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,过多的常量尤其是字符串也会导致方法区溢出。

       java.lang.StackOverflowError:不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小。

SOF(StackOverFlowError)- 堆栈溢出

       如果线程请求的栈深度大于虚拟机所允许的深度,抛出该异常;

  • 栈溢出的原因:

    递归调用
    大量循环或死循环
    全局变量是否过多
    数组、List、map数据过大

Exception(异常)

       程序本身可以捕获并且可以处理的异常。

       Exception 这种异常又分为两类:运行时异常和编译时异常。

异常的分类

受检异常与非受检异常

       受检(checked)异常:编译器要求必须处理的异常。除 RuntimeException 及其子类外,其他的Exception 异常都属于受检异常。

       非受检(unchecked)异常 :编译器不会进行检查并且不要求必须处理的异常,该类异常包括 运行时异常(RuntimeException及其子类) 和 错误(Error)

运行时异常(属于非受检异常)

       定义:RuntimeException 类及其子类,表示 JVM 在运行期间可能出现的异常。

       Java 编译器不会检查它。也就是说,当程序中可能出现这类异常时,既没有通过"throws"声明抛出它,也没有用"try-catch"语句捕获它,还是会编译通过。

       RuntimeException 异常会由 Java 虚拟机自动抛出并自动捕获,该异常的出现绝大数情况是代码本身有问题,应该从逻辑上去解决

编译时异常(非运行时异常,属于受检异常)

       定义: Exception 中除 RuntimeException 及其子类之外的异常。

       Java 编译器会检查它。编译器会检查此类异常,如果程序中出现此类异常,比如说IOException,必须对该异常进行处理,要么使用try-catch捕获,要么使用throws语句抛出,否则编译不通过。

常用异常举例

异常说明
NullPointerException空指针异常
ArrayIndexOutOfBoundsException数组下标越界异常
ArithmeticException算术异常,如除数为零
ClassNotFoundException不能加载所需要的类
IllegalArgumentException接受非法参数
InvocationTargetException当被调用的方法内部抛出异常未被捕获时,会抛出这个异常
SQLException操作数据库异常
IOException输入输出异常
InputMisMatchException类型不匹配
NumberFormatException格式化数据异常
FileNotFoundException文件未找到异常
InstantiationException实例化异常
当试图通过Class的newInstance方法创建某个类的实例,但程序无法通过该构造器来创建该对象时引发
IllegalAccessException没有访问权限
ClassCastException类型转换异常

处理异常

       分为两块:抛出异常 和 捕获异常。

throw,throws

throw

       语法:throw (异常对象);

       throw:将产生的异常抛出,是抛出异常的一个动作。

        一般会用于程序出现某种逻辑时,程序员主动抛出某种特定类型的异常,throw是明确了这个地方要抛出这个异常。

throws

       throws:声明将要抛出何种类型的异常。

       当某个方法可能会抛出某种异常时,用throws 声明可能抛出的异常,然后交给上层调用它的方法程序处理。

       throws E1,E2,E3只是告诉程序这个方法可能会抛出这些异常,方法的调用者可能要处理这些异常,而这些异常E1,E2,E3可能是该函数体产生的。

public void show() throws RemoteException{
	// 抛出异常
	throw new RemoteException();
}

throw与throws的区别

不同点throwthrows
位置出现在函数体(方法体内)出现在方法函数头
是否出现执行throw则一定抛出了某种异常对象表示出现异常的一种可能性,并不一定会发生这些异常
处理由方法体内的语句处理由该方法的调用者来处理

       相同点:两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用来处理。

try,catch,finally

介绍

      try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。

      catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。

// 多重捕获块
try {
     // 代码块
} catch (ClassNotFoundException e) {
     e.printStackTrace();
} catch (Exception e) {
     e.printStackTrace();
} finally {
     // 其它处理;
}

      try语句块不可以独立存在,必须与 catch 或者 finally 块同存。也就是存在3种情况:try-catch,try-finally,try-catch-finally。

      多个catch块处理的异常类,要按照先catch子类 后catch父类的处理方式(顺序不对编译器会提醒错误)。catch 不能独立于 try 存在。

      无论是否发生异常,finally 代码块中的代码总会被执行,前提是try执行。在 finally 代码块中,可以运行清理类型等收尾善后性质的语句,用于关闭和释放资源。

      在 try/catch 后面添加 finally 块并非强制性要求的,try,catch,finally 块之间不能添加任何代码。

至少有两种情况下finally语句是不会被执行的

  • try语句没有被执行到,如在try语句之前就返回了,这样finally语句就不会执行,这也说明了finally语句被执行的必要而非充分条件是:相应的try语句一定被执行到
  • 在try块中有System.exit(0);这样的语句,System.exit(0);是终止Java虚拟机JVM的,连JVM都停止了,所有都结束了,当然finally语句也不会被执行到。

try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

      会执行,finally是在try或catch中的retrun语句执行后,return返回之前执行的。

      try或catch中的return语句已经执行了,不过并没有直接返回,而是等finally语句执行完了再返回结果。

      如果finally里有return,则直接返回,就不管try或catch中是否还有返回语句了。finally里加上return后,finally外面的代码就变成不可达语句了,也就是永远不能被执行到,所以需要注释掉否则编译器报错。

try或catch中有return X,finally修改 X,返回值是否有影响?

      如果finally中有return,则直接返回,就不管try或catch中是否还有返回语句。

      如果finally中没有return,考虑返回值类型。如果返回值是以地址返回的,finally中修改则影响最后的结果(数组,List,Map,Set…);如果返回值是以实际值返回的,finally中修改不影响结果(int,Integer,String…)。

       finally中修改返回值的做法是不好的,应该避免。finally中存在return也是不好的做法,应该避免。

public class Test {
	public static void main(String[] args) {
		// int
		System.out.println("返回结果:" + new Test().test());
		System.out.println();
		// String
		System.out.println("返回结果:" + new Test().test1());
		System.out.println();
		// String[]
		String[] a = new Test().test2();
		System.out.println("返回结果:" + a[0]);
		System.out.println();
		// Integer
		System.out.println("返回结果:" + new Test().test3());
	}

	public int test() {
		int a = 100;
		try {
			System.out.println("try异常前");
			System.err.println(a / 0);
			System.out.println("try异常后");
			a = 10;
		} catch (ArithmeticException e) {
			System.out.println("catch子异常");
			a = 20;
			return a;
		} catch (Exception e) {
			System.out.println("catch父异常");
			a = 30;
			return a;
		} finally {
			System.out.println("finally");
			a = 40;
		}
		System.out.println("外部");
		return a;
	}

	public String test1() {
		String a = "100";
		try {
			System.out.println("try异常前1");
			System.err.println(100 / 0);
			System.out.println("try异常后");
			a = "10";
		} catch (ArithmeticException e) {
			System.out.println("catch子异常");
			a = "20";
			return a;
		} catch (Exception e) {
			System.out.println("catch父异常");
			a = "30";
			return a;
		} finally {
			System.out.println("finally1");
			a = "40";
		}
		System.out.println("外部");
		return a;
	}

	public String[] test2() {
		String[] a = { "0" };
		try {
			System.out.println("try异常前2");
			System.err.println(100 / 0);
			System.out.println("try异常后");
			a[0] = "10";
		} catch (ArithmeticException e) {
			System.out.println("catch子异常");
			a[0] = "20";
			return a;
		} catch (Exception e) {
			System.out.println("catch父异常");
			a[0] = "30";
			return a;
		} finally {
			System.out.println("finally2");
			a[0] = "40";
		}
		System.out.println("外部");
		return a;
	}

	public Integer test3() {
		Integer a = Integer.valueOf("0");
		try {
			System.out.println("try异常前3");
			System.err.println(100 / 0);
			System.out.println("try异常后");
			a = Integer.valueOf("10");
		} catch (ArithmeticException e) {
			System.out.println("catch子异常");
			a = Integer.valueOf("20");
			return a;
		} catch (Exception e) {
			System.out.println("catch父异常");
			a = Integer.valueOf("30");
			return a;
		} finally {
			System.out.println("finally3");
			a = Integer.valueOf("40");
		}
		System.out.println("外部");
		return a;
	}
}

运行结果:

try异常前
catch子异常
finally
返回结果:20

try异常前1
catch子异常
finally1
返回结果:20

try异常前2
catch子异常
finally2
返回结果:40

try异常前3
catch子异常
finally3
返回结果:20

自定义异常

       Java中要想创建自定义异常,需要继承Throwable或者他的子类Exception。

  • 所有异常都必须是 Throwable 的子类
  • 如果写一个检查性异常类,需要继承 Exception 类
  • 如果写一个运行时异常类,需要继承 RuntimeException 类
public class MyException extends Exception {
	// 一个无参构造函数
	public MyException(){ }
	// 一个带有详细描述信息的构造函数
	// Throwable的 toString 方法会打印这些详细信息,调试时很有用
	public MyException(String msg){
		super(msg);
	}
	// ...
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值