乐趣区

关于java:cs61b-week5-Exceptions-Iterators-Iterables

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 ExceptionDemo
Exception 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 reason
actually 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.java
What.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());
        }
    }
}

打印后果:

hello
syrups
kingdom

至此实现了咱们的 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;
        }
    }
    
退出移动版