Throwable是所有异常和错误的老祖。他有两大分支:Error、Exception
异常用来处理程序中出现异常的代码,异常可以使程序中的异常处理与正常业务代码分离,保证程序更加健壮。
异常体系中,又区分运行期异常和编译期异常。
java的异常处理关键字:try、catch、finally、throw、throws五个关键字。
在编写代码中出现的问题,可以用代码进行控制。
如最常见的,空指针异常NullPointerException。
class TestException{
static Person person;
public static void main(String[] args){
//空指针异常
person.method();
}
}
class Person{
void method(){
}
}
算数异常:ArithmeticException
class TestException{
public static void main(String[] args){
//算数异常
int i = 1 / 0;
}
}
数组角标越界:ArrayIndexOutOfBoundsException
class TestException{
public static void main(String[] args){
int[]arr = new int[5];
//数组角标越界.ArrayIndexOutOfBoundsException
arr[6]= 1;
}
}
类型转换错误:ClassCastException
class TestException{
public static void main(String[] args){
Person person = new Person();
//将父类对象,转为子类引用类型,报错,类型转换错误:ClassCastException
Childchild = (Child)person;
}
}
class Person{}
class Child extends Person{}
Error:
针对程序来说,是一个致命性,不可挽回的一个错误。无法用代码阻止或者处理该错误的发生。
内存溢出错误:OutOfMemoryError
class TestException{
public static void main(String[] args){
//错误,内存溢出.OutOfMemoryError
Person[] persons = new Person[1008611000];
for(inti = 0 ; i < persons.length ; i++){
persons[i]= new Person(i+ "岁","名字" + i,"地址" + i);
}
}
}
class Person{
String age;
String name ;
String address;
public Person(String age,String name ,String address){
this.age= age;
this.name= name;
this.address= address;
}
}
错误是无法用代码控制的,所以我们这里就不研究错误了,主要研究异常体系。
两种方式,一种是直接内部处理try{}catch(){},第二种是抛出,谁调用出现异常的方法,谁去处理。
第一种方式
try{
//可能出现异常的代码块
}catch(异常类型 参数名){
//当异常发生,处理异常的代码
}finally{
//该代码块为可选项
//不管发生不发生异常,一定会执行的代码块
}
在catch中捕获的异常,其实也是一个对象,是异常类型的对象,我们可以通过该异常的参数名调用一些异常对象的方法,因为Exception中并没有定义方法,所以我们可以使用它的父类,Throwable类中的方法来获取关于异常信息的方法。
class TestException{
static Person person;
public static void main(String[] args){
excetionMethod();
}
//异常方法
static void excetionMethod(){
//空指针异常
try{//将可能会出现异常的代码包括起来
person.method();
}catch(Exceptione){
//如果try代码块中的代码真的发生了异常,那么就执行这个代码块中的代码。
System.out.println("发生了空指针异常,这里进行了异常处理的代码");
}finally{
//不管异常是否发生,该代码块一定会执行
System.out.println("finally代码块中的代码执行了");
}
System.out.println("***********");
}
}
class Person{
void method(){
System.out.println("正常执行的代码");
}
}
第二种方式:抛出
方法() throws Exception{}
注意:
1、 使用该方式,将异常抛出,让调用该方法者去处理异常。
2、 调用异常方法,未处理,一直向上抛出,最终抛给虚拟机去处理。虚拟机打印异常信息并终止程序。
3、 抛出的异常必须大于或等于该类型异常。
class TestException{
static Person person;
public static void main(String[] args) throws Exception{
//此处处理抛出的异常
excetionMethod();
}
}
//异常方法
static void excetionMethod()throws Exception{
//空指针异常
person.method();
}
}
class Person{
void method(){
System.out.println("正常执行的代码");
}
}
我们会发现,如果不对异常进行处理,那么,发生异常,默认是虚拟机执行。终止程序,打印异常信息。
虚拟机处理异常的方式就是:打印异常信息,结束程序。而打印异常信息,就相当于调用了异常对象的printStackTrace()方法
异常类型处理:
发生异常,抛出的异常类型必须是发生异常类型的本身异常或其父类异常。try…catch处理,catch括号中接收的异常对象必须是try代码块代码发生异常类型的本身或父类异常对象,否则就无法处理try代码块中的异常。
try…catch……方式处理,以下案例异常处理是错误的,因为try…catch捕获的ArithmeticException异常并不是方法抛出的NullPointerException类型。
class TestException{
static Person person;
public static void main(String[] args) {
//此处处理抛出的异常
try{
excetionMethod();
}catch(ArithmeticExceptione){
}
}
//异常方法
static void excetionMethod()throws NullPointerException{
//空指针异常
person.method();
}
}
class Person{
void method(){
System.out.println("正常执行的代码");
}
}
thorws抛出异常,必须是发生的异常类型本身或父类类型异常。
//错误的异常抛出方式,因为该方法发生异常类型是空指针异常,结果抛出了类型强制转换异常。
static void excetionMethod()throws ClassCastException{
//空指针异常
person.method();
}
必须抛出其类型本身异常或者父类异常,如下是正确的
//发生空指针异常,抛出空指针异常的父类异常,正确
static void excetionMethod()throws Exception{
//空指针异常
person.method();
}
class TestException2{
public static void main(String[] args){
//处理异常
try{
method();
}catch(Exceptione){
try{
anmethod();
System.out.println("第一异常执行了");
}catch(Exceptione2){
System.out.println("这是另一个异常执行了");
}
}
}
//抛出异常
public static void method() throws Exception{
int i =1/0;
}
public static void anmethod()throws Exception{
int i =1/0;
}
}
多个异常的抛出:当执行的代码遇到多个异常时,可以抛出多个类型的异常,然后,调用者通过捕获该类型异常,来执行处理对应异常的代码。
格式:
try{
}catch(异常类型A ea){
//这里做针对发生A异常的操作
}catch(异常类型B eb){
//这里做针对发生B异常的操作
}
class TestException2{
static Person person;
public static void main(String[] args){
//多个异常的捕获
try{
method();
}catch(ArithmeticExceptione){
System.out.println("算数异常");
}catch(NullPointerExceptione2){
System.out.println("空指针异常");
}
//外部的正常代码
System.out.println("外部最后一条语句");
}
static void method()throwsNullPointerException,ArithmeticException{
//空指针异常
person.method();
//算数异常
int i= 1/0;
}
}
class Person{
void method(){};
}
catch(Exceptione),catch代码块之后,需要捕获一个异常对象,通过该异常对象,就可以操作获取该类型异常信息,所以说,抛出了什么异常,就应该在对应的catch处理代码块中接收什么类型的异常,否则就无法捕获。
如:抛出一个空指针异常,但是catch代码块只接收一个算数异常,那么发生的异常,就无法被捕获处理。
class TestException2{
static Person person;
public static void main(String[] args){
//多个异常的捕获
try{
method();
}
//抛出空指针异常,结果这里只有算数异常,所以该method方法抛出的空指针异常无法手动捕获处理,默认交给虚拟机处理,打印异常信息并结束程序。
catch(ArithmeticExceptione){
System.out.println("算数异常");
}
static void method()throwsArithmeticException{
//空指针异常
person.method();
}
}
class Person{
void method(){};
}
classTestException2{
static Person person;
public static void main(String[] args){
//多个异常的捕获
try{
method();
}
//父类异常短路了子类异常,无法执行到子类异常,编译期报错
catch(Exceptionexception){
System.out.println("父类异常");
}catch(NullPointerExceptione){
System.out.println("空指针异常执行");
}
}
static void method()throwsNullPointerException{
//空指针异常
person.method();
}
}
class Person{
void method(){};
}
正确的操作:
class TestException2{
static Person person;
public static void main(String[] args){
//多个异常的捕获
try{
method();
}catch(NullPointerExceptione){
System.out.println("空指针异常");
}
catch(Exceptione){
System.out.println("其他异常");
}
}
static void method()throwsNullPointerException,ArithmeticException{
//空指针异常
int i= 1/0;
}
}
class Person{
void method(){};
}
异常对象的常用方法:
printStackTrace():打印异常信息
try{
method();
}catch(NullPointerExceptione){
e.printStackTrace();
}
Java的异常别分为两大类:编译期异常和Runtime异常(运行时异常),所有的类及子类的实例都被称为RuntimeExceptionRuntime异常,不是Runtime异常的其他都是编译期异常。
编译期异常:
遇到编译期异常时,必须显式的处理该类型异常,try..catch或向上throws抛出,如果没有处理,程序就无法通过编译。
import java.io.IOException;
class TestException2{
static Person person;
public static void main(String[] args){
//该方法抛出一个编译期异常,但是并没有进行捕获或者抛出,所以编译期报错
method();
}
static void method()throws IOException{
//空指针异常
person.method();
}
}
运行期异常:
Runtime异常则更加灵活,Runtime异常不需要显式的处理,当然,如果程序需要捕获Runtime异常,也可以使用try…catch块来实现。
1、 即便不处理运行期异常,也不会在编译期报错。
import java.io.IOException;
class TestException2{
static Person person;
public static void main(String[] args){
//
method();
}
static void method()throwsNullPointerException{
//编译期不会报错(编译不报错)
person.method();
}
}
class Person{
void method(){};
}
2、 运行期异常只会在运行期报错。
import java.io.IOException;
class TestException2{
staticPerson person;
public static void main(String[] args){
//
method();
}
static void method()throwsNullPointerException{
//编译期不会报错(编译不报错)
//会在程序运行的时候,执行到这行代码,就会报错
person.method();
}
}
class Person{
voidmethod(){};
}
3、 运行期异常也可以进行手动try…catch捕获处理,处理完毕以后,还会正常执行程序。
import java.io.IOException;
class TestException2{
static Person person;
public static void main(String[] args){
//运行期异常也可以手动捕获
try{
method();
}catch(NullPointerExceptione){
System.out.println("运行期异常也可以手动捕获,发生了:" + e);
}
System.out.println("try..catch处理完异常以后,其他代码继续正常执行!");
}
static void method()throwsNullPointerException{
//编译期不会报错(编译不报错)
//会在程序运行的时候,执行到这行代码,就会报错
person.method();
}
}
class Person{
void method(){};
}
之前,产生异常,就是代码发生了异常,或者手动在方法上抛出一个异常。
1、代码本身就会发生异常
staticvoid method(){
//1、代码本身就会发生异常
int i= 1 / 0;
}
2、手动抛出一个异常
//2、在方法上声明抛出异常
static void method() throws Exception{
}
java也允许自行抛出异常,使用throw抛出,虽然使用throw抛出异常,但是,遇到抛出的是编译期异常,也要必须在方法上声明,thorws抛出该编译期异常。反之,运行期异常则不需要显示的处理,当然,也可以try catch进行手动捕获处理运行期异常。
import java.io.IOException;
import javax.print.PrintException;
class TestException2{
static Person person;
public static void main(String[] args) throwsIOException,PrintException{
//自行抛出异常
int i= 3;
if(i== 1){
throw new IOException();
}elseif(i == 2){
throw new NullPointerException();
}elseif(i == 3){
throw new PrintException();
}
}
}
处理异常时,选择合适的异常处理对应的问题,但如果系统提供的异常无法满足我们的需要,我们就可以自己定义异常。
格式:
1 继承Exception类
2 提供无参构造器和带字符串参数构造器
自定义异常的使用:
class TestException2{
public static void main(String[] args){
try{
method();
}catch(MyExceptionmy Exception){
String str = myException.getMessage();
System.out.println(str);
}
}
public static void method()throws MyException{
throw new MyException("自定义的异常");
}
}
class MyException extends Exception{
public MyException(String message){
super(message);
}
public MyException(){};
}
自定义运行期异常:
class TestException2{
public static void main(String[] args){
try{
method();
}catch(MyException myException){
String str = myException.getMessage();
System.out.println(str);
}
}
public static void method(){
thrownew MyException("自定义的运行期异常");
}
}
class MyException extends RuntimeException{
public MyException(String message){
super(message);
}
public MyException(){};
}
为什么自定义异常要有无参构造器和传一个字符串的构造器?
1、 因为Throwable有一个无参构造器
2、 throwable有一个有参构造器
3、为什么还需要定义一个无参构造器,默认就有无参构造器的!
因为,定义了构造器以后,默认的无参构造器就消失了,所以还需要自定义一个无参构造器。
Sring getMessage();为什么返回的是构造器传入的字符串?
源码如下:
我们发现,在执行有参构造器创建异常对象的时候,就发现,throwable构造器将该字符串赋值给了detailMessage变量,所以,当再次获取该变量,就是我们之前传入的message信息。