Java基础之异常
文章目录
一、异常体系结构
在java语言中,将程序执行中发生的不正常情况
称为异常。
根据异常的种类,可分为两种。
-
ERROR
:Error 类是指 java 运行时系统的内部错误和资源耗尽错误。应用程序不会抛出该类对象。如果出现了这样的错误,除了告知用户,剩下的就是尽力使程序安全的终止。
例如:StackOverflowError和OOM。 -
EXCEPTION
:其他因变成错误或偶然的一些外在因素导致的一般性问题,可以使用针对行的代码进行处理。例如,空指针访问,试图读取不存在的文件,网络连接中断,数组角标越界
。
几点说明:
1.Error和Exception都继承于超类Throwable(可抛出的)。
2.Exception又可分为
RunTimeException(运行时异常):NullPointerException 、 ClassCastException,因为是java虚拟机正常运行期间的异常,该异常则被认为一定是程序员的异常。一般来说,不处理的话也不会有什么大问题。
编译时异常:就是我们通常所说的编译时异常,外部原因导致,例如FileNotFoundException,IOException以及SQL Exception等等,对这些异常我们必须捕获或者声明
。
所有的
异常根据受检和不受检又可分为checkedException和uncheckedException。
受检异常
编译器要求必须处理的异常。正确的程序在运行过程中,经常容易出现的、符合预期的异常情况。一旦发生此类异常,就必须采用某种方式进行处理。除 RuntimeException 及其子类外,其他的Exception 异常都属于受检异常。
非受检异常
编译器不会进行检查并且不要求必须处理的异常,也就说当程序中出现此类异常时,即使我们没有try-catch捕获它,也没有使用throws抛出该异常,编译也会正常通过。该类异常包括运行时异常(RuntimeException极其子类)和 Error(错误)
。
二、异常的处理
过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处跑出异常类的对象(throw new XXXException),并将此对象抛出。一旦抛出以后,其后的代码不再运行。(throw 就是代表着终止)
。
关于异常对象的产生:1.系统自动生成的异常对象,2,自己手动生成的异常的对象,并抛出throw。这边自己其实也是没解决的。
过程二:“抓”:可以理解为异常的处理方式:try-catch-finally (自己真正解决了) throws(自己没解决)
1.try-catch-finally的使用
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}catch(异常类型3 变量名3){
//处理异常的方式3
}
...finally{}
try-catch-finally 如何使用?
- try块: 用于捕获异常。其后可接零个或多个 catch 块,
如果没有 catch 块,则必须跟一个 finally 块。
- catch块: 用于处理 try 捕获到的异常。
- finally 块: 无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。
几点说明:
1.finally是可选的(finally不一定会执行)
2.使用try将可能出现的异常代码包装出来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配。
3.一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没有写finally的情况下),继续执行之后的代码。4catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。
catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错。(如代码段中的异常类型2的范围要大于异常类型1中的范围)
5.常用的异常对象处理方式 String getMessage() void printStackTrace();
6.在try结构中声明的变量,再出了try结构以后,就不能再被调用
7.try-catch-finally结构可以嵌套
体会1:这个是处理编译时异常,是将程序在编译时不再报错,但是运行时可能报错。
只是将一个编译时可能出现的异常,延迟到运行时异常。
体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。针对于编译时异常,我们说一定要考虑异常的处理。
异常代码示例
public class ExceptionTest1{
@Test
public void test1(){
String str="123";
str="abc";
try{
int num=str.parseInt(str);
System.out.println("hello1");
}catch(NumberFormatException e){
System.out.println("出现数值转换异常了,不要着急");
}
System.out.println("hello 2");
}
}
输出结果:出现数值转换异常了,不要着急
hello 2
public class exceptiontest {
@Test
public void test(){
try {
int a = 0;
if(a<1) throw new RuntimeException("数字不合法");
}catch (Exception E){
System.out.println(E.getMessage());;
}finally {
System.out.println("hello");
}
}
}
输出:数字不合法
hello
public class exceptiontest {
@Test
public void test(){
try {
int a =4/0;
throw new RuntimeException("数字不合法");//如果这边是Exception则需要抛出异常
}catch (Exception E){
System.out.println(E.getMessage());;
}finally {
System.out.println("hello");
}
}
}
并没有走到“数字不合法那里”
这两张图作为对比,我们可以看到try中也可以throw自己想定义的语句,否则会调用她固定的话语。
补充说明:
try-catch-finally中finally的使用:
1.finally是可选的
2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有return语句等情况。特殊情况如:jvm退出,线程死亡以及cpu终止运行则不会运行finally中的语句。
3.像数据库连接、输入输出流、网络编程Socket等资源。JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
2.throws+异常类型
1.throws+异常类型写在方法的声明处,指明此方法执行时,可能会抛出的异常类型。一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行。
代码示例
public class exceptiontest {
@Test
public void test() throws FileNotFoundException {
File file=new File("test.txt");
FileInputStream fileInputStream = new FileInputStream(file);
System.out.println("hello");
}
}
输出结果:java.io.FileNotFoundException: test.txt (No such file or directory)
可以看到下面的hello并没有输出
3.throws与throw的区别
1.throw 在方法体内,只能一个,抛出的是异常对象
,throws 函数名后或者参数列表后方法体前,可以跟多个,后面是异常类
2.thows代表一种倾向,可能但不一定实际发生。throw则是抛出了异常,一定抛出了某个异常对象。
3.throws用来声明异常,throw则是抛出具体的异常对象,执行到throw,功能就已经结束了。
三 、自定义异常类
1.继承于现有的异常结构:RuntimeException,Exception
2.提供全局常量:serialVersionUID
3.提供重载构造器
//编写MyException类
//或者继承RuntimeException(运行时异常)
public class MyException extends Exception {
private static final long serialVersionUID = 1L;
// 提供无参数的构造方法
public MyException() {
}
// 提供一个有参数的构造方法,可自动生成
public MyException(String msg) {
super(msg);// 把参数传递给Throwable的带String参数的构造方法
}
}
//写一个Student类,手动抛出MyException
class Student{
private int id;
// 该异常继承自Exception类,需要捕获或者向上抛出异常
public void regist(int id) throws Exception {
if ( id > 0){
this.id = id;
}else{
// 手动抛出异常
throw new MyException("不能输入负数");
}
}
}
//测试类StudentTest
public class StudentTest{
public static void main(String args[]){
try{
Student s = new Student();
s.regist(-1001);
// 对Student类throws的异常进行try-catch处理;
System.out.println(s);
}catch (Exception e){
System.out.println(e.getMessage());
}
//注意这边已经是调用的主函数了,所以必须捕获
}
}
四、异常的一些练习
答案为“进入方法A”,"、调用A方法的finally",“制造异常”,“进入方法B”,“调用B方法的finally”
分析:
首先我们从主函数进入,主函数捕获了异常,所以后面的会输出。
分析A:try中没有用catch来捕获异常,首先输出“进入方法A”,接着throw异常了,所以后面的语句就不会再运行,不管咋样,finally总会运行的
,接着在主函数这个地方捕获异常。
B的分析较常规。
五、Try-With-Resources
Java库中有很多资源需要手动关闭,例如打开的文件、连接的数据库等。在java7之前都是try-finally的方式关闭资源,try后面总是跟着一个“{}”。
举个例子:try-finally方式关闭资源
package com.chapter6;
import java.io.*;
public class Example6_8 {
public static void main(String[] args) {
copy("E:/MyFirst.java","F:/First.Java");//复制文件
}
private static void copy(String src, String des) {
InputStream in = null;
OutputStream out = null;
try{
in = new FileInputStream(src);
out = new FileOutputStream(des);
byte[] buff = new byte[1024];//创建一个长度为1024字节的字节数组
int n;
//从输入流一次最多流入buff.length个字节的数据到buff中,直到文件末尾结束
while ((n=in.read(buff))>=0){
//将数组buff中的数据从0位置开始,长度为n的字节输出到输出流中
out.write(buff,0,n);
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(in!=null){
try{
in.close();
}catch (IOException e){
e.printStackTrace();
}
}
if(out!=null){
try{
out.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
从上例可以看出,这种实现非常的杂乱冗长。
Java7之后,推出了Try-With-Resourcesz
声明代替之前的方式,try后跟括号"(",括号内的部分称为资源规范头。**资源规范头中可以包含多个定义,通过分号进行分隔。**规范头中定义的对象必须实现java.lang.AntoCloseable接口,这个接口中有一个close()方法,因此无论是否正常退出try语句块,这些对象都会在try语句块运行结束之后调用close方法,从而替代以前的在finally中关闭资源的功能,且不需要冗长的代码,另外,Try-With-Resources中的try语句可以不包含catch或者finally语句块而独立存在。
public class Example {
public static void main(String[] args) {
copy("E:/MyFirst.java","F:/First.Java");//复制文件
}
private static void copy(String src, String des) {
try (InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(des)) {
byte[] buff = new byte[1024];//创建一个长度为1024字节的字节数组
int n;
//从输入流一次最多流入buff.length个字节的数据到buff中,直到文件末尾结束
while ((n=in.read(buff))>=0){
//将数组buff中的数据从0位置开始,长度为n的字节输出到输出流中
out.write(buff,0,n);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
总结
出来混的都是要还的,希望自己好好积累,夯实基础。这就去看看异常的面试题,看看今天的学习成果咋样。