Java异常

六. 异常

  • 程序执行中出现异常,JVM会把异常信息打印到控制台,以便修改,增强程序的健壮性

  • 异常在java 中以类的形式存在,每一个异常类都可以创建对象

NumberFormatException nfe = new NumberFormatException("数字格式化异常");
System.out.println(nfe);	//输出java.lang.NumberFormatException: 数字格式化异常

NullPointerException npe = new NullPointerException("空指针异常");
System.out.println(npe);	//输出java.lang.NullPointerException: 空指针异常

int a = 10;
int b = 0;
//程序执行到此处时会创建异常对象:new ArithmeticException("/ by zero");
int c = a / b;
System.out.println(c);		//输出异常java.lang.ArithmeticException: / by zero
  • UML:是一种统一建模语言,图标式语言(画图)

    在UML图中描述类和类之间的关系、程序执行流程、对象状态等

  • Error、Exception都是继承Throwable类,Throwable继承Object。Exception的直接子类和RuntimeException继承Exception类

    Throwable:错误和异常都是可抛出的

    Error:发生错误只能终止程序运行,退出JVM,错误无法处理

    Exception:可处理的

    所有Exception的直接子类都叫做编译时异常(编译时异常表示必须在编写程序时预先对这种异常进行处理,否则编译器报错),又被称为受检异常(CheckedException)、受控异常

    RuntimeException:所有RuntimeException及其子类都属于运行时异常(运行时异常在编写程序时可以处理也可以不处理),又被称为未受检异常(UnCheckedException)、非受控异常

  • 编译时异常和运行时异常都发生在运行阶段,编译阶段不会发生异常

    编译时异常一般发生概率较高,对于一些发生概率较高的异常,需要在运行前进行预处理

    运行时异常一般发生概率较低

  • Java语言中对异常处理的方式:

    1. 在方法声明的位置上使用throws关键字,抛给上一级(谁调用我就抛给谁),异常发生后如果选择上抛,上一级对这个异常也有两种处理方式
    2. 使用try…catch语句进行异常的捕捉
  • Java中异常发生后如果一直上抛,最终抛给main方法,main方法抛给调用者JVM,最终只能终止java程序的执行

/*
程序执行到此处发生ArithmeticException异常,底层创建异常对象,抛给调用者main方法。main方法没有处理,将这个异常自动抛给JVM,JVM终止程序的运行
ArithmeticException继承RuntimeException,属于运行时异常
*/
public static void main(String[] args) {
	System.out.println(10/0);
	System.out.println("hello world");		//这行代码没有执行
}
  • 方法声明位置上使用了throws ClassNotFoundException,是编译时异常,需要在编写代码时处理,否则报错
public static void main(String[] args) {
//报错信息:未报告的异常错误java.lang.ClassNotFoundException; 必须对其进行捕获或声明以便抛出
	doSome();	//调用doSome()方法时必须对ClassNotFoundException异常进行预处理
}
public static void doSome() throws ClassNotFoundException{
	System.out.println("doSome");
}

//解决方法:
//1. 继续使用throws完成异常的继续上抛(上抛类似于推卸责任)
public static void main(String[] args) throws ClassNotFoundException{
	doSome();
}
//2. 使用try...catch进行捕捉(捕捉等于把异常拦下,异常解决,调用者不知道异常发生)
public static void main(String[] args) {
	try {
		doSome();
	}catch (ClassNotFoundException e){		//e是引用
		e.printStackTrace();
	}
}
  1. 在方法声明的位置上使用throws关键字抛出,抛给上一级/调用者
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ExceptionTest03 {
//不建议在main方法上使用throws,抛给JVM后只能终止程序,建议使用try...catch捕捉
    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("main begin");
        m1();
        System.out.println("main end");
    }
/*
继续上抛,只能抛调用方法的异常或者其父类,多个异常可以用逗号分开
比如m1()方法可以throws FileNotFoundException, ClassNotFoundException
也可以throws IOExpection,或者throws Exception
*/
    private static void m1() throws FileNotFoundException {
        System.out.println("m1 begin");
        m2();
        System.out.println("m1 end");
    }

