笔记:第四讲 Java的例外处理和I/O流
【课前思考】 难点: 4.1 什么是例外 4.1.2 例外处理机制 抛弃(throw)例外: 两种处理例外的机制: 4.1.3 例外类的层次 java中的例外类可分为两大类: 4.2 例外的处理 ...... }catch( ExceptionName1 e ){ ...... }catch( ExceptionName2 e ){ ...... } ...... }finally{ ...... } ◇ try 捕获例外的第一步是用try{…}选定捕获例外的范围,由try所限定的代码块中的语句在执行过程中可能会生成例外对象并抛弃。 ◇ catch 每个try代码块可以伴随一个或多个catch语句,用于处理try代码块中所生成的例外事件。catch语句只需要一个形式参数指明它所能够捕获的例外类型,这个类必须是Throwable的子类,运行时系统通过参数值把被抛弃的例外对象传递给catch块。 在catch块中是对例外对象进行处理的代码,与访问其它对象一样,可以访问一个例外对象的变量或调用它的方法。getMessage( )是类Throwable所提供的方法,用来得到有关异常事件的信息,类Throwable还提供了方法printStackTrace( )用来跟踪异常事件发生时执行堆栈的内容。例如: try{ ...... }catch( FileNotFoundException e ){ System.out.println( e ); System.out.println( "message: "+e.getMessage() ); e.printStackTrace( System.out ); }catch( IOException e ){ System.out.println( e ); } catch 语句的顺序: 捕获例外的顺序和catch语句的顺序有关,当捕获到一个例外时,剩下的catch语句就不再进行匹配。因此,在安排catch语句的顺序时,首先应该捕获最特殊的例外,然后再逐渐一般化。也就是一般先安排子类,再安排父类。 ◇ finally 捕获例外的最后一步是通过finally语句为例外处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。不论在try代码块中是否发生了异常事件,finally块中的语句都会被执行。 4.2.2 声明抛弃例外 1.声明抛弃例外如果在一个方法中生成了一个例外,但是这一方法并不确切地知道该如何对这一异常事件进行处理,这时,一个方法就应该声明抛弃例外,使得例外对象可以从调用栈向后传播,直到有合适的方法捕获它为止。 声明抛弃例外是在一个方法声明中的throws子句中指明的。例如: public int read () throws IOException{ ...... } throws子句中同时可以指明多个例外,之间由逗号隔开。例如: public static void main(String args[]) throws IOException,IndexOutOfBoundsException {…} 2.抛出例外 抛出例外就是产生例外对象的过程,首先要生成例外对象,例外或者由虚拟机生成,或者由某些类的实例生成,也可以在程序中生成。在方法中,抛出例外对象是通过throw语句实现的。 例如: IOException e=new IOException(); throw e ; 可以抛出的例外必须是Throwable或其子类的实例。下面的语句在编译时将会产生语法错误: throw new String("want to throw"); 4.3 自定义例外类的使用 自定义例外类必须是Throwable的直接或间接子类。 4.4 I/O 流概述 4.4.1 I/O流的层次 1.InputStream 4.4.3 I/O中的例外 进行I/O操作时可能会产生I/O例外,属于非运行时例外,应该在程序中处理。如:FileNotFoundException,EOFException,IOException。 4.5 文件处理 I/O处理中,最常见的是对文件的操作,java.io包中有关文件处理的类有:File、FileInputStream、FileOutputStream、RamdomAccessFile和FileDescriptor;接口有:FilenameFilter。 public File(String path);/*如果path是实际存在的路径,则该File对象 /*表示的是目录;如果path是文件名,则该File对象表示的是文件。*/ public File(String path,String name);//path是路径名,name是文件名 public File(File dir,String name);//dir是路径名,name是文件名 ◇ 文件名的处理 String getName( ); //得到一个文件的名称(不包括路径) String getPath( ); //得到一个文件的路径名 String getAbsolutePath( );//得到一个文件的绝对路径名 String getParent( ); //得到一个文件的上一级目录名 String renameTo(File newName); //将当前文件名更名为给定文件的 完整路径 ◇ 文件属性测试 boolean exists( ); //测试当前File对象所指示的文件是否存在 boolean canWrite( );//测试当前文件是否可写 boolean canRead( );//测试当前文件是否可读 boolean isFile( ); //测试当前文件是否是文件(不是目录) boolean isDirectory( ); //测试当前文件是否是目录 ◇ 普通文件信息和工具 long lastModified( );//得到文件最近一次修改的时间 long length( ); //得到文件的长度,以字节为单位 boolean delete( ); //删除当前文件 ◇ 目录操作 boolean mkdir( ); //根据当前对象生成一个由该对象指定的路径 String list( ); //列出当前目录下的文件 在java的windows编程中,目录用如下形式:File dir = new File("e://myapp//src"); 4.5.2 文件的顺序处理 FileInputStream fis; 接口DataInput 中定义的方法主要包括从流中读取基本类型的数据、读取一行数据、或者读取指定长度的字节数。如:readBoolean( )、readInt( )、readLine( )、readFully( ) 等。 4.6 过滤流 过滤流在读/写数据的同时可以对数据进行处理,它提供了同步机制,使得某一时刻只有一个线程可以访问一个I/O流,以防止多个线程同时对一个I/O流进行操作所带来的意想不到的结果。类FilterInputStream和FilterOutputStream分别作为所有过滤输入流和输出流的父类。 为了使用一个过滤流,必须首先把过滤流连接到某个输入/出流上,通常通过在构造方法的参数中指定所要连接的输入/出流来实现。例如: 几种常见的过滤流: ◇ BufferedInputStream和BufferedOutputStream缓冲流,用于提高输入/输出处理的效率。 ◇ DataInputStream 和 DataOutputStream 不仅能读/写数据流,而且能读/写各种的java语言的基本类型,如:boolean,int,float等。 ◇ LineNumberInputStream 除了提供对输入处理的支持外,LineNumberInputStream可以记录当前的行号。 ◇ PushbackInputStream 提供了一个方法可以把刚读过的字节退回到输入流中,以便重新再读一遍。 ◇ PrintStream 打印流的作用是把Java语言的内构类型以其字符表示形式送到相应的输出流。 4.7 字符流的处理 java中提供了处理以16位的Unicode码表示的字符流的类,即以Reader和Writer 为基类派生出的一系列类。 这两个类是抽象类,只是提供了一系列用于字符流处理的接口,不能生成这两个类的实例,只能通过使用由它们派生出来的子类对象来处理字符流。 4.7.2 InputStreamReader和OutputStreamWriter public InputStreamReader(InputStream in); /*in是字节流,而InputStreamReader是字符流,但是其来源是字节流in, 因此InputStreamReader就可以把字节流in转换成字符流处理。/* public InputStreamReader(InputStream in,String enc) throws UnsupportedEncodingException; /*enc是编码方式,就是从字节流到字符流进行转换时所采用的编码方式, 例如 ISO8859-1,UTF-8,UTF-16等等*/ public OutputStreamWriter(OutputStream out); /*out是字节流,而OutputStreamReader是字符流 */ public OutputStreamWriter(OutputStream out,String enc) throws UnsupportedEncodingException; //enc是编码方式 InputStreamReader和OutputStreamWriter的方法: ◇ 读入和写出字符 基本同Reader和Writer。 ◇ 获取当前编码方式 public String getEncoding(); ◇ 关闭流 public void close() throws IOException; 4.7.3 BufferedReader和BufferedWriter ?◇ 生成流对象
【例4-4】 4.8 对象的串行化 4.8.1 串行化的定义 1. 什么是串行化对象的寿命通常随着生成该对象的程序的终止而终止。有时候,可能需要将对象的状态保存下来,在需要时再将对象恢复。我们把对象的这种能记录自己的状态以便将来再生的能力,叫做对象的持续性(persistence)。对象通过写出描述自己状态的数值来记录自己,这个过程叫对象的串行化(Serialization)。 2. 串行化的目的 串行化的目的是为java的运行环境提供一组特性,其主要任务是写出对象实例变量的数值。 4.8.2 串行化方法 public class Student implements Serializable{ int id; //学号 String name; //姓名 int age; //年龄 String department //系别 public Student(int id,String name,int age,String department){ this.id = id; this.name = name; this.age = age; this.department = department; } } 2. 构造对象的输入/输出流 ? 要串行化一个对象,必须与一定的对象输入/输出流联系起来,通过对象输出流将对象状态保存下来,再通过对象输入流将对象状态恢复。 java.io包中,提供了ObjectInputStream和ObjectOutputStream将数据流功能扩展至可读写对象。在ObjectInputStream中用readObject()方法可以直接读取一个对象,ObjectOutputStream中用writeObject()方法可以直接将对象保存到输出流中。 Student stu=new Student(981036,"Liu Ming",18,"CSD"); FileOutputStream fo=new FileOutputStream("data.ser"); //保存对象的状态 ObjectOutputStream so=new ObjectOutputStream(fo); try{ so.writeObject(stu); so.close(); }catch(IOException e ) {System.out.println(e);} FileInputStream fi=new FileInputStream("data.ser"); ObjectInputStream si=new ObjectInputStream(fi); //恢复对象的状态 try{ stu=(Student)si.readObject(); si.close(); }catch(IOException e ) {System.out.println(e);} 在这个例子中,我们首先定义一个类Student,实现了 Serializable接口,然后通过对象输出流的writeObject()方法将Student对象保存到文件data.ser中。之后,通过对象输入流的readObject()方法从文件data.ser中读出保存下来的Student对象。 4.8.3 串行化的注意事项 1.串行化能保存的元素 只能保存对象的非静态成员变量,不能保存任何的成员方法和静态的成员变量,而且串行化保存的只是变量的值,对于变量的任何修饰符,都不能保存。 2.transient关键字 对于某些类型的对象,其状态是瞬时的,这样的对象是无法保存其状态的,例如一个Thread对象,或一个FileInputStream对象,对于这些字段,我们必须用transient关键字标明,见3.2.1节 3. 定制串行化 缺省的串行化机制,对象串行化首先写入类数据和类字段的信息,然后按照名称的上升排列顺序写入其数值。如果想自己明确地控制这些数值的写入顺序和写入种类,必须定义自己的读取数据流的方式。就是在类的定义中重写接口serializeble的writeObject()和readObject()方法。 例如可在4.8.2的例子中,加入重写的writeObject()和readObject()方法,对Student 类定制其串行化。 private void writeObject(ObjectOutputStream out)throws IOException { out.writeInt(id); out.writeInt(age); out.writeUTF(name); out.writeUTF(department); } private void readObject(ObjectInputStream in)throws IOException { id=in.readInt(); age=in.readInt(); name=in.readUTF(); department=in.readUTF(); } 4.9 其它常用的流 4.9.1 管道流 管道用来把一个程序、线程或代码块的输出连接到另一个程序、线程或代码块的输入。 管道输入流作为一个通信管道的接收端,管道输出流作为发送端。在使用管道之前,管道输出流和管道输入流必须进行连接。下面有两种连接的方法: 1. ByteArrayInputStream和ByteArrayOutputStream SequenceInputStream 把几个输入流顺序连接起来。顺序输入流提供了把若干不同的流统一为同一个流的功能,使得程序变得更加简洁。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |