博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java语言程序设计与数据结构(基础篇)第12章 异常处理和文本I/O 笔记
阅读量:668 次
发布时间:2019-03-15

本文共 23813 字,大约阅读时间需要 79 分钟。

Java语言程序设计与数据结构(基础篇)第12章 异常处理和文本I/O 笔记

文章目录

一、引言

  • 异常时运行时错误,异常处理使得程序可以处理运行时错误,并且继续通常的执行
  • 在程序运行过程中,如果JVM检测出一个不可能执行的操作,就会出现运行时错误
  • 运行时错误会作为异常抛出,异常就是一种对象,表示阻止正常运行的错误或者情况
  • 如果异常没有被处理,那么程序将会非正常终止

二、异常处理概述

1.异常简介

  • 异常是从方法抛出的,方法的调用者可以捕获以及处理该异常
  • 一个浮点数除以0是不会产生异常的
package com.java;import java.util.Scanner;public class QuotientWithException {
public static int quotient(int number1, int number2) {
if (number2 == 0) throw new ArithmeticException("Divisor cannot be zero"); return number1 / number2; } public static void main(String[] args) {
Scanner input = new Scanner(System.in); System.out.println("Enter two integers: "); int number1 = input.nextInt(); int number2 = input.nextInt(); try {
int result = quotient(number1, number2); System.out.println(number1 + "/" + number2 + " is " + result); } catch (ArithmeticException ex) {
System.out.println("Exception an integer " + "cannot be divided by zero"); } System.out.println("Execution continues ..."); }}
  • 在这种情况下,抛出的值为new ArithmeticException(“Divisor cannot be zero”),称为一个异常,throw语句执行称为抛出一个异常,异常就是一个从异常类创建的对象,在这种情况下,异常类就是ArithmeticException,构造方法ArithmeticException(str)被调用以构建一个异常对象,其中str是描述异常的消息
  • ”抛出异常“就是将异常从一个地方传递到另一个地方,调用方法的语句包含在一个try块和一个catch块中,try块包含了正常情况下执行的代码。异常被catch块所捕获,catch块中的代码被执行以处理异常,之后catch块之后的语句被执行。
  • throw语句类似于方法的调用,但不同于调用方法的是,它调用的是catch块,从某种意义上讲,catch块就像带参数的方法定义,这些参数匹配抛出的值得类型,但是它不像方法,在执行完catch块之后,程序控制不返回到throw语句;而是执行catch块后得下一条语句
  • 一个异常可能是通过try块中的throw语句直接抛出,或者调用一个可能会抛出异常的方法而抛出

2.异常处理的优点

  • 它能使方法抛出一个异常给它的调用者,并由调用者处理该异常。如果没有这个能力,那么被调用的方法就必须自己处理异常或终止该程序。
  • 被调用的方法通常不知道在出错的情况下该做些什么,这是库方法的通常情形
  • 异常处理最根本的优势是将检测错误从处理错误中分离出来
package com.java;import java.util.InputMismatchException;import java.util.Scanner;public class InputMismatchExceptionDemo {
public static void main(String[] args) {
Scanner input = new Scanner(System.in); boolean continueInput = true; do {
try {
System.out.print("Enter an integer: "); int number = input.nextInt(); System.out.println("The number entered is " + number); continueInput = false; }catch (InputMismatchException ex){
System.out.println("Try again. (" + "Incorrect input: an integer is required)"); input.nextLine(); } }while(continueInput); }}

三、异常类型

  • 异常是对象,而对象都采用类来定义。异常的根类是java.lang.Throwable
  • Error、Exception和RuntimeException有时候容易引起混淆。这三种类都是异常,这里讨论的错误都发生在运行时。
  • Throwable类是所有异常类的根,所有的Java异常类都直接或间接地继承自Throwable。可以通过继承Exceprion或者Exception地子类来创建自己的异常。

1.异常类的三种主要类型

  • 系统错误
  • 异常
  • 运行时异常

2.系统错误

  • 系统错误由Java虚拟机抛出的,用Error类表示,Error类描述的是内部系统错误