//调用FileInputStream()方法,创建输入流对象,该流指向一个文件
    private static void m2() throws FileNotFoundException {
    //该方法声明时抛出FileNotFoundException,该异常父类是IOException
    //IOException父类是Exception,所以是编译时异常
        new FileInputStream("C:\\Users\\10434\\Desktop\\成绩单.JPG");
    }
}
  • 程序执行顺序分析
public static void main(String[] args) {
	System.out.println("main begin");		//1
	try {
		m1();
		//m1()方法出现异常,结束try语句块执行catch语句块
		System.out.println("hello");		//这里不执行
	}catch (FileNotFoundException e){
		System.out.println("文件不存在");		//3
	}
	System.out.println("main end");		//4
}

public static void m1() throws FileNotFoundException {
	System.out.println("m1 begin");		//2
	m2();
	System.out.println("m1 end");
}

private static void m2() throws FileNotFoundException {
	new FileInputStream("C:\\Users\\1043\\Desktop\\成绩单.JPG");	//错误路径
	System.out.println("hello");	//方法体中代码出现异常,若采用上报方式,该方法结束
}
  1. try…catch语法深入
    2.1. catch后的异常类型可以是具体异常类型,也可以是其父类型
try {
	new FileInputStream("C:\\Users\\10434\\Desktop\\成绩单.JPG");
} catch (IOException e){	//多态:IOException e = new FileNotFoundException();
	System.out.println("文件不存在");
}

2.2. catch可以写多个,建议catch时针对具体异常一个一个处理

try {
	FileInputStream fis = new FileInputStream("C:\\Users\\10434\\Desktop\\成绩单.JPG");
	fis.read();
} catch (FileNotFoundException e) {
	System.out.println("文件不存在");
} catch (IOException e) {
	System.out.println("读文件报错");
}

2.3. 有多个catch时,从上到下必须遵守从小到大(父类在上子类在下会报错)

try {
	FileInputStream fis = new FileInputStream("C:\\Users\\10434\\Desktop\\成绩单.JPG");
	fis.read();
} catch (Exception e) {
	System.out.println("文件不存在");
} catch (IOException e) {		//编译报错,IOException是Exception的子类
	System.out.println("读文件报错");
}
try {
	FileInputStream fis = new FileInputStream("C:\\Users\\10434\\Desktop\\成绩单.JPG");
	fis.read();
	System.out.println(10 / 0);
}	//JDK8新特性,允许多种异常使用"|"连接
	catch (FileNotFoundException | ArithmeticException e) {
	System.out.println("文件不存在或算数异常");
}
  • 异常对象的getMessage()方法:获取简单的异常描述信息
NullPointerException e = new NullPointerException("空指针异常");
String msg = e.getMessage();		//msg就是上面构造方法的String参数
System.out.println(msg);		//输出“空指针异常”
  • 异常对象的printStackTrace()方法:打印异常追踪的堆栈信息

    Java后台打印异常堆栈信息时,采用异步线程方式(输出先后顺序可能有变化)

public static void main(String[] args) {
	try {
		m1();
	} catch (FileNotFoundException e) {
		e.printStackTrace();
/*
查看方法:从上往下(SUN的代码不会有错,只看自己的)
java.io.FileNotFoundException: C:\Users\1043\Desktop\成绩单.JPG (系统找不到指定的路径。)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at java.io.FileInputStream.<init>(FileInputStream.java:93)
	at exception.ExceptionTest06.m2(ExceptionTest06.java:21)
	at exception.ExceptionTest06.m1(ExceptionTest06.java:17)
	at exception.ExceptionTest06.main(ExceptionTest06.java:10)
21行错误 --> (导致)17行错误 --> (导致)10行错误,21行是错误的根源
*/
	}System.out.println("hello");		//这行代码依然可以执行,程序健壮
}

private static void m1() throws FileNotFoundException {
	m2();
}

private static void m2() throws FileNotFoundException {
	new FileInputStream("C:\\Users\\1043\\Desktop\\成绩单.JPG");
}
  • try…catch中的finally子句

    1. 在finally子句中的代码一定会执行,即使try语句块中的代码出现异常;且是最后执行的。finally子句必须和try一起出现,不能单独使用
    2. 通常在finally语句块中完成资源的释放/关闭,比较有保障
