目录
throws + 异常类型 写在方法的声明后,throws+可能会出现的异常类型。
异常的理解:
在java语言中,将程序执行过程中发生的不正常情况称为“异常”(开发过程中语法错误和逻辑错误不算异常);就是在语法和逻辑上都没有错误的情况下,出现的不正常情况,我们称为异常。
异常的分类:
异常分为Error和Exception,
Error是java虚拟机无法解决的严重问题,如资源耗尽,一般不编写针对性的代码解决。例如:递归调用(栈溢出stackoverflowerError )、堆空间溢出,(new一个很大空间的int数组)。只能改代码
Exception是可以编写针对性代码解决,常见的空指针异常,角标越界,文件访问失败等。
上图中RuntimeException是编译时异常,派生类包括1,错误的强制类型转换2,数组访问越界3,空指针异常。 不是RuntimeException的异常叫做运行时异常:如打开一个不存在的文件等,
编译时(检查型)异常和运行时(非检查型)异常区分:
一个java代码要执行需要经过两个阶段,编译可以通过的java源程序首先要生成字节码文件,然后java虚拟机在通过字节码文件,在内存中加载执行。(如下图)
如何看待代码中的编译时异常和运行时异常?
*体会1:使用try-catch-finally处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
*体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。针对于编译时异常,我们说一定要考虑异常的处理 。
那么有了异常之后,我们就需要处理异常,下面介绍异常处理机制:
异常处理机制——抛抓模型
过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出。(在java中我们将一些常见的异常都定义成了各个不同的类,抛出异常指的是抛出相对应的异常类的对象。)
一旦抛出对象以后,其后的代码就不再执行。
关于异常对象的产生:① 系统自动生成的异常对象
② 手动的生成一个异常对象,并抛出(throw)
过程二:“抓":可以理解为异常的处理方式:① try-catch-finally ② throws
(这里,第一种方式是将编译时的异常处理成了运行时的异常,就是使得我们的程序在编译时不会报错,他是一种对异常的实际处理的机制,而第二种方式仅仅只是将异常抛出没有做出实质性对异常的操作)
异常处理方式
了解异常处理方式之前,我们首先要明白一件事:
异常处理实际是代码在执行之前我们预测可能会出现异常,然后我们提前做个预案,说一旦出现异常我们该怎么办到实际应用中就是出现异常了我们不要报乱码了,我们给出一个友好的提示,回去该改代码还是得改
并不是说用上述两种方式处理异常后就不用管这个异常了。
异常处理机制一:try-catch-finally
try-catch-finally的使用模型
tryf {
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
} catch(异常类型3 变量名3){
//处理异常的方式3
}finally{
}
try-catch-finally注意点
*1. finally是可选的(try—catch后可有可无)。
*2.使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
*3.一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的 try-catch结构(在没有写finally的情况)(一旦对应到一个catch将不再去对应后面的catch)。继续执行其后的代码
*4. catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。
catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错;原因:(一旦对应到一个catch将不再去对应后面的catch)
*5. 常用的异常对象处理的方式: String getMessage(); printStackTrace();
*6. 在try结构中声明的变量,再出了try结构以后,就不能再被调用
*体会1:使用try-catch-finally处理编译时异常,使得程序在编译时就不再报错,但是运行时仍可能报错。* 相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
体会2:开发中,由于运行时异常很常见,所以我们不需要针对运行时异常去try-catch,然而编译时异常我们就必须去try-catch了,不然会编译不过去。
finally使用注意点
1. finally是可有可无的
2. finally中声明的代码一定会被执行,即使catch中出现异常,或者try和catch中有return语句。
3. 像数据库连接,输入输出流,网络编程Socket等资源,JVM是不会去主动的回收的,我们便需要手动的去释放资源,就需要在finally中去声明,因为你之前的代码无论是否出现异常,finally中的代码都回去执行的。
4. try-catch-finally结构也可以去嵌套。
5. 编译时异常,我么就需要try-catch,不然会编译不过去。本身是个运行时异常就不要try-catch了
异常处理机制二:
throws + 异常类型 写在方法的声明后,throws+可能会出现的异常类型。
1, 一旦方法执行时出现异常,仍会在异常代码处生成一个异常类对象,,
此对象满足throws后异常类型时就会被抛出异常代码后面的代码不会被执行,
2, 体会:try-catch-finally是实实在在的把异常处理了(但是代码我们仍需要改),然而throws方式仅仅只是把异常抛给了调用者,异常还存在,仍需要在最后去try-catch处理。
开发中如何选择两个方式:
3,1 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws意味着如果子类重写的方法中有异常,只能使用try-catch方式。
3,2 try-catch的方式中出了异常的代码后面的代码也可以执行,而throws方式出异常的代码后面不执行。
3,3 执行的方法a中,先后有调用了另外的几个方法,这几个方法又是递进的关系,我们建议这几个先方法使用throws方式,而在方法a可以考虑使用try-catch方式进行异常处理。
public class ExceptionTest2 {
public static void main(String[] args) {
try{
method2();
//因为main是静态方法,所以他调用的方法或者属性也必须是静态的这个从虚拟机加载层面理解。}
}catch(FileNotFoundException e){
e.printStackTrace();
/*
在catch中,我们还要注意,在前面先出现的catch中的异常类型必须小于后面出现的catch中的异常
类型。而且一旦在try中处出现异常之后就会在catch中去匹配,从上往下一一一匹配,一旦匹配到就
不在往下匹配。
*/
}catch(IOException e ){
System.out.println(e.getMessage());
}
}
public static void method2() throws IOException {
method1();
}
public static void method1()throws FileSystemNotFoundException, IOException {
File file=new File("hello2.txt");
FileInputStream fis=new FileInputStream(file);
int data=fis.read();
while(data!=-1){
System.out.print((char)data);
data=fis.read();
}
fis.close();
System.out.println("我出现了!");
/*
通过使用throws方式处理后的异常,出现异常代码后面的代码不会再执行,不是说我们用以上两种方法
处理异常之后,就不用再管了,我们该修改代码还的修改代码。用了以上两种方式只是说,我们将异常
用我们能看懂的方式告诉我们,我们在知道哪里出了异常之后,我们该修改代码时还的修改代码。
*/
}
}
手动抛出异常
之前关于异常对象的产生,都是系统自动生成(throws)的异常对象,而我们也可以手动(throw)抛出对象。
体会:所谓的手动的抛出异常,其实是抛出了一个手动写的异常的对象,通知程序这里有异常,然后我们再在方法的声明后面将这个异常对象抛出去(你也可以使用try-catch去直接处理异常,但是我们通常会throws抛给调用者,让调用者处理)给main方法,main方法在去使用try-catch处理异常。然后再显示的的通知程序员改代码
public class StudentTest {
public static void main(String[] args) {
student s=new student();
try {
s.regist(-1001);
System.out.println(s);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class student{
private int id;
public void regist(int id) throws Exception {
if(id>0){
this.id=id;
}else{
// System.out.println("你输入的数据有误!");
// throw new Exception("你输入的数据有误!");
throw new MyException("你输入的数据有误!");//自己手动写的异常类
/*
Exception有一个含参的构造器
public Exception(String message) {
super(message);
}
我们又知道一个方法 String getMassage();
*/
}
}
@Override
public String toString() {
return "student{" +
"id=" + id +
'}';
}
}
throw 和throws区别:
throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
throws属于异常处理的一种方式,声明在方法的声明处。
自定义异常类
1,要想说明自定义的类是一个异常类,我们就让它去继承现有的异常结构RuntimeException
或者Exception, RuntimeException是运行时异常,编译可以通过。而Exception是编译时异常,需要我们作出处理,不然编译时会报错。
2,我们编写一个异常类,是我们可以借鉴 RuntimeException 是怎么定义的,Ctrl+B;
自定义异常步骤:
* 1,继承现有结构;Exception或RuntimeException
* 2,提供全局常量,序列化标识,serialVersionUID
* 3,重载的构造器。
public class MyException extends RuntimeException {
static final long serialVersionUID = -7034897191745766939L;
public MyException(){
}
public MyException(String message){
super(message);
}
}
补充:存在方法重写时子父类抛出的异常的关系
方法重写的规则:
* 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型;
* 父类不抛异常,子类不能抛异常。
public class OverrideTest {
public static void main(String[] args) {
OverrideTest test=new OverrideTest();
test.display(new subclass());
}
public void display(Superclass s){
try {
s.method();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Superclass{
public void method()throws IOException{
}
}
class subclass extends Superclass{
public void method()throws FileNotFoundException {
}
}