曾有人说过:“一个程序项目内30%的代码就能实现功能,剩下70%的代码都在检查异常、增加约束……”,为何会如此呢?
针对这些影响程序正常运行的意外,我们称之为异常(Exception)
;
package cn.rowyet.J0004Exception;
public class C0001FirstExcept {
private String str;
public static void main(String[] args) throws Exception
{
int a=0;
int b=1;
System.out.println(b/a);
}
}
以上代码就是最好理解的一个异常,俺上小学老师就教导我们,除数不能为0,不然除不尽,因此在Java代码中,如果出现除数为0的代码,就会出现图2.1的异常,那么异常该怎么解决呢?且听后文分晓。
如图3.1,本着面向对象的思想,Java对异常进行了分类,所有的异常根类为java.lang.Throwable
,java.lang.Throwable
派生出两个子类Error
和Exception
,而Exception
又派生出CheckedException
和RuntimeException
,是不是只有这些异常呢?当然不是,根据继承的特性,原则上后续还有无限的子子孙孙异常类,这里是列举不完,博主就展开讲讲这几个最基本的,后续的一场子类你们也就能举一反三了。
Error(错误)
:是指程序无法处理的严重问题,可能写Java遇到最多的一个问题就是OutOfMemoryError
,即JVM虚拟机所需的内存不足时报错,一般会导致JVM无法继续工作而终止,解决方法就是JVM可用内存调小点,或者加大JVM的所需内存,图3.2是Error
的API;
注意:Error一般和你的程序关系不大,属于JVM内的错误,但是也不是完全没关系哈,程序写的过烂,也可能导致资源滥用而出现Error;
Exception(异常)
是程序员能够处理的意外,Exception
类是所有异常类的父类,派生出来的RuntimeException(运行时异常)
和CheckedException(已检查异常,也叫编译时异常)
,也就是说通常Java的异常有分为运行时异常
和编译型异常
;
RuntimeException(运行时异常)
,顾名思义就是派生于RuntimeException
类的异常类型,如空指针异常(NullPointerException)、数组下标越界异常(ArrayIndexOutOfBoundsException)、类型转换异常(ClassCastException)、算术异常(ArithmeticException)
等。如果显式的声明或捕获将会对程序可读性和运行效率影响很大。 因此由系统自动检测并将它们交给缺省的异常处理程序(用户可不必对其处理)。
这类异常通常是由编程错误导致的,所以在编写程序时,并不要求必须使用异常处理机制来处理这类异常,经常需要通过增加“逻辑处理来避免这些异常”,接下来代码实战一番;
ArithmeticException(运算条件异常)
代码举例:
public class C0001FirstExcept {
public static void main(String[] args) throws Exception {
int a=0;
int b=1;
System.out.println(b/a); //运算条件异常,除数为0触发异常
}
}
解决方法:
public class C0001FirstExcept {
public static void main(String[] args) throws Exception {
int a=0;
int b=1;
if (a!=0) { //加逻辑条件,避免异常
System.out.println(b/a);
}
}
}
NullPointerException(空指针异常)
public class C0001FirstExcept {
public static void main(String[] args) throws Exception {
String str=null;
System.out.println(str.charAt(2)); //空指针
}
}
解决方法:
public class C0001FirstExcept {
public static void main(String[] args) throws Exception {
String str=null;
if (str!=null) { //加逻辑判断
System.out.println(str.charAt(2));
}
}
}
ClassCastException (类型转换异常)
public class C0001FirstExcept {
public static void main(String[] args) throws Exception {
Animals dog=new Dog();
Cat cat=(Cat) dog; //类型转换
}
}
class Animals
{
}
class Dog extends Animals
{
}
class Cat extends Animals
{
}
解决方法:
public class C0001FirstExcept {
public static void main(String[] args) throws Exception {
Animals dog=new Dog();
Cat cat=(Cat) dog; //类型转换
if (dog instanceof Cat) { //加逻辑判别
Cat cat=(Cat) dog;
}
}
}
class Animals
{
}
class Dog extends Animals
{
}
class Cat extends Animals
{
}
ArrayIndexOutOfBoundsException(数组跨界异常)
代码举例:
public class C0001FirstExcept {
public static void main(String[] args) throws Exception {
int[] intA=new int[5];
System.out.println(intA[5]);
}
}
解决方法:
public class C0001FirstExcept {
private String str;
public static void main(String[] args) throws Exception {
int[] intA=new int[5];
a=5;
if (a<intA.length) {
System.out.println(intA[a]);
}
}
}
NumberFormatException(数据格式异常)
public class C0001FirstExcept {
public static void main(String[] args) throws Exception {
String str2="123456hij";
System.out.println(Integer.parseInt(str2));
}
}
解决方法:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class C0001FirstExcept {
public static void main(String[] args) throws Exception {
String str2="123456hij";
Pattern p=Pattern.compile("^\\d+$"); //正则表达式,以数字开头和结尾
Matcher m=p.matcher(str2); //字符串满足正则才可以
if (m.matches()) {
System.out.println(Integer.parseInt(str2));
}
}
}
那些不属于RuntimeException的异常,统称为Checked Exception(编译型异常)
,如IOException、SQLException
等以及用户自定义的Exception异常。 这类异常在编译时就必须做出处理,否则无法通过编译,即如图3.8会出现红线提示异常;
一般怎么解决呢,就要用到接下的第四章的异常的处理;
package cn.rowyet.J0004Exception;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class C0003TryCatch {
public static void main(String[] args) {
FileReader reader=null;
try {
reader=new FileReader("src/test.txt"); //第一个异常文件可能不存在,对应第一个catch
char read = (char)reader.read(); // 第二个异常,字符可能为空
char read2 = (char)reader.read();
System.out.println(read+read2);
} catch (FileNotFoundException e) { //第一个catch的异常类型要小于等于后续的catch异常
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally { //finally一定会执行
try {
if(reader!=null)
{
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
以一个读文件的例子来列举下try-catch-finally
的用法要点:
try
语句指定了一段代码,该段代码就是异常捕获并处理的范围。在执行过程中,当任意一条语句产生异常时,就会跳过该条语句中后面的代码,所以不要每句都去try
一遍;try
不能单独存在,必须跟着catch
或者finally
,注意是或哈
,catch
可以有多个,但是finally
只能有一个;catch
捕捉出来的异常类,如果这些类型不同且存在继承关系,那么越是父级的类越要往下放,否则会报错,原理也很简单,因为如果你catch
出来的最大父类放在了第一个,那么后续的catch
出来的子类根本发挥不了作用;finally
大多数情况下都是会执行的,不管发没发生异常,除非在finally之前遇到System.exit(0)
或程序被断电、网络等因素终止了;当CheckedException
产生时,不一定立刻处理它,可以再把异常throws
到上级,一直可以抛到JRE
,有点类似甩锅,终究是JRE
抗下了一切;
方法重写中声明异常原则:子类重写父类方法时,如果父类方法有声明异常,那么子类声明的异常范围不能超过父类声明的范围。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class C0002CheckedException {
public static void main(String[] args) throws FileNotFoundException {
FileInputStream fileInputStream=new FileInputStream("");
}
}
自定义异常的要点:
在程序中,可能会遇到JDK提供的任何标准异常类都无法充分描述清楚我们想要表达的问题,或者想要加一些特殊的异常描述,比如中文描述,这种情况下可以创建自己的异常类,即自定义异常类,在开源项目中一般会定义一套服务于该项目的自定义异常;
自定义异常类一般只需从Exception类或者它的子类派生一个子类即可。但是如果自定义异常类如果继承Exception类,则为受检查异常,必须对其进行处理;如果不想处理,可以让自定义异常类继承运行时异常RuntimeException类。
习惯上,自定义异常类应该包含2个构造器:一个是默认的构造器,另一个是带有详细信息的构造器。
不需要定义新类,也可以直接throw
一个异常包含自定义的描述,只是写项目这样显得很乱不规范,少用;
案例代码:
package cn.rowyet.J0004Exception;
public class C0005SelfException {
public static void main(String[] args) throws HeightExcept {
Person person = new Person();
person.setName("RowYet");
person.setHeight(500.0); //传一个超过3米的人引发异常
}
}
class HeightExcept extends Exception
{
public HeightExcept(){};
public HeightExcept(String message){
super(message);
}
}
class Person
{
private String name;
private double height;
public String getName() {
return name;
}
public double getHeight() {
return height;
}
public void setName(String name) {
this.name = name;
}
public void setHeight(double height) throws HeightExcept {
if(height>300.0) //判断身高,超过3米则异常
{
throw new HeightExcept("人的身高不能超过3米");
}
this.height = height;
}
}
当然也可以直接抛出某一个异常类但是自定义抛出的异常内容,但是你抛出的什么类型就要遵循该类型的使用规范,代码如下;
public class C0005SelfException {
public static void main(String[] args) {
int b=0;
if(b!=0)
{
System.out.println(1/b);
}
else
{
throw new ArithmeticException("除数不能为0");
}
}
}
实际开发中,异常是家常便饭,而且只有解决的异常越多,你才举例大神越来越近,那么遇到异常该怎么处理呢?
自己动手丰衣足食,第一步是先尝试自己动手,定位异常的位置,查看异常的原因,看看能否自己解决;
百度、必应、谷歌,三大神器,基本上常见的异常都有前人遇见过,针对自己处理不了的异常,可以将异常的关键字去百度、必应、谷歌查找相关网页寻找思路。
以上两步都解决不了,可以尝试求助同行的大神,寻求帮助;
切忌一看到异常,又是英文就慌了神,我不会,看不懂,逃避就去问别人,这样不但是对自己不负责,也是不尊重他人的成果。