4.异常
4.1 异常的体系结构
/*
1.异常的体系结构
* java.lang.Throwable
* |-----java.lang.Error:一般不编写针对性的代码进行处理。
* |-----java.lang.Exception:可以进行异常的处理
* |------编译时异常(checked)
* |-----IOException
* |-----FileNotFoundException
* |-----ClassNotFoundException
* |------运行时异常(unchecked,RuntimeException)
* |-----NullPointerException
* |-----ArrayIndexOutOfBoundsException
* |-----ClassCastException
* |-----NumberFormatException
* |-----InputMismatchException
* |-----ArithmeticException
2.从程序执行过程,看编译时异常和运行时异常
2.1 编译时异常:执行javac.exe命令时出现的异常
2.2 运行时异常:执行java.exe命令时出现的异常
*/
public class Main {
/*
3.常见的异常类型
3.1 编译时异常
*/
@Test
public void test1(){
/* File file = new File("hello.txt");
FileInputStream fileInputStream = new FileInputStream(file);
int data=fileInputStream.read();
while (data!=-1){
System.out.println((char)data);
data=fileInputStream.read();
}
fileInputStream.close();*/
}
/*
3.2 运行时异常
3.2.1 NullPointerException
*/
@Test
public void test2(){
int[] arr=null;
System.out.println(arr[0]);
}
/*
3.2.2 ArrayIndexOutOfBoundsException
*/
@Test
public void test3(){
int[] ints=new int[3];
System.out.println(ints[3]);
}
/*
3.2.3 ClassCastException
*/
@Test
public void test4(){
Object obj= new Date();
String str=(String) obj;
}
/*
3.2.4 NumberFormatException
*/
@Test
public void test5(){
String str="123";
str="abc";
int num=Integer.parseInt(str);
}
/*
3.2.5 InputMismatchException
*/
@Test
public void test6(){
Scanner scanner = new Scanner(System.in);
int score=scanner.nextInt();//输入非整型
System.out.println(score);
scanner.close();
}
/*
3.2.6 ArithmeticException
*/
@Test
public void test7(){
int a=10;
int b=0;
System.out.println(a/b);
}
}
4.2 异常的处理
public class Main {
/*
1.java异常处理的抓抛模型
1.1 过程一:"抛":
1.1.1 程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出
一旦抛出对象以后,其后的代码就不再执行
1.1.2 关于异常对象的产生:
1.1.2.1 系统自动生成的异常对象
1.1.2.2 手动的生成一个异常对象,并抛出(throw)
1.2 过程二:"抓":可以理解为异常的处理方式
1.2.1 try-catch-finally
1.2.2 throws
2.异常处理方式一:try-catch-finally
2.1 格式:
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}catch(异常类型3 变量名3){
//处理异常的方式3
}
....
finally{
//一定会执行的代码
}
2.2 说明:
2.2.1 finally是可选的
2.2.2 使用try将可能出现异常的代码包装起来,在执行过程中,一旦出现异常,就会异常代码处生成一个对应异常类的对象,并将此对象抛出,然后根据此对象的类型,去catch中进行匹配。一旦抛出对象以后,其后的代码就不再执行
2.2.3 一旦try中的异常对象匹配某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没写finally的情况)。继续执行其后的代码
2.2.4 catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓
catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错
2.2.5 常用的异常对象处理的方式:
2.2.5.1 String getMessage()
2.2.5.2 void printStackTrace()
2.2.6 在try结构中声明的变量,在出了try结构以后,就不能再被调用
2.2.7 try-catch-finally结构可以嵌套
2.2.8 如何看待代码中的编译时异常和运行时异常?
2.2.8.1 使用try-catch-finally处理编译时异常,使得程序在编译时不再报错,但是运行时仍可能报错(报的依然是编译时异常)
2.2.8.2
2.2.8.2.1 开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了(进行异常处理和不进行异常处理基本无区 别,我们真正要做的是把bug修复)
2.2.8.2.2 针对于编译时异常,我们一定要考虑异常的处理(不然程序跑不起来)
2.3 finally的再说明:
2.3.1 finally是可选的
2.3.2 finally中声明的是一定会被执行的代码。即使catch中又出现了异常,try中有return语句,catch中有return语句等情况
2.3.3 像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动地进行资源的释放。此时的资源释放,就需要声明在finally中
*/
@Test
public void test1(){
FileInputStream fileInputStream=null;
try {
File file = new File("hello.txt");
fileInputStream = new FileInputStream(file);
int data=fileInputStream.read();
while (data!=-1){
System.out.println((char)data);
data=fileInputStream.read();
}
} catch (FileNotFoundException e) {
System.out.println(e.getMessage());
}catch (IOException e){
e.printStackTrace();
} finally {
if(fileInputStream!=null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public int test2(){
try {
int a=10;
int b=0;
System.out.println(a/b);//异常被catch,进入到ArithmeticException e
return 1;
}catch (ArithmeticException e){
e.printStackTrace();
int[] arr=new int[10];
System.out.println(arr[10]);//异常抛出但是没有catch,所以程序结束
return 2;
}catch (Exception e){
e.printStackTrace();
return 3;
}finally {
System.out.println("我一定会被执行");//但是finally一定会被执行
return 4;
}
}
@Test
public void test3(){
System.out.println(test2());
/*输出:
java.lang.ArithmeticException: / by zero
我一定会被执行
4
*/
}
/*
3.异常处理方式二:
3.1
3.1.1 "throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
3.1.2 一旦当方法体执行时出现异常,就会在异常代码处生成一个对应异常类的对象,并通过throws抛给方法的调用者。一旦抛出对象以后,其后的代码就不再执行
3.2 最后的方法的调用者(至少是Main)必须使用try-catch-finally来处理异常
*/
public static void main(String[] args) {
try {
method2();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
System.out.println(e.getMessage());
}
try {
method3();
} catch (IOException e) {
e.printStackTrace();
}
method4();//异常已经在method4()中处理了,不用try-catch
}
public static void method1() throws FileNotFoundException,IOException{
File file = new File("hello.txt");
FileInputStream fileInputStream = new FileInputStream(file);
int data=fileInputStream.read();
while (data!=-1){
System.out.println((char)data);
data=fileInputStream.read();
}
fileInputStream.close();
}
//如果FileNotFoundException与IOException处理方式不一样,则都抛出
public static void method2() throws FileNotFoundException,IOException{
method1();
}
//如果FileNotFoundException与IOException处理方式一样可只抛出IOException
public static void method3() throws IOException{
method1();
}
//在method4()中使用try-catch-finally来处理异常
public static void method4(){
try {
method1();
} catch (IOException e) {
e.printStackTrace();
}
}
/*
4.对比两种处理方式
4.1 try-catch-finally:真正地把异常给处理掉了
4.2 throws的方式只是将异常抛给了方法的调用者,并没有真正将异常处理掉
5.体会开发中应该如何选择两种处理方式?
5.1 如果父类中被重写的方法没有用throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理
5.2 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理(因为使用throws,当一个方法出现异 常,后面的方法就不会继续执行了,而如果使用try-catch-finally方式把异常处理后,还会继续执行下一个方法),而执行的方法a可以考虑使用try-catch- finally方式进行处理
6.方法重写的规则之一:子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
*/
}
class Person{
public void method() throws IOException{
}
public void method1(){
}
}
class Boy extends Person{
@Override
public void method() throws IOException {
super.method();
}
//因为父类没有用throws方式处理异常,所以子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理
@Override
public void method1() {
FileInputStream fileInputStream=null;
try {
File file = new File("hello.txt");
fileInputStream = new FileInputStream(file);
int data=fileInputStream.read();
while (data!=-1){
System.out.println((char)data);
data=fileInputStream.read();
}
} catch (FileNotFoundException e) {
System.out.println(e.getMessage());
}catch (IOException e){
e.printStackTrace();
} finally {
if(fileInputStream!=null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4.3 手动抛出异常对象
public class Main {
public static void main(String[] args) {
Person person = new Person();
try {
person.test1(-1);
} catch (Exception e) {
e.printStackTrace();
}
person.test2(-1);
}
}
/*
1.使用说明:在程序执行中,除了系统自动抛出异常对象的情况之外,我们还可以手动throw一个异常类的对象
2.throw 和 throws区别:
2.1 throw 表示抛出一个异常类的对象,是生成异常对象的过程,声明在方法体内。
2.2 throws 属于异常处理的一种方式,声明在方法的声明处。
*/
class Person{
private int id;
public void test1(int id) throws Exception {
if(id>0){
this.id=id;
}else{
//Exception包括了编译时异常,所以要进行异常处理
throw new Exception("你输入的数据非法!");
}
}
public void test2(int id){
if(id>0){
this.id=id;
}else{
//RuntimeException是运行时异常,可以不进行异常处理
throw new RuntimeException("你输入的数据非法!");
}
}
}
4.4 自定义异常类
public class Main {
public static void main(String[] args) {
Person person = new Person();
try {
person.test1(-1);
} catch (Exception e) {
e.printStackTrace();
}
person.test2(-1);
}
}
/*
1.如何自定义一个异常类?
1.1 继承于现有的异常结构:RuntimeException 、Exception
1.2 提供全局常量:serialVersionUID
1.3 提供重载的构造器
*/
class MyException extends Exception{
public static final long serialVersionUID = -3387516993124229942L;
public MyException(){
}
public MyException(String msg){
super(msg);
}
}
class YourException extends RuntimeException{
static final long serialVersionUID = -3387516993124229943L;
public YourException(){
}
public YourException(String msg){
super(msg);
}
}
class Person{
private int id;
public void test1(int id)throws Exception{
if(id>0){
this.id=id;
}else{
//因为继承的是Exception,所以要进行异常处理
throw new MyException("你输入的数据非法!");
}
}
public void test2(int id){
if(id>0){
this.id=id;
}else{
//因为继承的是RuntimeException,所以可以不进行异常处理
throw new YourException("你输入的数据非法!");
}
}
}