Day02【异常、线程】
- 异常
1.1异常的概念:
- 异常: 指的是程序在执行过程中,出现的非正常的情况。最终会导致的JVM的正常停止。
- 异常本身就是一个类,产生异常就是创建异常对象并抛出了一个异常对象,java处理异常的方式,就是中断处理。
- 异常:指的并不是语法错误,编译不通过,不能产生字节码文件,程序根本无法正常运行。
- 编写一个良好的程序,应该避免异常发生从而终止整个程序,而是通过一定的技术来捕获异常并且能够处理好,让程序继续运行。
- Java这门语言,本身就有处理异常的能力,我们学习的就是怎么掌握去处理异常的能力。
- Java异常的处理:其实是5个关键字: try 、catch 、throw 、throws 、finally
1.2 异常的原理
- 抛出异常: 当程序运行期间,如果发生了异常,会自动的产生一个相应的异常类对象,并且会自动把这个异常类对象交给jvm,这个过程就是就是抛出异常。
- 捕获并处理:当jvm接收到异常类对象时,会自动的寻找有没有相应的代码能够处理该异常,如果存在能够处理该异常的代码,则把异常让其捕获并处理,如果没有找到相应的代码,jvm会终止,程序退出。
1.3异常体系的分类
- 异常的处理机制就是帮助我们找到程序中的问题,异常的根类是:java.lang.Throwable.
他底下有两个子类:java.lang.Error与java.lang.Exception.我们平常所说的异常,值的就是:java.lang.Exception.
- Throwable 体系:
- Error: 严重的错误,无法通过处理的错误,只能事先避免,比如绝症。例如:服务器宕机,jvm内部出错。服务器资源耗尽。
- Exception: 表示异常,当异常出现后,程序猿或者攻城狮能够通过代码方式去及时纠正,使程序继续运行,是必须要处理的。
- 程序猿能够处理的只有Exception,对Error 无能为力。
- Exception
- Exception根据异常的功能分为:受检异常和非受检异常。
- 受检异常:指的就是程序在编译期间需要处理的异常。(javac 编译期间异常)
- 非受检异常: 程序在运行期间需要处理的异常。(java 运行期间的异常)
- 数组下标越界。
- 除数为零。ArithmeticException
- java.lang.RuntimeException 是所有非受检异常的父类
1.5Throwable的常用方法
- public void printStackTrace():打印异常的详细信息.
- 包含了异常的原因,异常的类型,还包括异常出现的位置。,在开发和调试阶段都得使用 printStackTrace()方法。
- Public String getMessage();获取异常发生的原因。提示给用户的时候,就提示异常发生的原因。
- public String toString():获取异常的类型和异常描述信息(不经常使用)
- 异常的处理
- 捕获异常 try..catch
- 当异常出现的时候,程序会立刻终止,我们的及时捕获并处理异常。
- 在使用方法中我们使用try…..catch 来捕获处理异常。
- Try..catch 的方式就是来捕获并处理异常,
- 捕获异常:java 中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。【很有针对性的去捕获并处理】。
2法格式:
Try{ //编译期间可能要出现异常的代码 }catch(异常类型 异常对象){ /打印异常的代码 /打印异常的信息 } |
解析:try: 该代码块中编写可能产生异常的代码。
Catch: 用来进行某种异常的捕获,实现对异常捕获的处理,
Try和catch 不能单独使用,必须结合使用。
public class Test0 { public static void main(String[] args) { int aa=10; int bb=0; try{ int cc=aa/bb; System.out.println(cc); }catch (ArithmeticException ce){ ce.printStackTrace(); } System.out.println("执行了吗??");
} } |
通过以上程序我们发现,catch 只能处理try中所对应的异常类型,如果发生的异常和catch不匹配。则是无法处理的。因此try 后面可以有多个catch出现,每个catch处理不同的异常的类型。
public class Test0 { public static void main(String[] args) { int aa=10; int bb=0; int[]sum={1,2,3,4}; try{ int cc=aa/bb; //数组下标越界://java.lang.ArrayIndexOutOfBoundsException System.out.println(sum[4]); System.out.println(cc); }catch (ArithmeticException ce){ ce.printStackTrace(); }catch (ArrayIndexOutOfBoundsException ce){ ce.printStackTrace(); } System.out.println("执行了吗??");
} } |
- 如果try中有多个异常存在,则那个异常先发生,发生后就直接去找对应的catch,
Catch执行完后,代码的顺序是继续从Catch往下执行的,不会返回try语句块中,所以try..catch 会帮我们捕获异常,但是会影响程序的正常的流程,最终我们还是要程序猿手动去处理发生的异常。
- 如果try下面有多个catch,catch中的异常类型如果是异常的父类,那么他只能位于最后一个catch中。
- finally
- finally: 有一些特定的代码,无论异常是否发生,都要去执行,另外,异常会引起程序的跳转,导致有些代码执行不到,而finally 就是解决这个问题,在finally 语句块中存放的代码,一定会被执行到的。
- 那么什么时候的代码必须最终执行:
- 当我们在 try 语句块中使用到了一些物理资源(磁盘文件、网络连接、数据库连接等对象时),我们都得在使用完之后,最终关闭这些对象,来释放它们所占的内存资源。
- Finally的语法:
- Try….catch…..finally.
- Finally 也不能单独使用。
try{ System.out.println(10/0); }catch (ArrayIndexOutOfBoundsException ce){ ce.printStackTrace(); }finally { System.out.println("不管上面的异常是否执行,finall永远执行!!"); } } |
-
- 抛出异常
- 在编写程序时,我们必须要考虑程序出现问题的情况。比如,在定义方法时,方法需要接收参数,那么,当调用方法时传递参数,那么在方法中首选需要对参数进行合法的判断,数据若不合法,就应该告诉调用者,传递合法的数据进来。这时需要使用抛出异常的方式来告诉调用者。
- 在 Java 中,提供了一个 throw 关键字,用来抛出一个指定的异常对象?那么抛出异常对象后,我们如何操做呢????
- 创建一个异常类对象,封住一些提示的信息。
- 需要将这个异常对象告知调用者,那么怎么才能告知调用者呢?怎么将这个异常对象传递到调用者处。那么我们通过throw 关键字就可以完成。
- 语法:throw new 异常类(“要提示的信息”);
- Throw用在方法内,用来抛出一个异常类对象,这个异常对象传递到调用者处,并结束当前方法的执行。
例如:
1.throw new ArithmeticException(“除数不能为零”); 2.throw new ArrayIndexOutOfBoundsException(“数组下标越界”); |
5.
/throw 抛出异常 public class Test2 { public static void main(String[] args) { int[]num={1,2,3,4,5,6,7}; int index=10; getSting(num,index); } public static void getSting(int[]num,int index){ //判断条件,如果满足,当执行完 throw 抛出异常对象后,方法已经无法继 续运行,这时就会结束当前方法的执行,并将其异常告知给调用者,这就是需要通 过异常来解决 if(index<0||index>num.length){ throw new ArrayIndexOutOfBoundsException("主人数组的下标太大的"); //System.out.println("ha"); //位于 throw new 下面的代码不会被执行,报错 }else{ System.out.println(num[index]); } } } //运行的结果:Exception in thread "main" // java.lang.ArrayIndexOutOfBoundsException: 主人数组的下标太大的 |
【注意】如果产生了异常,我们就会将异常抛出,也就是将问题返回给该方法的调用者,那么对于调用者来说,一种就是进行捕获处理,另一种就是继续将异常抛出去,使用throw来声明。
-
- 声明异常throws
- 声明异常: 就是将问题标识出来,报告给调用者,如果方法内通过throw 抛出了编译时(受检)异常,而没有捕获处理,那么必须通过throws 来进行声明,让调用者自己去处理。
- 关键字throws 用于方法上来声明一个异常,用于表示当前的方法不处理异常,而是提醒该方法的调用者来处理异常。
- 声明异常的格式:
[访问修饰符] 返回值类型 方法名(参数列表) throws 异常类名1….异常类2{ //代码块 } |
【注意】:throws只能用于方法的签名的后面,而throw 只能用于方法体中。
-
- 异常实践
- 尽量减少try语句块的体积,因为try中每句代码的执行都需要判断有没有异常发生,影响了代码执行的效率。
- 运行时异常被抛出可以不处理,即不捕获也不声明,但是如果是受检(编译)异常被抛出,必须处理,一种throws来进行声明,另一种是利用 try….catch() 来进行捕获并处理。
- 如果父类抛出 了多个异常,子类重写父类的该方法时,只能抛出相同的异常或异常的子类。
- 父类方法没有抛出异常,子类重写父类的该方法时,也不可以抛出异常,此时如果子类产生异常,只能声明并处理,不能声明抛出。
- 在开发和调式阶段,建议吧异常的信息打印处理,方便解决问题。
-
练习题:要求:
我们模拟登录操作,如果用户名已存在,则抛出异常并提示:该用户名已被注册。
a)首选自定义一个登录异常类 LoginException
b)模拟登录操作,使用数组或者 ArrayList 模拟数据库中存储的数据,并且提供当前注册账号是否存在的方法用于判断。
//自定义一个异常类 public class LoginException extends Exception{ public LoginException() { }
public LoginException(String message) { super(message); }
public LoginException(String message, Throwable cause) { super(message, cause); }
public LoginException(Throwable cause) { super(cause); }
public LoginException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
public class Login { static String[]stuName={"liwenjie","zhangsan","李文杰","桑凤娇","郭朝旭"};
public static void main(String[] args){ Scanner input=new Scanner(System.in); System.out.println("请输入用户名"); String userName=input.next(); try{ boolean flag=display(userName); if(flag){ System.out.println("用户注册成功"); } }catch (Exception ce){ ce.printStackTrace(); } } public static boolean display(String name)throws Exception{ for(String ss:stuName){ if(ss.equals(name)){ //抛出一个异常 throw new LoginException("用户名已被注册过!!!"); } } return true; } }
//集合版的模拟登录系统 public class Student{ public static void main(String[] args) { Scanner input=new Scanner(System.in); System.out.println("请输入用户名:"); String username=input.next(); try{ boolean flag=meth(username); if(flag){ System.out.println("注册成功"); } }catch (Exception ce){ ce.printStackTrace(); } } public static boolean meth(String name)throws Exception{ ArrayList<String>list=new ArrayList<>(); list.add("sangfengjiao"); list.add("liwnejie"); list.add("mashitian"); list.add("赵云"); list.add("zhangsan"); for(String ss:list){ if(ss.contains(name)){ throw new LoginException("用户名已被注册过!!!"); } } return true; } } |
2.6 3. 自定义异常类
每一个学生(Student)都有学号,姓名和分数,分数永远不能为负数。如果老师给学生赋值一个负数,抛出一个自定义异常。
//自定义异常类 public class Student2 { private String stuName; private Integer stuXue; private Double stuScore; public Student2(){
} public Student2(String stuName,Integer stuXue,Double stuScore){ this.stuName=stuName; this.stuXue=stuXue; this.stuScore=stuScore; }
public String getStuName() { return stuName; }
public void setStuName(String stuName) { this.stuName = stuName; }
public Integer getStuXue() { return stuXue; }
public void setStuXue(Integer stuXue) { this.stuXue = stuXue; }
public Double getStuScore() { return stuScore; }
public void setStuScore(Double stuScore){ if(stuScore<0){ throw new StudentScore("分数不能为负数!!!!"); }else{ this.stuScore = stuScore; }
} } public class StudentScore extends RuntimeException{ public StudentScore() { }
public StudentScore(String message) { super(message); }
public StudentScore(String message, Throwable cause) { super(message, cause); }
public StudentScore(Throwable cause) { super(cause); }
public StudentScore(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } public class Stundent3 { public static void main(String[] args) { //创建Student对象 Student2 ss=new Student2(); ss.setStuName("李文杰"); //抛出:day02.StudentScore: 分数不能为负数!!!! ss.setStuScore(-100.0); ss.setStuXue(33); System.out.println(ss.getStuName()); System.out.println(ss.getStuScore()); System.out.println(ss.getStuXue()); } } |