基础知识点-异常

1、Error和Exception有什么区别?

Error表示编译时和系统错误,通常不能预期和恢复,比如硬件故障、JVM崩溃、内存不足等;

Exception分为“被检查的异常(checked exception)”和”运行时的异常(runtime exception即非检查的异常)”

2、UnsupportedOperationException是什么?

使用Arrays.asList()后调用add、remove方法时出现java.lang.UnsupportedOperationException。

原因:Arrays.asList()返回java.util.Arrays$ArrayList,而不是ArrayList。Arrays$ArrayList和ArrayList都继承AbstractList,remove、add等方法在AbstractList中默认抛出异常且不作任何操作。ArrayList重写了这些方法来对list进行操作,Arrays$ArrayList没有重写导致。

解决方法是使用Iterator,或转为ArrayList。

3、NullPointerException和ArrayIndexOutOfBoundException之间有什么相同之处?

都是非检查型异常,都继承自RuntimeException。

4、什么是受检查的异常,什么是运行时异常?

被检查的异常(Checked exception):在程序中能预期,并要尝试修复,如FileNotFoundException。我们必须捕获此类异常,并为用户提供有用信息和合适日志来进行调试。Exception是所有被检查异常的父类。

运行时异常(Runtime Exception):又称不受检查异常。比如检索数组元素前必须确认数组长度,否则可能会抛出ArrayIndexOutOfBoundException。RuntimeException是所有运行时异常的父类。

5、运行时异常与一般异常有何异同?

相同点:都继承于Exception父类。

不同点:

1)运行时异常都是RuntimeException类及其子类异常;

2)一般异常是RuntimeException以外的异常;

6、简述一个你最常见到的runtime exception

ClassCastException类型强制转换异常

ArithmeticException算术异常类

NullPointerException空指针异常类

StringIndexOutOfBoundsException

IllegalArgumentException参数异常

NumberFormatException数字格式异常

ArrayIndexOutOfBoundsException数组下标越界异常

ClassNotFoundException找不到类异常

NoSuchMethodException方法未找到异常

FileNotFoundException文件未找到异常

7、如果执行finally代码块之前方法返回了结果,或者JVM退出了,finally块中的代码还会执行吗?

java中的finally块并不一定会被执行。至少有两种情况finally语句是不会执行的。

1)try语句没有被执行到

2)在try/catch块中有System.exit(0)来退出JVM

8、try里有return,finally还执行么?那么紧跟在这个try后的finally{}里的code会不会被执行,什么时候被执行,在return前还是后?

在异常处理中,无论是执行try还是catch,finally{}中的代码都会执行(除非特殊情况)。

执行return意味着结束对当前函数的调用并跳出这个函数体,任何语句要执行都只能在return前执行。

1)如果try-catch-finally中都有return,则finally块中的return将会覆盖别处的return,最终返回finally中的return值。

2)在try/catch中有return时,在finally块中改变基本类型的数据对返回值没有影响;而在finally中改变引用类型的数据会对返回结果有影响。

9、throw和throws有什么区别?

throws用来声明一个方法可能抛出的所有异常,throws将异常声明但不处理,把异常传给调用者处理。

throw则是抛出一个具体的异常类型。

10、OOM你遇到过哪些情况?如何解决的?

java Heap溢出:

除程序计数器外,JVM的其他几个运行区域都有发生OutOfMemoryError(OOM)异常的可能。

一般的异常信息:java.lang.OutOfMemoryError:Java heap spacess

堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,就会在对象到达最大堆容量限制后产生内存溢出异常。出现这种异常,一般通过内存影响分析工具对dump出来的堆转存快照进行分析,重点是确认内存中的对象是否必要,先分清是内存泄漏还是内存溢出(Memory Overflow)。

如果是内存泄漏,引用可进一步通过工具查看泄漏对象到GC Roots的链,就能找到泄露对象时是通过怎样的途径导致垃圾收集器无法自动回收。

如果不存在内存泄漏,应该检查虚拟机的参数(-Xmx与-Xms)设置是否适当。

虚拟机栈和本地方法栈溢出:

如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。

如果虚拟机无法申请到足够的内存空间,则抛出OutOfMemeryError异常;

注意当栈的大小越大,可分配的线程就越小。

运行时常量池溢出:

异常信息:java.lang.OutOfMemoryError:PermGen space

如果要向运行时常量池中添加内容,最简单的做法是使用String.interm()这个Native方法。该方法的作用是:如果池中已经包含一个等于此String的字符串,则返回池中这个字符串的String对象,否则,将此String对象包含的字符串添加到常量池中,并返回此String对象的引用。由于常量池分配在方法区,可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,从而间接限制其中常量池的容量。

方法区溢出:

方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。

异常信息:java.lang.OutOfMemoryError:PermGen space

一个类如果要被垃圾收集器回收,判定条件是很苛刻的,在经常动态生成大量Class的应用中,要特别注意。

11、SOF你遇到过哪些情况?StackOverFlow

