异常(Exception)的概述
在项目开发中,即是程序员想要把代码尽善尽美,在系统的运行过程中仍会遇到一些问题,因为很多问题不是靠代码能避免的,比如:客户输入的数据格式,读取文件是否存在,网络是否通畅等。
Java异常事件可以分为两类:
- Error:Java虚拟机无法解决的严重问题。如系统内部错误、资源耗尽等。一般不编写针对性的代码进行处理。
- Exception:因为编写程序的错误或者外在因素导致的一般性问题,可以使用针对性的代码进行处理;例如:
- 空指针
- 试图读取不存在的文件
- 网络连接中断
- 数组角标越界
异常(Exception)分类
JVM运行过程:
- 编译时异常:执行
javac.exe
可能会出现的异常; - 运行时异常(java.lang.RuntimeException):执行
java.exe
可能出现的异常。
异常体系结构
java.lang.Throwable
|----java.lang.Error:一般不编写针对性的代码进行处理
|----java.lang.Exception:可以进行异常的处理
|----编译时异常(checked)
|----IOException
|----FileNotFoundException
|----ClassNotFoundException
|----运行时异常(unchecked,RuntimeException)
|----NullPointerException
|----IndexOutOfBoundsException
|----ArrayIndexOutOfBoundsException
|----StringIndexOutOfBoundsException
|----ClassCastException
|----NumberFormatException
|----InputMismatchException
|----ArithmeticException
常见的编译时异常
- FileNotFoundException
- IOException
@Test
public void test6() {
File file = new File("http.txt");
FileInputStream s = new FileInputStream(file);
int read = s.read();
}
常见的运行时异常
- NullPointerException
- IndexOutOfBoundsException
- ArrayIndexOutOfBoundsException
- StringIndexOutOfBoundsException
- NumberFormatException
- InputMismatchException
- ClassCastException
- ArithmeticException
// ClassCastException
@Test
public void test5() {
Object ob = new Date();
String s = (String) ob;
System.out.println(s);
}
// InputMismatchException
@Test
public void test4() {
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
System.out.println(i);
}
// NumberFormatException
@Test
public void test3() {
String a = "123a";
int i = Integer.valueOf(a);
System.out.println(i);
}
// IndexOutOfBoundsException
@Test
public void test2() {
// ArrayIndexOutOfBoundsException
String[] arr = new String[3];
System.out.println(arr[3]);
// StringIndexOutOfBoundsException
String a = "aaa";
System.out.println(a.charAt(44));
}
// NullPointerException
@Test
public void test1() {
String[] arr = null;
System.out.println(arr[2]);
}
异常(Exception)处理机制
抓抛模型
-
"抛":程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出。
关于异常对象的产生:①系统自动产生的异常对象;②手动生成异常得想,并抛出(throw) -
“抓”:可以理解为异常的处理方法
try_catch_finally
throws + 异常类型
try_catch_finally
- 格式:
try{
//可能存在异常的代码块
}catch(异常类型1 异常名1){
//处理异常类型1的解决方法
}catch(异常类型2 异常名2){
//处理异常类型2的解决方法
}catch(异常类型3 异常名3){
//处理异常类型3的解决方法
}
...
finally{
//最后一定要执行的内容
}
- finally是可选的。
- 使用try将可能出现的代码包装起来,一旦出现异常,就会生成一个对应异常类型的对象,根据此类型的对象去catch中匹配
- 一旦异常对象匹配到某一个catch中,就会进行异常的处理。处理完成后就会跳出try-catch结构(没有finally结构的情况下),继续执行其后的代码。
// NullPointerException
@Test
public void test1() {
try {
String[] arr = null;
System.out.println(arr[2]);
System.out.println("hello1");
} catch (NullPointerException e) {
// 出现异常
System.out.println("出现空指针异常");
}
System.out.println("hello2");
}
-
catch中的异常类型没有父子类关系的,谁声明在上,谁声明在下都没有关系。
-
catch中的异常类型有父子类关系的,子类声明在上,父类在下。
-
Exception中的常用方法:
String getMessage()
:打印错误内容void printStackTrace()
:打印详细的错误内容(包括错误行数等)
-
try-catch
结构可以嵌套。 -
使用
try-catch
中声明的变量,出了try
结构以后,就不能被调用。 -
finally中声明的代码一定会被执行,即使是catch中出异常,try中有return语句,catch中有return语句等情况。
-
向数据库链接、输入输出流、网络编程socket等资源,JVM是不能自动回收的,我们需要手动的进行关闭,所以资源的释放放到finally中。
public class FinallyTest {
@Test
public void test1() {
FileInputStream s = null;
try {
File file = new File("http1.txt");
s = new FileInputStream(file);
int read = s.read();
while (read != -1) {
System.out.print((char) read);
read = s.read();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 释放资源
try {
if (s != null)
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 体会
- 体会1:我们使用
try_catch_finally
处理编译时异常,是使得程序在编译时不再报错,但是在运行时仍可能会报错。相当与我们使用try_catch_finally
将一个编译时可能出现的异常,延迟到运行时出现。 - 体会2:开发室,由于运行时异常比较常见,所以我们通常不针对运行时异常进行使用
try_catch_finally
。针对编译时异常,我们一定考虑异常的处理。
- 体会1:我们使用
面试题
问:final\finally\finalize三者的区别。
答:
- final:最终的,可以修饰方法(最终的方法,子类不可以重写)、类(不能被继承)、属性(常量)
- finally:用在
try-catch-finally
结构中,最后一定会执行的代码块。 - finalize:方法名
throws + 异常类型
throws + 异常类型
写在方法的声明处,指明此方法在执行时,可能会抛出的异常;一旦方法体执行时,出现异常,仍会在代码异常处生成一个异常类的对象,此对象满足throws后异常的类型时,就会被抛出。- 异常后续的代码不会再被执行。
public class ThrowsTest {
public static void main(String[] args) {
try {
method2();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void method2() throws IOException {
//调用有throws异常的方法
//处理方式有两种,一种是向上抛,一种是try-catch解决
//调用异常方法的方法抛出的异常类型要大于等于异常方法
method1();
}
// 往上抛出异常
public static void method1() throws FileNotFoundException, IOException {
File file = new File("http1.txt");
FileInputStream s = new FileInputStream(file);
int read = s.read();
System.out.println("hello");
}
}
- 体会:
try-catch
真正的将异常处理了,throws
方法只是将异常抛给了调用者,没有真正的处理掉。
重写方法异常处理
- 子类重写方法异常要小于等于父类被重写方法的异常
public class ThrowsTest2 extends SuperClass {
//重写方法异常等于被重写方法的异常,可以的
@Override
public void method1() throws IOException {
}
//重写方法异常小于被重写方法的异常,可以的
@Override
public void method2() throws FileNotFoundException {
}
//重写方法异常大于被重写方法的异常,错误的
@Override
public void method3() throws Exception {
}
}
class SuperClass {
public void method1() throws IOException {
File file = new File("http1.txt");
FileInputStream s = new FileInputStream(file);
int read = s.read();
System.out.println("hello");
}
public void method2() throws IOException {
File file = new File("http1.txt");
FileInputStream s = new FileInputStream(file);
int read = s.read();
System.out.println("hello");
}
public void method3() throws IOException {
File file = new File("http1.txt");
FileInputStream s = new FileInputStream(file);
int read = s.read();
System.out.println("hello");
}
}
处理方式的选择
- 如果父类的方法没有抛出异常,那么子类重写的方法必须不能使用
throws
。如果要处理异常必须使用try-catch
。 - 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。
手动抛出异常
- 格式:
throw new 异常类型();
- 常用抛出异常:
- RuntimeException()
- Exception()
public class ThrowTest {
public static void main(String[] args) {
User user = new User();
try {
user.register(-1);
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println(user);
}
}
class User {
// id必须大于0
private int id;
public void register(int id) throws Exception {
if (id > 0) {
this.id = id;
} else {
// 如果在这里不处理小于0的数
// 那么id都是默认值0,没有变
// 所以小于0的时候我们就手动抛出异常
throw new RuntimeException("输入数据非法");
}
}
@Override
public String toString() {
return "User [id=" + id + "]";
}
}
throws和throw区别
- throws是抛出异常;声明在方法名后;是异常处理的一种方式;
- throw是声明异常;声明在方法体内;是生成异常对象的一种方式。
自定义异常类
- 步骤:
- 继承
Exception
结构(一般继承Exception
或RuntimeException
) - 定义全局常量serialVersionUID
- 定义构造器
- 继承
public class MyException extends Exception{
//定义全局常量serialVersionUID
static final long serialVersionUID = -3311516993124229948L;
//定义构造器
public MyException() {
super();
}
public MyException(String message) {
super(message);
}
}
public class MyExceptionTest {
public static void main(String[] args) {
try {
method();
} catch (MyException e) {
e.printStackTrace();
}
}
private static void method() throws MyException{
}
}
练习
- 编写应用程序EcmDef.java,接收命令行的两个参数,要求不能输入负数,计算 两数相除。
- 对 数 据 类 型 不 一 致 (NumberFormatException) 、 缺 少 命 令 行 参 数 (ArrayIndexOutOfBoundsException、 除0(ArithmeticException)及输入负数(EcDef 自定义的异常)进行异常处理。
- 提示:
- 在主类(EcmDef)中定义异常方法(ecm)完成两数相除功能。
- 在main()方法中使用异常处理语句进行异常处理。
- 在程序中,自定义对应输入负数的异常类(EcDef)。
- 运行时接受参数 java EcmDef 20 10 //args[0]=“20” args[1]=“10”
- Interger类的static方法parseInt(String s)将s转换成对应的int值。 如:int a=Interger.parseInt(“314”); //a=314;
public class EcmDef {
public static void main(String[] args) {
double result;
try {
result = ecm(args);
System.out.println(result);
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (ArithmeticException e) {
e.printStackTrace();
} catch (EcDef e) {
System.out.println(e.getMessage());
}
}
private static double ecm(String[] args)
throws ArrayIndexOutOfBoundsException, NumberFormatException, ArithmeticException, EcDef {
int a = Integer.parseInt(args[0]);
int b = Integer.parseInt(args[1]);
if (a < 0 || b < 0) {
throw new EcDef("输入数据不能为小数");
}
return a / b;
}
}
class EcDef extends RuntimeException {
static final long serialVersionUID = -1387516994229948L;
public EcDef() {
super();
}
public EcDef(String message) {
super(message);
}
}
本文由 Gorrywang 创作,严禁转载与复制!
最后编辑时间为: Aug 27,2016