java中的异常、处理异常的方法、getMessage和printStackTrace方法、finally语句块、自定义异常&手动抛出异常
1、异常的定义及其作用
package com.company.exception;
/*
* 1、异常是什么?
* 第一、异常模拟的是现实世界中“不正常的”事件
* 第二、java中采用“类”去模拟异常
* 第三、类是可以创建对象的
* NullPointerException e = 0x1234;
* e是引用类型,e中保存的内存地址指向堆中的“对象”
* 这个对象一定是NullPointerException类型
* 这个对象就表示真实存在的异常事件
* NullPointerException是一类异常
*
* 2、异常机制的作用?
* java语言为我们提供了一种完善的异常处理机制
* 作用是:程序发生异常事件之后,为我们输出详细的信息。
* 程序员通过这个信息,可以对程序进行一些处理,使程序
* 更加健壮
* */
public class ExceptionTest01 {
public static void main(String[] args) {
int a = 10;
int b = 0;
int c = a / b; // ArithmeticException e = oxxxxx;
// 上面的代码出现异常,“没有处理”,下面的代码不会执行,直接退出JVM
System.out.println("hello world!");
}
}
/*
* 以上程序编译通过了,但是运行时出现了异常,表示发生了某个异常事件。
* JVM向控制台输出如下的信息:
* Exception in thread "main" java.lang.ArithmeticException: / by zero
* at com.company.exception.ExceptionTest01.main(ExceptionTest01.java:20)
*本质:程序执行过程中发生了算数异常这个事件,JVM为我们创建了一个ArithmeticException类型的对象。
*并且这个对象中包含了详细的异常信息,并且JVM将这个对象中的信息输出到控制台。
* */
2、处理异常的两种方法:
2.1 第一种方法:声明抛出 throws
package com.company.exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/*
* 处理异常有两种方法:
* 1、声明抛出
* throws
*
* 2、捕捉
* try...catch...
*
* 以下程序演示第一种方式:声明抛出,在方法声明的位置上使用throws关键字向上抛出异常。
* */
public class ExceptionTest02 {
//public static void main(String[] args) {
// 创建文件输入流,读取文件
// 思考:java编译器是如何知道以下的代码执行过程中可能出现异常,java编译器是如何知道这个异常发生的几率比较高呢?
// 实际上编译器并没有那么智能,这是因为在声明构造方法的时候写了throws FileNotFoundException
/*
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}
*/
// FileInputStream fis = new FileInputStream("c:/ab.txt"); // 文件不存在
//}
// 第一种方法:声明抛出
// 这是编译不会出错
// ① public static void main(String[] args) throws FileNotFoundException {
// ② public static void main(String[] args) throws IOException {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("c:/ab.txt");
}
}
/*
* 以上程序编译不通过
* Error:(19, 31) java: 未报告的异常错误java.io.FileNotFoundException; 必须对其进行捕获或声明以便抛出
* */
2.2 深入了解 throws
package com.company.exception;
/*
* 深入throws
* */
import java.io.*;
public class ExceptionTest03 {
public static void main(String[] args) throws FileNotFoundException{
//m1();
// 使用throws处理异常不是真正的处理异常,而是推卸责任
// 谁调用了就会抛给谁
// 上面的m1方法如果出现了异常,因为采用的是上抛,给了JVM,JVM遇到这个异常就会退出JVM,下面的代码就不会执行
// 真正处理:
try{
m1();
}catch(FileNotFoundException e){}
System.out.println("hello world!");
}
public static void m1() throws FileNotFoundException{
m2();
}
public static void m2() throws FileNotFoundException{
m3(); // 此时这里会出现编译异常,需要加throws,依次类推m1()、main()方法声明均需要加入throws
}
public static void m3() throws FileNotFoundException{
new FileInputStream("c/ab.txt"); // FileInputStream构造方法声明位置上使用throws(向上抛)
}
}
/*
* // 在程序运行过程中发生了FileNotFoundException类型的异常
* // JVM为我们创建了一个FileNotFoundException类型的对象
* // 该对象中携带以下的信息
* // JVM负责将该对象的信息打印到控制台,并且JVM停掉了程序的运行
* Exception in thread "main" java.io.FileNotFoundException: c\ab.txt (系统找不到指定的路径。)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
at com.company.exception.ExceptionTest03.m3(ExceptionTest03.java:25)
at com.company.exception.ExceptionTest03.m2(ExceptionTest03.java:21)
at com.company.exception.ExceptionTest03.m1(ExceptionTest03.java:17)
at com.company.exception.ExceptionTest03.main(ExceptionTest03.java:8)
* */
2.3 第二种方法: try…catch…
package com.company.exception;
import java.io.*;
/*
* 处理异常的第二种方式:
* 捕捉
* try...catch...
*
* 语法:
* try{
* 可能出现异常的代码;
* }catch(异常类型1 变量){
* 处理异常的代码;
* }catch(异常类型2 变量){
* 处理异常的代码;
* }...
*
* 1、catch语句块可以写多个
* 2、但是从上到下catch,必须从小型异常到大型异常进行捕捉。
* 3、虽然小型异常可以去掉,但是为了更加准确的找到异常,最好不要舍掉。
* 4、try...catch...中最多执行1个catch语句块。执行结束之后try...catch...就结束了
* */
public class ExceptionTest04 {
public static void main(String[] args) {
// 情况①
// 以下代码编译无法通过,因为FileNotFoundException没有处理。
/*
try{
// 下面语句是FileNotFoundException异常
FileInputStream fis = new FileInputStream("c:/ab.txt");
}catch(ArithmeticException e){ //捕获的代码是算术异常
}
*/
// 情况②
// 以下程序编译通过
/*
try{
// 下面语句是FileNotFoundException异常
FileInputStream fis = new FileInputStream("c:/ab.txt");
}catch(FileNotFoundException e){
}
*/
// 情况③
// 以下程序编译无法通过
// 因为还有更多的IOException没有处理
/*
try{
// 下面语句是FileNotFoundException异常
FileInputStream fis = new FileInputStream("c:/ab.txt");
fis.read(); // 因为它抛出的是throws IOException异常
}catch(FileNotFoundException e){
}
*/
// 情况④
// 以下编译可以通过
/*
try{
// 下面语句是FileNotFoundException异常
FileInputStream fis = new FileInputStream("c:/ab.txt");
fis.read(); // 因为它抛出的是throws IOException异常
}catch(FileNotFoundException e){
}catch(IOException e){
}
*/
// 情况⑤
/*
// 这个也是编译通过的,因为IOException包含FileNotFoundException
try{
// 下面语句是FileNotFoundException异常
FileInputStream fis = new FileInputStream("c:/ab.txt");
fis.read(); // 因为它抛出的是throws IOException异常
}catch(IOException e){
}
*/
// 情况⑥
// 这个也会报错,因为IOException包含FileNotFoundException,程序自上而下
// 第二个catch永远不会执行到,因此会报错。
// 【注】catch可以写多个,但是必须从上到下,从小到大捕捉!!!
/*
try{
// 下面语句是FileNotFoundException异常
FileInputStream fis = new FileInputStream("c:/ab.txt");
fis.read(); // 因为它抛出的是throws IOException异常
}catch(IOException e){
}catch(FileNotFoundException e){
}
*/
}
}
2.4 一个小例子
package com.company.exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ExceptionTest05 {
// 编译无法通过
// IOException没有处理
/*
public static void main(String[] args) throws FileNotFoundException{
FileInputStream fis = new FileInputStream("c:/ab.txt");
fis.read();
}
*/
// 通过
/*
public static void main(String args[]) throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("c:/ab.txt");
fis.read();
}
*/
public static void main(String args[]){
// 程序执行到此处发生了FileNotFoundException类型的异常
// JVM会自动创建一个FileNotFoundException类型的对象,该对象的内存地址赋值给catch语句中的e变量
try{
FileInputStream fis = new FileInputStream("c:/ab.txt");
//上面的代码出现了异常,try语句块的代码不再继续执行,直接进入catch语句块中执行。
System.out.println("TTTTTTTTTTT"); // 这句话并没有执行
fis.read();
}catch(FileNotFoundException e){ // 内存地址指向堆中的那个对象是“FileNotFoundException类型的”事件
System.out.println("读取的文件不存在");
// 说明FileNotFoundExceptiom将Object中toString方法重写了
System.out.println(e); //java.io.FileNotFoundException: c:\ab.txt (系统找不到指定的文件。)
}catch(IOException e){
System.out.println("其他IO异常");
}
// 下面代码会执行
// catch里面程序语句有执行,就默认异常有处理,下面的就可以执行了
System.out.println("hello world!");
}
}
3、关于getMessage和printStackTrace方法的应用
package com.company.exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/*
* 关于getMessage和printStackTrace方法的应用
* */
public class ExceptionTest06 {
public static void main(String[] args) {
try{
FileInputStream fis = new FileInputStream("c:/abc.txt");
// JVM为我们执行了一下这段代码,这是我们看不到的
// FileNotFoundException e = new FileNotFoundException("c:/abc.txt");
// 然后输出异常是调用了e.getMessage();方法
}catch(FileNotFoundException e){
// 打印异常堆栈信息
// 一般情况下都会使用该方式去调试程序
e.printStackTrace();
/*
java.io.FileNotFoundException: c:\abc.txt (系统找不到指定的文件。)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
at com.company.exception.ExceptionTest06.main(ExceptionTest06.java:12)
*/
// 上面的方式会常用一点,因为信息比较详细
String msg = e.getMessage();
System.out.println(msg); //c:\abc.txt (系统找不到指定的文件。)
}
// 这段代码会执行
System.out.println("ABC...");
}
}
4、关于finally语句块
package com.company.exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/*
* 关于finally语句块
* 1、finally语句块可以直接和try语句块联用。 try....finally...
* 2、try....catch....finally 也可以
* 3、在finally语句中的代码块是一定会执行的
*
* */
public class ExceptionTest07 {
public static void main(String[] args) throws FileNotFoundException {
/*
try{
System.out.println("ABC");
return ;
}finally{
System.out.println("Test");
}
*/
/*
try{
FileInputStream fis = new FileInputStream("c:/ab.txt");
// 下一句代码不会执行
System.out.println("TTTTT");
}finally{
// 异常上抛没有解决,IOException没有处理,仍然会报错
// 即使这样下面的语句还是执行了
System.out.println("AAAAAAA");
}
*/
// 只有在执行finally语句块之前退出JVM,则finally语句块不会执行
try{
// 退出JVM
System.exit(0);
}finally{
// 没有执行
System.out.println("BBBBB");
}
}
}
深入finally语句块的一个小例子
package com.company.exception;
/*
* 深入finally语句块
* */
public class ExceptionTest08 {
public static void main(String[] args) {
int i = m1();
System.out.println(i); // 10
}
public static int m1(){
int i = 10;
try{
return i;
}finally{
i++;
System.out.println("m1()-->" + i); // m1()-->11
}
// 以上代码的执行原理
/*
int i = 10;
try{
int temp = i;
return temp;
}finally{
i++;
System.out.println("m1()-->" + i);
}
*/
}
}
finally语句块的应用
package com.company.exception;
import java.io.*;
/*
* finally语句是一定会执行的,所以通常在程序中
* 为了保证某资源一定会释放,所以一般在finally语句块中释放资源
* */
public class ExceptionTest09 {
public static void main(String[] args) {
//声明一个文件流类型,由于大括号作用域的原因{}
FileInputStream fis = null;
try{
fis = new FileInputStream("ExceptionTest08.java");
}catch(FileNotFoundException e){
e.printStackTrace();
}finally{
// 为了保证资源一定得到释放
if(fis == null){
try{
fis.close(); // 文件流关闭也会引起IOException
}catch(IOException e){
e.printStackTrace();
}
}
}
}
}
5、自定义异常&手动抛出异常
自定义异常&手动抛出异常的一个例子
package com.company.exception;
/*
* 如何手动抛出异常?如何自定义异常?
*
* 自定义有两种方向:
* 1、编译时异常,直接继承Exception
* 2、运行时异常,直接继承RuntimeException
*
* 例子:自定义”无效名字的异常“(名字长度不能小于6),然后动手抛出异常信息。
* */
// 自定义异常
public class IllegalNameException extends Exception{ //编译时异常
//public class IllegalName Exception extends RuntimeException{ //运行时异常
// 定义异常一般提供两个构造方法
public IllegalNameException(){}
public IllegalNameException(String msg){
super(msg);
}
}
package com.company.exception;
// 顾客提供相关的业务
public class CustomerService {
// 对外提供一个注册的方法
public void register(String name) throws IllegalNameException{
// 检查名字长度是否小于6位
if(name.length() < 6){
// 非法名字异常
// 创建异常对象
//IllegalNameException e = new IllegalNameException("用户名长度不能小于6位");
// 手动抛出异常
//throw e;
// 或者一步搞定
throw new IllegalNameException("用户名长度不能小于6位");
/*
【注意】:
这里有一个重点需要注意:手动抛出的异常在register方法中编译会报错,需要做处理,
而此处异常处理应该采用 throws 还是 try...catch...?
try...catch...:是捕获异常,然后对异常进行处理
我们手动抛出的异常,如果在此处又我们手动捕获给处理掉,完全就没有意义了。
所以应该采用 throws 向上抛,然后再在顾客调用register方法的时候对其进行异常检测捕获。
*/
}
System.out.println("注册完成");
}
}
package com.company.exception;
/*
* 模拟注册
* */
public class Test {
public static void main(String[] args) {
// 提供用户名
String username = "jack";
// 注册
CustomerService c = new CustomerService();
// 如果有异常,就获取,然后进行相关操作
try{
c.register(username);
}catch(IllegalNameException e){
System.out.println(e.getMessage());
}
// 如果没有异常,就执行完register()函数,输出“注册成功”
}
}
6、在方法重写时说到的一点:重写的方法不能比被重写的方法抛出更宽泛异常。具体指的是什么?
package com.company.exception;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
* 重写的方法不能比被重写的方法抛出更宽泛异常
* */
public class ExceptionTest10 {
}
class A{
public void m1(){}
}
class B extends A{
// 下面这句会报错,因为重写的方法不能比被重写的方法抛出更多的异常
// 父类中m1()方法没有抛出异常,而子类中m1()方法却抛出异常
// public void m1() throws Exception{}
}
class C{
public void m2() throws FileNotFoundException {}
}
class D extends C{
// 下面这句会报错,因为FileNotFoundException属于IOException
// 重写的方法不能比被重写的方法抛出更高等级的异常
//public void m2() throws IOException{}
}
class E{
public void m2() throws IOException{}
}
class F extends E{
// 这个编译就没有任何问题
public void m2() throws FileNotFoundException {}
}