运行时异常与受检异常如何处理?
- 异常表示程序运行过程中可能出现的非正常状态。
- 运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种创建运行错误,只有程序设计得没有问题通常就不会发生
- 受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发
- Java编译器要求方法必须声明抛出可能发生的受检异常,但是并不要求必须声明抛出未被捕获的运行时异常
异常和继承一样,是面向对象程序设计中经常被滥用的东西,在Effective Java中对异常的使用给出了以下指导原则:
- 不要将异常处理用于正常的控制流
- 对可以恢复的情况使用受检异常,对编程错误使用运行时异常
- 避免不必要的使用受检异常(可以通过一些状态检测手段来避免异常的发生)
- 优先使用标准的异常
- 每个方法抛出的异常都要有文档
- 保持异常的原子性
- 不要在catch中忽略捕获到的异常
列出一些你常见的运行时异常?
NullPointerException
:空指针异常ClassCastException
:类型转换异常IndexOutOfBoundsException
:角标越界异常ArithmeticException
:算术异常IllegalArgumentException
:非法参数异常SecurityException
:安全异常
阐述final、finally、fianlize的区别?
-
final
- 修饰类:表示该类不能被继承
- 修饰方法:表示方法不能被重写
- 修饰变量:表示变量只能一次赋值以后不能被修改(常量)
-
finally
通常放在
try...catch...
的后面构造总是执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中 -
finalize
Object
类中的方法,Java中允许使用finalize
方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize方法可以整理系统资源或者执行其他清理工作。
类ExceptionA继承Exception,类ExceptionB继承ExceptionA,如下代码的输出结果是什么?
try{
throw new ExceptionB("b");
}catch(ExceptionA e){
System.out.println("ExceptionA");
}catch(Exception e){
System.out.println("Exception");
}
输出:ExceptionA
根据里氏代换原则[能使用父类型的地方一定能使用子类型],抓取ExceptionA
类型异常的catch块时能够抓住try块中抛出的ExceptionB
类型的异常。
class Annoyance extends Exception {}
class Sneeze extends Annoyance {}
public class Human {
public static void main(String[] args) throws Exception {
try {
try {
throw new Sneeze();
} catch (Annoyance a) {
System.out.println("Caught Annoyance");
throw a;
}
} catch (Sneeze s) {
System.out.println("Caught Sneeze");
return;
} finally {
System.out.println("Hello World");
}
}
}
输出:
Caught Annoyance
Caught Annoyance
Hello World
List、Set、Map是否继承自Collection接口?
List、Set是,Map不是。
Map是键值对映射容器,与List和Set有明显的区别。Set存储的零散的元素且不允许有重复元素,List是线性结构的容器,适用于按数值索引访问元素的情形。
阐述ArrayList、Vector、LinkedList的存储性能和特性?
ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector中的方法由于添加了synchronized修饰,因此Vector是线程安全的容器,但性能上较ArrayList差,因此已经是Java的遗留容器。
LinkedList使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度快。
Vector属于遗留容器(Java早期的版本中提供的容器,除此之外,hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),已经不推荐使用。但是由于ArrayList和LinkedList都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)。
Collection和Collections的区别?
- Collection是一个接口,它是Set、List等容器的父接口
- Collections是一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。
List、Map、Set三个接口存取元素,各有什么特点?
- List以特定索引来存取元素,可以有重复元素
- Set不能存放重复元素(用对象的equals方法来判断元素是否重复)
- Map保存键值对映射,映射关系可以是一对一或多对一
- Set和Map容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的版本理论上存取时间复杂度为0(1),而基于排序树版本的实现在插入或删除元素时会按照元素或元素的键(key)构成排序树从而达到排序和去重的效果。
Thread类的sleep方法和对象的wait方法都可以让线程暂停执行,它们有什么区别?
- sleep方法(休眠)是线程类Thread类的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态)
- wait是Object类的方法,调用对象的wait方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池,只有调用对象的notify方法(或notifyAll方法)时才能唤醒等待池中的线程进入等锁池,如果线程重新获得对象锁就可以进入就绪状态。
线程的sleep方法和yield方法有什么区别?
- sleep方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield方法只会给相同优先级或更高优先级的线程以运行的机会
- 线程执行sleep方法后转入阻塞状态,而执行yield方法后转入就绪状态
- sleep方法声明抛出
InterruptedException
,而yield方法没有声明任何异常 - sleep方法比yield方法(跟操作系统CPU调度有关)具有更好的可移植性