8.偏头痛杨的Java入门教学系列之异常篇

版权声明:偏头痛杨:本文为博主原创文章,未经作者允许不得转载,版权必究! https://blog.csdn.net/piantoutongyang/article/details/73687445
复习
1.使用接口的好处是什么?
2.Java的工作机制?
3.什么是方法重写与方法重载?
4.什么是Java三要素?
5.抽象类与接口的区别?
6.多态的三个必要条件?
7.this与super的用法?
8.final的用法?
9.方法有几种?分别是什么?区别是什么?
10.JDK&JRE&JVM分别是什么以及他们的关系?

前文链接
1.偏头痛杨的Java入门教学系列之认识Java篇
2.偏头痛杨的Java入门教学系列之变量&数据类型篇
3.偏头痛杨的Java入门教学系列之表达式&运算符&关键字&标识符&表达式篇
4.偏头痛杨的Java入门教学系列之初级面向对象篇
5.偏头痛杨的Java入门教学系列之流程控制语句篇
6.偏头痛杨的Java入门教学系列之数组篇
7.偏头痛杨的Java入门教学系列之进阶面向对象篇


前戏
程序是程旭猿编的,程序猿也是人,是人就会有出错的地方,
那么程序在运行的时候就会有出错的地方,一旦程序出错了之后,我们应该如何处理?如何解决?
有没有一套机制来hold?是把异常信息记录到日志中?还是在出现某种异常后,需要运行一些代码?
还是出现某种异常后短信&微信报警?还是什么也不做?
今天我们来学习Java的异常体系。


什么是异常
所谓异常就是不正常的情况,程序在运行中出现了程序猿们始料未及的状况。。
这里分两种情况讨论:运行期间出现的错误与编译时的语法错误。

编译时的语法错误
写程序的时候少写了一个分号,或者关键字写错了,语法错了。
此时你的IDE工具(例如eclipse)会给你报错,注意,此时还没有进行编译。
一旦你编译后,便会出现java.lang.Error的提示,然后编译失败。

运行期间出现的错误
一种是编译通过后,程序开始运行,运行期间,由于种种原因导致程序出现异常情况,
例如你使用11/0,Java是不允许0当作除数的,那么便会抛出java.lang.ArithmeticException,
此时程序继续执行,而不会中断。
当然,在运行时,也可能会出现java.lang.Error,导致程序中断,例如内存溢出等错误。

Java中的异常信息通过对象来表示。
Java对异常的处理是按异常分类处理的,不同异常有不同的分类,
每种异常都对应一个类型(class),每个异常都对应一个异常(类的)对象。


异常的分类与层次结构
异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。
Throwable: 有两个重要的子类:Exception(异常)和 Error(错误),
二者都是Java异常处理的重要子类,各自都包含大量子类。
通常,这些实例是在异常情况的上下文中新近创建的,因此包含了相关的信息(比如堆栈跟踪数据)。

Throwable类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,
才能通过 Java 虚拟机或者 Java throw 语句抛出。
类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。

注意事项
~所有的异常类是从 java.lang.Exception 类继承的子类。
~Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。

检查性异常checked exception
若系统运行时可能产生该类异常,则必须写出相应的处理代码,否则无法通过编译 。
最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。

非检查性异常unchecked exception(也叫RuntimeException运行时异常)
运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
RuntimeException表示一种设计或实现问题,如果程序运行正常,从不会发生的情况,
默认不需要进行异常处理。比如,如果数组索引扩展不超出数组界限,
那么,ArrayIndexOutOfBoundsException异常不会抛出。
当程序中可能出现这类异常时,即使没有用try...catch语句捕获它,
也没有用throws字句声明抛出它,还是会编译通过,这种异常可以通过改进代码实现来避免。

