1.抛出异样

本节课咱们应用上一节创立的ArrayMap进行解说,假如你在main()中get()一个并不存在的key,如:

public static void main(String[] args) {   ArrayMap<String, Integer> am = new ArrayMap<String, Integer>();   am.put("hello", 5);   System.out.println(am.get("yolp"));}

那么当你运行时,会失去如下报错信息:

$ java ExceptionDemoException in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1    at ArrayMap.get(ArrayMap.java:38)    at ExceptionDemo.main(ExceptionDemo.java:6)

根据报错信息咱们能够得悉在ArrayMap.java代码第38行与ExceptiomDemo.java第6行出错,出错起因是数组越界。
当遇到谬误时,Java可能主动抛出异样并进行程序的运行。有时候,Java给出的报错信息并不显著,不容易得出谬误,咱们能够手动增加报错信息,也就是抛出一些异样。
应用关键字 throw 咱们能够抛出自定义的异样:

public V get(K key) {   int location = keyIndex(key);   if (location < 0) { throw new IllegalArgumentException("Key " +                             key + " does not exist in map.");  }   return values[keyIndex(key)];}

一些容易出现异常的例子:

  • You try to use 383,124 gigabytes of memory.
  • You try to cast an Object as a Dog, but dynamic type is not Dog.
  • You try to call a method using a reference variable that is equal to null.
  • You try to access index -1 of an array.
    抛出异样其实是创立一个异样类型的Object,也就是相当于实例化一个异样类,所以即便程序自身并无谬误,你也能够平白无故地抛出一个异样:
public static void main(String[] args) {    throw new RuntimeException("For no reason.");}

失去:

Exception in thread "main" java.lang.RuntimeException: For no reason.

2.捕捉异样

如果咱们仅仅只是在程序的某处抛出异样而不做任何解决,程序便会解体。然而咱们能够通过捕捉异样从而让程序持续运行上来,应用

try {   throw new SomeException();} catch (Exception e) {   doSomething;}

例如:

        try {            throw new RuntimeException("for no reason");        } catch (Exception e) {            System.out.println(e);        }        System.out.println("actually it's still running");

能够看到即便抛出RuntimeException,程序并没有解体:

java.lang.RuntimeException: for no reasonactually it's still running

除了打印出异样信息 Excepetion e 之外,也能够在 catch 外面纠正程序谬误

优化语法


假如咱们写了一个读入文件的函数 readFile 那么须要思考:

  • 文件是否存在
  • 内存是否足以读入全副字节
  • 读入失败怎么样

那么依照惯例的应用 if 判断这些非凡状况:

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")    } }

可见当 if else 很多时影响可读性,此时能够应用 try catch 进行优化语法:

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;    }}

Exceptions and the Call Stack

假如某main()中办法调用是

GuitarHeroLite.main()-->GuitarString.sample()-->ArrayRingBuffer.peek()

假如peek()办法执行的时候抛出了异样,那么异样就会从栈顶向下追踪(相似于逐渐pop栈元素),寻找其余办法里是否会有 catch 异样,如果达到栈底都没找到,程序就会解体并且Java会打印出栈的追踪信息

java.lang.RuntimeException in thread “main”:     at ArrayRingBuffer.peek:63     at GuitarString.sample:48     at GuitarHeroLite.java:110

Checked Exceptions

咱们平时写的代码大多都没有应用 try catch 捕捉异样(unchecked exceptions),然而有时候,如果你不进行异样查看,编译器会给出"Must be Caught or Declared to be Thrown" 这样的报错信息
根本的思维是
编译器须要这些异样被catch 或被确定,其被编译器认为是可防止的程序解体

比方,当咱们 throw new IOException()的话:

public class Eagle {    public static void gulgate() {       if (today == “Thursday”) {           throw new IOException("hi"); }    }}public static void main(String[] args) {    Eagle.gulgate();}

以上代码便会报错:

$ javac What.javaWhat.java:2: error: unreported exception IOException; must be caught or declared to be thrown        Eagle.gulgate();

然而简略改一下,当咱们 throw new RuntimeException():

public class UncheckedExceptionDemo {    public static void main(String[] args) {       if (today == “Thursday”) {           throw new RuntimeException("as a joke"); }    }}

那么报错就会隐没,因为 RuntimeException()是无需查看的异样,而 IOException()是需查看的异样,一些具体的异样辨别如图:

比照RuntimeException()与IOException()

对于 RuntimeException(),再具体介绍一下:

对于这些须要查看的异样,咱们的方法是

  1. Catch Exception
    应用 try catch 语法捕捉异样:
public static void gulgate() {    try {        if (today == “Thursday”) {           throw new IOException("hi"); }    } catch (Exception e) {        System.out.println("psych!");    }}
  1. 以关键字 throws 在method的header开端申明异样的:
public static void gulgate() throws IOException {   ... throw new IOException("hi"); ...}

相当于通知编译器 I'm dangerous method
如果一个method 调用一个 dangerous method,那么须要小心该办法自身也会变成 dangerous办法,正如

