> 文章列表 > java day7

java day7

java day7

第七章 异常线程

  • 7.1 异常
    • 异常类
  • 7.2 管理异常
    • 7.2.1 异常一致性检测
    • 7.2.2 保护代码和捕获异常
    • 7.2.3 finally 子句
  • 7.3 声明可能引发异常的方法
    • 7.3.1 throws 子句
    • 7.3.2 应引发哪些异常
    • 7.3.3 传递异常
    • 7.3.4 throws和继承
  • 7.4 创建并引发自己的异常
    • 7.4.1 引发异常
    • 7.4.2 创建自己的异常
    • 7.4.3 结合使用throws、try和throw
  • 7.5 在什么情况下不使用异常
    • 7.5.1 糟糕的异常使用方式
  • 7.6 线程
    • 7.6.1 编写线程化程序
    • 7.6.2 线程化应用程序
    • 7.6.3 终止线程

7.1 异常

在Java中,导致程序出错的奇怪事件被称为异常。
如果使用if…case和switch…case 块等处理异常,这些错误管理代码会导致java类难以阅读和维护。且用此方法来处理错误,将无法让编译器采用一致的方式进行如下检查:调用方法时是否使用了正确的参数;是否将变量设置成了正确的对象。

异常类

1.异常: 发生异常时,应用程序可能退出,并且屏幕上会显示一组难以理解的错误。这些错误就是异常。
2. 引发异常:程序没有完成其工作就退出时,说明引发了异常。
3. 捕获异常:捕获异常指的是对异常情况进行处理,以避免程序崩溃,后面将对此做更详细的介绍。

在Java中,异常是对象,即它们是从类Throwable派生而来的类的实例。当异常被引发时,将创建Throwable类的一个实例。Throwable有两个子类:Error和Exception。

  • Error实例是JVM内部的错误,这种错误很少见,但通常是致命的,对于它们,您无能为力(要么捕获它们,要么引发它们)。
  • Exception类与编程的关系更为紧密。Exception的子类分为两组。
  1. unchecked异常(RuntimeException类的子类),如ArrayIndexOutofBounds、SecurityException和NullPointeerException。
  2. checked异常,如EOFException和MalformedURLException。
    unchecked异常也叫运行阶段异常,通常是由于代码不够健壮造成的。

如果程序导致unchecked异常,应通过改善代码来消除这些问题。对于可在创建Java程序时消除的编程错误,不要依赖于异常管理来处理

主要的异常类(Throwable、Exception和RuntimeException)都位于java.lang包中。在Java类库中,还有很多其他的包定义了其他异常,这些异常用于整个类库中。

7.2 管理异常

java编译器将要求您对异常进行管理:您必须在代码中对这些异常进行处理,否则程序将不能通过编译。

7.2.1 异常一致性检测

编译器以相同的方式对代码进行检测,以确保您调用方法时提供的参数数目和类型都是正确的。它降低了程序由于致命错误而崩溃的可能性,因为您将预先知道程序使用的方法将引发什么样的异常。

7.2.2 保护代码和捕获异常

假设您编写代码,并在编译时遇到异常消息。根据该消息,您必须捕获这种错误或声明方法必须引发它。
首先捕获可能的异常,这需要完成两项工作:

  • 将包含可能引发异常的方法的代码放在try块中
  • 在catch块中对异常进行处理