3.异常

  • 异常是用Exception类表示的,它描述的是由你的程序和外部环境所引起的错误,这些错误能被程序捕获和处理

4.运行时异常

  • 运行时异常是用RuntimeException类表示的,它描述的是程序设计错误,运行时异常通常表明了编程错误

5.免检异常和必检异常

  • RuntimeException类和Error类以及它们的子类都称为免检异常
  • 所有其他异常都称为必检异常,意味着编译器会强制程序员检查并通过try-catch块处理它们,或者在方法头进行声明。
  • 免检异常可能在程序的任何一个地方出现,为避免过多地使用try-catch块,Java语言不强制要求编写代码捕获或声明免检异常

四、关于异常处理的更多讨论

1.异常处理模型基于的三种操作

  • 声明一个异常
  • 抛出一个异常
  • 捕获一个异常

2.声明异常

  • 每个方法都必须声明它可能抛出的必检异常的类型,这称为声明异常
  • 关键字throws用于声明异常
  • 如果方法可能抛出多个异常,就可以在关键字throws后添加一个用逗号分隔的异常列表
  • 如果父类中的方法没有声明异常,那么就不能在子类中对其重写时声明异常

3.抛出异常

  • 检测到错误的程序可以创建一个合适的异常类型的实例并抛出它,这称为抛出一个异常

4.捕获异常

  • try-catch语句块
try{
statements;}catch(Exception exVar1){
handler for exception1;}catch(Exception exVar2){
handler for exception2;}catch(Exception exVar3){
handler for exception3;}
  • 如果在执行try块的过程中没有出现异常,则跳过catch子句
  • 如果try块中的某条语句抛出一个异常,Java就会跳过try块中剩余的语句,然后开始处理这个异常的代码。
  • 处理这个异常的代码称为异常处理器
  • 各种异常类可以从一个共同的父类中派生,如果一个catch块可以捕获一个父类的异常对象,它就能捕获那个父类的所有子类的异常对象
  • 在catch块中异常被指定的顺序时非常重要的,如果父类的catch块出现在子类的catch块之前,就会导致编译错误

5.从异常中获取信息

package com.java;public class TestException {
public static void main(String[] args) {
try {
System.out.println(sum(new int[]{
1, 2, 3, 4, 5})); } catch (Exception ex) {
ex.printStackTrace(); System.out.println("\n" + ex.getMessage()); System.out.println("\n" + ex.toString()); System.out.println("\nTrace Info Obtained from getStackTrace"); StackTraceElement[] traceElements = ex.getStackTrace(); for (int i = 0; i < traceElements.length; i++) {
System.out.print("method " + traceElements[i].getMethodName()); System.out.print("(" + traceElements[i].getClassName() + ":"); System.out.println(traceElements[i].getLineNumber() + ")"); } } } private static int sum(int[] list) {
int result = 0; for (int i = 0; i <= list.length; i++) result += list[i]; return result; }}
C:\Users\MuhammadAfif\.jdks\corretto-11.0.10\bin\java.exe "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=52317:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath C:\MyFiles\MyJavaCode\Java_Book\out\production\Chapter12 com.java.TestExceptionjava.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5	at com.java.TestException.sum(TestException.java:24)	at com.java.TestException.main(TestException.java:6)Index 5 out of bounds for length 5java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5Trace Info Obtained from getStackTracemethod sum(com.java.TestException:24)method main(com.java.TestException:6)Process finished with exit code 0

6.实例学习:声明、抛出和捕获异常

