大话Java异常丨【奔跑吧!JAVA】

异常

异常的概述


  • 异常就是不正常的意思,Java语言中主要是指程序在运行阶段产生的错误
  • Throwable(可抛出的,可扔出的)
    • java.lang.Throwable 类是Java程序所有错误或异常的超类
    • 主要有两个字类
      • Error
        • Error主要描述比较严重的错误
        • 无法通过编程来解决的重大的错误
      • Exception
        • Exception主要m描述比较轻量级的错误
        • 可以通过编程来解决

Exception类的主要分类

RuntimeException -> 运行时异常,也叫非检测性异常类


  • 非检测性异常类就是指b编译阶段无法被编译器检测出来的异常
  • 主要子类
    • ArithmeticException -> 算数异常类
    • ArrayIndexOutOfBoundsException(间接子类) -> 数组下标异常类
    • NullPointerException -> 空指针异常
    • ClassCastException -> 类型转换异常
    • NumberFormatException(间接子类)-> 数字格式异常
  • 注意
    • 当程序的执行过程中产生异常,若没有手动进行处理,则由Java虚拟机采用默认的方式进行处理,默认方式是打印异常名称、异常原因以及异常发生的位置并终止程序,后序代码无法被执行

IOException和其他异常类 -> 其他异常类,也叫做非检测性异常

案例

TestRuntimeException.java

package demo1;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

/*
 * 		ArithmeticException -> 算数异常类
		ArrayIndexOutOfBoundsException(间接子类) -> 数组下标异常类
		NullPointerException -> 空指针异常
		ClassCastException -> 类型转换异常
		NumberFormatException(间接子类)-> 数字格式异常
 */

public class TestRuntimeException {
	public static void main(String[] args) {
		
		
		// 观察检测性异常
		// FileInputStream fis = new FileInputStream("c:/a.txt");
	
		// java.lang.ArithmeticException 算数异常
		int a = 10; 
		int b = 0;
		if (b != 0) {
			System.out.println(a/b);
		}

		// java.lang.ArrayIndexOutOfBoundsException 数组下标越界异常

		int[] arr = new int[3];
		int num = 3;
		if (num >= 0 && num < arr.length) {
			System.out.println(arr[num]);
		}
 

		// java.lang.NullPointerException

		String str = null; 
		if (str != null) {
			System.out.println(str.length());
		}

		// java.lang.ClassCastException

		Exception ex = new Exception(); 
		if (ex instanceof IOException) {
			IOException ie = (IOException) ex;
		}

		// java.lang.NumberFormatException

		String s = "12be"; 
		if (s.matches("\\d+")) {
			System.out.println(Integer.parseInt(s));
		}
		System.out.println("运行程序结束");

	}
}

异常处理

运行时异常的处理方式


  • 对于绝大数运行时异常来说,都可以通过条件判断避免异常的发生

异常的捕获


  • 语法格式
    try{
    可能产生异常对象的语句块
    }catch(异常类型 引用名){
    针对当前异常类型对象的处理语句块
    }
    … (可以写多个catch)
    finally{
    无论是否发生异常,都应该执行的语句块
    }

  • 注意事项

    • 当捕获的结构中有多个catch分支时,切记小范围的异常类型放在大范围的异常类型上面
    • 懒人写法:
      catch(Exception e) {…}
  • 执行流程

      try {
      	a;
      	b;  // 可能产生异常的语句
      	c;
      } catch (Exception e) {
      	e;
      } finally {
      	f;
      }
    
    • 当没有产生异常的时候,程序的执行流程是:a b c f
    • 当产生异常时,程序的执行流程是: a b e f
  • 案例

    • TestExceptionCatch.java
    package demo2;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    public class TestExceptionCatch {
    	public static void main(String[] args) {
    	
    		// 声明引用指向本类的对象,用于读取文件中的内容
    		FileInputStream fis = null;
    		try {
    			// 随时可能产生文件找不到y异常对象, new FileNotFoundException()
    			fis = new FileInputStream("d:/a.txt");
    		} catch (FileNotFoundException e) {
    			// 打印异常的名称、异常原因、异常的位置等信息
    			e.printStackTrace();
    		}
    		
    		try {
    			fis.close();
    		} catch (IOException e) {
    			
    			e.printStackTrace();
    		} catch (NullPointerException e) {
    			// System.out.println("输出");
    			e.printStackTrace();
    		}
    	}
    }
    
    
    • TestFinally.java
    package demo3;
    
    public class TestFinally {
    	public static void main(String[] args) {
    		int a = 10;
    		int b = 0;
    		
    		try {
    			System.out.println(a/b);
    		} catch (Exception e) {
    			e.printStackTrace();
    			return ;
    		} finally {
    			System.out.println("无论是否发生异常都会执行");
    		}
    		System.out.println("程序结束");
    	}
    }
    
    
      java.lang.ArithmeticException: / by zero
      无论是否发生异常都会执行
      at demo3.TestFinally.main(TestFinally.java:9)
    