try{// ....}
catch(NumberFormatException nfe){// ...}
catch(IOException | FileNotFoundException exp){// ...
}

7.2.3 finally 子句

假设不管发生什么情况,也无论异常是否被引发,代码中的一些操作必须都被执行。这通常是释放外部资源,关闭打开的文件等。虽然可以同时将执行这些操作的代码放在catch块中和块外,但编程时应尽可能避免将代码放在两个不同的地方。相反,应将这种代码放在try…catch块的可选部分finally中

try {readTextFile();
} catch(IOException ioe){// deal with IO errors
} finally {closeTextFile();
}

7.3 声明可能引发异常的方法

前面介绍了如何处理可能引发异常的方法(通过保护代码并捕获可能发生的异常)。Java编译器将进行检查,确保您对方法的异常进行了处理,但它如何知道需要处理哪些异常呢?

要指出方法可能引发的异常,可在方法定义中使用特殊子句throws。

7.3.1 throws 子句

要指出方法中的某些代码可能引发异常,只需在方法的右括号后面加上关键字throws和将引发的异常的名称,例如:

public void getPoint(int x,int y) throws NumberFormatException,EOFException{//	body of method
}

记住,将关键字throws加入到方法定义中只是意味着:如果发生错误,该方法可能引发异常,而不是发生这种情况。throws子句只是在方法定义提供了有关潜在异常的信息,并让java能够确保其他类正确地使用该方法。

7.3.2 应引发哪些异常

决定指出方法可能引发异常后,必须判断它可能引发哪些异常,并实际引发它们或调用一个将引发它们的方法。

具体地说,在throw子句中,无须列出类Error、RuntimeException(或其子类)的异常。这是因为这些异常可能发生在Java程序中的任何地方,而且通常这不是由程序员直接造成的。
所有其他异常都是需要检查的异常,您可以在方法的throws子句中列出它们。

7.3.3 传递异常

有时候,在方法中对某个异常进行处理是不合理的,由于调用该方法的方法进行处理更合适。

7.3.4 throws和继承

如果您的方法定义覆盖率超类中包含throws子句的方法,则对于覆盖方法如何处理throws,有一些特殊的规则。

public class HexReader {public void startPlaying() throws SoundException{// body of method}
}public class StereoPlayer extends HexReader{public void startPlaying(){// body of method}
}

新方法可以引发更少的异常,甚至可以不引发任何异常。但反过来不成立:子类方法不能引发其超类方法没有引发的checked异常,包括其他类型的异常和更笼统的异常。

7.4 创建并引发自己的异常

每个异常都有两面:引发和捕获。被捕获前,异常可能被多次传递给多个方法,但最终它将被捕获并得到处理。

7.4.1 引发异常

要引发异常,必须创建该异常类的实例,然后使用throw语句来引发它。

NotInServiceException nise = new NotInServiceException();
throw nise;

异常的构造函数可能接受参数,这取决于您使用的异常类。最常见的参数是字符串,它让您能够更详细地描述问题。

NotInServiceException nise = new NotInServiceException("Database Not in Service");
throw nise;

7.4.2 创建自己的异常

所有用户创建的异常都应是Exception而非Error层次结构的一部分,后者用于表示涉及JVM的错误。

异构类通常有两个构造函数:第一个接受任何参数;第二个接受一个字符串参数。对于后一种构造函数,应在其中调用super(),以确保该字符串被应用到正确的地方。

public class SunSpotException extends Exception{public SunSpotException(){}public SunSpotException(String message){super(message);}
}

7.4.3 结合使用throws、try和throw

仅仅使用try和catch无法传递异常,而仅仅使用throws子句则无法处理异常。

要管理异常并把它传递给调用方法,需要使用3种机制:throws子句、try语句和throw语句(显示的引发异常)

public void readMessage() throws IOException{MessageReader mr = new MessageReader();try{mr.loadHeader();}catch (IOException e){// do something to handle the// IO exception and then rethrow// the exceptionthrow e;}
}

在上述情况下:在文件阅读器中使用了一条try-catch语句来捕获IOException;由于阅读到了文件末尾而发生EOFException;该异常被catch块捕获,因为IOException是EOFException的超类。

如果使用了throw来引发异常,它引发的将是IOException,而不是EOFException。java8中引入了一种技术,让您能够引发更精确的异常:在catch语句中指定的异常类型前面使用关键字final。

try{mr.loadHeader();}catch (final IOException e){// do something to handle the// IO exception and then rethrow// the exceptionthrow e;}
}

7.5 在什么情况下不使用异常

  1. 首先,对于可使用代码轻松避免的情形,不使用异常。
  2. 此外,如果用户输入的数据必须是整数,则引发异常并在其他地方进行处理相比,对其进行检测以确保它是整数将好得多。

异常将占用大量的处理时间。简单的测试比异常处理的速度快得多,可提高程序的效率。仅当您无法控制异常情况时,才应使用异常。

7.5.1 糟糕的异常使用方式

虽然在方法中加入空的catch子句或throws语句是合法的,但不经处理就将异常丢弃将破坏Java编译器进行的检查工作

7.6 线程

7.6.1 编写线程化程序

在Java中,线程是用包java.lang中的Thread类实现的。

  • 最简单的线程用法是,让程序暂停一段时间,并在此期间处于空闲状态。为此,可以调用Thread类的sleep(long)方法,并将要暂停的时间(单位为毫秒)作为参数传递给它。
try{Thread.sleep(3000);
} catch (InterruptedException ie) {// do nothing
}

线程的用途之一是,将所有耗时的行为都放到单独的类中。

创建线程的方式有两种:

  1. 创建一个从Thread派生而来的类;
  2. 在另一个类中实现Runnable接口。

创建线程的第一步是创建一个到Thread对象的引用:

Thread runner;

上述语句创建一个线程引用,但没有将Thread对象赋予它。要创建线程,可调用构造函数Thread(Object),并将要线程化的对象作为参数传递给它。下面的语句创建了一个线程化的StockTicker对象:

StockTicker tix = new StockTicker();
Thread tickerThread = new Thread(tix);

适合创建线程的两个地方是应用程序的构造函数和组件的构造函数。

要启动线程,可调用它的start()方法,

tickerThread.start();
Thread runner = null;
if (runner == null){runner =new Thread(this);runneer.start();
}

将对象赋值给变量之前,它的值为null,因此if语句确保线程不会被多次启动。

7.6.2 线程化应用程序

7.6.3 终止线程

终止线程比启动线程复杂些

  1. 终止线程的最佳方式是,让线程的run()方法的循环在某个变量的值发生变化后结束,如下例所示:
Public void run(){while(okToRun == true){// ...}
}

变量okToRun可以是线程类的实例变量,如果它变成false,则run()方法中的循环将结束。
2. 另一种终止线程的方式是,仅当当前运行的线程有一个指向它的变量时,才执行方法run()中的循环。类方法Thread.currentThread()返回指向当前线程(即对象在其中运行的线程)的引用。只要runner()和currentThread()指向相同的对象,下面的方法run()就将继续循环。

public void run(){Thread thisThread = Thread.currentThread();while(runner == thisThread){// body of loop}
}

如果您使用了类似这样的循环,则可以在类的任何地方使用下面的语句来终止线程:

 runner = null;

煤炭资源网