1. Throwing and catching
1.1 Throwing Exceptions
在程序运行过程中,可能会遇到某些错误需要终止,这时程序就会抛出一个异常。
让我们看看一个常见的情况——IndexOutOfBounds
异常。下面的程序向ArrayMap中插入value为5,key为”hello”的一组数据,试图获取key为”yolp”的value:
public static void main (String[] args) {
ArrayMap<String, Integer> am = new ArrayMap<String, Integer>();
am.put("hello", 5);
System.out.println(am.get("yolp"));
}
运行这个程序时,发现这个key值根本不存在,然后造成混乱,结果打印了一些错误信息:
$ java ExceptionDemo
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
at ArrayMap.get(ArrayMap.java:38)
at ExceptionDemo.main(ExceptionDemo.java:6)
这是隐式的异常,是由Java本身抛出的错误。这个错误告诉我们ArrayIndexOutOfBoundsException
错误造成了程序混乱,但是它并没有明确那个数据或是那个步骤造成了这个错误,对用户来讲并没有给出很多有价值的提示。
不过,我们可以使用throw
关键字,抛出自己的异常信息。比如说,我们可以重新实现get方法:
public V get(K key) {
intlocation = findKey(key);
if(location < 0) {
throw newIllegalArgumentException("Key " + key + " does not exist in map."\);
}
return values[findKey(key)];
}
这时候再运行程序,我们就可以清楚的看到出错的位置:
$java ExceptionDemo
Exception in thread "main" java.lang.IllegalArgumentException: Key yolp does not exist in map.
at ArrayMap.get(ArrayMap.java:40)
at ExceptionDemo.main(ExceptionDemo.java:6)
1.2 Catching Exceptions
我们已经知道,当程序运行出错时会抛出异常,那么当抛出异常时我们还能做些什么呢?考虑一下下面这些异常情况:
- 你试图使用3831240亿个字节的内存
- 你试图把一个对象转换成dog,但这个对象的动态类型不是dog
- 你试图调用一个指向null的引用类型的方法
- 你试图访问数组-1位置的元素
我们进行如下操作:
Object o = "mulchor";
Planet x = (Planet) o;
将会导致问题:
Exception in thread "main" java.lang.ClassCastException:
java.lang.String cannot be cast to Planet
接着,我们用显示的异常处理提供错误信息:
public static void main(String[] args) {
System.out.println("ayyy lmao");
throw new RuntimeException("For no reason.");
}
这种情况下运行程序:
$ java Alien
ayyy lmao
Exception in thread "main" java.lang.RuntimeException: For no reason.
at Alien.main(Alien.java:4)
于是我们发现,即使程序中没有错误,我们也可以实例化RuntimeException
类来抛出异常
更为常规的做法是,使用try
和break
方法捕捉可能发生异常的代码,考虑下面的例子:
Dog d = new Dog("Lucy", "Retriever", 80);
d.becomeAngry();
try {
d.receivePat();
} catch (Exception e) {
System.out.println("Tried to pat: " + e);
}
System.out.println(d);
程序的结果可能会是这样:
$ java ExceptionDemo
Tried to pat: java.lang.RuntimeException: grrr... snarl snarl
Lucy is a displeased Retriever weighing 80.0 standard lb units.
1.3 The Philosophy Of Exceptions
异常的设计不仅仅是为了捕获程序中的错误,提供给用户更准确的信息。更重要的是,它能把处理错误的程序彼此分开。考虑一下下面的情况:
func readFile: {
open the file;
determine its size;
allocate that much memory;
read the file into memory;
close the file;
}
这是一些对文件操作的程序,每个部分都可能导致错误,没有try-catch
的异常处理手段,我们可能写出这样的代码:
func readFile: {
open the file;
if (theFileIsOpen) {
determine its size;
if (gotTheFileLength) {
allocate that much memory;
} else {
return error("fileLengthError");
}
if (gotEnoughMemory) {
read the file into memory;
if (readFailed) {
return error("readError");
}
...
} else {
return error("memoryError");
}
} else {
return error("fileOpenError")
}
}
然而,用异常处理的手段代码就变得简洁多了:
func readFile: {
try {
open the file;
determine its size;
allocate that much memory;
read the file into memory;
close the file;
} catch (fileOpenFailed) {
doSomething;
} catch (sizeDeterminationFailed) {
doSomething;
} catch (memoryAllocationFailed) {
doSomething;
} catch (readFailed) {
doSomething;
} catch (fileCloseFailed) {
doSomething;
}
}
2. Checked vs Unchecked Exceptions
上面谈论到的异常都是运行时异常,我们可以称之为“unchecked” exceptions
;可有些错误简直连编译器都看不过去了,编译时期就会报错,我们称之为“checked” exceptions
。
3. Iterators and Iterables
3.1 Iteration
我们知道Java允许我们这样来遍历一个链表:
List<Integer> friends =
new ArrayList<Integer>();
friends.add(5);
friends.add(23);
friends.add(42);
for (int x : friends) {
System.out.println(x);
}
我们也可以使用iterator
来遍历,先在List.java
中定义:
public Iterator<E> iterator();
然后我们就可以使用它来遍历:
List<Integer> friends = new ArrayList<Integer>();
...
Iterator<Integer> seer = friends.iterator();
while (seer.hasNext()) {
System.out.println(seer.next());
}