问题:
1、常见的异常有哪些
2、遇到异常是如何解决的?有几种解决方式?
一、异常概述与异常体系结构
1、异常 :在Java语言中,将程序执行中发生的不正常情况称之为“异常”。
注:开发过程中的语法错误和逻辑错误不是属于异常
2、java程序在执行的过程中所发生的异常事件可分为两类:
2.1 Error
Java 虚拟机无法解决的严重问题。如: JVM 系统内部错误 、资源 耗尽等严重 情况 。比如: StackOverflowError 和 OOM 。一般不编写针对性 的代码进行处理 。
StackOverflowError
/**
* StackOverflowError 栈溢出
* @author dongxinxin
* @create 2022-03-05 14:59
*/
public class ErrorTest01 {
public static void main(String[] args) {
main(args);
}
}
Error 异常
Exception in thread "main" java.lang.StackOverflowError
at com.java.test01.ErrorDemo01.main(ErrorDemo01.java:9)
OOM
/**
* OOM 异常
* 堆内存不足
* @author dongxinxin
* @create 2022-03-05 15:13
*/
public class ErrorDemo01 {
public static void main(String[] args) {
Integer[] arr = new Integer[1024 * 1024 * 1024];
}
}
OOM 异常
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.java.test01.ErrorDemo01.main(ErrorDemo01.java:10)
2.2 Exception
其它因编程错误或偶然的外在素导致一般性问题 ,可以使 用针对性的代码进行处理 。例如:
空指针异常 如果创建一个对象,赋值为null,再用对象调用该类中的属性
IO异常
数组角标越界
3、如何解决这些写错误
对于这些错误 ,一般有两种 解决方法 :一是遇到错误就终止程序 的运行 。另一种方法是由程序员在编写时 ,就考虑到错误的检测 、错误消息的提示 ,以及错误的处理 。
捕获错误最理想的是在编译期间 ,但有的错误只在运行时才会发生 。 比如: 除数为 0,数组下标越界等
可分为:编译时异常和运行时异常
受检异常:编译异常
/**
* 编译时异常的一种情况
*
* 解决办法:使用try-catch-finally机制解决
*/
public static void test01(){
File file = null;
FileInputStream fis = null;
try {
file = new File("a.txt");
fis = new FileInputStream(file);
//读取数据
int data = fis.read();
while (data != -1) {// 读取数据的一个逻辑
System.out.print((char)data);
data = fis.read();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fis != null){
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
非受检异常:运行时异常
/**
* 异常体系结构
* java.lang.throwable
* Error: 一般不需要编写代码去解决
* Exception:可以进行异常处理,需要进行异常处理
* 编译时异常(checked):
* IOException
* FileException
* ClassNotFoundException
* 运行时异常(unchecked):
* NullPointException
* ArrayIndexOutOfBoundsException
* ArithmeticException(算术异常)等
* @author dongxinxin
* @create 2022-03-05 15:25
*/
public class ExceptionTest01 {
public static void main(String[] args) {
Integer[] arr = new Integer[2];
// System.out.println(arr[2]);// ArrayIndexOutOfBoundsException
// String[] str = null;
// System.out.println(str[2]);// NullPointerException
Object date = new Date();
// String str = (String)date;// ClassCastException 类型转换异常
String str = "123";
String str1 = "abc";
// int parseInt = Integer.parseInt(str1);// NumberFormatException
}
二、常见异常
三、异常处理机制:try-catch-finally
使用try-catch-finally处理编译时异常
/**
* 异常的处理:抓抛模型
*
* “抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,
* 并将此对象抛出
*
* “抓”:异常的处理方式:
* 方式一:try-catch-finally
* try {
* // 可能出现异常的代码
* } catch(异常类型1 变量名) {
* //处理异常
* }catch(异常类型2 变量名) {
* //处理异常
* }···{
*
* } finally { // 不一定非得写
* // 一定会执行的代码
* }
* 说明:
* finally 可选
* catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓
* 如果有子父类关系,则子类声明一定在父类的上面,否则报错
* 在try结构中定义中的变量,try外不可以用,如果外面想用,我们可以在try外声明
* 注:为了避免报错,要给try外定义的变量要进行初始化
* 方式二:throws
* @author dongxinxin
* @create 2022-03-05 16:17
*/
public class ExceptionTest02 {
public static void runExceptionTest(){
String str = "123";
str = "abc";
try {
int parseInt = Integer.parseInt(str);
System.out.println("hello ------ 1"); // 不会去执行
} catch (NumberFormatException e) {
// e.printStackTrace();
// System.out.println("出现数制转换异常");
e.printStackTrace();
}
System.out.println("hello ------ 2");
}
public static void main(String[] args) {
runExceptionTest();
}
}
finally的使用
/**
* try-catch-finally中的finally的使用
* 1、finally是可选的
* @author dongxinxin
* @create 2022-03-05 20:29
*/
public class FinallyTest {
/**
* finally 必须执行的
*/
public static void finallyTest01(){
try {
int a = 10;
int b = 0;
System.out.println(a / b);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("你好");
}
}
/**
* finally 中的return的使用
* @return
*/
public static int finallyTest02(){
try {
int a = 10;
return a;
} catch (Exception e) {
e.printStackTrace();
} finally {
return 0;// 作为最终的返回值结果
}
}
public static void main(String[] args) {
System.out.println(finallyTest02());
}
}
一种关于finally的小注意点:
如果try中出现异常,我们在catch中解决异常,但是如果catch中出现异常,后面的finally中地代码是否会被执行?
/**
* 如果try-catch中有异常我在catch中处理了,
* 如果catch中有异常,按道理来说,程序应该结束,不应该再去执行了,
* 但是这种情况下finally中的代码还回去执行,因为finally中表示的是最后一定要执行的
* 如果没有finally则直接程序结束
*
* 该方法的执行结果是:
* 我是finally中的代码
* Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
*/
public static void finallyTest03(){
try{
int a = 10;
int b = 0;
System.out.println("我是try中的代码:" + (a / b));
}catch (ArithmeticException e){
Integer[] arr = new Integer[10];
System.out.println("我是catch中的代码:" + arr[10]);
}finally {
System.out.println("我是finally中的代码");
}
}
/**
* 如果try-catch中有异常我在catch中处理了,
* 如果catch中有异常,按道理来说,程序应该结束,不应该再去执行了,
* 但是这种情况下finally中的代码还回去执行,因为finally中表示的是最后一定要执行的 情况1
* 但是如果没有finally,则程序结束,不会再去执行try-catch-finally之外的代码了 情况2
* 如果有finally,则也不会执行finally之外的程序,但是finally之内的是要进行处理的 情况3
*
* 执行结果:
* 情况2 :Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
* 情况3:我是finally中的代码
* Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
*/
public static void finallyTest04(){
try {
int a = 10;
int b = 0;
System.out.println("我是try中的代码:" + (a / b));
} catch (ArithmeticException e){
Integer[] arr = new Integer[10];
System.out.println("我是catch中的代码:" + arr[10]);// ArrayIndexOutOfBoundsException
} finally {
System.out.println("我是finally中的代码");
}
System.out.println("我是try-catch执行完之后的代码");// 不会去执行的
}
public static void main(String[] args) {
finallyTest04();
}
什么情况下才把代码写在finally中呢?
1、比如像流操作
2、数据库中的连接资源
3、网络编程中的socket等
/**
* finally中必须写的代码
* 以下出现的错误都会在 catch 中处理
* 情况一:
* 如果在流操作当中try中的 1 出会出现错误,则 fis 流不会创建,直接到 5
* 情况二:
* 如果try中 1 没有出现错误,则 fis 创建成功,接着往下执行代码 执行到 2 ,
* 如果 2 没有出现错误继续执行,执行到 3 ,有错误,则跳到 catch 当中,如果我将fis.close
* 关闭操作放在 4,则是不是不会执行,因为刚才执行到 3 时,跳到 catch 当中处理了错误,
* 因此岂不是造成了资源的浪费,只好将关闭资源的操作放在 finally 当中
*/
public static void finallyTest05(){
File file = null;
FileInputStream fis = null;
try {
file = new File("hello.txt");
fis = new FileInputStream(file);// 会出现编译错误 1
int read = fis.read();// 会出现编译错误 2
while (read != -1) {
System.out.println((char)read);
read = fis.read();// 会出现编译错误 3
}
// fis.close();// 4
} catch (IOException e) {
e.printStackTrace();// 5
} finally {
try {
if(fis != null){
fis.close();// 会出现编译错误 4
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
四、异常处理机制二:throws
throws跟在方法的括号后面,可以声明多个异常,以逗号分隔。这个声明的含义是说,我这个方法内可能抛出这些异常,我没有进行处理,至少没有处理完,调用者必须进行处理。这个声明没有说明,具体什么情况会抛出什么异常,作为一个良好的实践,应该将这些信息用注释的方式进行说明,这样调用者才能更好的处理异常。
一般对于运行时异常不要求使用throws进行声明的,但是对于编译异常则是必须进行生声明的。
对于一个方法调用了另一个声明抛出的编译异常,则此时的方法就应该处理这个编译异常,但是至于选择处理异常的方法,可以使用try-catch,也可以使用抛出的方式。
RuntimeException(unchecked)表示编程的逻辑错误,编程时应该检查以避免这些错误,比如说像空指针异常,如果真的出现了这些异常,程序退出也是正常的,程序员应该检查程序代码的bug而不是想办法处理这种异常。Checked exception表示程序本身没问题,但由于I/O、网络、数据库等其他不可预测的错误导致的异常,调用者应该进行适当处理。
五、手动抛出异常:throw
/**
* @Description TODO
* @Author dongxinxin@e6yun.com
* @Created Date: 2022/3/8 16:55
* @ClassName ThrowsTest
* @Remark
*/
public class ThrowsTest {
public static void main(String[] args) {
Integer[] arr = {1,2,3};
System.out.println(getArr(arr,4));
}
public static int getArr(Integer[] arr,int index){
if(arr == null){
throw new NullPointerException("有可能会导致空指针异常");
}
if(index < 1 || index > arr.length){
throw new ArrayIndexOutOfBoundsException("请重新传参,角标越界");
}
return arr[index - 1];
}
}
/**
ArrayList 中添加的方法中判断
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
throws 和 throw 的区别:
1、throws:用来声明一个方法可能产生的所有异常,不做任何处理而是将异常往上传,谁调用我我就抛给谁。
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
throws表示出现异常的一种可能性,并不一定会发生这些异常
2、throw:则是用来抛出一个具体的异常类型。
用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
表示抛出异常,由方法体内的语句处理
throw则是抛出了异常,执行throw则一定抛出了某种异常
六、用户自定义异常类
/**
* 自定义异常
* 如何自定义异常类?
* 1、继承现有的异常结构
* 2、Exception 仿照现有异常类进行构造
* 3、重载的构造器
* @author dongxinxin
* @create 2022-03-08 21:01
*/
public class MyExceptionTest extends Exception{
static final long serialVersionUID = -3387516993124229948L;
public MyExceptionTest(){}
public MyExceptionTest(String msg){
super(msg);
}
}
class Demo01{
public static void show(int index){
try {
if (index > 2){
throw new MyExceptionTest("有异常了");
} else {
System.out.println("已解决");
}
} catch (Exception e){
System.out.println(e.getMessage());
}
}
}
七、练习
/**
* 练习
* @author dongxinxin
* @create 2022-03-08 21:16
*/
public class DemoException {
public static void methodA(){
try {
System.out.println("进入方法A");
throw new RuntimeException("制造异常");
} finally {
System.out.println("A方法中的finally");
}
}
public static void methodB(){
try {
System.out.println("进入方法B");
return;
} finally {
System.out.println("B方法中的finally");
}
}
public static void main(String[] args) {
try {
methodA();
} catch (Exception e) {
System.out.println(e.getMessage());
}
methodB();
}
}