Definition: 程序在运行过程中出现的例外情况
Java中所有“问题”的体系结构
Throwable【可以向外抛出的】
Error【错误】 Exception【异常】
RunTimeException【运行时异常】
//Error 和 Exception 的区别
Error通常是由硬件环境 或者系统原因导致的相对较严重的问题
Exception是指程序运行过程当中出现的例外而已相对较轻
//运行时异常 和 非运行时异常
非运行是异常在编译的时候 就要求给出处理方案 否则编译无法通过
运行时异常编译的时候不需要给出处理方案编译能够直接通过 问题会在运行时直接体现
//无论运行时异常还是非运行时异常都在运行时出现 编译时不会出现异常 只会要求给出处理方案
常见运行时异常
运算符: ArithmeticException 算术异常(数学中0不能做除数 Java中如果用整数除以0 将直接触发该异常 除以0.0 得到Infinity 0.0 / 0.0 得到NaN(not a number) NaN == 任何值都是false 包括它本身)
数组: NegativeArraySizeException 负数数组大小异常
(定义数组时)
ArrayIndexOutOfBoundsException 数组索引值超出下标异常
(访问时超出 0 - length - 1 )
字符串: NullPointerException 空指针异常
(引用数据类型的默认值就是null 拿它访问任何属性 调用任何方法 都将触发该异常)
//NullPointerException => 空指针异常
String str1 = null;
System.out.println(str1.length());
StringIndexOutOfBoundsException 字符串索引值超出边界
NumberFormatException 数字格式异常(包装类提供了从String到对应基本数据类型转换的方法 例如Integer.parseInt() / Double.parseDouble() 如果提供的字符串当中有不合法的内容 将直接触发该异常)
//NumberFormatException => 数字格式异常
String str3 = "123a";
int price = Integer.parseInt(str3);
System.out.println(price + 3);
类型转换:ClassCastException 类型造型异常 (对象要转换成毫无关系的类型时触发该异常)
//ClassCastException => 类型造型异常
Object stu = new Student();
Cat cc = (Cat)stu;
集合 IllegalArgumentException 非法参数异常 (构造时传入负数)
IndexOutOfBoundsException 索引值超出边界异常
//IndexOutOfBoundsException => 索引值超出边界异常(集合)
List<Integer> list2 = new ArrayList<>();
Collections.addAll(list2,11,22,33);
System.out.println(list2.get(3));
IllegalStateException 非法状态异常 (使用迭代器的时候 next()能够让光标下移指向元素 这之后才能使用元素直接操作remove() 将会触发该异常)
//IllegalStateException => 非法状态异常
List<Integer> list3 = new ArrayList<>();
Collections.addAll(list3,11,22,33);
Iterator<Integer> car = list3.iterator();
car.remove();
import java.util.*;
public class TestIllegalStateException{
public static void main(String[] args){
List<String> list = new ArrayList<>();
Collections.addAll(list,"Andy","Aaron","Jacky","Jay","Leon");
for(Iterator<String> car = list.iterator(); car.hasNext(); ){
String name = car.next();
if(name.startsWith("J")){
car.remove();
}
if(name.endsWith("y")){
car.remove();
}
}
System.out.println(list);//?
}
}
ConcurrentModificationException 并发修改异常 (使用迭代器遍历集合过程中 不允许对集合整体进行任何添加删除操作 否则迭代器的下一次next()将会触发该异常)
为什么要处理异常:
1. 如果是非运行时异常 不处理将无法完成编译
2.一旦程序(线程)运行过程中出现未作处理的异常 虚拟机将直接中断运行
如何处理异常
1.抛还上级 throws
throws出现在方法签名的最后 用于表达本方法当中出现指定种类的异常 方法中不做处理 抛还给调用的上级进行处理
2.自行处理 try and catch
try{
可能出现异常的语句
通常只写一句
除非需求决定 前者出问题 后者跳过
}catch(要不获得的异常类型 异常代号){
对捕获到的异常进行处理
0.隐瞒不报
1.简要提审
异常代号.getMessage();
2.详细的审
异常代号.printStackTrace();
}finally{
无论是否出现异常 最终都要执行的操作
通常是释放和关闭资源的操作
}
//一个try 后面可跟并列关系或比它大的catch分支 不允许前者包含后者
//在JDK7.0之前 如果捕获到两种异常以后 要做相同的操作 也必须将catch写两次
JDK7.0开始 支持多重catch catch(类型1 | 类型2 异常代号){。。。}
//不要在finally当中写return语句 否则try catch中的return则无意义
如何在本没有异常的情况下 主动制造异常出现的场景
throw 用在方法体当中 用于在本没有异常出现的情况下
主动制造异常出现的场景...
//throw 和 throws 的区别?
throws 用在方法签名的最后
用于表达本方法中出现指定种类的异常
本方法中不做处理 抛还给调用的上级进行处理
如何自定义异常...
自己开发一个类 选择继承Exception / RuntimeException
非运行时异常 运行时异常
在其构造方法的首行使用super("指定异常的描述信息");
public class ExecThrow{
public static void main(String[] args){
showSeason(-15);
}
public static void showSeason(int month){
if(month <= 0 || month > 12){
throw new IllegalMonthException();
}
if(month <= 3){
System.out.println("春天在哪里");
}else if(month <= 6){
System.out.println("宁静的夏天");
}else if(month <= 9){
System.out.println("秋天不回来");
}else if(month <= 12){
System.out.println("大约在冬季");
}
}
}
class IllegalMonthException extends RuntimeException{
public IllegalMonthException(){
super("指定的月份不合法~");
}
}
Plus01:
当类体当中某个静态变量是通过调用某个有异常声明的方法
来完成赋值的时候 我们不能在类的签名上直接throws
也不能在类体当中直接try catch 此时如果想要编译通过
必须借助静态初始化块 在初始化块当中完成try catch处理
*: 如果是个非静态变量 可以使用非静态初始化块
或者使用构造方法完成赋值
public class TestExceptionPlus01{
public static void main(String[] args){
}
}
class X{
static int a;
static{
try{
a = get();
}catch(Exception e){
e.printStackTrace();
}
}
public static int get()throws Exception{
int x = (int)(Math.random()*5)+5;//5-9
if(x == 7 || x == 9){
throw new Exception("生成的数字很邪性~");
}
return x;
}
}
Plus02:
在方法覆盖的时候 如果父类方法并没有任何异常声明
那么子类方法在覆盖它的时候 能不能向外抛出异常呢?
可以 但是只能向外抛出运行时异常
因为Java当中所有的方法默认都会抛出所有运行时异常
相当于每一个方法签名的最后
都有一行默认的 throws RuntimeException...
尽管能 但是这样的行为没有意义...
public class TestExceptionPlus02 {
public static void main(String[] args)throws RuntimeException{
C cc = new C();
cc.test();
}
}
class A extends Object{
public void test()throws RuntimeException{
System.out.println("父类的test()方法");
}
}
class C extends A{
@Override
public void test()throws IllegalArgumentException,ClassCastException,NullPointerException,ArithmeticException,NegativeArraySizeException{
System.out.println("子类的test()方法");
}
}
Plus03:
当程序当中有多行有异常声明的语句
我们需要无论前者执行是否出异常 后者都要尝试执行
此时必须借助try catch finally的finally当中
嵌套使用try catch...
我们把这种语法戏称为:连环try~
public class TestExceptionPlus03{
public static void main(String[] args){
SLT no1 = new SLT();
SLT no2 = new SLT();
SLT no3 = new SLT();
try{
no1.close();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
no2.close();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
no3.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
}
class SLT{
public void close()throws Exception{
int x = (int)(Math.random()*2);//0 or 1
if(x == 0){
throw new Exception("拧坏了关不上了的异常");
}
System.out.println("正常的关闭了水龙头");
}
}
Plus04:
为了进行异常处理而添加的try catch语法结构是有{}
这对儿大括号同样能够控制变量的作用范围
如果我们在下文程序当中还需要使用的变量
就不能再try{}当中定义 而要将其定义拿到try{}的前面
并且以默认值赋值 在try{}当中只做重新赋值 不做变量定义
public class TestExceptionPlus04{
public static void main(String[] args){
int x = 0;
try{
x = get();
}catch(Exception e){
e.printStackTrace();
}
System.out.println(x);//?
}
public static int get()throws Exception{
int num = (int)(Math.random()*5);
if(num == 2 || num == 4){
throw new Exception("生成的数字不吉利");
}
return num;
}
}
Plus05:
在某些场景下 学会使用异常处理的机制代替传统的分支和判断
public class TestExceptionPlus05{
public static void main(String[] args){
System.out.println(check("12a345"));
}
public static boolean check(String str){
try{
Integer.parseInt(str);
return true;
}catch(Exception e){
return false;
}
/*
for(int i = 0;i<str.length();i++){
char x = str.charAt(i);
if(x < '0' || x > '9'){
return false;
}
}
return true;
*/
}
}