Java使用异常来表示错误,并通过try ... catch
捕获异常;
Java的异常是一种类,并且从Throwable
继承;
Error
是无需捕获的严重错误,Exception
是应该捕获的可处理的错误;
RuntimeException
无需强制捕获,非RuntimeException
需强制捕获,或者用throws
声明;
- 编译时异常:没有继承RuntimeException的异常,直接继承于
Exception
,编译阶段就会错误提示。 - 运行时异常:RuntimeException本身和子类。编译阶段没有错误提示,运行时才会出现错误。
不推荐捕获了异常但不进行任何处理。
1.常用异常:
Exception
├─ RuntimeException
│ ├─ NullPointerException
│ ├─ IndexOutOfBoundsException
│ ├─ SecurityException
│ └─ IllegalArgumentException
│ └─ NumberFormatException
├─ IOException
│ ├─ UnsupportedCharsetException
│ ├─ FileNotFoundException
│ └─ SocketException
├─ ParseException
├─ GeneralSecurityException
├─ SQLException
└─ TimeoutException
2.捕获异常:
作用:当代码出现异常时,程序会继续向下执行。如果没有捕获,JVM就会因为异常而终止程序。
捕获异常时,多个catch
语句的匹配顺序非常重要,子类必须放在前面;
finally
语句保证了有无异常都会执行,它是可选的;
一个catch
语句也可以匹配多个非继承关系的异常。
3.NullPointerException:
这是最常见的异常。空指针异常,俗称NPE。如果一个对象为null
,调用其方法或访问其字段就会产生NullPointerException
,这个异常通常是由JVM抛出的。
4.抛出异常:
throws:
写在方法定义处,表示声明一个异常,告诉调用者,使用本方法可能会有哪些异常。
- 编译时异常:必须要写
- 运行时异常:可以不写
public void Fname()throws 异常名1,异常名2...{
....
}
throw:
写在方法内,手动抛出异常。
当某个方法抛出了异常时,如果当前方法没有捕获异常,异常就会被抛到上层调用方法,直到遇到某个try ... catch
被捕获为止:
示例1:
public class Main {
public static void main(String[] args) {
try {
process1();
} catch (Exception e1) {
e.printStackTrace();
}
}
static void process1() {
try {
process2();
} catch (NullPointerException e2) {
throw new IllegalArgumentException();
}
}
static void process2() {
throw new NullPointerException();
}
输出:
java.lang.IllegalArgumentException
at ExceptionTest.Test1.process1(Test1.java:16)
at ExceptionTest.Test1.main(Test1.java:6)转换异常:
可以看到:process2()中的NullPointerException异常被抛出到上层process1()中,process1()中捕获到2中异常,产生的异常IllegalArgumentException被抛出到上层main中,被捕获。相当于转换异常;
最原始的异常信息被丢失了,而这并不是我们想要的结果。
要得到完整的异常信息,可以将process1()中catch到的e2再传入到p1中构造的异常:IllegalArgumentException()中。
如:
public static void process1(){
try {
process2();
}catch (NullPointerException e2){
throw new IllegalArgumentException(e2);//将p1()中catch到的e2再传入到p1中构造的异常
}
}
得到:
java.lang.IllegalArgumentException: java.lang.NullPointerException
at ExceptionTest.Test1.process1(Test1.java:16)
at ExceptionTest.Test1.main(Test1.java:6)
Caused by: java.lang.NullPointerException
at ExceptionTest.Test1.process2(Test1.java:21)
at ExceptionTest.Test1.process1(Test1.java:14)
… 1 more
有了完整的异常栈的信息,我们才能快速定位并修复代码的问题。
5.案例:
输入一个Student类对象的信息,要求:姓名在2-4个字符;年龄在16-22岁之间。
分析:对于一次输出,我们可以进行if判断来完成。但如果在其他地方也要使用这个Student类进行对象的创建呢,就得每次都写判断。我们可以直接将判断条件写在set方法,若不符合条件,则抛出异常,如下:
Student.java
package ExceptionTest;
public class Student {
private String name;
private int age;
...
public void setName(String name) {
if (name.length()>=5||name.length()<=1)
throw new RuntimeException();
this.name = name;
}
...
public void setAge(int age) {
if(age<16||age>22)
throw new RuntimeException();
this.age = age;
}
...
}
test:
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
Student student =new Student();
while (true) {
try {
System.out.println("请输入姓名");
String name=scanner.nextLine();
student.setName(name);
System.out.println("请输入年龄");
String strage=scanner.nextLine();
int age =Integer.parseInt(strage);
student.setAge(age);
break;
} catch (NumberFormatException e) {
//e.printStackTrace();
System.out.println("年龄输入有误或不合法");
} catch (RuntimeException e){
//e.printStackTrace();
System.out.println("姓名输入有误或不合法");
}
}
System.out.println(student);
}