package com.java;public class CircleWithException {
private double radius; private static int numberOfObjects = 0; public CircleWithException() {
this(1.0); } public CircleWithException(double newRadius) {
setRadius(newRadius); numberOfObjects++; } public double getRadius() {
return radius; } public void setRadius(double newRadius) throws IllegalArgumentException {
if (newRadius >= 0) radius = newRadius; else throw new IllegalArgumentException("Radius cannot be negative"); } public static int getNumberOfObjects() {
return numberOfObjects; } public double findArea() {
return radius * radius * Math.PI; }}
package com.java;public class TestCircleWithException {
public static void main(String[] args) {
try {
CircleWithException c1 = new CircleWithException(5); CircleWithException c2 = new CircleWithException(-5); CircleWithException c3 = new CircleWithException(0); } catch (IllegalArgumentException ex) {
System.out.println(ex); } System.out.println("Number of objects created: " + CircleWithException.getNumberOfObjects()); }}//输出://java.lang.IllegalArgumentException: Radius cannot be negative//Number of objects created: 1

五、finally子句

  • 无论异常是否发生,finally子句总会被执行
  • 有时候,不论异常是否出现或者是否被捕获,都希望执行某些代码,Java的finally子句可以用来实现这个目的,finally子句的语法如下所示:
try{
statements;}catch(TheException ex){
handling ex;}finally{
finalStatemnts;}
  • 如果try块中没有出现异常,执行finalStatements,然后执行try语句的下一条语句
  • 如果try块中有一条语句引起了异常并被catch块捕获,会跳过try块的其他语句,执行catch块和finally子句。执行try语句后的下一条语句
  • 如果try块中的一条语句引起异常,但是没有被任何catch块捕获,就会跳过try块中的其他语句,执行finally子句,并且将异常传递给这个方法的调用者
  • 即使在到达finally块之前有一个return语句,finally块还是会执行
  • 使用finally子句时可以略去catch块

六、何时使用异常

  • 当错误需要被方法的调用者处理的时候,方法应该抛出一个异常
  • try块包含正常情况下执行的代码
  • catch块中包含异常情况下执行的代码

七、重新抛出异常

  • 如果异常处理器不能处理一个异常,或者只是简单地希望它的调用者注意到该异常,Java允许该异常处理器重新抛出该异常
try{
statements;}catch(TheException ex){
perform operations before exit; throw ex;}

八、链式异常

