java异常的学习2

200 阅读10分钟

异常的继续学习

好长时间不更新写东西了,自己太懒了,而且之前忙着学框架做项目,然后发现自己的基础实在是太差了,现在有返回来学基础,真的感觉基础的东西挺重要的,最近打算继续学习基础的东西,先写一篇简单的基础知识的入门,然后再写一个深入一点的(我的理解有限,大部分也是看书看博客)。加油吧!

一 异常的基本概念

什么是异常处理机制?之前的代码出现错误,肯定是要处理的,但是非常的麻烦,因为每次都要去处理文件是非常麻烦的,然后就有了一个异常处理机制,可以把问题的异常交给方法的调用者,然后统一去处理代码异常。

异常都是继承于Throwable接口,其中包括了error 和Exception,而对于exception,又分为RuntimeException, 而RuntimeException是为运行期异常是不需要进行异常声明的,如果你看到代码他直接抛出了异常,而有没有进行异常声明或者异常的处理,那么是运行期异常

1.1 异常的基本思想

如果出现问题之后,也许你不知道现在如何去处理这个问题,但是我们不应该置之不理,我需要看看是不是别人或别的地方能够处理这个问题。**我们采用如果当前环境中还没有足够的信息来解决这个问题,所以就把这个问题抛到一个更高级别的环境中 **(书上的话)

异常处理机制就是把描述再正常执行过程中的代码出错的代码怎么办的代码进行分离

1.2 异常的常用的几个方法

1 e.getMessage()//打印异常信息,是更具抛出的异常对象,如果抛出的无参的异常,那么就是得到的信息就是null,相反如果抛出的是有参数的那就就是构造方法里面的

