异常
1. 异常概述
- 异常:程序执行过程中,产出的问题,因为异常的问题程序的正常的逻辑中断
- Java程序在执行过程中所发生的异常事件可分为两类:异常最顶层的类Throwable
-
- Error: Java虚拟机无法解决的严重问题。 如: JVM系统内部错误、 资源耗尽等严重情况。
-
-
- StackOverflowError:栈深度溢出异常,比如递归方法,没有退出条件
- -Xss128k 设置栈大小
- java.lang.OutOfMemoryError:OOM内存溢出异常,栈,堆,方法区,程序计数器不会发生
- -Xms4g
- -Xmx4g
- -xx:MaxMetespaceSize=512M
-
- Exception: 其它因编程错误或偶然的外在因素导致的一般性问题, 可以使用针对性的代码进行处
-
-
- RuntimeException:运行期异常,编译是不需要处理
-
-
-
- ClassCastException
- ArrayIndexOutOfBoundsException
- ArithmeticException
- NullPointerException
-
-
- 非RuntimeException:编译期需要处理(try catche 或者throws)
-
-
-
- FileNotFoundException
- IOException
- SQLException
1.1 堆内存OOM
堆溢出异常:
public class Test1 {
static class OOM{}
/**
* xms:初始化堆内存
* xmx:最大堆内存
* 发生异常前的运行次数
* 360146 10m
* 810326 20m
* 1215488 40m
* @param args
*/
public static void main(String[] args) {
System.out.println("前面代码");
List<OOM> list = new ArrayList<>();
int i=0;
try {
while (true) {
i++;
list.add(new OOM());
}
} catch (Throwable e) {
System.out.println("=================多少次后发生异常:"+i);
e.printStackTrace();
}
}
设置jvm参数:
- -Xmx40m:最大堆内存大小40m
- -Xms10m:初始堆内存大小10m
执行结果:
1.2 StackOverflowError栈深度溢出异常
// 栈寻址深度溢出异常 java.lang.StackOverflowError 一直将f2()方法压入栈中
@Test
public void f2()
{
new ExceptionTest().f2();
}
1.5 方法区(元空间)OOM
利用了springboot框架自带的CGLIB的Enhancer类(通过不断循环创建这个新类的实例,模拟不断生成类的过程。)来不断的创建新的静态类来模拟元空间移除的实验。(注:一般静态类只会创建一次)
实验代码:
public class MetaspaceDemo {
// static int i =0;
//
// public static void a() {
// i++;
// System.out.println(i);
// a();
// }
//
//
static class OOM{}
public static void main(String[] args) {
int i = 0;//模拟计数多少次以后发生异常
try {
while (true){
i++;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOM.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(o,args);
}
});
enhancer.create();
}
} catch (Throwable e) {
System.out.println("=================多少次后发生异常:"+i);
e.printStackTrace();
}
}
}
设置最大方法区(元空间大小)
-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
实验结果:
1.4 其他异常
使用测试类来进行实验:
1.导入junitjar包
#创建lib目录
#拷贝junit的jar文件到目录
2.进行实验
/**
* 空指针异常
*/
@Test
public void f3() {
System.out.println("前面代码");
String s = null;
s.trim();
System.out.println("后续代码");
}
/**
* java.lang.ArithmeticException: / by zero:0作为除数异常
*/
@Test
public void f4() {
System.out.println("前面代码");
int i = 1/0;
System.out.println("后续代码");
}
}
2. 异常处理
2.1 try catch finally
try catch:
-
try:在try块中,我们编写可能会引发异常的代码。这些代码段被称为"受监控代码",因为程序会监视这些代码是否会抛出异常。
-
catch:如果在try块中的代码执行时抛出异常,catch块会捕获并处理该异常。在catch块中,我们可以编写处理异常的代码逻辑,以便程序在异常发生时不会崩溃,而是继续执行其他操作。catch块根据捕获到的异常类型来选择执行哪个代码块。
finally:
-
finally块是可选的,它跟在try-catch块之后。不论是否发生异常,finally块中的代码都会执行。通常在finally块中放置一些清理代码,比如释放资源(文件句柄、网络连接等)或确保某些操作总是会被执行。
- 不管try块中的代码是否抛出异常,finally块中的代码都会被执行,除非在try块中执行了System.exit()或关闭了JVM等导致程序终止的情况。
package com.example.java.day7;/**
* @User HASEE
* @Author WeiXu
* @Createtime 2023/7/26-26-10:09
* @PACKAGE_NAME com.example.java.day7
*/
import org.junit.Test;
/**
*@ClassName:ExceptionTest2
*@author weixu
*@date 2023/7/26 10:09
*/
public class ExceptionTest2 {
static void f1()
{
//除数为0异常
int i = 10 /0;
//数组下标异常
int[] i2 =new int[4];
i2[4] = 10;
//空指针异常
String string = null;
}
@Test //测试类必须是公开的 以下异常都为运行时异常的子类 运行期异常RuntimeException的子类,不需要处理,编译也不报错
public void f2()
{
try {
f1();
System.out.println("try内异常后的代码");
}
catch (ArithmeticException e)
{
e.printStackTrace();//打印异常信息
}
catch (NullPointerException e)//捕获空指针异常
{
e.printStackTrace();
}
catch (ArrayIndexOutOfBoundsException e)//捕获数组下标越界异常
{
e.printStackTrace();
}
catch (Exception e)//用来兜底的 捕获除erro以外的所有的异常
{
//输出异常信息
e.printStackTrace();
}
finally {
System.out.println("这条是必定执行的");//释放资源,必须执行的代码 如out.close(); 释放资源
}
System.out.println("异常后的代码");
}
}
e.printStackTrace()
打印异常信息
案例2
package com.example.java.day7;/**
* @User HASEE
* @Author WeiXu
* @Createtime 2023/7/26-26-11:04
* @PACKAGE_NAME com.example.java.day7
*/
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
*@ClassName:ExceptionTest3
*@author weixu
*@date 2023/7/26 11:04
*/
public class ExceptionTest3 {
public static void fn1() {
DataOutputStream out = null;
try {
out = new DataOutputStream(new FileOutputStream("d:\\a.txt"));//创建文件输出流
out.writeChar(97);
out.writeInt(1);
out.writeChar(98);
} catch (FileNotFoundException e) //捕获文件未找到异常
{
e.printStackTrace();
} catch (IOException e)//捕获io异常
{
e.printStackTrace();
} catch (Exception e) //捕获除erro以外的所有的异常
{
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
throw new RuntimeException(e);//抛出运行时异常
}
}
}
public static void main(String[] args) {
fn1();
}
}
2.2 throws
throws:声明异常,当前方法不捕获(catch)异常,而是由调用这个方法的方法catch异常(将异常抛给这个方法的调用者)
注意:RuntimeException(运行时异常)不需要catch异常
案例:
public class ExceptionTest3 {
public static void fn1() {
DataOutputStream out = null;
try {
out = new DataOutputStream(new FileOutputStream("d:\\a.txt"));//创建文件输出流
out.writeChar(97);
out.writeInt(1);
out.writeChar(98);
} catch (FileNotFoundException e) //捕获文件未找到异常
{
e.printStackTrace();
} catch (IOException e)//捕获io异常
{
e.printStackTrace();
} catch (Exception e) //捕获除erro以外的所有的异常
{
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
throw new RuntimeException(e);//抛出运行时异常
}
}
}
public static void main(String[] args) {
fn1();
}
}
2.3 throw和自定义异常
throw:抛出一个异常,可以是一个自定义异常,语法
throw new MyException(); //抛出自定义异常
throw new RuntimeException(e);//抛出运行期异常
1.自定义异常
自定义异常的主要用途是提供更清晰的错误信息和更精细的错误处理,帮助代码更易读懂和维护。它们让程序员可以定义特定类型的异常,以便更好地描述和处理特定的错误情况。
案例:自定义一个当赋值的学生编号id小于等于0时的异常
MyException:
//自定义异常
//public class MyException extends Exception{
public class MyException extends RuntimeException{
public MyException() {
}
public MyException(String message) {
super(message);
}
}
Student类:
public class Student {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
if (id > 0)
{
this.id = id;
}
else
{
//throw new RuntimeException(new MyException("学生编号不能小于等于0"));
throw new MyException("学生编号不能小于等于0"); //这样需要在本方法声明抛出异常
}
}
}
Test1类:
public class Test1 {
@Test
public void f1() {
Student stu = new Student();
stu.setId(-10);
}
}