程序中出现死循环或大量递归调用,在不断压栈过程中,造成栈容量超过默认大小而导致溢出。

栈溢出的原因:递归调用,大量循环或死循环,全局变量是否过多,数组、List 、map数据过多。

12、既然我们可以用RuntimeException来处理错误,为什么Java中还存在检查型异常?

其中一个理由是,存在检查型异常是一个设计上的决定,受到了诸如C++等更早编程语言设计经验的影响。绝大多数检查型异常位于java.io包内,在你请求不存在的系统资源时,一段强壮的程序必须能够优雅的处理这种情况。通过把IOException声明为检查型异常,确保你能优雅的对异常进行处理。

另一个可能的理由是,可以使用catch或finally来确保数量受限的系统资源(比如文件描述符)在使用后尽早得到释放。

13、当自己创建异常类的时候应该注意什么?

1)统一异常,团队开发一定要有规则,可以对每种业务新建一个异常。

2)结合业务中断程序运行,有时候java代码本身不会有错误,但是不符合业务逻辑。

3)可以隐藏底层异常,只打印业务日志,不用输出堆栈信息。

14、导致空指针异常的原因?

空指针异常出现在对引用类型的操作上,当引用变量指向空仍然去操作它的方法和属性时。

1)当在栈区声明了引用类型的变量,而未让该引用变量指向堆区的内存时(即只声明未初始化),再操作该引用变量则出空指针错。

2)声明和初始化了引用变量,但操作该引用变量的语句作用域不在声明该引用变量的语句作用域内。

15、怎么利用JUnit来测试一个方法的异常?

1)try…fail...catch…

2)@Test(expected=xxx) 

3)ExpectedException Rule

16、catch块里不写代码有什么问题?

1)调用方法时返回布尔值来代替返回null,这样可以避免NullPointerException。

2)catch块里别不写代码。空catch块是异常处理里的错误事件,因为它只是捕获了异常,却没有任何处理或者提示。通常要打印出异常信息,最好根据需求对异常信息进行处理。

3)通过去掉重复的异常处理代码,可以提高代码的可读性。

4)尽量不抛非控异常,绝对不要让你的数据库相关异常显示到客户端。由于绝大多数数据库和SQLException异常都是受控异常,在Java中,你应该在DAO层把异常信息处理掉,然后返回处理过的能让用户看懂并根据异常提示信息改正操作的异常信息。

5)在Java中,一定要在数据库连接,数据库查询,流处理后,在finally块中调用close()方法。

17、你曾经自定义实现过异常吗?

显然我们绝大多数都写过自定义或者业务异常,像AccountNotFoundException。这可以更准确和精致的去处理异常,当然这也跟你选择checked还是unchecked exception息息相关。通过为每一个特定的情况创建一个特定的异常,你就为调用者更好的处理异常提供了更好的选择。相比通用异常(general exception),我更倾向更为精确的异常。大量的创建自定义异常会增加项目class的个数,在自定义异常和通用异常之间维持一个平衡是成功的关键。

18、什么是异常链?

指在进行一个异常处理时抛出了另外一个异常,由此产生了一个异常链条。多用于将“ 受检查异常”(checked exception)封装成“非受检查异常”(unchecked exception)或RuntimeException。如果因为异常你决定抛出一个新的异常,一定要包含原有的异常,这样处理程序才可以通过getCause()和initCause()方法来访问异常最终的根源。

19、在try块中可以抛出异常吗?

可以。Java异常处理是通过5个关键字来实现的:try,catch,throw,throws,finally。

throw和throws都是抛出异常的,但有区别:

throws关键字通常被应用在声明方法时,用来指定可能抛出的异常,多个异常可以使用逗号隔开。

throw关键字通常用在方法体中,并且抛出一个异常对象。程序在执行到throw语句时立即停止,它后面的语句都不执行。通过throw抛出异常后,如果想在上一级代码中来捕获并处理异常,则需要在抛出异常的方法中使用throws关键字在方法声明中指明要跑出的异常;如果要捕捉throw抛出的异常,则必须使用try/catch语句。

20、Java与C++对比,C++或Java中的异常处理机制的简单原理和应用?

Java使用面向对象的方式来处理异常,它把程序中发生的每个异常分别封装到一个对象中,该对象中包含有异常的信息。

Java可以自定义异常类,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception。

1)Error表示应用程序本身无法克服和恢复的严重问题,例如内存溢出和线程死锁等系统问题。

2)Exception表示程序还能够克服和恢复的问题,又分为运行时异常和检查异常。例如,数组越界(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException)、类转换异常(ClassCastException);检查异常是运行环境变化或异常所导致的问题,是用户能够克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不应该死掉。

Java为运行时异常和检查异常提供了不同的解决方案,编译器强制检查异常必须try/catch处理或用throws声明继续抛给上层调用方法处理,而运行异常可以处理也可以不处理,编译器不强制用try/catch处理或throws声明,所以运行异常也称为Runtime异常。

你可能感兴趣的:(基础知识点-异常)