异常
定义:异常指的是程序运行过程当中出现的例外情况(Exception=例外)
异常的体系结构:
在 Java 中,所有的异常都有一个共同的祖先 Throwable(可抛出)。Throwable 指定代码中可用异常传播机制通过 Java 应用程序传输的任何问题的共性。
*: Error和Exception的区别
Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。(指由于硬件问题或者系统原因导致的)
Exception(异常):是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeException。RuntimeException 类及其子类表示“JVM 常用操作”引发的错误。
*:运行时异常和非运行时异常的区别?
运行时异常: 在编译的时候 不需要程序员给出解决方案,编译直接可以通过 问题会在运行的时候直接体现出来。
运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
非运行时异常:是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。在编译的时候必须给出解决方,否则编译无法通过。
注意事项: 非运行异常和运行时异常都是在运行过程中出现的异常
常见的运行时异常:11个
- ArithmeticException算数异常
-
System.out.println(12/0)
拓展
System.out.println(5.0/5.0);//infinity无穷大 System.out.println(0.0/0.0);//NaN不是一个数字 double num1=0.0/0.0; double num2=0.0/0.0; System.out.println(num1==num2);//NaN与任何数字连等都为false
- NegativeArraySizeException负数数组大小异常
-
int[] data=new int[-1];//数组开辟的空间不能为负数
- ArrayIndexOutOfBoundsException数组索引值超出边界异常
-
int[] data1=new int[]{1,2,3,4,5}; System.out.println(data1[7]);//数组下标不能>=数组长度
- NullPointerException空指针异常
-
String str=null; System.out.println(str.length());//不能用null.调用任何方法、属性
- StringIndexOutOfBoundsException字符串索引值超出边界异常
-
String str1="qwert"; System.out.println(str1.charAt(5));//字符串下标不能>=字符串长度
- NumberFormatException数字格式异常
-
String str2="123q"; int number=Integer.parseInt(str2);//转化为int类型的字符串中不能包含非数字字符 System.out.println(number);
- ClassCastException类型造型异常
-
Object stu=new Student(); Cat cat=(Cat)stu;//不同类的对象不能强转,除非他们在一个分支上/或有继承关系
- IllegalArgumentException非法参数异常
-
List<Integer> list=new ArrayList<>(-5);//不能传负数作为集合的空间的开辟值
- IndexOutOfBoundException索引值超出边界异常
-
List<Integer> list1=new ArrayList<>(); Collections.addAll(list1,12,34,56); System.out.println(list1.get(3));//集合下标不能>=集合大小
- IllegalStateException非法(迭代器)状态异常
-
List<String> list2=new ArrayList<>(); Collections.addAll(list2,"Andy","Anron","Lee","Jack","Mary"); for(Iterator<String> ite=list2.iterator();ite.hasNext();){ String name=ite.next(); if(name.startsWith("A")){ ite.remove(); } if(name.endsWith("y")){ ite.remove();//两个if触发迭代器状态异常 } }
- ConcurrentModificationException并发修改异常
-
List<String> list2=new ArrayList<>(); Collections.addAll(list2,"Andy","Anron","Lee","Jack","Mary"); for(Iterator<String> ite=list2.iterator();ite.hasNext();){ String name=ite.next(); if(name.startsWith("A")){ list2.remove();//集合的moCount值与迭代器的下一次next()操作前的校验不同,会触发CME } }
常见的非运行时异常
- AWTException
- IOException输入输出异常
- CloneNotSupportedException克隆不支持异常
为什么要处理异常:
A. 非运行时异常 如果不做处理 编译都无法通过。
B.一旦程序(线程)运行过程当中 出现了异常而且没做处理虚拟机将直接中断程序(线程)执行
异常的处理机制:
-
抛还上级:throws
throws:出现在方法签名的最后
表达的是在本方法当中出现指定种类的异常;本方法当中不做处理,抛还给调用的上级进行处理 -
自行处理:try- catch -finally
格式及说明:
private static void testException() { try { //1、对可能产生异常的代码进行检视 //2、如果try代码块的某条语句产生了异常, 就立即跳转到catch子句执行, try代码块后面的代码不再执行 //3、try代码块可能会有多个受检异常需要预处理, 可以通过多个catch子句分别捕获 } catch (异常类型1 e1) { //捕获异常类型1的异常, 进行处理 //在开发阶段, 一般的处理方式要么获得异常信息, 要么打印异常栈跟踪信息(e1.printStackTrace()) //在部署后, 如果有异常, 一般把异常信息打印到日志文件中, 如:logger.error(e1.getMessage()); } catch (异常类型2 e1) { //捕获异常类型2的异常, 进行处理 //如果捕获的异常类型有继承关系, 应该先捕获子异常再捕获父异常 //如果没有继承关系, catch子句没有先后顺序 } finally { //不管是否产生了异常, finally子句总是会执行 //一般情况下, 会在finally子句中释放系统资源 } }
-
try
中包含了可能出现异常的语句;通常只有一句
除非需求决定 前面语句出现异常;后面的操作都跳过 那么可能出现多行 -
try
后面是catch
,catch
可以有一个或多个,catch
中是需要捕获的异常,后面的catch
中的异常类型要大于或者等于前catch
中的异常类型[TwoException>=OneException] -
当
try
中的代码出现异常时,出现异常下面的代码不会执行,马上会跳转到相应的catch
语句块中,如果没有异常不会跳转到catch
中 -
finally
表示,不管是出现异常,还是没有出现异常,finally里的代码都执行,finally和catch可以分开使用,但finally必须和try一块使用,通常finally是释放和关闭资源的操作,如下格式使用finally也是正确的try { } finally { }
-
在JDK7.0之前 如果多个catch分支,要进行相同的处理,必须要将catch写多份,一个catch只能捕获一类异常;
从JDK7.0开始 catch(类型1 | 类型2 | 类型3 e){操作} -
finally当中永远不该出现return语句,否则try catch当中的return 就都失效了,所有return都变成了finally中的return;
-
finally中的代码一定会执行,只有在 try 里面通过 System.exit(0) 来退出 JVM 的情况下 finally 块中的代码才不会执行,其他 return 等情况都会执行finally 代码块。
-
异常的捕获顺序:
一般按照由小到大的顺序
,也就是先截获子异常
,再截获父异常
Throwable类中常用方法:
1. 返回异常发生时的详细信息 public string getMessage(); 2. 返回异常发生时的简要描述 public string toString(); 3. 返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可以声称本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同 public string getLocalizedMessage(); 4. 在控制台上打印Throwable对象封装的异常信息 public void printStackTrace();
-
主动抛异常
语法关键字:throw
throw 用在方法体当中 在本没有异常的情况下,主动抛出异常
面试题: throw和throws的区别?
throw
throw 语句用在方法体内,表示抛出异常,由方法体内的语句处理。
throw是具体向外抛出异常的动作,所以它抛出的是一个异常实例,执行throw一定是抛出了某种异常。
throw一般用于抛出自定义异常。
throws
throws语句是用在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常的处理。
throws主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型。
throws表示出现异常的一种可能性,并不一定会发生这种异常
自定义异常
当Java内置的异常都不能明确的说明异常情况的时候,需要创建自己的异常
自己写一个类 选择继承Exception 或者 RuntimeException
然后在其构造方法的首行,利用super()传参指定异常的简要描述信息(message);
自定义受控异常
/**
* 自定义受控异常(编译时、检查时异常(checkedException)),继承Exception
*/
class MyCheckedException extends Exception {
public MyCheckedException() {
//调用父类的默认构造函数
super();
}
public MyCheckedException(String message) {
//手动调用父类的构造方法
super(message);
}
}
自定义非受控异常
/**
* 自定义非受控异常(运行时异常-unCheckedException)
*/
class MyRuntimeException extends RuntimeException {
public MyRuntimeException() {
//调用父类的默认构造函数
super();
}
public MyRuntimeException(String message) {
//手动调用父类的构造方法
super(message);
}
}
使用自定义异常
public class TestThrow{
public static void main(String[] args)throws Exception{
System.out.println(getNumber());
}
public static int getNumber()throws Exception{
int x=(int)(Math.random()*10)+1;
if(x==2||x==4){
throw new UnluckyNumberException();
}
return x;
}
}
class UnluckyNumberException extends Exception{
public UnluckyNumberException(){
super("不是幸运数字!");
}
}
重点:
-
当类体当中某个静态变量 是通过调用有异常声明的方法完成赋值的时候
我们不能在类体上throws 也不能在类体当中直接try catch,此时如果想要成功的给静态变量赋值 必须借助静态初始化块,在静态初始化块当中 使用try catch进行异常处理(如果是个非静态的普通成员变量 还可以使用构造方法哦) -
在方法覆盖的时候 如果父类的方法没有任何throws声明,子类的方法在覆盖它的时候,可以向外抛出
运行时异常
,因为运行时异常是默认就抛出的,每个方法签名的最后 都有一行看不见的throws RuntimeException;但是声明抛出运行时异常
的行为 没有任何意义,因为它默认就抛出。 -
当程序当中出现连续的多行有异常声明的语句的时候,我们想让无论前者执行是否顺利 都要尝试执行后者;此时必须借助try catch finally的finally当中嵌套使用try catch,我们把这种语法结构,戏称为连环try
-
我们为了进行异常处理而加的try catch语法是有{},而在{}当中定义的变量是特殊的局部变量,会伴随大括号的执行结束而消亡
所以如果这个变量在下文程序当中还要继续使用,就不能直接定义在try{}当中,我们需要将其定义在try{}前面 并且以默认值赋值
在{}当中只做重新赋值,而不做变量的定义。 -
在某些场景下,可以使用异常处理的机制代替传统的分支和判断,会更加高效!
静态代码块复制
static int i;
static{//静态初始化块
try{
i = get();
}catch(Exception e){
e.printStackTrace();
}
}
int j;
public A()throws Exception{
j = get();
}
子类父类抛异常
class A{
public void test(){//父类默认抛出运行时异常
System.out.println("这是父类当中的test()在执行");
}
}
class C extends A{
@Override
public void test()throws NullPointerException,ClassCastException,IndexOutOfBoundsException,ArithmeticException{
System.out.println("这是子类当中的test()在执行");
}
}
连环try:例:水龙头案例
public class TestTap{
public static void main(String[] args)throws Exception{
Tap tap1=new Tap();
Tap tap2=new Tap();
Tap tap3=new Tap();
try{
tap1.close();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
tap2.close();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
tap3.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
}
class Tap{
public void close()throws Exception{
int x=(int)(Math.random()*2);
if(x==1){
throw new Exception("水龙头坏了,关不上!");
}System.out.println("水龙头成功关闭");
}
}
try{}中的变量复用案例
public static void main(String[] args){
int lucky = 0;//在方法体内声明并赋初始值
try{
lucky = get();//在try里面重新赋值
}catch(Exception e){
e.printStackTrace();
}
System.out.println(lucky);//!
}
public static int get()throws Exception{
int x = (int)(Math.random()*5)+1;
if(x == 2 || x == 4){
throw new Exception("生成的数字不吉利");
}
return x;
}
异常处理的机制代替传统的分支和判断
例:如果str当中的内容全是合法数字 则返回true否则false
//try-catch方法
try{
Integer.parseInt(str);
return true;
}catch(Exception e){
return false;
}
//if-else方法
for(int i = 0;i<str.length();i++){
char c = str.charAt(i);
if(c < '0' || c > '9'){
return false;
}
}
return true;
Error类和Exception类的父类都是Throwable类,他们的区别如下:
Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议让程序终止。
Exception类表示程序可以处理的异常,可以捕获且可能恢复。这种异常是由与程序设计的不完善而出现的问题,遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
Exception类又分为未检查异常(UnCheckedException【运行时异常】)和受检查的异常(CheckedException【非运行时异常】)。
运行时异常ArithmeticException,IllegalArgumentException编译能通过,但是一运行就终止了,程序不会处理运行时异常,出现这类异常,程序会终止。
而受检查的异常,要么用 try…catch 捕获,要么用throws字句声明抛出,交给它的父类处理,否则编译不会通过。
谢谢你的浏览与点赞,让我们一起快乐学java