第9章 Java高级编程 学习导读 本章将介绍Java语言中的一些高级特性:异常处理、多线程机制、流式IO以及网络通信,其中每个部分都能扩展成一个专题。读者在本章学习到的只是其中很小的一部分,但是能够从中了解一些基本概念和基本操作,为今后的深入学习打下基础。异常处理提供对错误的捕捉和处理机制;多线程机制使得程序的多个子任务能够“同时”执行;流式IO提供了对输入输出的读写机制;网络通信特性允许Java程序通过Socket实现底层通信,并利用Java提供的扩展组件实现高层服务。 教学重点与难点: ●异常处理、多线程机制、流式IO以及网络通信的基本概念和模型 ●异常的捕获和处理 ●多线程的实现及Runnable接口的应用 ●流式输入输出的操作、文件读写及随机访问 ●基于Socket的面向连接的网络底层通信及高层服务 9.1 异常处理 在Java中,提供了错误捕捉和处理机制,即异常处理机制。在程序运行过程中发生错误时,Java允许其不按照正常路径完成任务,由发现错误的方法抛出封装了错误信息的对象(异常)到其调用程序,发出已经发生问题的信号,然后立即退出;而且,程序并不在调用该方法的代码处继续执行,而是由异常处理机制开始搜索一个能够处理这种特定错误情况的异常处理器。 异常(Exception)也称例外、差错、违例等,是特殊的运行错误对象,它对应于java中的特定的运行错误处理机制。Java中引入了异常和异常类。 Java中的异常处理 一般来讲错误可以在编译时捕获,但是有些错误得在运行期间解决,比如除0等。要考虑到这些方面对可能发生的异常事件作出相应的处理。 Java采用面向对象的方法来处理异常如果一个方法在运行时产生了异常,则这个方法生成代表该异常的一个对象,并把它交给运行时系统,系统会寻找相应的代码来处理异常。 把生成异常对象并把它交给运行时系统的过程称为抛出(throw)异常。系统在方法的调用栈中查找直到找到包含相应异常处理的方法为止,这一过程称为捕获(catch)异常。 Throwable与Exception The Throwable class is the superclass of all errors and exceptions in the Java language. Java中的异常类都是java.lang.Throwable的子类,派生两个子类:Error and Exception。其中Error系统保留,Exception供应用程序使用。 Error:JVM系统内部错误、资源耗尽等,应用程序不处理Error类。 Exception:其他编程错误等一般性问题。一般所说的异常都指Exception及其子类。 Exception也有自己的方法和属性。它有两个构造方法: public Exception(); public Exception(String s); s通常是对该例外所对应的错误的描述。 Exception类还继承了父类的方法,常用的: public String toString(); 返回描述当前Exception 类信息的字符串。 public void printStackTrace(); 在当前的输出上打印当前例外对象的堆栈使用轨迹,即程序使用执行了哪些对象、类,使得产生了例外。 系统定义的异常: Exception的子类分为RuntimeException和非RuntimeException。 前者是一种设计和实现时的问题,如数组越界等,这种异常可以通过编程避免。 后者是在程序运行过程中由环境原因造成的异常。 用户定义的异常 这类异常是由Exception或其子类所派生出来的类,用于处理与具体应用相关的异常。 异常处理 捕获和处理 异常处理机制: 1、程序执行时出现异常,会自动生成一个异常类对象,该对象被提交给java的运行是系统,此过程称为抛出异常,也可由程序强制执行。 2、系统接收到异常对象,会寻找处理这一异常的代码并把当前异常对象交给它处理,该过程称为捕获异常。 3、如果系统找不到可以捕获异常的方法,则运行时系统将终止,程序也会退出。 异常处理 抛出异常 所有的系统定义的运行异常都可以由系统自动抛出。 用户程序自定义的异常不能由系统自动抛出,必须 throw语句定义何种情况算是产生了异常对应的错误,并且应该抛出这个异常类的对象。 throw 异常对象; 注:1、throw语句一般被定义为满足一定条件时执行。如放在if分支中。 2、使用throw语句的方法,或者调用其他类的有异常抛出的方法时,应在方法头定义中增加throws异常类名列表。 捕获异常 当一个异常被抛出时,应该由专门的语句来接收这个异常对象。一个异常类的对象被捕获后,程序就会跳转至专门的异常处理语句块,或者直接跳出当前程序和JVM。 异常对象是依靠catch语句块(异常处理语句块)来不活和处理异常的。 try{ …… } catch(异常类名 异常形参名){ …… } catch(异常类名 异常形参名){ …… } finally{ …… } catch语句可以有一个或多个,紧跟在try语句块后面,每个catch必须有一个try对应。 多异常的处理: 在实际应用中,一个try块可能产生多种不同的异常,如果希望采取不同的方法来处理,就需要使用多异常处理机制。 多异常处理通过在一个try块后面定义若干个catch块来实现。当try抛出异常时,程序首先转向第一个catch,查看是否能够接收该异常。 接收异常指异常对象与catch的参数的匹配: 1、异常对象与参数属于相同的例外类 2、异常对象属于参数例外类的子类 3、异常对象实现了参数所定义的接口 如果被第一个catch接收,则程序直接执行这个块,完毕后退出当前方法,try块中没有执行的语句及其他catch块将被忽略。否则,一次匹配其他的catch块,直到找到一个可以接收该异常对象的catch块。 如果所有的catch都不匹配,则程序会返回到调用该方法的上层方法。如果这个上层方法定义了与所产生的异常相匹配的catch块,则会跳到这个catch块,否则继续回溯。如果最终都没有找到,则这是系统一般会终止程序,然后打印除相关的 异常信息。 如果没有异常发生,那么所有的catch块都会被忽略。 设计catch 由于catch 块是按照先后排列顺序执行的,所以一般来讲将处理较为具体的和常见的异常的catch 块放在前面,而与多种异常相匹配的catch 块放在靠后的位置。 catch 块根据异常的不同执行不同的操作,一般来讲是将异常和错误的信息打印出来,方便修改调试程序。 finally finally语句为异常处理提供一个统一的接口,也就是说,无论是否发生异常,程序都要执行一段代码,那么将这段代码放在finally语句块中。 finally语句是可选的,try后面至少要有一个catch或者finally块。 异常处理 覆盖方法中声明异常 如果一个类的方法中声明了throw异常,则它的子类如果要覆盖该类的方法的时候,也可以抛出(throw)异常。 但是要注意的是:子类方法抛出的异常只能是父类方法所抛出的异常的同类或者子类,不能抛出比父类更一般的异常。 异常处理 import java.io.*; class A{ public void methodA() throws IOException {……} } class B1 extends A{ public void methodA() throws FileNotFoundException {……} } class B2 extends A{ public void methodA() throws Exception //出错 {……} } 抛出异常补充 有时候用户程序自己定义的异常不能依靠系统自动抛出,则必须依靠throw。比如有时try内代码不会产生异常,而用户自己希望它产生异常,则可以用throw抛出异常。 一种是在方法中自己处理发生的异常,另一种是在方法之外处理异常. 创建自己的异常类: Java软件包中尽管已经有了很多现成的异常,但在实际编程时,也需要建立自己的异常类来处理某个应用所特有的运行错误 创建用户定义的异常时,要完成: 1、声明一个新的异常类,这个类必须继承系统现有的异常类。 2、为新的异常类定义属性和方法,或者重载父类的方法,使得这些属性和方法能体现该类所对应的错误的信息。
9.2 Java多线程机制 创建线程:将需要独立运行的子任务代码放到从Thread类派生出来的类的run方法中。然后在主线程中原先调用该子任务的地方先创建一个该线程类的实例,再调用线程类中的start方法启动线程。 前面介绍了如何通过创建自己的线程类来实现多线程,即将线程类(Thread)与程序的主类(Main)分离。 class CounterSubTask extends Thread { ... public void run() { ... } } public class CounterMultiThread { public static void main(String[] args) { ... } } 如果CounterSubTask类,也就是run方法所在的类已经拥有另一个超类,那将无法继承Thread类,此时如何实现多线程呢?典型的情况是,对于一个GUI程序,需要从JFrame或JApplet类派生。这时,虽然不能继承Thread类,但是可以通过实现Runnable接口来实现多线程。 9.3 流式输入输出与文件处理 在Java中,应用程序所需要读入的数据和写出的数据是通过I/O操作实现的。这些读写数据的源或目的包括文件、内存、网络连接等,其中,最常用的是文件。 Java中的输入输出流可以分为两大类:输入流和输出流。输入流是能够读取字节的对象,而输出流是能够写字节序列的对象。最初设计的输入输出类是面向字节流的,即能够支持8位的字节流,分别由派生自抽象类InputStream和OutputStream的类层次来表示。但是随着对国际化支持的需求出现,面向字节的流不能很好地处理使用Unicode(每个字符使用两个字节)的数据,因此引入了派生自抽象类Reader和Writer的类层次,用于读写双字节的Unicode字符,而不是单字节字符。
9.3.1 Java输入输出类库继承关系 9.3.2基于标准输入输出的IO操作 在Java里,还提供了“标准输入流”和“标准错误输出流”,分别对应于System.in和System.err。System.out和System.err已经被封装成PrintStream对象,因此具有强大的输出功能;但是System.in却仍然是原始的InputStream,需要在使用的时候进行封装。 9.3.3 文件读写及随机访问 文件是保存在磁盘等二级存储设备上的数据,由记录组成,文件中的一行可以看作是一条记录。对文件的读写和标准输入输出是十分类似的,需要注意的是要采用专门对文件操作的流,并应该在合适的时候关闭流,否则系统资源无法得到释放。对于输出流,如果不执行关闭流的操作,则缓冲区的数据将有可能没有写入文件,造成文件损坏。 9.3.4 Java的文件管理 对于文件或目录的其他操作,如重命名、删除、列表显示等,需要使用Java的文件管理File类。 在Java中,文件和目录都是用File对象来表示的,创建和区分方法:先创建一个File对象,并指定文件名或目录名,若指定文件名或目录名不存在,则File对象的新建并不会创建一个文件或目录;需要用createNewFile方法或mkdir方法来分别创建文件或目录。区分File对象代表的是文件还是目录,可以通过isFile方法和isDirectory方法来判断。 9.4 Java网络通信 传统的网络编程是一项非常细节化的工作,程序员必须处理和网络有关的大量细节,如各种协议,甚至要理解网络相关的硬件知识。而Java则将底层的网络通信细节予以屏蔽,使得使用的编程模型是一个文件模型,也就是说,可以象操作流一样来操作网络数据传输。另外,由于在网络连接中,通常都需要一个服务器同时为多个客户端服务,因此Java的多线程机制也大派用场。
9.4.1网络基础知识及Java网络模型 9.4.2无连接的数据报 对于类似传输速度更重要的应用,使用无连接的数据报协议UDP,即“用户数据报协议”。UDP并不刻意追求数据包会完全发送出去,也不能担保它们抵达的顺序与它们发出时一样,因此,这是一种“不可靠协议”。由于其速度比TCP快得多,所以还是能够在很多应用中使用。 Java对数据报的支持与它对TCP套接字的支持大致相同,使用DatagramSocket类来表示无连接的socket,接收和发送数据报。接收和要发送的数据报内容保存在DatagramPacket对象中。 UDP也有自己的端口,和TCP端口是相互独立的。也就是说,可以在端口8080同时运行一个TCP和UDP服务程序,两者之间不会产生冲突。 9.4.3 Java访问网络资源 在因特网上,已经开发了许多服务,如WWW浏览、Email等,而Java也提供了相应的扩展组件,如对于Email应用,Java提供了JavaMail API,使用时只需要调用其提供的方法就可以完成如发送邮件的操作: Transport.send(message); 在因特网上,我们使用通用资源定位符URL(Uniform Resource Locator)来查找资源。URL包含了用于查找某个资源的信息,如一张图片、一个文件等。URL资源可以包括很多种,如HTTP资源、FTP资源等。下面就是一个图像资源的URL,属于HTTP资源。 本章小结 通过本章的学习,了解了Java异常处理、多线程机制、流式IO以及网络通信的基本概念和模型。 在异常处理中,通过Java语言提供的先进的错误纠正与恢复机制,可以有效地增强代码的健壮程度,并使用尽可能精简的代码创建大型、可靠的应用程序,同时排除程序里那些不能控制的错误。Java强迫遵守违例所有方面的问题,所以无论库设计者还是客户程序员,都能够连续一致地使用它。 使用多线程机制的主要目的是对大量任务进行有序的管理,从而可以通过“轻度”切换来更有效地利用计算机资源,或者对用户来说使用界面更加友好,相比进程之间的“重度”切换,效率得到很大提高。有效利用计算机资源的典型应用是在IO等候期间如何利用CPU;用户方面的界面友好性的典型体现是如何在一个长时间的数据下载过程中灵敏地对“停止”(stop)操作进行反应。 在流式输入输出中,Java提供了通过控制台、文件、内存块甚至因特网等多种不同数据源或目的进行不同方式访问的流库。通过对流过滤器的正确使用,将提供灵活的I/O操作。Java不仅提供了对文件的流式访问,而且提供了随机访问和文件管理。 在网络通信中,Java不仅提供了面向连接和无连接数据报的底层通信,而且还提供了高层服务,如Email和WWW服务等。通过Java提供的网络功能,可以以流的方式来进行网络数据的传输,而且不需要关注连网的细节问题。