当前位置: 首页 > 后端技术 > Java

cs61bweek5--异常、迭代器、迭代器

时间:2023-04-01 17:53:50 Java

1。Throwinganexception这节课我们用上一节创建的ArrayMap来讲解。假设你get()一个main()中不存在的key,比如:publicstaticvoidmain(String[]args){ArrayMapam=newArrayMap();am.put("你好",5);System.out.println(am.get("yolp"));}然后运行时会得到如下错误信息:$javaExceptionDemoExceptioninthread"main"java.lang.ArrayIndexOutOfBoundsException:-1atArrayMap.get(ArrayMap.java:38)atExceptionDemo.main(ExceptionDemo.java:6)根据报错信息,我们可以知道ArrayMap.java代码的第38行和ExceptionDemo的第6行有错误。java代码。错误原因是数组越界。当遇到错误时,Java可以自动抛出异常并停止程序运行。有时候,Java给出的错误信息并不明显,不容易出错。我们可以手动添加错误信息,即抛出一些异常。使用关键字throw我们可以抛出自定义异常,具体格式为:thrownewExceptionObject(parameter1,...)例子:publicVget(Kkey){intlocation=keyIndex(key);if(location<0){thrownewIllegalArgumentException("Key"+key+"doesnotexistinmap.");}returnvalues[keyIndex(key)];}一些容易出现异常的示例:您尝试使用383,124GB的内存。您尝试将对象转换为Dog,但动态类型不是Dog。您尝试使用等于null的引用变量来调用方法。您尝试访问数组的索引-1。抛出异常其实就是创建了一个异常类型的对象,相当于实例化了一个异常类,所以即使程序本身没有错误,也可以无缘无故抛出异常:publicstaticvoidmain(String[]args){thrownewRuntimeException("Fornoreason.");}get:Exceptioninthread"main"java.lang.RuntimeException:Fornoreason.2.Catchtheexception如果我们只是在程序的某处抛出一个异常而不做任何处理,程序就会崩溃。但是我们可以通过捕获异常让程序继续运行,使用try{thrownewSomeException();}catch(Exceptione){doSomething;}例如:}catch(Exceptione){System.out.println(e);}System.out.println("实际上它还在运行");可以看到即使抛出RuntimeException,程序也没有崩溃:java.lang.RuntimeException:fornoreasonactually它还在运行除了打印出异常信息Exceptione之外,还可以在catch和优化语法。假设我们写了一个读取文件的函数readFile。然后我们需要考虑:文件是否存在,内存是否足够读取所有字节。阅读输入失败如何?像往常一样使用if来判断这些特殊情况:funcreadFile:{openthefile;if(theFileIsOpen){确定它的大小;if(gotTheFileLength){分配那么多内存;}else{返回错误(“fileLengthError”);}if(gotEnoughMemory){将文件读入内存;如果(读取失败){返回错误(“读取错误”);}...}别的{返回错误(“内存错误”);}}else{returnerror("fileOpenError")}}可见,当ifelse很多的时候,会影响可读性。这时可以使用trycatch进行优化语法:funcreadFile:{try{openthefile;确定其大小;分配那么多内存;将文件读入内存;关闭文件;}catch(fileOpenFailed){doSomething;}catch(sizeDeterminationFailed){doSomething;}catch(memoryAllocationFailed){doSomething;}catch(readFailed){doSomething;}catch(fileCloseFailed){doSomething;}}ExceptionsandtheCallStackassumesthatamethodcallinmain()isGuitarHeroLite.main()-->GuitarString.sample()-->ArrayRingBuffer.peek()assumespeek()方法执行时,异常被抛出,则异常会从栈顶向下追踪(类似于逐级出栈元素),寻找其他方法是否有catch异常,如果没有则在栈底,程序将崩溃,Java将在线程“main”中打印出堆栈跟踪java.lang.RuntimeException:atArrayRingBuffer.peek:63atGuitarString.sample:48atGuitarHeroLite.java:110CheckedExceptions我们平时写的代码大多不会用trycatch来捕获异常(uncheckedexceptions),但是有时候,如果不检查异常,编译器会给出类似“MustbeCaughtorDeclaredtobeThrown”的错误信息"基本思想是编译器需要捕获或确定这些异常,编译器认为这是为了避免程序崩溃。例如,当我们抛出newIOException()时:}}}publicstaticvoidmain(String[]args){Eagle.gulgate();}上面的代码会报错:$javacWhat.javaWhat。java:2:错误:未报告的异常IOException;必须被捕获或声明被抛出Eagle.gulgate();但只需更改它,当我们抛出newRuntimeException()时:}}}然后错误就会消失,因为RuntimeException()是不需要检查的异常,而IOException()是需要检查的异常,具体区分一些异常如图:比较RuntimeException()和IOException()关于RuntimeException(),再详细介绍一下:对于这些需要检查的异常,我们的方法是catchException使用trycatch语法来捕获异常:publicstaticvoidgulgate(){try{if(today=="Thursday"){thrownewIOException("hi");}}catch(Exceptione){系统。出去。println("心理!");}}异常在方法头的末尾用关键字throws声明:publicstaticvoidgulgate()throwsIOException{...thrownewIOException("hi");...}相当于告诉编译器I'mdangerousmethod如果一个方法调用了一个危险的方法,那么你需要小心这个方法本身会变成一个危险的方法,就像“与怪物战斗的人应该看看他自己并没有变成怪物。当你凝视深渊时,深渊也在凝视你。-BeyondGoodandEvil(Nietzsche)当我们在main()中调用一个危险的方法时,main()本身就变成了一个危险的方法,而main()需要被纠正:publicstaticvoidmain(String[]args){Eagle.gulgate();}3.Iteration我们之前在List中使用过增强循环来迭代元素(称为“foreach”或“增强for”循环):Listfriends=newArrayList();friends.add(5);friends.add(23);friends.add(42);for(intx:friends){System.out.println(x);}本节的目标是构建我们自己的增强循环(enhanceloop)。首先介绍下Java内置List接口的iterator()方法:publicIteratoriterator();因为iterator()的返回值是Iterator类型的,所以我们定义一个Iterator类型的seer来接收:Listfriends=newArrayList();...Iteratorseer=friends.iterator();while(seer.hasNext()){System.out.println(seer.next());}可以看出迭代器接口包含两个方法:hasNext():检查下一个是否itemexistsnext():返回当前item的值,并将next指针向后移动一位增强型For循环的秘密如上图所示,左边的代码是一个增强型循环,其原理是相当于右边的迭代器版本,也就是说增强版for循环的源码是相当于图中右边的代码。首先,编译器检查Lists是否有iterator()方法并返回Iterator。How:List接口extendsIterable接口,它继承了Iterable接口的抽象方法iterator()另外,我还省略了Iterable接口中的一些默认方法)接下来,编译器检查Iterator是否有hasNext()和next()方法:Iterator接口明确定义了这些抽象方法。满足以上两步(这里是fall2020的slides):构建我们自己的KeyIterator()在实现我们自己的增强循环之前,我们先简单构建我们自己的迭代器KeyIterator在ArrayMap()中定义KeyIterator的内部类,并声明hasNext()和next():publicclassKeyIterator{publicbooleanhasNext(){returnfalse;}publicKnext(){returnnull}}接下来,按照上面的hasNext()和next()函数完成KeyIterator:publicclassKeyIterator{privateintPosition;publicKeyIterator(){位置=0;}publicbooleanhasNext(){returnPositionam=newArrayMap();am.put("你好",5);am.put("糖浆",10);am.put("王国",10);ArrayMap.KeyIteratorami=am.newKeyIterator();while(ami.hasNext()){System.out.println(ami.next());}}}打印结果:hellosyrupskingdom至此,我们的KeyIterator就完成了。值得注意的是ArrayMap.KeyIteratorami=am.newKeyIterator();以上是演示如何使用嵌套类。如果我们要创建一个非静态的嵌套类,就必须有一个特定的实例。如果我们创建的KeyIterator没有关联ArrayMap,那么它就没有意义了。本质上,KeyIterator的功能是迭代访问ArrayMap的keys[]数组接下来准备实现增强循环ArrayMapam=newArrayMap();for(Strings:am){System.out.println(s);}因为刚才说了,增强循环源码分为两步:KeyIterator类需要封装成一个Iterator方法,这个方法的返回类型是IteratorpublicIteratoriterator(){returnnew键迭代器();}Iterator()方法返回Iterator类型的对象应该有hasNext()和next(),所以我们需要声明我们的KeyIterator继承了Iterator接口,并建立“is-a”关系,即,KeyIterator是一个Iterator,有hasNext()和next()方法我们的ArrayMap类中的方法,你可能会疑惑,这么大的方法不是明明在那里吗?你怎么可能不知道。要知道,这就是我们看到的,编译器不是人(==)为了让Java知道我们的ArrayMap中实现了iterator()方法,让ArrayMap继承iterable接口,建立一个“is-a”的关系。即ArrayMap是可迭代的,承诺ArrayMap实现iterablepublicclass中的iterator方法ArrayMapimplementsMap61B,Iterable{......}至此,我们可以自己写迭代器KeyIterator使用增强循环总结:Iterable接口有Iterator()方法返回一个Iterator对象Iterator接口有hasnext()和next()方法,即实例化的Iterator对象也有这两个方法来支持增强的for循环:在你的类中添加一个iterator()方法,它返回一个Iterator。返回的Iterator应该有一个有用的hasNext()和next()method.AddimplementsIterable到定义类的行。当然也可以使用List自带的Iterator:publicIteratoriterator(){Listkeylist=keys();返回keylist.iterator();}上面三行代码相当于使用内部类实现迭代器:publicIteratoriterator(){returnnewKeyIterator();}公共类KeyIterator实现entsIterator{privateintPosition;publicKeyIterator(){位置=0;}publicbooleanhasNext(){returnPosition