public static void main(String[] args) {
	FileInputStream fis = null;
	try {
		fis = new FileInputStream("C:\\Users\\10434\\Desktop\\成绩单.JPG");
		String s = null;
		s.toString();
		System.out.println("real");		//发生异常,这里不执行
		//fis.close();	流占用资源,使用完要关闭。放在这里如果出现异常就无法关闭
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} catch (NullPointerException e){
		e.printStackTrace();
	} finally {
		System.out.println("heisenberg");		//输出顺序:1
		if (fis != null) {		//避免空指针异常
			try {
				fis.close();
			} catch (IOException e) {		//close()方法抛出IOException
				e.printStackTrace();
			}
		}
	}System.out.println("hello");		//2
}
  • try和finally可以连用(可以没有catch)
//以下代码执行顺序:先执行try...再执行finally...最后执行return;
public static void main(String[] args) {
	try {
		System.out.println("try...");
		return;		//return;语句只要执行,方法必然结束
	} finally {
		System.out.println("finally...");
	}
	System.out.println("hello");		//编译报错:无法访问的语句
}
  • 退出JVM会使finally语句不执行
public static void main(String[] args) {
	try {
		System.out.println("try...");		//只输出try...
		System.exit(0);		//退出JVM
	} finally {
		System.out.println("finally...");		//不执行
	}
}
  • finally与return的组合
/*
java中亘古不变的语法:
	1. 方法体中的代码必须遵守自上而下的顺序执行
	2. return语句一旦执行,整个方法必须结束
*/
public static void main(String[] args) {
	System.out.println(m());		//输出100
}
public static int m(){
	int i = 100;
	try {
		return i;
	} finally {
		i ++;
	}
}
//反编译后的代码:
public static int m(){
	int i = 100;
	int j = i;
	i ++;
	return j;
}
  • final、finally、finalize的区别

    1. final是一个关键字,表示最终的
    2. finally也是一个关键字,在异常处理机制中和try联合使用
    3. finalize()是Object类中的方法(这个方法是由GC负责调用的),所以finalize是标识符
  • 自定义异常

    1. 编写一个类继承Exception(编译时异常)或RuntimeException(运行时异常)
    2. 提供两个构造方法:一个无参,一个带有String参数
public class MyException extends Exception{		//编译时异常
    public MyException() {
    }
    public MyException(String message) {
        super(message);
    }
}

public class MyExceptionTest01 {
    public static void main(String[] args) {
        MyException me = new MyException("XX异常");
        me.printStackTrace();		//打印异常追踪堆栈信息
        String s = me.getMessage();
        System.out.println(s);		//输出“XX异常”
    }
}

public class MyException extends RuntimeException{}		//运行时异常
  • 子类重写后的方法不能比父类方法抛出更多(更宽泛)(编译时)异常,可以更少

    子类可以抛更多运行时异常,编译不报错

class Animal{
	public void doSome(){}
	public void doOther() throws Exception{}
}

class Cat extends Animal{
	public void doSome() throws FileNotFoundException{}	//编译报错
	//public void doSome() throws NullPointerException{}	运行时异常,编译通过
	public void doOther() throws FileNotFoundException{}	//编译通过
}
  • throws和throw:

    throws:在方法声明位置使用,给调用者上报异常信息

    throw:手动抛出异常,配合throws使用

public void doSome () throws FileNotFoundException {
	//不能用try..catch,没有意义
	throw new FileNotFoundException("XX异常");
}
  • 题目:模拟用户注册,用户名或密码必须在[6,14]之间
public class ExceptionTest11 {
    public static void main(String[] args) {
        UserService u = new UserService();
        System.out.print("请输入用户名:");
        Scanner s = new Scanner(System.in);
        String usn = s.next();
        System.out.print("请输入密码:");
        String pwd = s.next();
        try {
            u.register(usn, pwd);
        } catch (StringException e) {
            System.out.println(e.getMessage());
        }
    }
}

class UserService {
    public void register(String username, String password) throws StringException{
        if(username == null || username.length() < 6 || username.length() > 14){		//防止空指针异常
            //username == null最好写成null == username,防止少敲一个等号变成赋值语句
            throw new StringException("用户名长度异常!");
        }else if (password == null || password.length() < 6 || password.length() > 14){
            throw new StringException("密码长度异常!");
        }
        System.out.println("注册成功,用户名:" + username + "\n密码:" + password);
    }
}

class StringException extends Exception{		//编译时异常
    public StringException() {}
    public StringException(String message) {
        super(message);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值