微信公众号html
本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到个人仓库里查看java
https://github.com/h2pl/Java-...
喜欢的话麻烦点下Star哈mysql
文章首发于个人我的博客:git
www.how2playlife.com
本文是微信公众号【Java技术江湖】的《夯实Java基础系列博文》其中一篇,本文部份内容来源于网络,为了把本文主题讲得清晰透彻,也整合了不少我认为不错的技术博客内容,引用其中了一些比较好的博客文章,若有侵权,请联系做者。
该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每一个Java知识点背后的实现原理,更完整地了解整个Java技术体系,造成本身的知识框架。为了更好地总结和检验你的学习成果,本系列文章也会提供每一个知识点对应的面试题以及参考答案。程序员
若是对本系列文章有什么建议,或者是有什么疑问的话,也能够关注公众号【Java技术江湖】联系做者,欢迎你参与本系列博文的创做和修订。github
<!-- more -->面试
首先咱们能够明确一点就是异常的处理机制能够确保咱们程序的健壮性,提升系统可用率。虽然咱们不是特别喜欢看到它,可是咱们不能不认可它的地位,做用。
在没有异常机制的时候咱们是这样处理的:经过函数的返回值来判断是否发生了异常(这个返回值一般是已经约定好了的),调用该函数的程序负责检查而且分析返回值。虽然能够解决异常问题,可是这样作存在几个缺陷:算法
一、 容易混淆。若是约定返回值为-11111时表示出现异常,那么当程序最后的计算结果然的为-1111呢?二、 代码可读性差。将异常处理代码和程序代码混淆在一块儿将会下降代码的可读性。sql
三、 由调用函数来分析异常,这要求程序员对库函数有很深的了解。数据库
在OO中提供的异常处理机制是提供代码健壮的强有力的方式。使用异常机制它可以下降错误处理代码的复杂度,若是不使用异常,那么就必须检查特定的错误,并在程序中的许多地方去处理它。
而若是使用异常,那就没必要在方法调用处进行检查,由于异常机制将保证可以捕获这个错误,而且,只需在一个地方处理错误,即所谓的异常处理程序中。
这种方式不只节约代码,并且把“概述在正常执行过程当中作什么事”的代码和“出了问题怎么办”的代码相分离。总之,与之前的错误处理方法相比,异常机制使代码的阅读、编写和调试工做更加层次分明。(摘自《Think in java 》)。
该部份内容选自http://www.cnblogs.com/chenss...
在《Think in java》中是这样定义异常的:异常情形是指阻止当前方法或者做用域继续执行的问题。在这里必定要明确一点:异常代码某种程度的错误,尽管Java有异常处理机制,可是咱们不能以“正常”的眼光来看待异常,异常处理机制的缘由就是告诉你:这里可能会或者已经产生了错误,您的程序出现了不正常的状况,可能会致使程序失败!那么何时才会出现异常呢?只有在你当前的环境下程序没法正常运行下去,也就是说程序已经没法来正确解决问题了,这时它所就会从当前环境中跳出,并抛出异常。抛出异常后,它首先会作几件事。
首先,它会使用new建立一个异常对象,而后在产生异常的位置终止程序,而且从当前环境中弹出对异常对象的引用,这时。异常处理机制就会接管程序,并开始寻找一个恰当的地方来继续执行程序,这个恰当的地方就是异常处理程序。
总的来讲异常处理机制就是当程序发生异常时,它强制终止程序运行,记录异常信息并将这些信息反馈给咱们,由咱们来肯定是否处理异常。
[外链图片转存失败(img-KNxcBTK0-1569073569353)(https://images0.cnblogs.com/b...]
从上面这幅图能够看出,Throwable是java语言中全部错误和异常的超类(万物便可抛)。它有两个子类:Error、Exception。
Java标准库内建了一些通用的异常,这些类以Throwable为顶层父类。
Throwable又派生出Error类和Exception类。
错误:Error类以及他的子类的实例,表明了JVM自己的错误。错误不能被程序员经过代码处理,Error不多出现。所以,程序员应该关注Exception为父类的分支下的各类异常类。
异常:Exception以及他的子类,表明程序运行时发送的各类不指望发生的事件。能够被Java异常处理机制使用,是异常处理的核心。
整体上咱们根据Javac对异常的处理要求,将异常类分为2类。
非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。因此若是愿意,咱们能够编写代码处理(使用try…catch…finally)这样的异常,也能够不处理。对于这些异常,咱们应该修正代码,而不是去经过异常处理器处理 。这样的异常发生的缘由多半是代码写的有问题。如除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。
检查异常(checked exception):除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常作预备处理工做(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,不然编译不会经过。
这样的异常通常是由程序的运行环境致使的。由于程序可能被运行在各类未知的环境下,而程序员没法干预用户如何使用他编写的程序,因而程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。
须要明确的是:检查和非检查是对于javac来讲的,这样就很好理解和区分了。
这部份内容摘自http://www.importnew.com/2661...
异常是在执行某个函数时引起的,而函数又是层级调用,造成调用栈的,由于,只要一个函数发生了异常,那么他的全部的caller都会被异常影响。当这些被影响的函数以异常信息输出时,就造成的了异常追踪栈。
异常最早发生的地方,叫作异常抛出点。
public class 异常 { public static void main (String [] args ) { System . out. println( "----欢迎使用命令行除法计算器----" ) ; CMDCalculate (); } public static void CMDCalculate () { Scanner scan = new Scanner ( System. in ); int num1 = scan .nextInt () ; int num2 = scan .nextInt () ; int result = devide (num1 , num2 ) ; System . out. println( "result:" + result) ; scan .close () ; } public static int devide (int num1, int num2 ){ return num1 / num2 ; } // ----欢迎使用命令行除法计算器---- // 1 // 0 // Exception in thread "main" java.lang.ArithmeticException: / by zero // at com.javase.异常.异常.devide(异常.java:24) // at com.javase.异常.异常.CMDCalculate(异常.java:19) // at com.javase.异常.异常.main(异常.java:12)
// ----欢迎使用命令行除法计算器---- // r // Exception in thread "main" java.util.InputMismatchException // at java.util.Scanner.throwFor(Scanner.java:864) // at java.util.Scanner.next(Scanner.java:1485) // at java.util.Scanner.nextInt(Scanner.java:2117) // at java.util.Scanner.nextInt(Scanner.java:2076) // at com.javase.异常.异常.CMDCalculate(异常.java:17) // at com.javase.异常.异常.main(异常.java:12)
[外链图片转存失败(img-9rqUQJQj-1569073569354)(http://incdn1.b0.upaiyun.com/...]
从上面的例子能够看出,当devide函数发生除0异常时,devide函数将抛出ArithmeticException异常,所以调用他的CMDCalculate函数也没法正常完成,所以也发送异常,而CMDCalculate的caller——main 由于CMDCalculate抛出异常,也发生了异常,这样一直向调用栈的栈底回溯。
这种行为叫作异常的冒泡,异常的冒泡是为了在当前发生异常的函数或者这个函数的caller中找到最近的异常处理程序。因为这个例子中没有使用任何异常处理机制,所以异常最终由main函数抛给JRE,致使程序终止。
上面的代码不使用异常处理机制,也能够顺利编译,由于2个异常都是非检查异常。可是下面的例子就必须使用异常处理机制,由于异常是检查异常。
代码中我选择使用throws声明异常,让函数的调用者去处理可能发生的异常。可是为何只throws了IOException呢?由于FileNotFoundException是IOException的子类,在处理范围内。
下面看一个例子
//错误即error通常指jvm没法处理的错误 //异常是Java定义的用于简化错误处理流程和定位错误的一种工具。 public class 错误和错误 { Error error = new Error(); public static void main(String[] args) { throw new Error(); } //下面这四个异常或者错误有着不一样的处理方法 public void error1 (){ //编译期要求必须处理,由于这个异常是最顶层异常,包括了检查异常,必需要处理 try { throw new Throwable(); } catch (Throwable throwable) { throwable.printStackTrace(); } } //Exception也必须处理。不然报错,由于检查异常都继承自exception,因此默认须要捕捉。 public void error2 (){ try { throw new Exception(); } catch (Exception e) { e.printStackTrace(); } } //error能够不处理,编译不报错,缘由是虚拟机根本没法处理,因此啥都不用作 public void error3 (){ throw new Error(); } //runtimeexception众所周知编译不会报错 public void error4 (){ throw new RuntimeException(); } // Exception in thread "main" java.lang.Error // at com.javase.异常.错误.main(错误.java:11) }
在编写代码处理异常时,对于检查异常,有2种不一样的处理方式:
使用try…catch…finally语句块处理它。或者,在函数签名中使用throws 声明交给函数调用者caller去解决。
下面看几个具体的例子,包括error,exception和throwable
上面的例子是运行时异常,不须要显示捕获。
下面这个例子是可检查异常需,要显示捕获或者抛出。
@Test public void testException() throws IOException { //FileInputStream的构造函数会抛出FileNotFoundException FileInputStream fileIn = new FileInputStream("E:\\a.txt"); int word; //read方法会抛出IOException while((word = fileIn.read())!=-1) { System.out.print((char)word); } //close方法会抛出IOException fileIn.close(); }
通常状况下的处理方式 try catch finally
public class 异常处理方式 { @Test public void main() { try{ //try块中放可能发生异常的代码。 InputStream inputStream = new FileInputStream("a.txt"); //若是执行完try且不发生异常,则接着去执行finally块和finally后面的代码(若是有的话)。 int i = 1/0; //若是发生异常,则尝试去匹配catch块。 throw new SQLException(); //使用1.8jdk同时捕获多个异常,runtimeexception也能够捕获。只是捕获后虚拟机也没法处理,因此不建议捕获。 }catch(SQLException | IOException | ArrayIndexOutOfBoundsException exception){ System.out.println(exception.getMessage()); //每个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。Java7中能够将多个异常声明在一个catch中。 //catch后面的括号定义了异常类型和异常参数。若是异常与之匹配且是最早匹配到的,则虚拟机将使用这个catch块来处理异常。 //在catch块中可使用这个块的异常参数来获取异常的相关信息。异常参数是这个catch块中的局部变量,其它块不能访问。 //若是当前try块中发生的异常在后续的全部catch中都没捕获到,则先去执行finally,而后到这个函数的外部caller中去匹配异常处理器。 //若是try中没有发生异常,则全部的catch块将被忽略。 }catch(Exception exception){ System.out.println(exception.getMessage()); //... }finally{ //finally块一般是可选的。 //不管异常是否发生,异常是否匹配被处理,finally都会执行。 //finally主要作一些清理工做,如流的关闭,数据库链接的关闭等。 }
一个try至少要跟一个catch或者finally
try { int i = 1; }finally { //一个try至少要有一个catch块,不然, 至少要有1个finally块。可是finally不是用来处理异常的,finally不会捕获异常。 } }
异常出现时该方法后面的代码不会运行,即便异常已经被捕获。这里举出一个奇特的例子,在catch里再次使用try catch finally
@Test public void test() { try { throwE(); System.out.println("我前面抛出异常了"); System.out.println("我不会执行了"); } catch (StringIndexOutOfBoundsException e) { System.out.println(e.getCause()); }catch (Exception ex) { //在catch块中仍然可使用try catch finally try { throw new Exception(); }catch (Exception ee) { }finally { System.out.println("我所在的catch块没有执行,我也不会执行的"); } } } //在方法声明中抛出的异常必须由调用方法处理或者继续往上抛, // 当抛到jre时因为没法处理终止程序 public void throwE (){ // Socket socket = new Socket("127.0.0.1", 80); //手动抛出异常时,不会报错,可是调用该方法的方法须要处理这个异常,不然会出错。 // java.lang.StringIndexOutOfBoundsException // at com.javase.异常.异常处理方式.throwE(异常处理方式.java:75) // at com.javase.异常.异常处理方式.test(异常处理方式.java:62) throw new StringIndexOutOfBoundsException(); }
其实有的语言在遇到异常后仍然能够继续运行
有的编程语言当异常被处理后,控制流会恢复到异常抛出点接着执行,这种策略叫作:resumption model of exception handling(恢复式异常处理模式 )而Java则是让执行流恢复处处理了异常的catch块后接着执行,这种策略叫作:termination model of exception handling(终结式异常处理模式)
throws是另外一种处理异常的方式,它不一样于try…catch…finally,throws仅仅是将函数中可能出现的异常向调用者声明,而本身则不具体处理。
采起这种异常处理的缘由多是:方法自己不知道如何处理这样的异常,或者说让调用者处理更好,调用者须要为可能发生的异常负责。
public void foo() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN { //foo内部能够抛出 ExceptionType1 , ExceptionType2 ,ExceptionTypeN 类的异常,或者他们的子类的异常对象。 }
finally块无论异常是否发生,只要对应的try执行了,则它必定也执行。只有一种方法让finally块不执行:System.exit()。所以finally块一般用来作资源释放操做:关闭文件,关闭数据库链接等等。
良好的编程习惯是:在try块中打开资源,在finally块中清理释放这些资源。
须要注意的地方:
一、finally块没有处理异常的能力。处理异常的只能是catch块。
二、在同一try…catch…finally块中 ,若是try中抛出异常,且有匹配的catch块,则先执行catch块,再执行finally块。若是没有catch块匹配,则先执行finally,而后去外面的调用者中寻找合适的catch块。
三、在同一try…catch…finally块中 ,try发生异常,且匹配的catch块中处理异常时也抛出异常,那么后面的finally也会执行:首先执行finally块,而后去外围调用者中寻找合适的catch块。
public class finally使用 { public static void main(String[] args) { try { throw new IllegalAccessException(); }catch (IllegalAccessException e) { // throw new Throwable(); //此时若是再抛异常,finally没法执行,只能报错。 //finally不管什么时候都会执行 //除非我显示调用。此时finally才不会执行 System.exit(0); }finally { System.out.println("算你狠"); } } }
throw exceptionObject
程序员也能够经过throw语句手动显式的抛出一个异常。throw语句的后面必须是一个异常对象。
throw 语句必须写在函数中,执行throw 语句的地方就是一个异常抛出点,==它和由JRE自动造成的异常抛出点没有任何差异。==
public void save(User user) { if(user == null) throw new IllegalArgumentException("User对象为空"); //...... }
后面开始的大部份内容都摘自http://www.cnblogs.com/lulipr...
该文章写的十分细致到位,使人钦佩,是我目前为之看到关于异常最详尽的文章,能够说是站在巨人的肩膀上了。
异常的链化
在一些大型的,模块化的软件开发中,一旦一个地方发生异常,则如骨牌效应同样,将致使一连串的异常。假设B模块完成本身的逻辑须要调用A模块的方法,若是A模块发生异常,则B也将不能完成而发生异常。
==可是B在抛出异常时,会将A的异常信息掩盖掉,这将使得异常的根源信息丢失。异常的链化能够将多个模块的异常串联起来,使得异常信息不会丢失。==
异常链化:以一个异常对象为参数构造新的异常对象。新的异对象将包含先前异常的信息。这项技术主要是异常类的一个带Throwable参数的函数来实现的。这个当作参数的异常,咱们叫他根源异常(cause)。
查看Throwable类源码,能够发现里面有一个Throwable字段cause,就是它保存了构造时传递的根源异常参数。这种设计和链表的结点类设计一模一样,所以造成链也是天然的了。
public class Throwable implements Serializable { private Throwable cause = this; public Throwable(String message, Throwable cause) { fillInStackTrace(); detailMessage = message; this.cause = cause; } public Throwable(Throwable cause) { fillInStackTrace(); detailMessage = (cause==null ? null : cause.toString()); this.cause = cause; } //........ }
下面看一个比较实在的异常链例子哈
public class 异常链 { @Test public void test() { C(); } public void A () throws Exception { try { int i = 1; i = i / 0; //当我注释掉这行代码并使用B方法抛出一个error时,运行结果以下 // 四月 27, 2018 10:12:30 下午 org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry loadTestEngines // 信息: Discovered TestEngines with IDs: [junit-jupiter] // java.lang.Error: B也犯了个错误 // at com.javase.异常.异常链.B(异常链.java:33) // at com.javase.异常.异常链.C(异常链.java:38) // at com.javase.异常.异常链.test(异常链.java:13) // at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) // Caused by: java.lang.Error // at com.javase.异常.异常链.B(异常链.java:29) }catch (ArithmeticException e) { //这里经过throwable类的构造方法将最底层的异常从新包装并抛出,此时注入了A方法的信息。最后打印栈信息时能够看到caused by A方法的异常。 //若是直接抛出,栈信息打印结果只能看到上层方法的错误信息,不能看到实际上是A发生了错误。 //因此须要包装并抛出 throw new Exception("A方法计算错误", e); } } public void B () throws Exception,Error { try { //接收到A的异常, A(); throw new Error(); }catch (Exception e) { throw e; }catch (Error error) { throw new Error("B也犯了个错误", error); } } public void C () { try { B(); }catch (Exception | Error e) { e.printStackTrace(); } } //最后结果 // java.lang.Exception: A方法计算错误 // at com.javase.异常.异常链.A(异常链.java:18) // at com.javase.异常.异常链.B(异常链.java:24) // at com.javase.异常.异常链.C(异常链.java:31) // at com.javase.异常.异常链.test(异常链.java:11) // 省略 // Caused by: java.lang.ArithmeticException: / by zero // at com.javase.异常.异常链.A(异常链.java:16) // ... 31 more }
若是要自定义异常类,则扩展Exception类便可,所以这样的自定义异常都属于检查异常(checked exception)。若是要自定义非检查异常,则扩展自RuntimeException。
按照国际惯例,自定义的异常应该老是包含以下的构造函数:
一个无参构造函数
一个带有String参数的构造函数,并传递给父类的构造函数。
一个带有String参数和Throwable参数,并都传递给父类构造函数
一个带有Throwable 参数的构造函数,并传递给父类的构造函数。
下面是IOException类的完整源代码,能够借鉴。
public class IOException extends Exception { static final long serialVersionUID = 7818375828146090155L; public IOException() { super(); } public IOException(String message) { super(message); } public IOException(String message, Throwable cause) { super(message, cause); } public IOException(Throwable cause) { super(cause); } }
异常的注意事项
当子类重写父类的带有 throws声明的函数时,其throws声明的异常必须在父类异常的可控范围内——用于处理父类的throws方法的异常处理器,必须也适用于子类的这个带throws方法 。这是为了支持多态。例如,父类方法throws 的是2个异常,子类就不能throws 3个及以上的异常。父类throws IOException,子类就必须throws IOException或者IOException的子类。
至于为何?我想,也许下面的例子能够说明。
class Father { public void start() throws IOException { throw new IOException(); } } class Son extends Father { public void start() throws Exception { throw new SQLException(); } }
/假设上面的代码是容许的(实质是错误的)*/
class Test { public static void main(String[] args) { Father[] objs = new Father[2]; objs[0] = new Father(); objs[1] = new Son(); for(Father obj:objs) { //由于Son类抛出的实质是SQLException,而IOException没法处理它。 //那么这里的try。。catch就不能处理Son中的异常。 //多态就不能实现了。 try { obj.start(); }catch(IOException) { //处理IOException } } } }
==Java的异常执行流程是线程独立的,线程之间没有影响==
Java程序能够是多线程的。每个线程都是一个独立的执行流,独立的函数调用栈。若是程序只有一个线程,那么没有被任何代码处理的异常 会致使程序终止。若是是多线程的,那么没有被任何代码处理的异常仅仅会致使异常所在的线程结束。也就是说,Java中的异常是线程独立的,线程的问题应该由线程本身来解决,而不要委托到外部,也不会直接影响到其它线程的执行。
下面看一个例子
public class 多线程的异常 { @Test public void test() { go(); } public void go () { ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0;i <= 2;i ++) { int finalI = i; try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } executorService.execute(new Runnable() { @Override //每一个线程抛出异常时并不会影响其余线程的继续执行 public void run() { try { System.out.println("start thread" + finalI); throw new Exception(); }catch (Exception e) { System.out.println("thread" + finalI + " go wrong"); } } }); } // 结果: // start thread0 // thread0 go wrong // start thread1 // thread1 go wrong // start thread2 // thread2 go wrong } }
首先一个不容易理解的事实:
在 try块中即使有return,break,continue等改变执行流的语句,finally也会执行。
public static void main(String[] args) { int re = bar(); System.out.println(re); } private static int bar() { try{ return 5; } finally{ System.out.println("finally"); } } /*输出: finally */
不少人面对这个问题时,老是在概括执行的顺序和规律,不过我以为仍是很难理解。我本身总结了一个方法。用以下GIF图说明。
[外链图片转存失败(img-SceF4t85-1569073569354)(http://incdn1.b0.upaiyun.com/...]
也就是说:try…catch…finally中的return 只要能执行,就都执行了,他们共同向同一个内存地址(假设地址是0×80)写入返回值,后执行的将覆盖先执行的数据,而真正被调用者取的返回值就是最后一次写入的。那么,按照这个思想,下面的这个例子也就不难理解了。
finally中的return 会覆盖 try 或者catch中的返回值。
public static void main(String[] args) { int result; result = foo(); System.out.println(result); /////////2 result = bar(); System.out.println(result); /////////2 } @SuppressWarnings("finally") public static int foo() { trz{ int a = 5 / 0; } catch (Exception e){ return 1; } finally{ return 2; } } @SuppressWarnings("finally") public static int bar() { try { return 1; }finally { return 2; } }
finally中的return会抑制(消灭)前面try或者catch块中的异常
class TestException { public static void main(String[] args) { int result; try{ result = foo(); System.out.println(result); //输出100 } catch (Exception e){ System.out.println(e.getMessage()); //没有捕获到异常 } try{ result = bar(); System.out.println(result); //输出100 } catch (Exception e){ System.out.println(e.getMessage()); //没有捕获到异常 } } //catch中的异常被抑制 @SuppressWarnings("finally") public static int foo() throws Exception { try { int a = 5/0; return 1; }catch(ArithmeticException amExp) { throw new Exception("我将被忽略,由于下面的finally中使用了return"); }finally { return 100; } } //try中的异常被抑制 @SuppressWarnings("finally") public static int bar() throws Exception { try { int a = 5/0; return 1; }finally { return 100; } } }
finally中的异常会覆盖(消灭)前面try或者catch中的异常
class TestException { public static void main(String[] args) { int result; try{ result = foo(); } catch (Exception e){ System.out.println(e.getMessage()); //输出:我是finaly中的Exception } try{ result = bar(); } catch (Exception e){ System.out.println(e.getMessage()); //输出:我是finaly中的Exception } } //catch中的异常被抑制 @SuppressWarnings("finally") public static int foo() throws Exception { try { int a = 5/0; return 1; }catch(ArithmeticException amExp) { throw new Exception("我将被忽略,由于下面的finally中抛出了新的异常"); }finally { throw new Exception("我是finaly中的Exception"); } } //try中的异常被抑制 @SuppressWarnings("finally") public static int bar() throws Exception { try { int a = 5/0; return 1; }finally { throw new Exception("我是finaly中的Exception"); } } }
上面的3个例子都异于常人的编码思惟,所以我建议:
不要在fianlly中使用return。不要在finally中抛出异常。
减轻finally的任务,不要在finally中作一些其它的事情,finally块仅仅用来释放资源是最合适的。
将尽可能将全部的return写在函数的最后面,而不是try … catch … finally中。
下面是我我的总结的在Java和J2EE开发者在面试中常常被问到的有关Exception和Error的知识。在分享个人回答的时候,我也给这些问题做了快速修订,而且提供源码以便深刻理解。我总结了各类难度的问题,适合新手码农和高级Java码农。若是你遇到了我列表中没有的问题,而且这个问题很是好,请在下面评论中分享出来。你也能够在评论中分享你面试时答错的状况。
1) Java中什么是Exception?
这个问题常常在第一次问有关异常的时候或者是面试菜鸟的时候问。我历来没见过面高级或者资深工程师的时候有人问这玩意,可是对于菜鸟,是很愿意问这个的。简单来讲,异常是Java传达给你的系统和程序错误的方式。在java中,异常功能是经过实现好比Throwable,Exception,RuntimeException之类的类,而后还有一些处理异常时候的关键字,好比throw,throws,try,catch,finally之类的。 全部的异常都是经过Throwable衍生出来的。Throwable把错误进一步划分为 java.lang.Exception
和 java.lang.Error. java.lang.Error 用来处理系统错误,例如java.lang.StackOverFlowError 之类的。而后 Exception用来处理程序错误,请求的资源不可用等等。
2) Java中的检查型异常和非检查型异常有什么区别?
这又是一个很是流行的Java异常面试题,会出如今各类层次的Java面试中。检查型异常和非检查型异常的主要区别在于其处理方式。检查型异常须要使用try, catch和finally关键字在编译期进行处理,不然会出现编译器会报错。对于非检查型异常则不须要这样作。Java中全部继承自java.lang.Exception类的异常都是检查型异常,全部继承自RuntimeException的异常都被称为非检查型异常。
3) Java中的NullPointerException和ArrayIndexOutOfBoundException之间有什么相同之处?
在Java异常面试中这并非一个很流行的问题,但会出如今不一样层次的初学者面试中,用来测试应聘者对检查型异常和非检查型异常的概念是否熟悉。顺便说一下,该题的答案是,这两个异常都是非检查型异常,都继承自RuntimeException。该问题可能会引出另外一个问题,即Java和C的数组有什么不一样之处,由于C里面的数组是没有大小限制的,绝对不会抛出ArrayIndexOutOfBoundException。
4)在Java异常处理的过程当中,你遵循的那些最好的实践是什么?
这个问题在面试技术经理是很是常见的一个问题。由于异常处理在项目设计中是很是关键的,因此精通异常处理是十分必要的。异常处理有不少最佳实践,下面列举集中,它们提升你代码的健壮性和灵活性:
1) 调用方法的时候返回布尔值来代替返回null,这样能够 NullPointerException。因为空指针是java异常里最恶心的异常
2) catch块里别不写代码。空catch块是异常处理里的错误事件,由于它只是捕获了异常,却没有任何处理或者提示。一般你起码要打印出异常信息,固然你最好根据需求对异常信息进行处理。
3)能抛受控异常(checked Exception)就尽可能不抛受非控异常(checked Exception)。经过去掉重复的异常处理代码,能够提升代码的可读性。
4) 绝对不要让你的数据库相关异常显示到客户端。因为绝大多数数据库和SQLException异常都是受控异常,在Java中,你应该在DAO层把异常信息处理,而后返回处理过的能让用户看懂并根据异常提示信息改正操做的异常信息。
5) 在Java中,必定要在数据库链接,数据库查询,流处理后,在finally块中调用close()方法。
5) 既然咱们能够用RuntimeException来处理错误,那么你认为为何Java中还存在检查型异常?
这是一个有争议的问题,在回答该问题时你应当当心。虽然他们确定愿意听到你的观点,但其实他们最感兴趣的仍是有说服力的理由。我认为其中一个理由是,存在检查型异常是一个设计上的决定,受到了诸如C++等比Java更早编程语言设计经验的影响。绝大多数检查型异常位于java.io包内,这是合乎情理的,由于在你请求了不存在的系统资源的时候,一段强壮的程序必须可以优雅的处理这种状况。经过把IOException声明为检查型异常,Java 确保了你可以优雅的对异常进行处理。另外一个可能的理由是,可使用catch或finally来确保数量受限的系统资源(好比文件描述符)在你使用后尽早获得释放。 Joshua
Bloch编写的 Effective Java 一书 中多处涉及到了该话题,值得一读。
6) throw 和 throws这两个关键字在java中有什么不一样?
一个java初学者应该掌握的面试问题。 throw 和 throws乍看起来是很类似的尤为是在你仍是一个java初学者的时候。尽管他们看起来类似,都是在处理异常时候使用到的。但在代码里的使用方法和用到的地方是不一样的。throws老是出如今一个函数头中,用来标明该成员函数可能抛出的各类异常, 你也能够申明未检查的异常,但这不是编译器强制的。若是方法抛出了异常那么调用这个方法的时候就须要将这个异常处理。另外一个关键字 throw 是用来抛出任意异常的,按照语法你能够抛出任意 Throwable (i.e. Throwable
或任何Throwable的衍生类) , throw能够中断程序运行,所以能够用来代替return . 最多见的例子是用 throw 在一个空方法中须要return的地方抛出 UnSupportedOperationException 代码以下 :
123 | private `static void show() {throw new UnsupportedOperationException("Notyet implemented" );`} |
---|
能够看下这篇 文章查看这两个关键字在java中更多的差别 。
7) 什么是“异常链”?
“异常链”是Java中很是流行的异常处理概念,是指在进行一个异常处理时抛出了另一个异常,由此产生了一个异常链条。该技术大多用于将“ 受检查异常” ( checked exception)封装成为“非受检查异常”(unchecked exception)或者RuntimeException。顺便说一下,若是由于由于异常你决定抛出一个新的异常,你必定要包含原有的异常,这样,处理程序才能够经过getCause()和initCause()方法来访问异常最终的根源。
) 你曾经自定义实现过异常吗?怎么写的?
很显然,咱们绝大多数都写过自定义或者业务异常,像AccountNotFoundException。在面试过程当中询问这个Java异常问题的主要缘由是去发现你如何使用这个特性的。这能够更准确和精致的去处理异常,固然这也跟你选择checked 仍是unchecked exception息息相关。经过为每个特定的状况建立一个特定的异常,你就为调用者更好的处理异常提供了更好的选择。相比通用异常(general exception),我更倾向更为精确的异常。大量的建立自定义异常会增长项目class的个数,所以,在自定义异常和通用异常之间维持一个平衡是成功的关键。
9) JDK7中对异常处理作了什么改变?
这是最近新出的Java异常处理的面试题。JDK7中对错误(Error)和异常(Exception)处理主要新增长了2个特性,一是在一个catch块中能够出来多个异常,就像原来用多个catch块同样。另外一个是自动化资源管理(ARM), 也称为try-with-resource块。这2个特性均可以在处理异常时减小代码量,同时提升代码的可读性。对于这些特性了解,不只帮助开发者写出更好的异常处理的代码,也让你在面试中显的更突出。我推荐你们读一下Java 7攻略,这样能够更深刻的了解这2个很是有用的特性。
10) 你遇到过 OutOfMemoryError 错误嘛?你是怎么搞定的?
这个面试题会在面试高级程序员的时候用,面试官想知道你是怎么处理这个危险的OutOfMemoryError错误的。必须认可的是,无论你作什么项目,你都会碰到这个问题。因此你要是说没遇到过,面试官确定不会买帐。要是你对这个问题不熟悉,甚至就是没碰到过,而你又有三、4年的Java经验了,那么准备好处理这个问题吧。在回答这个问题的同时,你也能够借机向面试秀一下你处理内存泄露、调优和调试方面的牛逼技能。我发现掌握这些技术的人都能给面试官留下深入的印象。
11) 若是执行finally代码块以前方法返回告终果,或者JVM退出了,finally块中的代码还会执行吗?
这个问题也能够换个方式问:“若是在try或者finally的代码块中调用了System.exit(),结果会是怎样”。了解finally块是怎么执行的,即便是try里面已经使用了return返回结果的状况,对了解Java的异常处理都很是有价值。只有在try里面是有System.exit(0)来退出JVM的状况下finally块中的代码才不会执行。
12)Java中final,finalize,finally关键字的区别
这是一个经典的Java面试题了。个人一个朋友为Morgan Stanley招电信方面的核心Java开发人员的时候就问过这个问题。final和finally是Java的关键字,而finalize则是方法。final关键字在建立不可变的类的时候很是有用,只是声明这个类是final的。而finalize()方法则是垃圾回收器在回收一个对象前调用,但也Java规范里面没有保证这个方法必定会被调用。finally关键字是惟一一个和这篇文章讨论到的异常处理相关的关键字。在你的产品代码中,在关闭链接和资源文件的是时候都必需要用到finally块。
https://www.xuebuyuan.com/324...
https://www.jianshu.com/p/49d...
http://c.biancheng.net/view/1...
https://blog.csdn.net/Lisilua...
https://blog.csdn.net/michael...
若是你们想要实时关注我更新的文章以及分享的干货的话,能够关注个人公众号【Java技术江湖】一位阿里 Java 工程师的技术小站,做者黄小斜,专一 Java 相关技术:SSM、SpringBoot、MySQL、分布式、中间件、集群、Linux、网络、多线程,偶尔讲点Docker、ELK,同时也分享技术干货和学习经验,致力于Java全栈开发!
Java工程师必备学习资源: 一些Java工程师经常使用学习资源,关注公众号后,后台回复关键字 “Java” 便可免费无套路获取。
做者是 985 硕士,蚂蚁金服 JAVA 工程师,专一于 JAVA 后端技术栈:SpringBoot、MySQL、分布式、中间件、微服务,同时也懂点投资理财,偶尔讲点算法和计算机理论基础,坚持学习和写做,相信终身学习的力量!
程序员3T技术学习资源: 一些程序员学习技术的资源大礼包,关注公众号后,后台回复关键字 “资料” 便可免费无套路获取。