错误error
Error表示恢复不是不可能但很困难的情况下的一种严重问题。
错误不是异常,而是脱离程序员控制的问题,不可能指望程序能处理这样的情况。
错误在代码中通常被忽略。
错误类定义被认为是不能恢复的严重错误条件。在大多数情况下,当遇到这样的错误时,
建议让程序中断。
大多数错误与代码编写者执行的操作无关。
例如:代码运行时JVM出现的问题,Java虚拟机运行错误(Virtual MachineError),
类定义错误(NoClassDefFoundError),
内存溢出错误(OutOfMemoryError),当JVM不再有继续执行操作所需的内存资源时,
这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。


注意事项
~Java中凡是继承自Exception,
而不继承自RuntimeException类以及其子类的异常都是checked exception,需要检查。
~异常类之间也有父子类继承的关系。


异常的出现时机
打开一个不存在的文件?
网络连接中断?
操作数组越界?
调用一个null对象的属性or方法?
用户输入了非法数据?

给2个小例子:
String name = null;
System.out.println(name.length());
上述代码执行之后会出现:
Exception in thread "main" java.lang.NullPointerException
这就是空指针异常,被誉为“世界上最简单的异常”。
int i = 0;
String greetings[] = {"Hello World", "Hello Dingdang", "Hello Kitty"};
while(i<4){
  System.out.println(greetings[i]);
  i++;
}
上述代码执行之后会出现:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
这就是数组下标越界异常。


异常处理
异常处理允许程序捕获异常,处理捕获的异常,然后继续程序执行。
异常的处理过程:
抛出异常->捕获异常->处理异常

在Java程序执行过程中如果出现异常,系统会发出异常报告,这时系统将生成一个异常类对象,
异常类对象封装了异常事件的信息并将其提交给Java运行时系统。

Java中可用于处理异常的两种方式:
自行处理
把可能引发异常的语句封入在try块内,而处理异常的相应语句则封入在catch块内。

回避处理
在方法声明中包含throws子句,通知潜在调用者,如果发生了异常,必须由调用者处理。

try块里的代码如果没有抛出异常则程序继续往下正常运行,如果有抛出异常则进入catch块。
catch块是用来捕获并处理try块抛出的异常的代码块。没有try块,catch块不能单独存在。
我们可以有多个catch块,以捕获不同类型的异常。

如果程序抛出多个不同类型的异常,我们需要多个catch()语句来处理。
catch (ArrayIndexOutOfBoundsException e) {
  System.out.println(“Out of Bounds!”);
} catch (RuntimeException e) {
  System.out.println(“Runtime Exception!”);
} catch (Exception e) {
  System.out.println(“Exception!”);
}
当引发异常时,会按顺序来查看每个catch语句,并执行第一个类型与异常类型匹配的语句。
程序执行其中的一条catch语句之后,其他的catch语句将被忽略。
catch块的顺序为异常类父子类顺序,父类写在下面,子类写在上面。

注意事项
~catch块里可以什么都不写,这样也算是处理异常,但通常不推荐这么做。
~一旦异常被catch住,那么程序可以继续往下进行,除非你在catch块中又抛出了异常或返回。
~try{…}和catch( ){…}之间不可以添加任何代码。
~try-catch是可以嵌套的。
~和特殊异常类相关联的catch()块必须写在和普通异常类相关联的catch()之前。
(父类放下面,子类放上面)


throws关键字
如果一个方法中的语句执行时可能抛出某种异常,但是并不能确定如何处理,
则可以在程序所在的方法声明后,使用throws关键字抛出异常 ,让调用者来解决这个问题。
class ThrowsDemo{
  public void proc() throws IOException{
    System.out.println("inside proc");
  }
}
你自己来决定出现异常后是由自己来处理还是往上抛,反弹给调用你方法的人。

注意:
~所有的RuntimeException以及其子类异常,都不用显式的进行throws与try-catch,
当然写了也不算错。
~throws后面的异常可以有多个,允许使用逗号分隔。


