一、Java 异常概念
1.概述:在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的。
-
比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等。
2.异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”,也就是在运行时出现的不正常情况,但是可以通过异常处理机制来处理的就叫做异常。
3.Java程序在执行过程中所发生的异常(运行时一切不正常情况)事件可分为两类:
①Error : Java虚拟机无法解决的严重问题。
-
例如:JVM系统内部错误、资源耗尽等严重情况。一般不编写针对性的代码进行处理。
public class Demo1 {
//对象内存不够用的情况,程序无法处理
public static void main(String[] args) {
List<String> list = new ArrayList<>();
while(true){
list.add(new String("abcdef"));
}
//Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
}
}
②Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以通过异常处理机制处理,处理之后程序可以继续向下执行。
-
例如: 访问数组下标越界、试图读取不存在的文件、网络连接中断
public class Demo2 {
//数组下表越界,通过try、catch语句处理
public static void main(String[] args) {
try {
int[] a = new int[5];
a[5]= 10;
/*若不加处理语句,则会抛出如下异常:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at com.ffyc.javaexception.Demo1.main(Demo1.java:8)
*/
}catch (Exception e){
System.out.println("程序出问题了");
}
}
}
举例几种常见的异常类型:
public class Demo2 {
public static void main(String[] args) {
//数组越界异常
int[] a = new int[5];
a[5]= 10;
/*
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at com.ffyc.javaexception.Demo2.main(Demo2.java:8)
*/
//算术异常
int c = 10;
int d = 0;
int e = c/d;
System.out.println("aaa");
/*
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.ffyc.javaexception.Demo2.main(Demo2.java:17)
*/
//空指针异常
String s = null;
s.length();
/*
Exception in thread "main" java.lang.NullPointerException
at com.ffyc.javaexception.Demo2.main(Demo2.java:26)
*/
//类转换异常
Object s1 = "abc";
Integer i = (Integer) s1;
/*
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at com.ffyc.javaexception.Demo2.main(Demo2.java:34)
*/
//数字格式化异常
new Integer("1a0");
/*
Exception in thread "main" java.lang.NumberFormatException: For input string: "1a0"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.<init>(Integer.java:867)
at com.ffyc.javaexception.Demo2.main(Demo2.java:41)
*/
}
}
Java中将程序中有可能出现的每一类问题,都包装成了一个类,当出现对应的某类的异常时,虚拟机会抛出响应的问题,对于这些异常,一般有两种解决方法:
①默认抛出异常信息。
②通过异常处理机制处理,在编码时,就需要对有可能出现问题的代码进行处理。
捕获异常最理想的是在编译期间,但有的异常只有在运行时才会发生。
比如:除数为0、数组下标越界...
分类:编译时异常和运行时异常
二、异常的体系
1.Throwable类有两个直接子类
①Error类:表示错误,可能是编译期错误或者系统错误,往往程序中并不处理。
②Exception类:表示异常,是所有异常类的父类,是程序员所关心的。
2.异常分为运行期异常和编译期异常两种
①运行期异常:程序运行时抛除的异常,所有RuntimeException的子类都是运行期异常 ,在编译期间,可以不用强制处理。
例如:数学异常、空指针异常、数组下标越界
②编译期异常(Checked Exception):没有继承RuntimeException,除去运行期的异常都是编译期异常,也称为检测异常,编译时就强制的要进行处理。
例如:IOException、SQLException...
三、异常处理
Java编程语言使用异常处理机制为程序提供了处理错误的能力。
注意:处理完毕并不代表是把坏的处理好了
例如:用户在使用计算器计算除法时,除数输入了0,此时不符合运算规则,但是我们不能改变用户的意愿,我们能做的处理就是将问题给用户提示出来,不让程序卡死,让后续程序可以继续执行。
Java的异常处理是通过5个关键字来实现的:try、catch、 finally、throw、throws
基本语法:
try{
可能会出现异常的代码
}catch(异常类型 引用名){
处理办法(处理不是把坏的处理好了)
}finally{
必须执行代码
}
try:检测不安全的代码块(发现异常)
try块中任何一条语句发生了异常,下面的代码将不会被执行,程序将跳转到异常处理代码块中,即catch块。因此,不要随意将不相关的代码放到try块中,因为随时可能会中断执行。
catch:把抓到的类型匹配的异常捕获,保证程序能继续运行下去
catch语句必须紧跟着try语句之后,称为捕获异常,也就是异常处理函数,一个try后面可以写多个catch,分别捕获不同类型的异常,要从子类往父类的顺序写,否则有编译错误 。
捕获异常的有关信息:
与其它对象一样,可以访问一个异常对象的成员变量或调用它的方法。
-
getMessage() 获取异常信息,返回字符串
-
printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。
finally:即使try里边的代码块出现了异常,该内容总是会执行的,只能有一个finally语句,所以finally中就可以写一些即使出现了异常,也要执行的代码
finally{
必须执行的逻辑
}
异常处理的方式:
①try、catch
案例:
public class Demo3 {
public static void main(String[] args) {
/*
1.什么样的代码写在try{ } ?
2.catch中异常的类型是什么?
try{
可能会出现异常的代码
}catch(异常类型 e){
处理办法(处理不是把坏的处理好了)
}
*/
try {
int a = 10;
int b = 0;
new Integer("a");
int c = a / b;
//ArithmeticException 算术异常
}catch(ArithmeticException a){
a.printStackTrace();
System.out.println("除数不能为0");
}catch (NumberFormatException n){
n.printStackTrace();
System.out.println("数字格式不正确"+n.getMessage());
}catch (Exception e){
//打印栈中出现的日志信息
e.printStackTrace();
System.out.println("系统忙,请稍后再试!");
}
System.out.println("aaa");
}
}
注意:
我们在不知道还会出现哪种类型的异常时,可以在后面加Exception,这是即使没有对应精确的异常类型捕获,也会被Exception捕获,因为它是所有异常的父类,可以用它来捕获它的子类。
②try、catch、finally
import java.io.FileNotFoundException;
public class Demo4 {
public static void main(String[] args) throws FileNotFoundException {
try {
int a = 10;
int b = 0;
int c = a / b;
new Integer("a");
}catch (NumberFormatException n){
n.printStackTrace();
System.out.println("数字格式不正确"+n.getMessage());
}finally {//无论是否出现异常,都会执行finally代码块
System.out.println("bbbbbbbbbbbbbbbb");
}
System.out.println("aaa");
}
}
③try、finally
一般在写底层方法时,异常是不需要用catch处理的,因为底层如果处理了并在catch中随便返回了一个结果,上层这个时候就无法知道底层出现了异常,还带着底层随便返回的结果继续向下执行,这样就会出问题,所以在有些场景下我们并不处理异常,我们将需要执行的代码放在finally中执行,底层方法就崩掉了,上层在调用时就知道底层方法出现了异常,也就是在出现异常时不要藏着掖着,留给上层调用时处理,即try里面出现异常直接执行finally,没有处理异常,由上层程序来处理。
import java.io.FileNotFoundException;
public class Demo5 {
public static void main(String[] args) throws FileNotFoundException {
try {
test();//由上层调用的程序来处理
}catch (Exception e){
System.out.println("除数不能为0");
}
System.out.println("aaaaaaaa");
}
public static void test(){
try {
int a = 10;
int b = 0;
int c = a / b;//没有处理异常
}finally {
System.out.println("sdfdsf");
}
}
}
四、throws 和 throw
(一) throws,定义一个方法的时候可以使用throws关键字声明,表示此方法不处理异常,而交给方法调用处进行处理。
例如:
public void test() throws 异常1,异常2,异常3{
}
注意:
①任何方法都可以使用throws关键字声明异常类型,包括抽象方法。
案例:
public abstract class Animal {
public abstract void eat() throws NullPointerException;
}
②子类重写父类中的方法,子类方法不能声明抛出比父类类型更大的异常(针对编译期异常,因为运行期异常在编译的时候不检测,所以怎么写无所谓)。
案例:
import java.io.IOException;
public abstract class Animal {
/*
方法重写要求:返回值、方法名、参数一致
访问权限等于、大于父类方法
声明异常等于、小于父类方法(针对编译期异常)
*/
abstract void eat() throws IOException;
}
public class Cat extends Animal{
@Override
protected void eat() throws NullPointerException {
}
}
③使用了throws的方法,调用时必须处理声明的异常,要么使用try-catch,要么继续使用throws声明。
案例:
import java.io.UnsupportedEncodingException;
public class Demo6 {
public static void main(String[] args) {
// "abc".getBytes("utf-8");
try {
test1();//main方式此时已经是最顶层了,就不能再抛出了,再抛就抛给虚拟机了. 此时在顶层就需要try catch处理
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
/*
继续向上抛出
*/
public static void test1() throws UnsupportedEncodingException, NumberFormatException{
test2();
System.out.println("ccccc");
}
/*
throws在方法参数列表后面声明此方法可能会出现异常,
谁调用此方法,在调用处需要根据情况对象异常是否处理
*/
public static void test2() throws UnsupportedEncodingException, NumberFormatException {
"abc".getBytes("gbk");
System.out.println("aaaaaaa");
}
}
(二)throw关键字用于显式抛出异常,抛出的时候是抛出的是一个异常类的实例化对象.
在异常处理中,try语句要捕获的是一个异常对象,那么此异常对象也可以自己抛出。
语法:
throw new 异常类构造方法
案例:getBytes()方法底层实现,如果传入的字符串为null,则抛出一个空指针异常的实例化对象
import java.io.UnsupportedEncodingException;
public class Demo7 {
public static void main(String[] args) {
try {
String s = null;
byte[] b = "abc".getBytes(s);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("aaa");
}
}
/*
Exception in thread "main" java.lang.NullPointerException
at java.lang.String.getBytes(String.java:917)
at com.ffyc.javaexception.Demo7.main(Demo7.java:11)
public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null) throw new NullPointerException();
return StringCoding.encode(charsetName, value, 0, value.length);
}
*/
如: throw new RunTimeException();
public static void someMethod() {
if (1==1) {
throw new RuntimeException("错误原因");
}
}
(三)throws和throw
① throw用于方法体中,用来抛出一个实际的异常对象,使用throw后,要么使用try、catch捕获异常,要么使用throws声明异常。
②throws用于方法声明处,用来声明该方法可能发生的异常类型,可以是多个异常类型,用来强制调用该方法时处理这些异常。
③抽象方法也可以使用throws。
五、自定义异常
自定义异常就是自己定义的异常类,也就是API中的标准异常类的直接或间接的子类
作用:用自定义异常标记业务逻辑的异常,避免与标准异常混淆自定义异常类
基本语法 :
public class 异常类名 extends Exception/RuntimeException{
public 异常类名(String msg){
super(msg);
}
}
注意:
①自定义异常类中往往不写其他方法,只重载需要使用的构造方法
②继承Exception,在方法中使用throw抛出后,必须在方法中try-catch或throws抛出
自定义异常示例:
/*
当分数不满足要求时抛出此类对象
*/
public class ScoreException extends Exception{
public ScoreException() {
}
public ScoreException(String message) {
super(message);
}
}
public class Demo7 {
/*
throw: 在方法体内,抛出一个异常的对象,抛出后,程序不继续向下执行,一般在方法中不符合业务逻辑要求时,可以主动使用throw抛出异常,告诉调用的地方法,程序出现问题。
throws: 在方法声明处,声明此方法可能会出现异常,此方法不处理异常。
*/
public static void main(String[] args) {
try {
test(10);
} catch (ScoreException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
public static char test(int score) throws ScoreException {
/*
throw:在方法体内,抛出一个异常对象,抛出后,程序不能继续向下执行,
一般在方法中不符合业务逻辑要求时,可以主动使用throw抛出异常,告诉调用的方法,程序出现问题
*/
if (score < 0 || score > 100) {
//不合法分数,没必要继续向下执行,此处可以使用throw直接在方法中抛出一个异常对象
//主动抛出一个异常对象,让方法终止
throw new ScoreException("不合法的分数");
}
if (score >= 80 || score <= 100) {
return 'A';
}
if (score >= 60 || score < 80) {
return 'B';
}
else {
return 'C';
}
}
}