异常
异常的概念与体系结构
在java中,将程序执行过程中发生的不正常行为称为异常。
1.算数异常
System.out.println(10/0);
//
Exception in thread"main" java.ArithmeticException:/by zero
2.数组越界异常
int[] arr={1,2,3};
System.out.println(arr[100]);
//
Exception in thread "main"java.langArrayIndexOutOfBroundsException:100
3.空指针异常
int[] arr=null;
System.out.println(arr.length);
//执行结果
Expection in thread "main"java.labg.NullPointerException
java中不同类型的异常,都有与其对应的类来进行描述。
1.Throwable:是异常体系的顶层端,其派生出两个重要的子类:Error和Exception
2.Error:指的是java虚拟机无法解决的严重问题,比如:JVM的内部错误,资源耗尽
3.Exception:异常产生后程序员可以通过代码进行处理,使程序继续进行。
异常的分类
1.编译时异常
public class Person{
private String name;
private Strinf gender;
int age;
//想要让该类支持深拷贝
public Person clone(){
return (Person)super.clone();
}
}
2.运行时异常
在程序执行期间发生的异常称为运行时异常,也称为非受检查异常。
RunTimeException以及子类对应的异常,都称为运行时异常。
异常的处理
防御式编程
错误在代码中是客观存在的,因此我们要让程序出现问题及时通知
1.LBY:在操作之前就做充分检查,即:事前防御型
boolean ret=flase;
ret =登陆游戏();
if(!ret){
处理登陆游戏错误;
return;
}
ret=开始匹配();
if(!ret){
处理匹配错误;
return;
}
ret=游戏确认();
if(!ret){
处理游戏确认错误;
return ;
}
ret=选择英雄();
if(!ret){
处理选择英雄错误;
return;
}
缺陷:正常流程和错误流程代码混在一起
2.EAFP:事后认错型
try{
登陆游戏();
开始匹配():
游戏确认():
选择英雄():
}
cash(登录游戏异常){
处理登录游戏异常;
}cash(开始匹配异常){
处理匹配异常;
}cash(游戏确认异常){
.....
异常处理主要五个关键字:throw,try,catch,final,throws
异常抛出
在编写程序时,如果程序中出现错误,此时需要将错误的信息告诉调用者
throw new XXXException(“异常产生的原因”);
[需求]实现一个获取数组中任意元素的方法
public static int getElement(int[] array,int index){
if(null==array){
throw new NullPointerException(“传递的数组为null");
}
if(index<0||index>=array.length){
throw new ArrayIndexOutOfBroundsException("传递的数组下标越界");
}
return array[index];
}
public static void main(String[] args){
int[] array={1,2,3};
getElement(array,3);
}
【注意事项】
1.throw必须写在方法体内部
2.抛出对象必须是Exception或者是Exception的子类对象
3.异常一旦抛出,其后的代码不会执行
异常的捕获
异常声明throws
处在方法声明时参数列表之后,当方法体跑出异常编译时异常,用户不象处理该异常,此时可以借助throws将异常抛给方法调用者来处理,即当前方法不处理异常提醒方法的调用者处理。
语法格式
修饰符 返回类型 方法名(参数列表)throws异常类型1,异常类型2…{
}
需求:加载指定的配置文件config.ini
public class Config{
File file;
/*
FileNotFoundException:编译时异常,表明文件不存在
public void OpenConfig(String filename)throws FileNotFoundException{
if(filename.equals("config.ini"){
throw new FileNotFoundException("配置文件名字不对");
}
//打开文件
}
1.throw必须跟在方法体的参数列表之后
2.声明的异常必须是Exception或者Exception的子类
3.方法体内部如果抛出异常,throw之后必须跟多个异常类型。之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可。
4.调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出
try-catch捕获或处理
throws对异常并没有真正处理,而是将异常报告给抛出异常的调用者。
语法格式:
try{
//将可能出现的异常的代码放这里
}catch(要捕获异常类型 e){
//如果try中的代码出现异常,此处catch捕获时异常类型与try中抛出异常类型一致时,或者try抛出异常基类时,会捕获
}[catch(异常类型 e){
//对异常进行处理
}finally{
此处代码一定会执行
}]
注意:
1【】中可表示选项,可以添加,不添加
2 try中的代码可能会抛出异常,也可能不会
需求:读取文件,如果配置文件名字不是指定文件名。抛出异常
public class Config{
File file;
public void openConfig(String filename)throws FileNotFoundException{
if(!filename.equals("config.ini"){
throw new FileNotFoundException("配置文件名字不对");
}
//打开文件
}
public void readConfig(){
}
public static void main(String[] args){
Config config=new Config();
try{
config.openConfig("config.txt");
System.out.println("打开文件成功");
}cash(IOException e){
//异常的处理方式
//System.out.println(e.getMessage());//只打印异常
//System.out.println(e);//打印异常类型:异常信息
e.printStackTrace()://打印信息最全面
}
```b
【注意事项】
1.try块内抛出异常位置之后的代码将不会被执行
2.如果抛出异常类型与catch时异常类型不匹配,即异常不会被捕获
```java
public static void main(String[] args){
try{
int[] array={1,2,3};
System.out.println(array[3]);//此时会抛出异常
}catch(NullPointerException e)//捕获时候的是空指针异常
e.printlnStackTrace();
}
System.out.println("后续代码“);
}
3.try中可能会抛出多个不同的异常对象,必须用多个catch来捕获
public static void main(Stirng[] args){
int[] arr={1,2,3};
try{
System.out.println("before");
//arr=null;
System.out.println(arr[100]);
System.out.println("after");
}catch(ArrayIndexOutOfBoudsException e){
System.out.println("这是个数组下标越界异常");
e.printStackTrace();
}catch(NullPointerException e){
System.out.println("这是个空指针异常"):
e.prinyStackTrace();
}
System.out.rpintln("after try catch");
}
如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch
public static void main(String[] args){
int[] arr={1,2,3};
try{
System.out.println("before");
arr=null;
System.out.println("after");
}catch(Exception e){
//Exception 可以捕获所有异常
e.printStackTrace();
}catch(NullPointerException e){
e.printtackTrace();
}
System.out.println("after try catch");
}
finally
在写程序,有些特定的代码,无论程序是否发生异常,都需要执行、在程序正常或者异常退出时,必须要对资源进行回收。
语法格式
try{
可能会发生异常的代码
}catch(异常类型 e){
//对捕获到的异常进行处理
}finally{
//一定会执行
}
如果没有抛出异常,或异常处理捕获,会执行代码
public static void main(String[] args){
try{
int[] arr={1,2,3};
arr[100]=10;
arr[0]=10;
}catch(ArrayIndexOutOfBoundsException e){
System.out.println(e);
}finally{
System.out.println("finally中的代码一定会执行");
}
System.out.println("如果没有抛出异常,或者异常被处理了,try-catch后代码也会被执行");
}
问题:既然finally和try—catch-finally后·的代码都会执行,为什么还有finally
需求:实现getData方法,内部输入一个整形数字,然后将数字返回,并在main1方法返回
public class TestFinally{
public static int getData(){
Scanner sc=null;
try{
sc=new Scanner(System.in);
int data =sc.nextInt();
return data;
}cash(InputMismatchException e){
e.printStackTrace();
}finally{
System.out.println("finally中代码");
}
System.out.println("Try-catch-finally之后代码");
if(null!=sc){
sc.close();
}
return 0;
}
public static void main(STring[] args){
int data=getData();
System.out.println(data);
}
}
异常处理流程
public static void main(String[] args){
try{
func();
}catch(ArrayindexOutOfBoundsException e){
e.printStackTrace();
}
System.out.println("after try catch");
}
public static viod func(){
int[] arr={1,2,3};
System.out.println(arr[100]);
}
【总结】
- 程序先执行try中的代码
- 如果try中的代码出现异常,就会结束try代码,看和catch中的异常是否匹配
- 如果找到匹配异常的类型,会执行catch代码
- 如果没有找到,会将异常向上传递
- 无论是否找到匹配类型,finally中的代码都会被执行
- 如果上层也没有处理,就再向上传递
- 一直到main方法后会给JVM来进行处理
自定义异常类
Java中虽然已经内置丰富的异常类,但并不完全表示实际开发中所遇到的一些异常。
例如:实现用户登录
public class Login{
private String useName="admin";
private String password="123456";
public static voif loginfo(String userName,Stringpassword){
if(!this.useNAme.equals(userName)){
}
if(!this.password.equals(password)){
}
System,out.println("登陆成功");
}
public static void main(String[] args){
loginfo("admin","123456");
}
}
此时我们在处理用户名密码错的时候可能就需要抛出两个异常,我们可以基于已有的异常类型进行拓展。
具体方式:
1.自定义异常类,然后继承自Exception或者RunTimeException
2.实现一个带有String类型参数的构造方法
class UserNameException extends Exception{
public UserNameException(String message){
super(message);
}
}
class PasswordException extends Exception{
public PasswordException(String mesage){
super(message);
}
}
此时我们login代码可以变成
public class Login{
private String uesrName="admin";
private String password="123456";
public static void loginfo(String userName String password){
throws UserNameException,PasswordException{
if(!userName.equals(password)){
throws new UserNameException("用户名错误"):
}
if(!password.equals(password)){
throws new PasswordException("用户名错误");
}
System.out.println("登陆成功");
}
public static void main(String[] args){
try{
}
}