文章目录
- 1. 异常
- 1.1 异常概述
- 1.2 异常机制概述
- 1.3 程序错误一般分为三种
- 1.4 异常继承结构
- 1.5 编译时异常和运行时异常别称
- 1.6 编译时异常和运行时异常的区别
- 1.7 Throwable中java的异常一般分为受查异常和非受查异常
- 1.8 异常处理方式
- 1.9 Java常见异常
- 1.10 throw和throws的区别
- 1.11 Final、Finally、Finalize区别
- 1.12 try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
- 2. 代码示例
- 2.1 try-catch
- 2.2 throw和自定义异常
- 2.3 异常与方法的重写(throws)
- 2.4 finally
- 2.5 异常常用方法
- 2.6 自动关闭特性
- 3. try、catch、finally的执行顺序
- 3.1 不带return的执行顺序
- 3.2 带return的执行顺序
- 3.2.1 try中带return的
- 3.2.2 catch中带return的
- 3.2.3 finally中带return的
1. 异常
1.1 异常概述
异常指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
异常在java中以 类 的形式存在,每一个 异常类 都可以创建 异常对象。
1.2 异常机制概述
异常机制是指当程序出现错误后,程序如何处理。具体来说,异常机制提供了程序退出的安全通道。当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器。
1.3 程序错误一般分为三种
- 编译错误: 编写程序时没有遵循语法规则,编译程序能够自己发现错误并提示位置和原因。
- 运行错误:程序在执行的时候运行环境发现了不能执行的操作。比如,JVM出错了,内存溢出等。
- 逻辑错误:程序没有按照预期的逻辑顺序执行。比如,在四则运算中除数不能为零,数组下标越界,空指针异常等。
1.4 异常继承结构
Throwable 是 Java 语言中所有错误或异常的超类。
下一层分为 Error 和 Exception 。Exception 又有两个分支
一个是运行时异常 RuntimeException ,一个是CheckedException。
RuntimeException 如 : NullPointerException 、 ClassCastException等;
CheckedException 是受查异常,如 I/O 错误导致的 IOException、SQLException。
RuntimeException 是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。
如果出现 RuntimeException,那么一定是程序员的错误.
Error:错误,无法被处理的
Exception:异常,能够被程序本身处理的,可以通过try…catch语句捕捉异常,或者是throws抛出异常。分为运行时异常和非运行时异常。
运行时异常:就是RuntimeException,编译时不会检查出错误的。一般是由于逻辑错误引起的,程序员可以手动去解决的,比如判空等。
非运行时异常:也叫编译异常,就是Exception下除了RuntimeException以外的异常。是必须进行处理的异常,编译器会进行异常提醒。如果不进行处理,程序编译不通过。
1.5 编译时异常和运行时异常别称
- 编译时异常
- 受检异常:CheckedException
- 受控异常
- 运行时异常
- 未受检异常:UnCheckedException
- 非受控异常
1.6 编译时异常和运行时异常的区别
- 编译时异常一般发生的概率 比较高。
- 运行时异常一般发生的概率 比较低。
- 编译时异常发生概率较高,需要在运行之前对其进行 预处理。
- 运行时异常发生概率较低,没必要提前进行预处理。
1.7 Throwable中java的异常一般分为受查异常和非受查异常
- 受查异常:编译器报错。Exception下除了RuntimeException以外的都是受查异常。这种异常都发生在编译阶段,Java 编译器会强制程序去捕获此类异常。
- 非受查异常:运行时报错。非受查异常包括Error和RuntimeException。RuntimeException表示编译器不会检查是否对RuntimeException进行了处理,在程序中不必去捕获RuntimeException异常,也不必在方法体抛出RuntimeException异常类。RuntimeException发生就说明程序出现了编程错误,应该找出错误去修改,而不是去捕获异常。
1.8 异常处理方式
- 捕捉异常:常用的try…catch模式
- 声明异常:常用的throws模式
- 自定义异常:
第一步:编写一个类继承 Exception 或者 RuntimeException.
第二步:提供两个 构造方法,一个无参数的,一个带有String参数的。
1.9 Java常见异常
- RuntimeException子类:
- IOException:
- 其他
1.10 throw和throws的区别
(1)throw
作用在方法内,表示抛出具体异常,由方法体内的语句处理;一定抛出了异常;
(2)throws
作用在方法的声明上,表示抛出异常,由调用者来进行异常处理;可能出现异常,不一定会发生异常;
1.11 Final、Finally、Finalize区别
(1)final可以修饰类、变量、方法
- 修饰类时,这个类不能被继承。
- 修饰变量时,这个变量必须赋值,在引用中只能读取不可修改,即为常量;
- 修饰方法时,这个方法不能被子类重写;
(2)finally:通常放在 try…catch 的后面构造最终执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要 JVM 不关闭都能执行,可以将 释放外部资源的代码写在 finally 块中。
(3)finalize() Object 类中定义的方法,Java 中允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集 器在销毁对象时调用的,通过重写 finalize() 方法可以整理系统资源或者执行其他清理工作。
1.12 try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
2. 代码示例
2.1 try-catch
package exception;
/**
* java异常处理机制
* java中所有的异常的顶级超类:Throwable
* Throwable下面派生了两个子类型:Error,Exception
* Error是描述系统错误的,是不可能被处理的,一旦出现Error意味着程序会结束.
* Exception描述的是程序异常,是可以被捕获且处理的.
*
* 异常处理机制中的:try-catch
* 语法:
* try{
* 可能出现异常的代码片段
* }catch(XXXException e){
* 当try中出现XXXException后的解决代码
* }
*
* 注:try语句块不能单独是有,后面要么跟catch语句块,要么跟finally语句块
*/
public class TryCatchDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
try {
// String line = null;
// String line = "";
String line = "abc";
System.out.println(line.length());
System.out.println(line.charAt(0));
System.out.println(Integer.parseInt(line));
// }catch(NullPointerException e){
// System.out.println("出现了空指针并处理了");
// }catch(StringIndexOutOfBoundsException e){
// System.out.println("出现了字符串下标越界,并处理了");
// }
//当多个异常使用同一种解决办法时,可以合并在一个catch处理.
}catch(NullPointerException|StringIndexOutOfBoundsException e){
System.out.println("空指针或下标越界的统一处理");
//子类异常在上,超类异常在下.
}catch(Exception e){
System.out.println("反正就是出了个错!");
}
System.out.println("程序结束了");
}
}
2.2 throw和自定义异常
- 自定义异常类
package exception;
/**
* 自定义异常
* 通常用来说明那些满足语法但是不满足业务场景的异常问题.
* 自定义异常通常要做如下几点:
* 1:类名要做到见名知义
* 2:继承自Exception(直接或间接)
* 3:提供超类异常提供的所有构造器
*/
public class IllegalAgeException extends Exception {
public IllegalAgeException() {
}
public IllegalAgeException(String message) {
super(message);
}
public IllegalAgeException(String message, Throwable cause) {
super(message, cause);
}
public IllegalAgeException(Throwable cause) {
super(cause);
}
public IllegalAgeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
- Person类
package exception;
/**
* throw关键字可以主动对外抛出一个异常,通常下列情况我们会这样做:
* 1:程序遇到一个异常,但是该异常不应当被当前代码片段处理时可以主动对外抛出
* 2:程序遇到了一个满足语法但是不满足业务场景时可以主动抛出一个异常
*/
public class Person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws IllegalAgeException {
if(age<0||age>100){
// throw new RuntimeException("年龄不合法");
/*
除了RuntimeException之外的其它异常如果使用throw
抛出则要在当前方法上使用throws声明该异常的抛出
*/
// throw new Exception("年龄不合法");
throw new IllegalAgeException("年龄不合法:"+age);
}
this.age = age;
}
}
- 测试类
package exception;
/**
* 异常的抛出
*/
public class ThrowDemo {
public static void main(String[] args) {
Person p = new Person();
/*
当我们调用一个含有throws声明异常抛出的方法时,编译器
要求我们必须写处理办法,否则编译不通过.
处理办法有两种:
1:可以在当前方法上继续使用throws将异常抛出
2:使用try-catch主动捕获并处理该异常
*/
try {
p.setAge(10000);//满足语法,但是不满足业务
} catch (IllegalAgeException e) {
e.printStackTrace();
}
System.out.println("此人年龄"+p.getAge()+"岁");
}
}
2.3 异常与方法的重写(throws)
package exception;
import java.awt.*;
import java.io.IOException;
import java.sql.SQLException;
/**
* 子类重写超类含有throws声明异常抛出的方法时,对throws的重写规则
*/
public class ThrowsDemo {
public void dosome()throws IOException, AWTException {}
}
class SubClass extends ThrowsDemo{
// public void dosome()throws IOException, AWTException {}
//允许子类方法仅抛出超类方法声明抛出异常的一部分
// public void dosome()throws IOException {}
//允许不再抛出任何异常
// public void dosome(){}
//允许抛出超类方法抛出异常的子类型异常
// public void dosome()throws FileNotFoundException {}
//不允许抛出额外异常(超类方法没有声明抛出的,且与声明抛出的异常没有继承关系的)
// public void dosome()throws SQLException {}
//不允许抛出超类方法抛出异常的超类型异常
// public void dosome()throws Exception {}
}
2.4 finally
package exception;
/**
* finally块
* finally块是异常处理机制的最后一块,它可以直接跟在try后面或者最后一个
* catch块后面.
*
* finally可以保证只要程序执行到try里面,无论try中的代码是否出现错误,
* finally块内容都必定执行.
*
* 通常我们会将释放资源这类操作放在finally块中.例如IO操作后的close调用.
*/
public class FinallyDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
try {
String line = "";
System.out.println(line.length());
/*
try语句块中出错代码以下的内容都不会执行,例如:
如果上面System.out.println(line.length());出现了
空指针,那么程序会直接跳过下面的return来到catch.
*/
return;
} catch (Exception e) {
System.out.println("出错了");
} finally {
System.out.println("finally块执行了");
}
System.out.println("程序结束了");
}
}
package exception;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 异常处理机制在IO中的应用
*/
public class FinallyDemo2 {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("fos.dat");
fos.write(1);
} catch (IOException e) {
System.out.println("出错了");
} finally {
try {
if(fos!=null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package exception;
/**
* finally相关面试题
* 请说明:final,finally,finalize是什么?
*/
public class FinallyDemo3 {
public static void main(String[] args) {
System.out.println(
test("0")+","+test("")+","+test(null)
);//3,3,3
}
public static int test(String str){
try {
return str.charAt(0)-'0';
} catch (StringIndexOutOfBoundsException e) {
return 1;
} catch (NullPointerException e){
return 2;
} finally {
return 3;
}
}
}
2.5 异常常用方法
package exception;
/**
* 异常常用方法
*/
public class ExceptionApiDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
try {
String line = "abc";
System.out.println(Integer.parseInt(line));
} catch (NumberFormatException e) {
System.out.println("出错了!");
/*
在控制台输出错误堆栈信息(该输出和System.out.println输出
顺序可能混乱,这是由于多线程原因.)
*/
e.printStackTrace();
/*
获取错误消息,一般用于记录日志或提示给用户使用
*/
String message = e.getMessage();//获取错误消息
System.out.println(message);
}
System.out.println("程序结束了");
}
}
2.6 自动关闭特性
package exception;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* JDK7之后java推出了自动关闭特性
* 可以用更简洁的语法完成如IO操作的异常处理中关闭的问题
*/
public class AutocloseableDemo {
public static void main(String[] args) {
try(
// 只有实现了java.io.AutoCloseable接口的类才可以在这里定义
// 编译器会将在这里定义的类最终在finally中调用close关闭
// 编译后的class文件的样子可以参考FinallyDemo2.java的样子
// 注:所有的流都实现了AutoCloseable接口
FileOutputStream fos = new FileOutputStream("fos.dat");
){
fos.write(1);
} catch (IOException e) {
System.out.println("出错了");
}
}
}
3. try、catch、finally的执行顺序
3.1 不带return的执行顺序
异常处理中,try、catch、finally的执行顺序:
- 如果try中没有异常,执行顺序为:try->finally
- 如果try中有异常,执行顺序为:try->catch->finally
public class Test {
public static int rank;
public static void main(String[] args) {
rank = 1;
solve2();
System.out.println("over");
}
public static void solve1() throws Exception {
try {
System.out.println("solve 1 try, rank: " + rank++);
throw new Exception("throw by solve 1");
} finally {
System.out.println("solve 1 finally, rank: " + rank++);
}
}
public static void solve2() {
try {
System.out.println("solve 2 try, rank: " + rank++);
solve1();
} catch (Exception e) {
System.out.println("catch exception: " + e.getMessage() + ", rank: " + rank++);
} finally {
System.out.println("solve 2 finally, rank: " + rank++);
}
}
}
// solve 2 try, rank: 1
// solve 1 try, rank: 2
// solve 1 finally, rank: 3
// catch exception: throw by solve 1, rank: 4
// solve 2 finally, rank: 5
// over
首先是try执行,如果发生异常,那就直接捕获异常,最后执行finally。但是,如果抛出异常,例如在solve1方法中,throw了一个异常,那么不会立刻回溯到上一方法,而是仍然执行finally。
3.2 带return的执行顺序
3.2.1 try中带return的
基本类型:
public class Test {
public static void main(String[] args) {
System.out.println(testReturn());
}
public static int testReturn() {
int i = 1;
try {
i++;
System.out.println("try: " + i);
return i;
} catch (Exception e) {
i++;
System.out.println("catch: " + i);
} finally {
i++;
System.out.println("finally: " + i);
}
return i;
}
}
// try: 2
// finally: 3
// 2
引用类型:
public static void main(String[] args) {
System.out.println(testReturn2());
}
public static List<Integer> testReturn2() {
List<Integer> list = new ArrayList<>();
try {
list.add(1);
System.out.println("try: " + list);
return list;
} catch (Exception e) {
list.add(2);
System.out.println("catch: " + list);
} finally {
list.add(3);
System.out.println("finally: " + list);
}
return list;
}
// try: [1]
// finally: [1, 3]
// [1, 3]
3.2.2 catch中带return的
public static void main(String[] args) {
System.out.println(testReturn3());
}
public static int testReturn3() {
int i = 1;
try {
i++;
System.out.println("try: " + i);
int x = i / 0;
} catch (Exception e) {
i++;
System.out.println("catch: " + i);
return i;
} finally {
i++;
System.out.println("finally: " + i);
}
return i;
}
// try: 2
// catch: 3
// finally: 4
// 3
3.2.3 finally中带return的
public static void main(String[] args) {
System.out.println(testReturn4());
}
public static int testReturn4() {
int i = 1;
try {
i++;
System.out.println("try: " + i);
return i;
} catch (Exception e) {
i++;
System.out.println("catch: " + i);
} finally {
i++;
System.out.println("finally: " + i);
return i;
}
}
//try: 2
//finally: 3
//3
- finally中的代码总会执行
- 当try、catch中有return时,也会执行finally。return的时候,要注意返回值的类型,是否受到finally中代码的影响
- finally中有return时,会直接在finally中退出,导致try、catch中的return失效