java中的异常

异常

定义:异常指的是程序运行过程当中出现的例外情况(Exception=例外)

异常的体系结构

在 Java 中,所有的异常都有一个共同的祖先 Throwable(可抛出)。Throwable 指定代码中可用异常传播机制通过 Java 应用程序传输的任何问题的共性。

在这里插入图片描述

*: Error和Exception的区别

Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。(指由于硬件问题或者系统原因导致的)

Exception(异常):是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeException。RuntimeException 类及其子类表示“JVM 常用操作”引发的错误。

*:运行时异常和非运行时异常的区别?

运行时异常: 在编译的时候 不需要程序员给出解决方案,编译直接可以通过 问题会在运行的时候直接体现出来。

运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。

非运行时异常:是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。在编译的时候必须给出解决方,否则编译无法通过。

注意事项: 非运行异常和运行时异常都是在运行过程中出现的异常

常见的运行时异常:11个

  1. 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
    
  1. NegativeArraySizeException负数数组大小异常
  • int[] data=new int[-1];//数组开辟的空间不能为负数
    
  1. ArrayIndexOutOfBoundsException数组索引值超出边界异常
  • int[] data1=new int[]{1,2,3,4,5};
    System.out.println(data1[7]);//数组下标不能>=数组长度
    
  1. NullPointerException空指针异常
  • String str=null;
    System.out.println(str.length());//不能用null.调用任何方法、属性
    
  1. StringIndexOutOfBoundsException字符串索引值超出边界异常
  • String str1="qwert";
    System.out.println(str1.charAt(5));//字符串下标不能>=字符串长度
    
  1. NumberFormatException数字格式异常
  • String str2="123q";
    int number=Integer.parseInt(str2);//转化为int类型的字符串中不能包含非数字字符
    System.out.println(number);
    
  1. ClassCastException类型造型异常
  • Object stu=new Student();
    Cat cat=(Cat)stu;//不同类的对象不能强转,除非他们在一个分支上/或有继承关系
    
  1. IllegalArgumentException非法参数异常
  • List<Integer> list=new ArrayList<>(-5);//不能传负数作为集合的空间的开辟值
    
  1. IndexOutOfBoundException索引值超出边界异常
  • List<Integer> list1=new ArrayList<>();
    Collections.addAll(list1,12,34,56);
    System.out.println(list1.get(3));//集合下标不能>=集合大小
    
  1. 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触发迭代器状态异常 
      }
    }
    
  1. 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
      }
    }
    

常见的非运行时异常

  1. AWTException
  2. IOException输入输出异常
  3. CloneNotSupportedException克隆不支持异常

为什么要处理异常

A. 非运行时异常 如果不做处理 编译都无法通过。

B.一旦程序(线程)运行过程当中 出现了异常而且没做处理虚拟机将直接中断程序(线程)执行

异常的处理机制

  1. 抛还上级:throws

    throws:出现在方法签名的最后
    表达的是在本方法当中出现指定种类的异常;本方法当中不做处理,抛还给调用的上级进行处理

  2. 自行处理: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后面是catchcatch可以有一个或多个,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("不是幸运数字!");
		}
	}

重点:

  1. 当类体当中某个静态变量 是通过调用有异常声明的方法完成赋值的时候
    我们不能在类体上throws 也不能在类体当中直接try catch,此时如果想要成功的给静态变量赋值 必须借助静态初始化块,在静态初始化块当中 使用try catch进行异常处理(如果是个非静态的普通成员变量 还可以使用构造方法哦)

  2. 在方法覆盖的时候 如果父类的方法没有任何throws声明,子类的方法在覆盖它的时候,可以向外抛出运行时异常,因为运行时异常是默认就抛出的,每个方法签名的最后 都有一行看不见的throws RuntimeException;但是声明抛出运行时异常的行为 没有任何意义,因为它默认就抛出。

  3. 当程序当中出现连续的多行有异常声明的语句的时候,我们想让无论前者执行是否顺利 都要尝试执行后者;此时必须借助try catch finally的finally当中嵌套使用try catch,我们把这种语法结构,戏称为连环try

  4. 我们为了进行异常处理而加的try catch语法是有{},而在{}当中定义的变量是特殊的局部变量,会伴随大括号的执行结束而消亡
    所以如果这个变量在下文程序当中还要继续使用,就不能直接定义在try{}当中,我们需要将其定义在try{}前面 并且以默认值赋值
    在{}当中只做重新赋值,而不做变量的定义。

  5. 在某些场景下,可以使用异常处理的机制代替传统的分支和判断,会更加高效!

静态代码块复制

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
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值