“He who fights with monsters should look to it that he himself does not become a monster. And when you gaze long into an abyss the abyss also gazes into you.” - Beyond Good and Evil (Nietzsche)

当咱们在main()中调用 dangerous method,main()自身也变成了 dangerous method,须要对main()进行修改:

public static void main(String[] args) {    Eagle.gulgate();}


3.Iteration

以前咱们有在List中应用过 增强循环去迭代其中的元素( called the “foreach” or “enhanced for” loop):

List<Integer> friends =  new ArrayList<Integer>();friends.add(5);friends.add(23);friends.add(42);for (int x : friends) {   System.out.println(x);}

本大节的指标是构建一个属于咱们本人的增强循环( enhance loop )
先介绍一下Java内置的List interface iterator() method:

public Iterator<E> iterator();

因为iterator()的返回值是Iterator<E>类型,所以咱们定义一个Iterator<Integer>类型的seer去接管:

List<Integer> friends =  new ArrayList<Integer>();...Iterator<Integer> seer     = friends.iterator();while (seer.hasNext()) {  System.out.println(seer.next());}

可见iterator接口蕴含两个办法;

  • hasNext():查看是否存在下一项
  • next():返回以后项的值并将next指针后移一位

The Iterable Interface

下面咱们只是简略介绍了iterator(),其内在的原理咱们并不分明,“就像在墙的另一端,一些僧侣举着火把挪动,咱们能看到的只是影子”

其原理是
首先编译器查看 Lists 是否有 iterator()办法并返回 Iterator<Integer>.
How:
List接口 extends Iterable 接口,继承了 Iterable接口的形象办法 iterator()
(实际上,List extends Collection,而Collection extends Iterable,但这曾经足够靠近事实了。
另外我还省略了Iterable接口中的一些默认办法)

接着,编译器查看 Iterator 是否有hasNext() 和 next()

How: Iterator接口明确定义了这些形象的办法

构建咱们本人的KeyIterator()

在ArrayMap()中定义外部类 KeyIterator,并申明hasNext()和next():

    public class KeyIterator {        public boolean hasNext() {            return false;        }        public K next() {            return null        }    }

接下来依照下面所说的 hasNext()与next()的性能实现KeyIterator:

    public class KeyIterator {        private int Position;        public KeyIterator() {            Position = 0;        }        public boolean hasNext() {            return Position < size;        }        public K next() {            K value = keys[Position];            Position = Position + 1;            return value;        }    }

在IteratorDemo.java中测试:

public class IterationDemo {    public static void main(String[] args) {        ArrayMap<String, Integer> am = new ArrayMap<String, Integer>();        am.put("hello", 5);        am.put("syrups", 10);        am.put("kingdom", 10);        ArrayMap.KeyIterator ami = am.new KeyIterator();        while(ami.hasNext()) {            System.out.println(ami.next());        }    }}

打印后果:

hellosyrupskingdom

至此实现了咱们的KeyIterator,值得注意的是

ArrayMap.KeyIterator ami = am.new KeyIterator();

以上是演示如何应用嵌套类
如果咱们要发明一个非动态的嵌套类,必须有一个特定的实例,如果咱们发明的KeyIterator没有与ArrayMap相关联,那将没有意义,实质上KeyIterator的职能是迭代拜访ArrayMap的keys[]数组
即使是咱们曾经胜利创立了咱们本人的KeyIterator class,然而还是不能实现咱们所期待的加强循环:

        ArrayMap<String, Integer> am = new ArrayMap<String, Integer>();        am.put("hello", 5);        am.put("syrups", 10);        am.put("kingdom", 10);        for (String s : am) {            System.out.println(s);        }    

因为Java也不晓得如何去获取它,它不晓得如何去实例化iterator,因而在这种状况下咱们须要做的是,确保咱们的class 领有 iterator()办法

    public Iterator<K> iterator() {        return new KeyIterator();    }    public class KeyIterator implements Iterator<K> {     ......}

然而,程序依然不能运行,因为Java回绝对数据结构进行 for each循环,除非你还申明了该数据结构实现了可迭代的接口,即在ArrayMap的header继承Iterable<K>:

public class ArrayMap<K, V>implements Map61B<K, V>, Iterable<K> {   ....... }

至此实现了用咱们本人写的迭代器KeyIterator应用增强循环
总结:
Implement iterable interface to support enhanced for loop.
iterator() method must return an object that implements the Iterator interface.
当然,你也能够应用内置的Iterator:

    public Iterator<K> iterator() {        List<K> keylist = keys();        return keylist.iterator();    }

以上三行代码等效于应用外部类实现iterator:

    public Iterator<K> iterator() {        return new KeyIterator();    }    public class KeyIterator implements Iterator<K> {        private int Position;        public KeyIterator() {            Position = 0;        }        public boolean hasNext() {            return Position < size;        }        public K next() {            K value = keys[Position];            Position = Position + 1;            return value;        }    }