package com.java;public class ChainedExceptionDemo {
public static void main(String[] args) {
try{
method1(); //6 }catch (Exception ex){
ex.printStackTrace(); } } public static void method1() throws Exception{
try {
method2(); //14 }catch (Exception ex){
throw new Exception("New info from method1",ex); //16 } } public static void method2() throws Exception{
throw new Exception("New info form method2"); //21 } }/*输出内容:java.lang.Exception: New info from method1 at com.java.ChainedExceptionDemo.method1(ChainedExceptionDemo.java:16) at com.java.ChainedExceptionDemo.main(ChainedExceptionDemo.java:6)Caused by: java.lang.Exception: New info form method2 at com.java.ChainedExceptionDemo.method2(ChainedExceptionDemo.java:21) at com.java.ChainedExceptionDemo.method1(ChainedExceptionDemo.java:14) ... 1 more*/
  • 注解:main方法调用method1,method1调用method2,method2在第21行抛出一个异常,该异常于method1中被捕获,捕获后method1又在第16行抛出一个新异常,新异常于main方法捕获。

  • 实例输出ex.printStackTrace()的内容,先输出method1抛出的新异常(16行),以及被调用的行数(6行)。再输出method2抛出的旧异常(21行),以及被调用的行数(14行)

  • 如果第16行被下面一行所替换,将输出什么?

package com.java;public class ChainedExceptionDemo {
public static void main(String[] args) {
try{
method1(); //6 }catch (Exception ex){
ex.printStackTrace(); } } public static void method1() throws Exception{
try {
method2(); //14 }catch (Exception ex){
throw new Exception("New info from method1"); //16 } } public static void method2() throws Exception{
throw new Exception("New info form method2"); //21 } }/*java.lang.Exception: New info from method1 at com.java.ChainedExceptionDemo.method1(ChainedExceptionDemo.java:16) at com.java.ChainedExceptionDemo.main(ChainedExceptionDemo.java:6)*/

九、创建自定义异常类

  • 可以通过继承java.lang.Exception类来定义一个自定义异常类

1.InvalidRadiusException类

package com.java;public class InvalidRadiusException extends Exception {
private double radius; public InvalidRadiusException(double radius){
super("Invalid radius" + radius); //调用父类Exception带信息的构造方法,该信息将会被设置在异常对象中,并且可以通过在该对象上调用getMessage()获得 this.radius = radius; } public double getRadius(){
return radius; }}

2.TestCircleWithCustomException类

package com.java;public class TestCircleWithCustomException {
public static void main(String[] args) {
try {
new CircleWithCustomException(5); new CircleWithCustomException(-5); new CircleWithCustomException(0); } catch (InvalidRadiusException ex) {
System.out.println(ex); } System.out.println("Number of objects created: " + CircleWithCustomException.getNumberOfObjects()); }}class CircleWithCustomException {
private double radius; //圆的半径 private static int numberOfObjects = 0; //对象个数 //半径1创建的圆 public CircleWithCustomException() throws InvalidRadiusException {
//调用了带参构造方法,带参构造方法调用了set方法,所以必须声明异常 this(1.0); } //newRadius创建的圆 public CircleWithCustomException(double newRadius) throws InvalidRadiusException {
//调用了set方法,所以也必须声明异常 setRadius(newRadius); numberOfObjects++; } //radius访问器 public double getRadius() {
return radius; } //radius修改器 public void setRadius(double newRadius) throws InvalidRadiusException {
if (newRadius >= 0) radius = newRadius; else throw new InvalidRadiusException(newRadius); //InvalidRadiusException是必检异常所以set方法必须声明异常 } //numberOfObjects访问器 public static int getNumberOfObjects() {
return numberOfObjects; } //返回面积 public double findArea() {
return radius * radius * Math.PI; }}

十、File类

  • 要点提示:File类包含了获得一个文件/目录的属性,以及对文件/目录进行改名和删除的方法

1.一些要点

  • 为了能够永久地保存程序中创建的数据,需要将它们存储到磁盘或者其他永久存储设备的文件中,这样,这些文件之后可以被其他程序传输和读取
  • 在文件系统中,每个文件都存放在一个目录下
  • 绝对文件名是有文件名和它的完整路径以及驱动器字母组成。绝对文件名是依赖机器的
  • 相对文件名是相对于当前工作目录的,对于相对文件名而言,完整目录被忽略
  • File类用于提供一种抽象,这种抽象是指以不依赖机器的方式来处理很多依赖于机器的文件和路径名的复杂性。
  • File类不包含读写文件内容的方法
  • 文件名是一个字符串,File类是文件名及其目录路径的一个包装类
  • 构建一个File实例并不会在机器上创建一个文件,不管文件是否存在,都可以创建任意文件名的File实例
  • 可以调用File实例上的exists()方法来判断这个文件是否存在
  • 在程序中,不要使用绝对路径名,如果使用了像c:\\book\\welcome.java之类的文件名,那它能在windows上工作,但是不能在其他平台上工作

2.TestFileClass类

package com.java;import java.io.File;import java.util.Date;public class TestFileClass {
public static void main(String[] args) {
File file = new File("image/us.gif"); System.out.println("Does it exsit? " + file.exists()); System.out.println("The file has " + file.length() + " bytes"); System.out.println("Can it be read? " + file.canRead()); System.out.println("Can it be written? " + file.canWrite()); System.out.println("Is it a directory? " + file.isDirectory()); System.out.println("Is it a file? " + file.isFile()); System.out.println("Is it a absolute? " + file.isAbsolute()); System.out.println("Is it hidden? " + file.isHidden()); System.out.println("Absolute path is " + file.getAbsolutePath()); System.out.println("Last modified on " + new Date(file.lastModified())); }}

十一、文件输入和输出

  • 使用Scanner类从文件中读取文本数据,使用PrintWriter类向文本文件写入数据
  • File对象封装了文件或路径的属性,但是它及不包括创建文件的方法,也不包括向/从文件写/读数据的方法

1.使用PrintWriter写数据

package com.java;import java.io.File;import java.io.IOException;import java.io.PrintWriter;public class WriteDate {
public static void main(String[] args) throws IOException {
File file = new File("scores.txt"); if (file.exists()) {
//如果没有这个if语句块,假如文件scores.txt已经存在,则文件中的内容将在不予用户确认的情况下被丢弃 System.out.println("File already exists"); System.exit(1); } PrintWriter output = new PrintWriter(file); output.print("John T Smith "); output.println(90); output.print("Eric K Jones "); output.println(85); output.close(); //必须使用close()方法关闭文件,如果没有调用该方法,数据就不能正确地保存在文件中 }}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f3E5dRM9-1612791850303)(C:\Users\MuhammadAfif\AppData\Roaming\Typora\typora-user-images\image-20210208175721050.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HNGzf881-1612791850305)(C:\Users\MuhammadAfif\AppData\Roaming\Typora\typora-user-images\image-20210208175647208.png)]

  • 如果没有这个if语句块,假如文件scores.txt已经存在,则文件中的内容将在不予用户确认的情况下被丢弃
package com.java;import java.io.File;import java.io.IOException;import java.io.PrintWriter;public class WriteDate {
public static void main(String[] args) throws IOException {
File file = new File("scores.txt");// if (file.exists()) { //如果没有这个if语句块,假如文件scores.txt已经存在,则文件中的内容将在不予用户确认的情况下被丢弃// System.out.println("File already exists");// System.exit(1);// } PrintWriter output = new PrintWriter(file); output.print("John T Smith "); output.println(90);// output.print("Eric K Jones ");// output.println(85); output.close(); //必须使用close()方法关闭文件,如果没有调用该方法,数据就不能正确地保存在文件中 }}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ipLLp9z9-1612791850306)(C:\Users\MuhammadAfif\AppData\Roaming\Typora\typora-user-images\image-20210208175818507.png)]

2.使用try-with-resources

  • 程序员经常会忘记关闭文件,可以使用try-with-resources语法来自动关闭文件
package com.java;import java.io.File;import java.io.PrintWriter;public class WriteDataWithAutoClose {
public static void main(String[] args) throws Exception{
File file = new File("myGirlFriend.txt"); if (file.exists()){
System.out.println("File already exists"); System.exit(0); } //为文件创建一个PrintWriter对象 try(PrintWriter output = new PrintWriter(file)){
output.print("Name: "); output.println("LiNa Wei"); output.println("Gender: Female"); output.println("Height: 162cm"); output.println("Weight: 50.5kg"); } }}

3.使用Scanner读取数据

package com.java;import java.io.File;import java.util.Scanner;public class ReadData {
public static void main(String[] args) throws Exception {
File file = new File("scores.txt"); //为文件创建一个Scanner对象 Scanner input = new Scanner(file); while(input.hasNext()){
String firstName = input.next(); String mi = input.next(); String lastName = input.next(); int socore = input.nextInt(); System.out.println(firstName + " " + mi + " " + lastName + " " + socore); } input.close(); //没有必要关闭输入文件,但这是一种释放被文件占用的资源的好做法 }}
package com.java;//使用try-with-resources语法重写import java.io.File;import java.util.Scanner;public class ReadData {
public static void main(String[] args) throws Exception {
File file = new File("scores.txt"); //为文件创建一个Scanner对象 try (Scanner input = new Scanner(file)) {
while (input.hasNext()) {
String firstName = input.next(); String mi = input.next(); String lastName = input.next(); int socore = input.nextInt(); System.out.println(firstName + " " + mi + " " + lastName + " " + socore); } } }}

4.Scanner如何工作

(1)基于标记的输入

  • nextByte()
  • nextShort()
  • nextInt()
  • nextLong()
  • nextFloat()
  • nextDouble()
  • next()
  • 一个标记读取方法首先跳过任意分隔符(默认情况下是空格),然后读取一个以分隔符结束的标记,然后该标记针对不同的方法,被转换成不同的类型的值,对于next()方法而言则没有进行转换。如果标记和期望的类型不匹配,就会抛出一个运行异常java.util.InputMismatchException

(2)基于行的输入

  • nextLine()

5.实例学习:替换文本

package com.java;import java.io.File;import java.io.PrintWriter;import java.util.Scanner;public class RepalceText {
public static void main(String[] args) throws Exception {
//检查命令行参数使用 if (args.length != 4) {
System.out.println("Usage: java ReplaceText sourceFile targetFile oldStr newStr"); System.exit(1); } //检查源文件是否存在 File sourceFile = new File(args[0]); if (!sourceFile.exists()) {
System.out.println("Source file " + args[0] + " does not exist"); System.exit(2); } //检查目标文件是否存在 File targetFile = new File(args[1]); if (targetFile.exists()) {
System.out.println("Target file " + args[1] + " already exists"); System.exit(3); } try (Scanner input = new Scanner(sourceFile); PrintWriter output = new PrintWriter(targetFile); ) {
while (input.hasNext()) {
String s1 = input.nextLine(); String s2 = s1.replaceAll(args[2], args[3]); output.println(s2); } } }}
  • 例如:

    java RepalceText FormatString.java t.txt StringBuilder StringBuffer

    args[0]到args[3]分别是FormatString.java t.txt StringBuilder StringBuffer

    用StrinBuffer替换FormatString.java中所有的StringBuilder,然后保存在t.txt中

十二、从Web上读取数据

1.一些要点

  • 如同从电脑中的文件中读取数据一样,也可以从Web上的文件中读取数据
  • 首先创建一个URL对象,URL字符串有语法错误的话,将会抛出一个MalformedURLException
try{
URL url = new URL("http://www.google.com/index.html");}catch(MalformedURLException ex){
ex.printStackTrace();}
  • 再用URL类中定义的openStream()方法来打开一个输入流
Scanner input = new Scanner(url.openStream());

2.一个实例

  • 实例如下:
package com.java;import java.io.IOException;import java.net.MalformedURLException;import java.net.URL;import java.util.Scanner;public class ReadFileFromURL {
public static void main(String[] args) {
System.out.print("Enter a URL: "); String URLstring = new Scanner(System.in).next(); //必须http://开头 try {
URL url = new URL(URLstring); int count = 0; //字符计数器 Scanner input = new Scanner(url.openStream()); while (input.hasNext()) {
String line = input.nextLine(); count += line.length(); } System.out.println("The file size is " + count + " characters"); } catch (MalformedURLException ex) {
System.out.println("Invalid URL"); //URLstring错误时会发生MalformedURLException,所以要处理 } catch (IOException ex) {
//openStream()可能发生IOException,所以要处理 System.out.println("I/O Errors: no such file"); } }}/*输入输出:Enter a URL: http://www.baidu.comThe file size is 2283 charactersEnter a URL: http://www.iqiyi.comThe file size is 138 charactersEnter a URL: https://www.bilibili.com/I/O Errors: no such fileEnter a URL: https://www.csdn.net/The file size is 1128133 charactersEnter a URL: https://www.youku.com/The file size is 432763 characters*/

十三、实例学习:Web爬虫

1.World Wide Web

  • World Wide Web,缩写为WWW、W3或者Web,是一个因特网上相互链接的超文本文档系统。通过使用Web浏览器,可以查看一个文档,以及沿着超链接来查看其他文档。

2.Web爬虫

  • 在本实例学习中,我们将开发一个程序,可以沿着超链接来自动遍历Web上的文档,这类程序通常称为Web爬虫

3.程序算法描述

  • 程序沿着URL来遍历Web,为了保证每个URL只被遍历一次,程序包含两个网址的列表,一个列表用于保存将被遍历的网址,一个用于保存已经被遍历的网址,程序算法描述如下
将起始URL添加到名为listOfPendingURLs的列表中;当listOfPendings不为空且listOfTraversedURLs的长度<=100{
//其中100是我们希望爬完100个页面就为止 从listOfPendingURLs移除一个URL; 如果该URL不在listOfTraversedURLs中{
将其添加到listOfTraversedURLs中; 显示该URL; 读取该URL页面,并且对该页面中包含的每个URL进行如下操作{
如果不在listOfTraversedURLs中,则将其添加到listOfPendingURLs中; } }}

4.一个实例(重点掌握)

package com.java;import java.net.URL;import java.util.ArrayList;import java.util.Scanner;public class WebCrawler {
public static void main(String[] args) {
Scanner input = new Scanner(System.in); System.out.print("Enter a URL: "); String url1 = input.nextLine(); crawler(url1, 100); //从url1开始爬Web,爬100个url } public static void crawler(String startingURL, int number) {
ArrayList
listOfPendingURLs = new ArrayList<>(); //用于存储将要爬的URL列表 ArrayList
listOfTraversedURLs = new ArrayList<>(); //用于存储已经爬过的URL listOfPendingURLs.add(startingURL); //将起始的url加入 while (!listOfPendingURLs.isEmpty() && listOfTraversedURLs.size() <= number) {
//当还有没爬完的,且爬过的不超过number时 String urlString = listOfPendingURLs.remove(0); //将第一个URL从就绪队列中移除 if (!listOfPendingURLs.contains(urlString)) {
//第一个URL没有被爬过 listOfTraversedURLs.add(urlString); System.out.println("Crawl " + urlString); for (String s : getSubURLs(urlString)) {
//遍历当前URL的子URL,并加以判断加入就绪队列中 if (!listOfTraversedURLs.contains(s)) listOfPendingURLs.add(s); } } } } //得到urlString的所有子url public static ArrayList
getSubURLs(String urlString) {
ArrayList
list = new ArrayList<>(); try {
URL url = new URL(urlString); Scanner input = new Scanner(url.openStream()); int current = 0; while (input.hasNext()) {
String line = input.nextLine(); //正确的URL不能包含分行符,因此只要在Web页面中的一行文本中查找URL即可 current = line.indexOf("http:", current); //找到http:的起始位置 while (current > 0) {
//该行有URL int endIndex = line.indexOf("\"", current); //假设一个URL以引号“结束 if (endIndex > 0) {
//确保一个正确的URL已经被找到 list.add(line.substring(current, endIndex)); //将其加入就绪队列 current = line.indexOf("http:", endIndex); //一行可能有多个URL,寻找下一个URL的起始位置 } else {
current = -1; //该行没有URL,返回-1; } } } } catch (Exception ex) {
System.out.println("Error: " + ex.getMessage()); } return list; }}

十四、关键术语

  • absolute file name 绝对文件名
  • chained exception 链式异常
  • checked exception 必检异常
  • declare exception 声明异常
  • directory path 目录路径
  • exception propagation 异常传播
  • relative file name 相对文件名
  • throw exception 抛出异常
  • unchecked exception 免检异常

十五、一些问题

1.使用异常处理的优势

  • 它允许方法向其调用方抛出异常,调用方可以处理此异常。如果没有此功能,被调用的方法本身必须处理该异常或终止程序。通常被调用的方法不知道如何处理异常。调用方需要传递它的异常处理

2.声明异常的目的是什么,怎么声明,在哪里声明,可以声明多个么

  • 声明异常的目的是告诉Java运行时系统会出什么问题。在方法声明中使用throws关键字声明异常。可以声明多个异常用”,“隔开

3.什么是必检异常,什么是免检异常

  • 如果方法抛出了必检异常,一个必检异常必须在方法声明中明确声明,必检异常必须在try-catch块中被捕获。
  • 免检异常不需要被声明,也不需要被捕获。
  • Java中只有RuntimeException和Error免检类及其子类免检

4.可以使用File类来进行I/O么?创建File对象会在磁盘上创建一个文件么?

  • 不可以,File类可用来获取文件属性和操作文件,但无法执行I/O

5.如何创建一个Scanner对象以从一个URL读取的文本

  • 首先创建一个URL类
  • 使用new Scanner(url.openStream)创建一个URL的Scanner对象,用于阅读来自URL流的数据

转载地址:http://umzqz.baihongyu.com/

你可能感兴趣的文章