Java异常
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
Java异常
异常概念
就像人会生病一样人生病就会产生异常行为比如感冒发烧等同样的当程序出错的时候程序执行过程中也会发生的不正常行为。
在Java中将程序执行过程中发生的不正常行为称为异常。
举几个我们常见的例子
- 算术异常
System.out.println(10 / 0); //在我们数学中0是不能做被除数数的这里也一样
当我们输入这段代码并执行时会产生如下结果
2. 数组越界异常
就是数组越界访问抛出的异常
int[] arr = {1, 2, 3, 4};
System.out.println(arr[4]); //数组元素下标最大为3没有4
结果
3. 空指针异常
int[] arr = null;
System.out.println(arr.length);
我们以后见到最多的应该就是空指针异常和数组越界异常了。
我们可以看出对于不同类型的异常都有与其对应的类来进描述。
异常的体系结构
大致如下
- Throwable是异常体系的顶层类其派生出两个重要的子类, Error 和 Exception
- Error指的是Java虚拟机无法解决的严重问题比如JVM的内部错误、资源耗尽等典型代表StackOverflowError和OutOfMemoryError一旦发生回天乏术。
- Exception异常产生后程序员可以通过代码进行处理使程序继续执行。比如感冒、发烧。我们平时所说的异常就是Exception。
异常的分类
也就是图中Exception派生出的子类图中的受查异常与非受查异常是什么意思呢
异常可能在编译时发生也可能在程序运行时发生根据发生的时机不同可以将异常分为受查异常与非受查异常。
在程序编译期间发生的异常称为编译时异常也称为受查异常。
编译时异常是表示必须在编写程序的时候预先对这种异常进行处理如果不处理编译器就报错。编译时异常发生概率较高。
这里我们调用Object的clone方法报错了是因为没有预先处理异常如下即可
在程序执行期间发生的异常称为运行时异常也称为非受查异常
RunTimeException以及其子类对应的异常都称为运行时异常。比如NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException。
注编译时出现的语法性错误不能称之为异常这是 “编译期” 出错。而运行时指的是程序已经编译通过得到 class 文件了, 再由 JVM 执行过程中出现的错误。
异常的抛出 throw
Java中可以借助throw关键字抛出一个指定的异常对象将错误信息告知给调用者。具体语法如下
throw new XXXException("异常产生的原因");
举个简单例子已知数组下标求该下标的元素。
public class Test {
public static int getElement(int[] array, int index){
if(null == array) {
throw new NullPointerException("传递的数组为null");
} if(index < 0 || index >= array.length){
throw new ArrayIndexOutOfBoundsException("传递的数组下标越界");
}
return array[index];
}
public static void main(String[] args) {
int[] array = {1,2,3};
getElement(array, 3);
}
}
注意几点
- throw必须写在方法体内部
- 抛出的对象必须是Exception 或者 Exception 的子类对象
- 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,也就是非受查异常则可以不用处理直接交给JVM来处理
- 如果抛出的是编译时异常用户必须处理否则无法通过编译
- 异常一旦抛出其后的代码就不会执行
异常的处理
异常的出理方式主要有两种异常声明throws 以及 try-catch捕获处理。
格式
修饰符 返回值类型 方法名(参数列表) throws 异常类型1异常类型2...{
}
我们可以看到一个方法可以声明多个异常那为什么要声明异常呢
当方法中抛出编译时异常用户不想处理该异常此时就可以借助throws将异常抛给方法的调用者来处理。即当前方法不处理异常提醒方法的调用者处理异常。
public class Test {
public static void func() throws ClassNotFoundException {
//throws ClassNotFoundException 该声明表示该方法在执行过程中可能会出现该异常
//属于编译时异常
System.out.println("func!");
}
public static void main(String[] args) {
func(); //这里报红这里func方法将它的异常抛给了main方法
//所以main方法必须对其进行处理不处理编译器就报错
}
}
怎么解决呢我们有两种方法
方法一throws抛给给调用者谁调用了谁处理调用者知道是什么异常
public class Test {
public static void func() throws ClassNotFoundException {
System.out.println("func!");
}
public static void main(String[] args) throws ClassNotFoundException {
//这里将异常抛给了main函数的调用者JVM让JVM来处理异常
func();
}
}
JVM处理结果就是程序中断抛出该异常对象本质上并没有对异常进行处理而下面的方法就是对其进行处理。
方法二try…catch 捕捉处理调用者不知道是什么异常
public class Test {
public static void main(String[] args) {
try {
func(); //可能出现异常的代码
} catch (ClassNotFoundException e) { //对该异常进行捕捉
e.printStackTrace(); //处理该异常
}
}
public static void func() throws ClassNotFoundException {
System.out.println("func!");
}
}
使用throws注意事项
- throws必须跟在方法的参数列表之后
- 声明的异常必须是 Exception 或者 Exception 的子类
- 方法内部如果抛出了多个异常throws之后必须跟多个异常类型之间用逗号隔开如果抛出多个异常类型具有父子关系直接声明父类即可。
- 调用声明抛出异常的方法时调用者必须对该异常进行处理或者继续使用throws抛出
try … catch 捕获并处理
举个栗子
public static void main(String[] args) {
try {
func(null); //如果这里发生异常那么就看下面的catch是否能捕获异常
//如果未发生异常则跳过catch
} catch (CloneNotSupportedException e) {
//如果catch里捕获的异常类型与try里抛出的异常类型相同则该异常被捕获然后程序进入该catch里
System.out.println("捕获到了 CloneNotSupportedException 异常");
} //catch(异常类型 e){
//如果可能程序出现多个异常则多加几个catch语句捕获异常
// 对异常进行处理
//}finally{
// 此处代码一定会被执行到,无论是否发生异常
//}
//程序处理完异常后就执行正常代码
System.out.println("正常逻辑");
}
public static void func(int[] array) throws CloneNotSupportedException {
if(array == null) {
throw new CloneNotSupportedException();
}
}
输出
总结
- try块内抛出异常位置之后的代码将不会被执行
- 如果抛出异常类型与catch时异常类型不匹配即异常不会被成功捕获也就不会被处理继续往外抛直到JVM收到后中断程序----异常是按照类型来捕获的。
- try中可能会抛出多个不同的异常对象则必须用多个catch来捕获----即多种异常多次捕获。如果异常之间具有父子关系一定是子类异常在前catch父类异常在后catch
- 可以通过一个catch捕获所有的异常即多个异常一次捕获(不推荐)
由于 Exception 类是所有异常类的父类. 因此可以用这个类型表示捕捉所有异常.
获取异常信息
在catch 语句里我们有三种处理方法以上述代码为例
public static void main(String[] args) {
try {
func(null);
} catch (CloneNotSupportedException e) {
// 异常的处理方式
//System.out.println(e.getMessage()); // 只打印异常信息
//System.out.println(e); // 打印异常类型异常信息
e.printStackTrace(); // 打印信息最全面
}
System.out.println("正常逻辑");
}
public static void func(int[] array) throws CloneNotSupportedException {
if(array == null) {
throw new CloneNotSupportedException();
}
}
关于finally
有些特定的代码不论程序是否发生异常都需要执行比如程序中打开的资源网络连接、数据库连接、IO流等在程序正常或者异常退出时必须要对资源进进行回收。另外因为异常会引发程序的跳转可能导致有些语句执行不到finally就是用来解决这个问题的。
try{
// 可能会发生异常的代码
}catch(异常类型 e){
// 对捕获到的异常进行处理
}finally{
// 此处的语句无论是否发生异常都会被执行到
}
自定义异常
系统定义的异常主要是用来处理一些常见的运行错误对于某端代码自己的特有错误需要程序员自己来创建自己的异常类来处理。
实现方式
- 自定义异常类然后继承自Exception 或者 RunTimeException
- 实现一个带有String类型参数的构造方法参数含义出现异常的原因
注意
自定义异常通常会继承自 Exception 或者 RuntimeException
继承自 Exception 的异常默认是受查异常
继承自 RuntimeException 的异常默认是非受查异常
举个例子实现一个用户登录功能
public class Test {
public static void fun(String name2, String code2) throws PasswordException, UsernameException {
String name = "张三";
String code = "666666";
if(!name2.equals(name)) { //如果输入的姓名或密码错误则抛出异常
throw new PasswordException("用户名错误!");
}
if(!code2.equals(code)) {
throw new UsernameException("密码错误");
}
System.out.println("输入成功");
}
public static void main(String[] args) throws PasswordException, UsernameException {
Scanner scanner = new Scanner(System.in);
String name = scanner.nextLine();
String code = scanner.nextLine();
fun(name,code);
}
}
public class PasswordException extends Exception{
public PasswordException(String message) {
super(message);
}
}
public class UsernameException extends Exception{
public UsernameException(String message) {
super(message);
}
}