public static void main(String[] args) {
    try {
        throw new Exception("诚实勇敢张博阳");
    } catch (Exception e) {
       e.printStackTrace();
        System.out.println("异常信息getMessage打印"+e.getMessage());//错误信息打印
        System.out.println("异常之getLocallizedMessage"+e.getLocalizedMessage());
        System.out.println("toString"+e);//对于异常进行一个简单的描述
        System.out.println("e,printStackTrace();");//打印标注的错误信息 
        e.printStackTrace(System.out);
    }

详细的可以查看api文档,其实用的比较多的就是直接打印出所用的错误信息

1.3 栈轨迹和异常的重新抛出

首先是栈轨迹,也就是堆栈,我的理解就是方法出现了异常,然后进行异常处理,然后转跳到其他的方法,把里面涉及到的方法统统放到栈里面显示,然后就有了栈轨迹。

代码如下:

书上的一个例子
static void f(){
    try {
        throw new Exception();
    } catch (Exception e) {
        e.printStackTrace();
    for(StackTraceElement st:e.getStackTrace()){
        System.out.println(st.getMethodName());
    }
    }
}
static void g(){
    f();
}
static void h(){
    g();
}
public static void main(String[] args) {
    f();
    System.out.println("-------------------------");
    g();
    System.out.println("-------------------------");
    h();
    System.out.println("--------------------------");
}

结果如下:

f
main //到main方法,先把mian方法入栈,然后再把f方法入栈,输出的时候依次弹栈。 
-------------------------
f
g
main //同理显示main方法,然后g方法调用f方法,分别先入g方法和f方法。
-------------------------
f
g
h
main
--------------------------

输出的顺序先f - g - m, f -g -h- main

我们再看下面的代码

public static void f() throws Exception {
    System.out.println("这是来自f方法的栈轨迹抛出的信息");
    throw new Exception("这是来自f方法");//设置进行打印,也就是通过getMessage()就能访问到
}
public static void g() throws Exception {
    try {
        f();
    } catch (Exception e) {
        System.out.println("以下是来自g方法的异常打印信息");
        e.printStackTrace(System.out);
        /**
         * 方法重抛只是打印了原来的抛出异常的地方,而没有进行更新异常。
         */
        //throw e;//重新抛出异常,在异常捕获的同时我有抛出了异常
        throw (Exception) e.fillInStackTrace();
    }

}
public static void h() throws Exception {
    try {
        g();
    } catch (Exception e) {
        System.out.println("以下是来自h方法打印的信息");
        e.printStackTrace(System.out);
        //throw e;//异常抛出
        //抛出更新代码更新信息
        throw (Exception) e.fillInStackTrace();
    }
}

/**
 * 异常处理的机制,集团抛出上层,之后统一处理
 * @param args
 */
public static void main(String[] args) {
    try {
    } catch (Exception e) {
        System.out.println("这是来自主方法的g方法的错误信息的打印");
        e.printStackTrace(System.out);
//        }
//        try {
//            h();
//        } catch (Exception e) {
//            System.out.println("这是来自主方法的h方法的错误信息的打印");
//            e.printStackTrace(System.out);
//        }
    }
}

运行结果:

这是来自f方法的栈轨迹抛出的信息
以下是来自g方法的异常打印信息
        g();
java.lang.Exception: 这是来自f方法
at javatink.javaAllException.ExceptionThrow.f(ExceptionThrow.java:12) //这边我们能清楚的看见他们入栈的顺序
at javatink.javaAllException.ExceptionThrow.g(ExceptionThrow.java:16)//和弹栈的顺序
at javatink.javaAllException.ExceptionThrow.main(ExceptionThrow.java:46)
这是来自主方法的g方法的错误信息的打印
java.lang.Exception: 这是来自f方法
at javatink.javaAllException.ExceptionThrow.f(ExceptionThrow.java:12)
at javatink.javaAllException.ExceptionThrow.g(ExceptionThrow.java:16)
at javatink.javaAllException.ExceptionThrow.main(ExceptionThrow.java:46)

但是看了上面的代码,我们发现了一个新的概念,那就是我在捕获异常的同时,还可以进行异常的重新的抛出。 书上说:重抛异常把异常抛给了上一级的环境中的异常处理程序中,(同一个try中catch后面的代码就不执行了(因为重新抛了异常肯定不会执行后面的)异常对象的所以信息得以保持,高一层环境中捕获此异常的处理程序可以从这个异常对象看到所有信息)

这也就是上面的看到的整个栈轨迹,但是他们大部分都是重复的,而我重抛出来的信息,也是原来的信息,也不是自己当前的异常的信息,我也应该如何操作,在重抛信息的同时,显示当前信息?通过代码:fillInStackTrace() 来更新信息 代码都是相同的,我在上面的代码加了注解,所以显示相同的信息,现在我把注解打开

代码如下:

	这里我只打开了g方法,同样的h方法也可以这样做
	public static void g() throws Exception {
    try {
        f();
    } catch (Exception e) {
        System.out.println("以下是来自g方法的异常打印信息");
        e.printStackTrace(System.out);
        /**
         * 方法重抛只是打印了原来的抛出异常的地方,而没有进行更新异常。
         */
//            throw e;//重新抛出异常,在异常捕获的同时我有抛出了异常
        throw (Exception) e.fillInStackTrace();*******重点看这里(我更新了栈轨迹)*******
    }
}

运行结果:

这是来自f方法的栈轨迹抛出的信息
以下是来自g方法的异常打印信息
java.lang.Exception: 这是来自f方法
	at javatink.javaAllException.ExceptionThrow.f(ExceptionThrow.java:12)
	at javatink.javaAllException.ExceptionThrow.g(ExceptionThrow.java:16)
	at javatink.javaAllException.ExceptionThrow.main(ExceptionThrow.java:46)
这是来自主方法的g方法的错误信息的打印
java.lang.Exception: 这是来自f方法
	at javatink.javaAllException.ExceptionThrow.g(ExceptionThrow.java:24)
	at javatink.javaAllException.ExceptionThrow.main(ExceptionThrow.java:46)

Process finished with exit code 0
//上面来自g方法的信息就进行了更新

二 异常捕获的方式

(之前异常的基础的文档里面说过了,这里就不重复说了)

2.1 异常的抛出

2.2 异常的声明

2.3 异常的捕获

2.4 异常的日志文件

三 finally深化学习

大家都知道finally的作用就是把内存之外的东西关闭掉(内存有垃圾回收机制)比如清理文件资源,数据库链接等。我最近看书发现对于那种简单一定不会抛出异常的我们可以直接采用try finally 的结构,直接在finally里面释放资源。 你没有想过如果设计构造器的时候,我们还能把资源放到finally里面进行资源释放吗?

例如:(书上的)一个文件打开的操作,这样的动作只有对象完全使用完毕之后,用户调用特殊的删除的方式进行删除。如果在对象创建构造方法的时候创建失败了,对象根本没有创建,那么我还要把资源的释放放到finally里面吗?

代码如下: private BufferedReader in;

public InputFile(String fname) throws Exception {
    try {
        in = new BufferedReader(new FileReader(fname));//这是对这个构造方法进行判断的
    } catch (FileNotFoundException e) {
        System.out.println("file no Open " + fname);
        throw e;//重抛异常
    } catch (Exception e) {
        //如果抛出别的异常进行资源的释放
        try {
            in.close();
        } catch (IOException se) {
            System.out.println("file is successful"); //如果还是不能关闭我们采用手动关闭i
        }
        throw e; //重抛异常
    }
}

/**
 * 判断是否有下一行数据
 *
 * @return
 */
public String getLine() {
    String s;
    try {
        s = in.readLine();
    } catch (IOException e) {
        throw new RuntimeException("readLine is failed");
    }
    return s;
}

/**
 * 关闭的操作
 */
public void dispose() {
    try {
        in.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

//下面是domain方法:
public static void main(String[] args) {
    try {
        InputFile inputFile = new InputFile("zbs.txt");//如果创建对象失败,直接进入catch
        try {
            String s ;
            int i = 1;
            while ((s = inputFile.getLine())!=null){
                System.out.println("zhang");
            }
        } catch (Exception e) {
            e.printStackTrace();//异常信息的打印
        } finally {
            inputFile.dispose();//用户释放资源
        }
    } catch (Exception e) {           
        e.printStackTrace();//上面创建对象失败,打印错误信息。
    }
}

上面我们可以发现,这个finally只有在对象创建成功的时候才可以执行,而对象创建失败了,直接进入catch语句。

这个是对容易出错的构造器,我们需要进行判断,而不是通常把资源放到finally里面,等待释放资源。

思考Exception 和RuntimeException的区别

0 Exception叫 已检查异常,而RuntimeExceptin叫为检查异常
1 当出现了RuntimeException异常的时候,我们可以不对异常进行处理,而是交给jvm进行异常的处理
2 而面对Exception的时候,我们必须要处理,要么你可以把异常抛出,或者自己捕获
3 其中RuntimeException 大多是都是由程序员的粗心大意引起的,**常见的运行期异常**有
1、NullPointerException:见的最多了,其实很简单,一般都是在null对象上调用方法了。
  String s=null;
  boolean eq=s.equals(""); // NullPointerException
   这里你看的非常明白了,为什么一到程序中就晕呢?
   public int getNumber(String str){
  if(str.equals("A")) return 1;
   else if(str.equals("B")) return 2;
   }
   这个方法就有可能抛出NullPointerException,我建议你主动抛出异常,因为代码一多,你可能又晕了。
   public int getNumber(String str){
  if(str==null) throw new NullPointerException("参数不能为空");
                                   //你是否觉得明白多了
  if(str.equals("A")) return 1;
      else if(str.equals("B")) return 2;
   }

	2、NumberFormatException:继承IllegalArgumentException,字符串转换为数字时出现。比如int i= Integer.parseInt("ab3");
	
	3、ArrayIndexOutOfBoundsException:数组越界。比如 int[] a=new int[3]; int b=a[3];
	
	4、StringIndexOutOfBoundsException:字符串越界。比如 String s="hello"; char c=s.chatAt(6);
	
	5、ClassCastException:类型转换错误。比如 Object obj=new Object(); String s=(String)obj;
	
	6、UnsupportedOperationException:该操作不被支持。如果我们希望不支持这个方法,可以抛出这个异常。既然不支持还要这个干吗?有可能子类中不想支持父类中有的方法,可以直接抛出这个异常。
	
	7、ArithmeticException:算术错误,典型的就是0作为除数的时候。
	
	8、IllegalArgumentException:非法参数,在把字符串转换成数字的时候经常出现的一个异常,我们可以在自己的程序中好好利用这个异常。
	

以上的来自信息是来自别人博客信息(找不到那个地址了)

补充:
RuntimeException是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。可能在执行方法期间抛出但未被捕获的RuntimeException 的任何子类都无需在 throws 子句中进行声明

Runtime Exception: 
在定义方法时不需要声明会抛出runtime exception; 在调用这个方法时不需要捕获这个runtime exception; runtime exception是从java.lang.RuntimeException或java.lang.Error类衍生出来的。 例如:nullpointexception,IndexOutOfBoundsException就属于runtime exceptio