很多问题不能靠代码能规避的:
- 客户输入数据格式
- 读取文件是否存在
- 网络是否保持通畅
Java程序异常事件分为两类:
- Error:如JVM系统内部错误、资源耗尽
如栈溢出 StackOverflowError、堆溢出 OutOfMemoryError
等。一般不编写针对性代码进行处理。 - Exception:因编程错误或偶然的外在因素导致的,可以使用针对性代码进行处理。例如
-
- 运行时异常
-
- 编译时异常
一、常见的异常
1.空指针异常 NullPointerException
int[] arr = null;
System.out.println(arr[3]);
String str = null;
System.out.println(str.charAt(0));
2.数组角标越界 ArrayIndexOutOfBoundsException
int[] arr = new int[10];
System.out.println(arr[10]);
类似的还有 StringIndexOutOfBoundsException。
3.类型转换异常 ClassCastException
Object obj = new Date();
String str = (String)obj;
4. 数字格式异常 NumberFormatException
String str = "a123";
int num = Integer.parseInt(str);
5.输入不匹配异常 InputMismatchException
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
//输入数据不是int类型会抛出异常
6. 算数异常 ArithmeticException
System.out.println(2/0);
二、异常处理机制
- 过程一:“抛”。程序一旦出现异常,就会在异常代码处形成一个对应异常类的对象,并将此对象抛出。之后的代码不再执行。
- 过程二:“抓”。对异常的处理,有以下两种方式。①try catch finally ②throws + 异常类型。
1. try-catch-finally
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}
...
finally{
//一定会执行的代码
}
- finally 是可选的。
- 一旦try 中的异常匹配到某一个 catch 时,就会进入异常处理。处理完成就跳出 try-catch 结构(没有finally的情况),继续执行后续代码。
- catch 中的异常类型,如果没有子父类关系,先后顺序无所谓。如果满足子父类关系,那么子类必须在父类上面。
- 常用的处理异常的方式:①
String getMessage()
②printStackTrace()
- try 结构中声明的变量,结构外不能调用。
- finally 中的语句一定会被执行,即使①catch中又出现异常②try中有 return ③catch中有 return。
- 像数据库连接、输入输出流、网络编程sockett等资源,JVM不能自动回收,需要手动进行资源释放。此时的资源释放语句需要声明在finally中。
- 实现结构的快捷方法:选中语句,右击—>Surround With —> Try/catch Block
结论
- 使用try-catch-finally结构处理编译时异常,程序在运行时仍然可能报错。
- 通常不针对运行时异常进行try-catch-finally处理。(写完代码没报错的情况一般就不考虑try-catch-finally了)
2. throws + 异常类型
-
throws + 异常类型 写在方法的声明中。
方法名() throws 异常类型{ }
-
异常代码后续的代码不会被执行。
-
方法1使用“throws + 异常类型”结构,那么异常会被扔给调用该方法1的方法去处理。
-
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常
例如以下代码:
- test()方法使用“throws + 异常类型”结构
- test2()方法调用了test(),并且也使用“throws + 异常类型”结构
- main()方法调用test2(),使用“try-catch”结构解决异常。
public static void main(String[] args) {
try{
test2();
}catch(IOException e){
e.printStackTrace();
}
}
@Test
public static void test() throws FileNotFoundException,IOException{
File file = new File("hello.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data !=-1){
System.out.println((char)data);
data = fis.read();
}
fis.close();
}
public static void test2() throws IOException{
test();
}
结论
- throws + 异常类型 结构只是将异常抛给方法的调用者,并没有真正的处理异常。
- try-catch-finally 是真正解决异常的。
3.使用哪一种?
- 如果父类被重写的方法没有throws方式处理异常,子类重写的方法就不能用throws,应用try-catch-finally。
- 多个方法递进调用,建议这几个方法使用throws 方式进行处理,最外层执行的方法使用try-catch-finally 解决异常。
三、手动抛出异常 throw
手动生成一个异常对象,并抛出。格式:throw new 异常类型("异常的说明");
异常类型一般可以选择RuntimeException
例如:定义了类CircleCompare,内有比较两个圆的半径的方法compare()。当比较两个对象时,需要考虑是否是CircleCompare类的对象,因此compare()方法抛出异常:
class CircleCompare{
public void compare(Object obj){
if(obj instanceof CircleCompare){
//比较半径
}else{
throw new RuntimeException("类型不合法");
}
}
}
main方法中使用try-catch 解决异常:
public static void main(String[] args) {
CircleCompare c1 = new CircleCompare();
try{
c1.compare(new Object());
}catch (Exception e) {
System.out.println(e.getMessage());
}
}
四、用户自定义异常类
- 首先继承现有异常类(RuntimeException、Exception):
class 自定义异常 extends 现有异常类 { }
- 提供全局常量:serialVersionUID
- 构造器
public 自定义异常名() { }
、public 自定义异常名(String msg) { super(msg); }