throw关键字
异常可以通过关键字throw抛出,由程序猿手动抛出异常,程序可以用throw语句引发明确的异常。
不要与throws关键字混淆。
void doA() throws Exception {
  try {
    ……
  } catch(Exception1 e) {
    throw e;
  } catch(Exception2 e) {
    System.out.println("出错了");
  }
throw语句用在方法体内,表示抛出异常,由方法体内的语句处理。
不能单独使用,要么和try.. catch…一起使用,要么和throws一起使 用。
throw语句的操作数一定是Throwable类类型或Throwable子类类型的一个对象 。


finally关键字(不要与final关键字混淆)
finally语句放在try …catch语句后。
finally语句中的代码块不管异常是否被捕获总是要执行。

通常在finally语句中可以进行资源的清除操作,如:关闭打开文件、删除临时文件。

对应finally代码中的语句,即使try代码块和catch代码块中使用了return语句退出当前方法,
相关的finally代码块都有执行。
catch (ArrayIndexOutOfBoundsException e) {
  System.out.println(“Out of Bounds!”);
  return;
} catch (RuntimeException e) {
  System.out.println(“Runtime Exception!”);
} catch (Exception e) {
  System.out.println(“Exception!”);
} finally{
  System.out.println(“program is running into finally!”);
}
注意:
~当try或catch代码块中执行了System.exit(0)时,finally代码块中的内容不被执行。
~在 try/catch 后面添加 finally 块并非强制性要求的。
~try 代码后不能既没 catch 块也没 finally 块。

finally的大坑,非常常见的面试题:
public static void main(String[] args) throws Exception {
  System.out.println("最后返回------>"+eat(null));
}

public static int eat(String name) {
  int temp = 0;
  try {
    if(name==null){
      throw new Exception();
    }
    return temp;
  } catch(Exception e){
    System.out.println("catch......");
    temp = 2;
    System.out.println("catch要准备返回了...");
    return temp;
  } finally {
    temp = 3;
    System.out.println("finally...");
    System.out.println("在finally的temp是"+temp);
  }
}
输出的结果是:
catch......
catch要准备返回了...
finally...
在finally的temp是3
最后返回------>2

为什么明明在finally里都已经把temp赋值3了,返回的依然是2呢?
在catch块中return的前一刻先按一下"暂停键",要返回的值依然是那个先不动,然后去调用finally,
无论finally怎么弄,都不会影响catch块中影响的结构,但除了一种情况,那就是在finally里写了return,
那么catch块中的return就会被忽略掉,而使用finally的返回值,因此建议不要在finally里写return。
finally {
  temp = 3;
  System.out.println("finally...");
  System.out.println("在finally的temp是"+temp);
  return temp;
}
输出的结果是:
catch......
catch要准备返回了...
finally...
在finally的temp是3
最后返回------>3


异常方法
我们在catch块中会使用异常类的对象,并调用异常对象的方法。
例如调用printStackTrace()方法则是打出堆栈信息。
try{

}catch(Exception ex){
  ex.printStackTrace();
}
下面列出常见的异常方法:
方法定义
方法描述
public String getMessage()
返回关于发生的异常的详细信息。
这个消息在Throwable 类的构造函数中初始化了。
public Throwable getCause()
返回一个Throwable 对象代表异常原因。
public String toString()
使用getMessage()的结果返回类的串级名字。
public void printStackTrace()
打印toString()结果和栈层次到System.err,即错误输出流。
public StackTraceElement [] getStackTrace()
返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,
最后一个元素代表方法调用堆栈的栈底。
public Throwable fillInStackTrace()
用当前的调用栈层次填充Throwable 对象栈层次,
添加到栈层次任何先前信息中。


常见异常类
Java 语言定义了一些异常类在 java.lang 标准包中。
由于 java.lang 包是默认加载到所有的 Java 程序的,
所以大部分从运行时异常类继承而来的异常都可以直接使用。 

在Java中提供了一些异常用来描述经常发生的错误,对于这些异常,
有的需要程序员进行捕获处理或声明抛出,
有的是由Java虚拟机自动进行捕获处理。

常见unchenck异常(RuntimeException以及其子类)
异常名字(英文)
异常名称(中文)
描述
ArrayIndexOutOfBoundsException
数组下标越界异常
数组索引越界异常。
当对数组的索引值为负数或
大于等于数组大小时抛出。
ArithmeticException
算术条件异常
整数除零等。
NullPointerException
空指针异常
当应用试图在要求使用对象的地方使用了null时,
抛出该异常。
譬如:调用null对象的实例方法、
访问null对象的属性、
计算null对象的长度、
使用throw语句抛出null等等。
IllegalArgumentException
非法参数异常
表明向方法传递了一个不合法或不正确的参数。
ClassCastException   
类型转换异常
当试图将对象强制转换为不是实例的子类时,
抛出该异常。
NumberFormatException
数字转换异常
字符串转换为数字抛出的异常,
当应用程序试图将字符串转换成一种数值类型,
但该字符串不能转换为适当格式时,抛出该异常。
   
  

常见chenck异常(除了RuntimeException以及其子类之外的类)
异常名字(英文)
异常名称(中文)
描述
ClassNotFoundException
找不到类异常
当应用试图根据字符串形式的类名构造类,
而在遍历CLASSPAH之后找不到对应名称的
class文件时,抛出该异常。
FileNotFoundException
文件未找到异常
 所需要的文件不存在。
NoSuchFieldException
属性未找到异常
所需要的属性不存在。
NoSuchMethodException
方法未找到异常
所需要的方法未不存在。
IllegalAccessException
非法访问某类异常
拒绝访问一个类的时候,抛出该异常。


自定义异常(新手可PASS)
异常类从哪里来?
1.Java语言本身定义的一些基本异常类型;
2.用户通过继承Exception类或者其子类自己定义的异常;
Exception类及其子类是Throwable一种形式,它指出了合理的应用程序想要捕获的条件。

如果Java提供的异常类型不能满足程序设计的需要,我们可以定义自己的异常类型。
用户自定义的异常类应为Exception类或者Exception类的子类。
我们可以通过判断自定义异常类,来做不同的异常处理,例如XXX异常需要短信报警,
YYY异常需要记录log,ZZZ异常需要去刷数据等等。

定义自己的异常类(这种异常类可以包含一个“普通”类所包含的任何东西)
public class YangException extends RuntimeException {
     /**
      *
      */
     private static final long serialVersionUID = -1998686844673228031L;
     private String reason;
     private int port;

     public YangException(String reason, int port) {
          this.reason = reason;
          this.port = port;
     }

     public String getReason() {
          return reason;
     }

     public void setReason(String reason) {
          this.reason = reason;
     }

     public int getPort() {
          return port;
     }

     public void setPort(int port) {
          this.port = port;
     }
}

跟其他的异常一样处理
public class YangExceptionMainDemo {
     public static void main(String[] args) {
          try {
              eat();
          } catch (YangException e) {
              e.printStackTrace();
          }
     }

     public static void eat(){
          throw new YangException("hahaha",8080);
     }
}
注意事项
~如果你想写一个运行时异常类(unchecked exception),
那么需要继承 RuntimeException 类或其子类。

总结
Java异常处理的目的是提高程序的健壮性,你可以在catch和finally代码块中给程序一个修正机会,
使得程序不因异常而终止或者流程发生以外的改变。同时,通过获取Java异常信息,
也为程序的开发维护提供了方便,一般通过异常信息就很快就能找到出现异常的问题&代码所在。


作业
编写一个方法,入参为一个整型变量。

输入1则抛出空指针异常;
输入2则抛出数组下标越界异常;
输入3则抛出IO异常;
否则不抛异常,输出:"今天没有异常耶"。

不管是否有异常,都要输出:"方法正在呗调用"
异常需要在调用者的方法处理,

空指针异常则输出"给jack打电话"
IO指针异常则输出"给sean打电话"
数组下标越界异常则输出"给clarck打电话"

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试