异常的抛出

  • 基本概念
    • 某些特殊的场合中,当产生异常后却无法直接处理/不想处理时,此时就可以将异常转移给当前方法的调用者,这就叫异常的抛出
  • 语法格式
    • 返回值类型 方法名称(形参列表) throws 异常类型{…}
  • 方法重写的原则
    • 要求方法名相同、参数列表相同、返回值类型也相同,从jdk1.5开始允许返回子类类型
    • 范围权限不能变小,可以相同或者变大
    • 不能抛出更大的异常
    • 注意
      • 子类中重写以后的方法可以选择抛出与父类一样的异常、更小的异常、不抛出异常,但是不能抛出更大的异常、不同的异常
      • 案例
        A.java
        package demo4;
        
        import java.io.IOException;
        
        public class A {
        	public void show() throws IOException{
        		System.out.println("A");
        	}
        }
        
        
        SubA.java
        package demo4;
        
        import java.io.IOException;
        import java.io.FileNotFoundException;
        import javax.print.PrintException;
        
        public class SubA extends A{
        
        	@Override
        	// public void show() throws IOException {
        	// public void show() throws FileNotFoundException {
        	// public void show() throws PrintException {
        	// public void show() throws Exception {
        	public void show() {
        	}
        }
        
        

      显然不能抛出更大的异常

自定义异常

  • 自定义异常的由来

    • Java官方库中虽然提供了大量的异常类,但不足以描述现实生活中所有的异常情况。当出现官方库中没有m描述的异常情况,这个时候就需要程序员自定义异常类加以描述,使得异常信息更加具备针对性和可读性
  • 自定义异常的流程

    • 自定义类继承自Exception类或者Exception类的子类
    • 提供两个版本的构造方法,一个是无参构造方法,另一个是字符串做参数的构造方法
  • 自定义异常 – >案例1

    • Person.java
    package demo5;
    
    public class Person {
    	private String name;
    	private int age;
    	
    	
    	
    	public Person() {
    		super();
    	}
    	public Person(String name, int age) throws Exception {
    		super();
    		setName(name);
    		setAge(age);
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) throws Exception {
    		if(age > 0 && age < 150) {
    			this.age = age;
    		} else {
    			// System.out.println("年龄不合理");
    			// 手动产生一个异常对象并抛出
    			// throw new Exception();
    			throw new AgeException("年龄不合理!");
    		}
    		System.out.println("产生异常的效果");
    	}
    	@Override
    	public String toString() {
    		return "Person [name=" + name + ", age=" + age + "]";
    	}
    	
    	
    }
    
    
    • AgeException.java
    package demo5;
    
    public class AgeException extends Exception {
    	
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1L;
    
    	// 自定义无参的构造方法
    	public AgeException() {
    		
    	}
    	
    	// 自定义使用字符串作为参数的构造方法
    	public AgeException(String msg) {
    		super(msg);
    	}
    }
    
    
    • TestPerson.java
    package demo5;
    
    public class TestPerson {
    	public static void main(String[] args) {
    	
    		Person p = null;
    		try {
    			p = new Person("张三", -12);
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		System.out.println(p);
    	}
    }
    
    
      demo5.AgeException: 年龄不合理!
      null
      at demo5.Person.setAge(Person.java:33)
      at demo5.Person.<init>(Person.java:15)
      at demo5.TestPerson.main(TestPerson.java:8)
    
  • 自定义异常 – > 案例2

    • Student.java
    package demo6;
    
    public class Student {
    	private String name;
    	private int id;
    	
    	
    	public Student() {
    		super();
    	}
    	public Student(String name, int id) throws IDException {
    		super();
    		setName(name);
    		setId(id);
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getId() {
    		return id;
    	}
    	public void setId(int id) throws IDException {
    		if (id > 0) {
    			this.id = id;
    		} else {
    			// System.out.println("学号不合理");
    			throw new IDException("学号不合理");
    		}
    		System.out.println("结束");
    	}
    	@Override
    	public String toString() {
    		return "Student [name=" + name + ", id=" + id + "]";
    	}
    	
    	
    }
    
    
    • IDException.java
    package demo6;
    
    public class IDException extends Exception {
    
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1L;
    
    	public IDException() {
    		
    	}
    	
    	public IDException(String msg) {
    		super(msg);
    	}
    }
    
    
    • TestStudent.java
    package demo6;
    
    public class TestStudent {
    	public static void main(String[] args) throws IDException {
    		
    		/*Student stu = null;
    		try {
    			stu = new Student("小王", -5);
    		} catch (IDException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}*/ 
    		
    		Student stu = new Student("小王", -5);
    		System.out.println(stu);
    	}
    }
    
    
      Exception in thread "main" demo6.IDException: 学号不合理
      at demo6.Student.setId(Student.java:30)
      at demo6.Student.<init>(Student.java:14)
      at demo6.TestStudent.main(TestStudent.java:14)
    

此处有一点要注意,在案例一的TestPerson中,在main函数中,我们使用try…catch语句,异常由内部进行处理,会打印后面的语句
而在案例二的TestStudent中,我们将异常直接交给main函数处理,也就是交给虚拟机处理,所以并不会执行后面的语句

异常对象的抛出

  • throw new 异常类型()
  • 例如:
    • throw new Exception()

最后简单介绍一下throws和throw的区别

throws和throw的区别

  • throws
    • 用在方法声明后面,跟的是异常类名
    • 可以跟多个异常类名,用逗号隔开
    • 表示抛出异常,由该方法的调用者来处理
    • throws表示出现异常的一种可能性,并不一定会发生这些异常
  • throw
    • 用在方法体内,跟的异常对象名
    • 只能抛出一个异常对象名
    • 表示抛出异常,由方法体内的语句实现
    • throw则是抛出了异常,执行throw则一定抛出了某种异常

【奔跑吧!JAVA】有奖征文火热进行中:https://bbs.huaweicloud.com/blogs/265241

(完)