男主角小明每天开车上班,正常车程1小时。但是,e4不出意外的话,可能会出现意外。
出现意外,即为异常情况。我们会做相应的处理。如果不处理,到不了公司。处理完了,就可以正常开车去公司。
在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美
,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式问题
,读取文件是否存在
,网络是否始终保持通畅
等等。
异常指的并不是语法错误和逻辑错误。
语法错了,编译不通过,不会产生字节码文件,根本不能运行。
代码逻辑错误,只是没有得到想要的结果,例如:求a与b的和,你写成了a-b
Java中是如何表示不同的异常情况,又是如何让程序员得知,并处理异常的呢?
Java中把不同的异常用不同的类表示,一旦发生某种异常,就创建该异常类型的对象
,并且抛出(throw)。然后程序员可以捕获(catch)到这个异常对象,并处理;如果没有捕获(catch)这个异常对象,那么这个异常对象将会导致程序终止。
举例:
运行下面的程序,程序会产生一个数组角标越界异常ArrayIndexOfBoundsException
。我们通过图解来解析下异常产生和抛出的过程。
public class ArrayTools {
// 对给定的数组通过给定的角标获取元素。
public static int getElement(int[] arr, int index) {
int element = arr[index];
return element;
}
}
测试类
public class ExceptionDemo {
public static void main(String[] args) {
int[] arr = { 34, 12, 67 };
intnum = ArrayTools.getElement(arr, 4)
System.out.println("num=" + num);
System.out.println("over");
}
}
上述程序执行过程图解:
对于程序出现的异常,一般有两种解决方法:一是遇到错误就终止程序的运行。另一种方法是程序员在编写程序时,就充分考虑到各种可能发生的异常和错误,极力预防和避免。实在无法避免的,要编写相应的代码进行异常的检测、以及异常的处理
,保证代码的健壮性
。
java.lang.Throwable
类是Java程序执行过程中发生的异常事件对应的类的根父类。
Throwable类是异常的根父类,它继承于Object类。
Throwable中的常用方法:
public void printStackTrace()
:打印异常的详细信息。public String getMessage()
:获取发生异常的原因。Throwable
可分为两类:Error
(错误)和Exception
(异常)。分别对应着java.lang.Error
与java.lang.Exception
两个类。
看一下API:
Throwable不做过多关注,更多的是它的这两个子类。
Error:Java虚拟机无法解决的严重问题。
如:JVM系统内部错误、资源耗尽等严重情况。
一般不编写针对性的代码进行处理。(如果出现Error问题,就回去改代码,后面说的try方式对它无效)
StackOverflowError
(栈内存溢出)和OutOfMemoryError
(堆内存溢出,简称OOM)。看一下API:
【举例1】
StackOverflowError
(栈内存溢出)
public class ErrorTest {
public static void main(String[] args) {
//举例1:栈内存溢出StackOverflowError
main(args); //递归--main方法中调用main方法
}
}
分析
main(args);
递归–main方法中调用main方法,每调用一次main方法,就进去一个栈帧,在栈空间有限的情况下,一直往里面放栈帧,总会满的。
️输出
栈内存溢出:
【举例2】
OutOfMemoryError
(堆内存溢出,简称OOM)
public class ErrorTest {
public static void main(String[] args) {
//举例2:堆内存溢出 OutOfMemoryError (简称OOM)
byte[] arr=new byte[1024*1024*100]; //100mb
}
}
分析
new出来的结构在堆空间中呈现,若此时堆空间不足100mb,就会将堆空间占满,这时候堆空间就会报一个溢出异常。
现在我们做一个操作,“运行”->“编辑配置”:
然后针对当前模块,“修改选项”->“添加VM选项”:
就是虚拟机参数,将它设置为-Xms10m -Xmx10m
,前面的-Xms10m
表示堆空间初始化的大小,后面的 -Xmx10m
表示最大的堆空间大小。就是固定下来堆空间大小为10ms。如下:
️输出
byte[] arr=new byte[1024*1024*100];
//100mb
显然此时100ms不够,运行一下就会有一个堆空间溢出:
看见后缀是Error的,其实都是一个错误。
Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,需要使用针对性的代码进行处理,使程序继续运行。否则一旦发生异常,程序也会挂掉。(这一章要学习异常处理 ,实际上针对的是这个Exception,而Error处理不了)
看一下API:
例如:
说明:
- 无论是Error还是Exception,还有很多子类,异常的类型非常丰富。当代码运行出现异常时,特别是我们不熟悉的异常时,不要紧张,把异常的简单类名,拷贝到API中去查去认识它即可。
- 我们本章讲的异常处理,其实针对的就是Exception。
Java程序的执行分为编译时过程和运行时过程。有的错误只有在运行时
才会发生。比如:除数为0,数组下标越界等。
因此,根据异常可能出现的阶段,可以将异常分为:
checked
异常、受检异常):在代码编译阶段,编译器就能明确警示
当前代码可能发生(不是一定发生)
xx异常,并明确督促
程序员提前编写处理它的代码。如果程序员没有编写
对应的异常处理代码,则编译器就会直接判定编译失败,从而不能生成字节码文件。通常,这类异常的发生不是由程序员的代码引起的,或者不是靠加简单判断就可以避免的,例如:FileNotFoundException(文件找不到异常)。runtime
异常、unchecked异常、非受检异常):在代码编译阶段,编译器完全不做任何检查,无论该异常是否会发生,编译器都不给出任何提示。只有等代码运行起来并确实发生了xx异常,它才能被发现。通常,这类异常是由程序员的代码编写不当引起的,只要稍加判断,或者细心检查就可以避免。
运行时异常:通常编译的时候不报错,相当于编译的时候将它放过去了,就是没有检查它,即:非受检异常。
编译时异常:编译的时候就会报错,一上来就检查它了,即:受检异常。
☕在API中,怎么看哪个是编译时异常还是运行时异常呢?
这个地方,除了RuntimeException
,其他都是编译时异常。如下:
点开它,里面都是运行时异常RuntimeException
,如下:
我们需要熟悉一下常见的异常都有哪些,太多了也学不过来。
Error有时也可以归为“非受检异常”,因为编译时不报错,看上面蓝色方块。
java.lang.Throwable
:异常体系的根父类
java.lang.Error
:错误。
java.lang.Exception
:异常。
我们可以编写针对性的代码进行处理。
编译时异常:(受检异常) 在执行javac.exe
命令时,出现的异常。
|----- ClassNotFoundException(类找不到异常)
|----- FileNotFoundException(文件找不到异常)
|----- IOException(输入输出异常)
运行时异常:(非受检异常) 在执行java.exe
命令时,出现的异常。
|---- ArrayIndexOutOfBoundsException (数组越界–用非法索引访问数组时抛出的异常)
|---- NullPointerException (空指针异常)
|---- ClassCastException (类转换异常)
|---- NumberFormatException (数据格式化异常)
|---- InputMismatchException (输入类型不一致)
|---- ArithmeticException (算术异常)
️ArrayIndexOutOfBoundsException
:数组角标越界
代码
package yuyi01;
import org.junit.Test;
public class ExceptionTest {
@Test
public void test1(){
//数组角标越界 ArrayIndexOutOfBoundsException
int[] arr=new int[10];
System.out.println(arr[10]);
}
}
异常
️NullPointerException
:空指针异常
代码1
package yuyi01;
import org.junit.Test;
public class ExceptionTest {
@Test
public void test2(){
//空指针异常 NullPointerException
String str="hello";
str=null; //指针指向为空
System.out.println(str.toString());
}
}
异常
代码2–一维数组
package yuyi01;
import org.junit.Test;
public class ExceptionTest {
@Test
public void test3(){
//空指针异常 NullPointerException
int[] arr=new int[10]; //一维数组
arr=null;
System.out.println(arr[0]);
}
}
异常
代码3–二维数组
package yuyi01;
import org.junit.Test;
public class ExceptionTest {
@Test
public void test4(){
//空指针异常 NullPointerException
int[][] arr1=new int[10][]; //二维数组
System.out.println(arr1[0][0]);
}
}
异常
只要对象指向的没有实体,调一个方法或者属性都算空指针。
️ClassCastException
:类转换异常
代码
package yuyi01;
import org.junit.Test;
import java.util.Date;
public class ExceptionTest {
@Test
public void test5(){
//类转换异常 ClassCastException
Object obj=new String(); //new一个String对象,将它赋值给Object类
//String str=(String) obj; //这里不会出现强转异常
Date date=(Date) obj; //转换为别的类型就不行了
}
}
异常
父类引用指向的对象的类型是子类的时候才可以强制类型转换。
而Date和String算是平等的关系,都继承于Object,它们是兄弟关系,不能强转。
️NumberFormatException
:数据格式化异常
代码
package yuyi01;
import org.junit.Test;
public class ExceptionTest {
@Test
public void test6(){
//NumberFormatException 数据格式化异常
String str1="123";
int i1 = Integer.parseInt(str1); //可以将String类型的“123”转化为int类型的123
String str2="abc";
int i2 = Integer.parseInt(str2); //不可以转换,会报错
System.out.println(i1);
System.out.println(i2);
}
}
异常
️InputMismatchException
:输入类型不一致
代码
package yuyi01;
import org.junit.Test;
import java.util.Scanner;
public class ExceptionTest {
@Test
public void test7(){
//InputMismatchException 输入类型不一致
Scanner scanner=new Scanner(System.in);
int num= scanner.nextInt(); //若输入的不是int类型,则会报错
System.out.println(num);
}
}
异常
️ArithmeticException
:算术异常
代码
package yuyi01;
import org.junit.Test;
public class ExceptionTest {
@Test
public void test8(){
//ArithmeticException 算术异常
int num=10;
System.out.println(num/0); //编译的时候不报错,运行的时候报错
}
}
异常
️ClassNotFoundException
:类找不到异常
代码
package yuyi01;
import org.junit.Test;
public class ExceptionTest {
@Test
public void test9(){
//ClassNotFoundException 类找不到异常
Class clz=Class.forName("java.lang.String"); //反射
}
}
异常
目前只能考虑先将它注释掉。
️FileNotFoundException
:文件找不到异常
代码
package yuyi01;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
public class ExceptionTest {
@Test
public void test10(){
File file=new File("hello.txt");
//FileNotFoundException 文件找不到异常
FileInputStream fis=new FileInputStream(file); //流直接操作文件
//把文件内容直接读到内存中输出
int data=fis.read(); //IOException 输入输出异常
while(data!=-1){ //data为-1的时候退出,就是读完了
System.out.print((char)data);
data=fis.read(); //IOException 输入输出异常
}
//资源关闭
fis.close(); //IOException 输入输出异常
}
}
异常
️IOException
:输入输出异常
代码
package yuyi01;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
public class ExceptionTest {
@Test
public void test10(){
File file=new File("hello.txt");
//可能报FileNotFoundException 文件找不到异常
FileInputStream fis=new FileInputStream(file); //流直接操作文件
//把文件内容直接读到内存中输出
int data=fis.read(); //可能报IOException 输入输出异常
while(data!=-1){ //data为-1的时候退出,就是读完了
System.out.print((char)data);
data=fis.read(); //可能报IOException 输入输出异常
}
//资源关闭
fis.close(); //可能报IOException 输入输出异常
}
}
异常
读的时候可能不通畅:
结束的时候可能也不通畅:
☕补充
FileNotFoundException
和IOException
都是可能出现的异常,怎么处理呢?
先写一个文件:(字符集改为UTF-8)
将路径改为:"D:\\hello.txt"
如下:
"D:\\hello.txt"
,一条右斜线会报错(一条表示转义的意思),需要写两条。或者写反斜杠 /。
现在文件就能找到了,但为啥还是报错?
所以需要提前预知一下处理的事情,大家感受一下,throws出去,将可能会报的异常给处理了,如下:
来运行一下:
这里出问题的原因是因为刚才文件里面写的是汉字,一个汉字可能会占多个字节,此时代码int data=fis.read();
是一个字节一个字节地读,以后讲IO流的时候会专门解决这个问题。
那就先写成英文吧:
再次运行:
刚才我们将异常处理了,那程序在运行当中出现异常了吗?
其实是没出现异常。但是不处理的话,编译又不让过。(虽然程序没有问题,但是也需要处理一下,未雨绸缪)
代码
package yuyi01;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ExceptionTest {
@Test
public void test10() throws FileNotFoundException, IOException {
File file=new File("D:\\hello.txt");
//可能报FileNotFoundException 文件找不到异常
FileInputStream fis=new FileInputStream(file); //流直接操作文件
//把文件内容直接读到内存中输出
int data=fis.read(); //可能报IOException 输入输出异常
while(data!=-1){ //data为-1的时候退出,就是读完了
System.out.print((char)data);
data=fis.read(); //可能报IOException 输入输出异常
}
//资源关闭
fis.close(); //可能报IOException 输入输出异常
}
}
仅供参考。
代码
package yuyi01;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Date;
import java.util.Scanner;
/**
* ClassName: ExceptionTest
* Package: yuyi01
* Description:
*
* @Author 雨翼轻尘
* @Create 2023/12/28 0028 10:28
*/
public class ExceptionTest {
@Test
public void test1(){
//数组角标越界 ArrayIndexOutOfBoundsException
int[] arr=new int[10];
System.out.println(arr[10]);
}
@Test
public void test2(){
//空指针异常 NullPointerException
String str="hello";
str=null; //指针指向为空
System.out.println(str.toString());
}
@Test
public void test3(){
//空指针异常 NullPointerException
int[] arr=new int[10]; //一维数组
arr=null;
System.out.println(arr[0]);
}
@Test
public void test4(){
//空指针异常 NullPointerException
int[][] arr1=new int[10][]; //二维数组
System.out.println(arr1[0][0]);
}
@Test
public void test5(){
//类转换异常 ClassCastException
Object obj=new String(); //new一个String对象,将它赋值给Object类
//String str=(String) obj; //不会出现强转异常
Date date=(Date) obj; //转换为别的类型就不行了
}
@Test
public void test6(){
//NumberFormatException 数据格式化异常
String str1="123";
int i1 = Integer.parseInt(str1); //可以将String类型的“123”转化为int类型的123
String str2="abc";
int i2 = Integer.parseInt(str2); //不可以转换,会报错
System.out.println(i1);
System.out.println(i2);
}
@Test
public void test7(){
//InputMismatchException 输入类型不一致
Scanner scanner=new Scanner(System.in);
int num= scanner.nextInt(); //若输入的不是int类型,则会报错
System.out.println(num);
}
@Test
public void test8(){
//ArithmeticException 算术异常
int num=10;
System.out.println(num/0); //编译的时候不报错,运行的时候报错
}
//**********************以上是运行时异常,以下是编译时异常****************************
@Test
public void test9(){
//ClassNotFoundException 类找不到异常
//Class clz=Class.forName("java.lang.String"); //反射
}
@Test
public void test10() throws FileNotFoundException, IOException {
File file=new File("D:\\hello.txt");
//可能报FileNotFoundException 文件找不到异常
FileInputStream fis=new FileInputStream(file); //流直接操作文件
//把文件内容直接读到内存中输出
int data=fis.read(); //可能报IOException 输入输出异常
while(data!=-1){ //data为-1的时候退出,就是读完了
System.out.print((char)data);
data=fis.read(); //可能报IOException 输入输出异常
}
//资源关闭
fis.close(); //可能报IOException 输入输出异常
}
}
输出结果