重点: 1.在编写程序时,要正确地使用捕获例外和声明抛出异常的两种例外处理的方法。
2.遇到实际问题时,要根据需要正确使用各种输入/输出流,特别是对中文使用适当的字符输入流。
3.正确使用对象串行化的方法。
难点:
1.如何使用Java中两种异常处理机制,抛弃异常和声明抛弃异常的区别与联系。
2.处理字符流时,其构造方法的参数是一个字节流。
3.对象串行化的概念。
(一)处理异常
有的异常在编译(输入命令行)时出现,而有的异常是在运行时出现。
异常处理机制:捕获异常(try-catch-finally)-积极机制、抛出异常(throw)-消极机制
异常类的层次:Throwable类中的Error、Exception类
Error类:LinkageError 动态链接失败、VirtualMachineError 虚拟机错误、AWTError
Exception类:RuntimeException运行时异常(算术异常除0错、数组下标越界等)、IOExcetpion(非运行时异常)、AWTError。。。
运行时的异常Java编译器允许程序员不做处理,而非运行时的异常Java编译器要求程序员必须进行捕获或抛出异常的处理:
1.try-catch-finally语句:
try{......}
catch( ExceptionName1 e
){......} //ExceptionName1
异常子类
catch( ExceptionName2 e
){......} //ExceptionName2
异常父类
finally{......}
【注意】catch
语句的顺序:
捕获例外的顺序和catch语句的顺序有关,当捕获到一个异常时,剩下的catch语句就不再进行匹配。因此,在安排catch语句的顺序时,首先应该捕获最特殊的异常,然后再逐渐一般化。也就是一般先安排子类,再安排父类。
通过finally语句为异常处理提供一个统一的出口,不论在try代码块中是否发生了异常事件,finally块中的语句都会被执行。
2.throws声明语句:
如果在一个方法中生成了一个异常,但是这一方法并不确切地知道该如何对这一异常事件进行处理,这时,一个方法就应该声明抛弃异常,使得异常对象可以从调用栈向后传播,直到有合适的方法捕获它为止。
public int read () throws IOException{
......
}
注意:throws子句中同时可以指明多个异常,之间由逗号隔开。例如:
public static void main(String
args[]) throws
IOException,IndexOutOfBoundsException {…}
3.throws语句:IOException e=new
IOException();throw
e ; //注意:此处抛出的对象必须是异常实例,不可抛出其他。
(二)自定义异常类的使用:
自定义异常类必须是Throwable的直接或间接子类。
【注意】:一个方法所声明抛出的异常是作为这个方法与外界交互的一部分而存在的。所以,方法的调用者必须了解这些异常,并确定如何正确的处理他们。
(三)I/O
流概述:
I/0流:输入/输出处理源(stream),比如从键盘读取数据、从文件中读取数据或向文件中写数据等等。Jdk
提供了包java.io,其中包括一系列的类来实现输入/输出处理。
1.字节流:(byte)
从InputStream和OutputStream派生出来的一系列类。
◇ InputStream、OutputStream
◇ FileInputStream、FileOutputStream
◇ PipedInputStream、PipedOutputStream
◇ ByteArrayInputStream、ByteArrayOutputStream
◇ FilterInputStream、FilterOutputStream
◇ DataInputStream、DataOutputStream
◇ BufferedInputStream、BufferedOutputStream
2.字符流:(以16位的Unicode码表示的字符)
从Reader和Writer派生出的一系列类:
◇ Reader、Writer
◇ InputStreamReader、OutputStreamWriter
◇ FileReader、FileWriter
◇ CharArrayReader、CharArrayWriter
◇ PipedReader、PipedWriter
◇ FilterReader、FilterWriter
◇ BufferedReader、BufferedWriter
◇ StringReader、StringWriter
3.对象流
◇ ObjectInputStream、ObjectOutputStream
4.其它:
◇ 文件处理:
File、RandomAccessFile;
◇ 接口
DataInput、DataOutput、ObjectInput、ObjectOutput;
InputStream
◇ 从流中读取数据:
int read(
); //读取一个字节,返回值为所读的字节 int
read( byte b[ ] ); //读取多个字节,放置到字节数组b中,通常读取的字节数量为b的长度
//返回值为实际读取的字节的数量
int read( byte b[ ], int off, int len
); //读取len个字节,放置到以下标off开始字节数组b中
//返回值为实际读取的字节的数量
int available( ); //返回值为流中尚未读取的字节的数量
long skip( long n ); //读指针跳过n个字节不读,返回值为实际跳过的字节数量
◇ 关闭流:
close( ); //流操作完毕后必须关闭
◇ 使用输入流中的标记:
void mark( int readlimit ); //记录当前读指针所在位置,读指针读出readlimit个字节后所标记的指针位置失效
void reset( ); //把读指针重新指向用mark方法所记录的位置
boolean markSupported( ); //当前的流是否支持读指针的记录功能
有关每个方法的使用,详见java
API。
OutputStream
◇ 输出数据:
void write( int b ); //往流中写一个字节b
void write( byte b[ ] ); //往流中写一个字节数组b
void write( byte b[ ], int off, int len
); //字节数组b中从下标off开始,长度len的字节写入流
◇ flush( ); //刷空输出流,并输出所有被缓存的字节
由于某些流支持缓存功能,该方法将把缓存中所有内容强制输出到流中。
◇ 关闭流:
close( ); //流操作完毕后必须关闭
5.I/O中的异常:进行I/O操作时可能会产生I/O异常,属于非运行时例外,应该在程序中处理。如:FileNotFoundException,
EOFException, IOException
(四)文件处理:
java.io包中有关文件处理的类有:File、FileInputStream、FileOutputStream、RamdomAccessFile和FileDescriptor;接口有:FilenameFilter。
文件类(File):
◇ 文件或目录的生成
public File(String path);
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( ); //列出当前目录下的文件
下面给出一个文件操作的实例:列出目录D:\\ex\所有的.java文件
import
java.io.*; //引入java.io包中所有的类
public class FileFilterTest{
public static void main(String args[]){
File dir=new File("d://ex"); //用File
对象表示一个目录
Filter filter=new Filter("java"); //生成一个名为java的过滤器
System.out.println("list java files in directory "+dir);
String files[]=dir.list(filter); //列出目录dir下,文件后缀名为java的所有文件
for(int i=0;i
File f=new File(dir,files[i]); //为目录dir下的文件或目录创建一个File对象
if(f.isFile()) //如果该对象为后缀为java的文件,则打印文件名
System.out.println("file "+f);
else
System.out.println("sub directory "+f
); //如果是目录则打印目录名
}
}
}
class Filter implements FilenameFilter{
String extent;
Filter(String extent){
this.extent=extent;}
public boolean accept(File dir,String name){
return
name.endsWith("."+extent); //返回文件的后缀名
}
}
【文件顺序处理】类FileInputStream和FileOutputStream用来进行文件I/O处理,由它们所提供的方法可以打开本地主机上的文件,并进行顺序的读/写。例如,下列的语句段是顺序读取文件名为text的文件里的内容,并显示在控制台上面,直到文件结束为止:FileInputStream
fis;
try{ fis = new FileInputStream( "text" );
System.out.print( "content of text is : ");
int b;
while( (b=fis.read())!=-1 ) //顺序读取文件text里的内容并赋值给整型变量b,直到文件结束 {
System.out.print( (char)b );
}
}catch( FileNotFoundException e ){
System.out.println( e );
}catch( IOException e ){
System.out.println( e );
}
【随机访问文件】
在java中,类RandomAccessFile提供了随机访问文件的方法。类RandomAccessFile的声明为:
public class RandomAccessFile extends Object
implements DataInput,
DataOutput接口DataInput中定义的方法:readBoolean(
)、readInt( )、readLine( )、readFully( )等
接口DataOutput中定义的方法:writeChar(
)、writeDouble( )、write( )等
类RandomAccessFile的方法:
◇ 构造方法: RandomAccessFile(String name,String
mode); //name文件名,mode打开方式("r"只读"rw"读写)
RandomAccessFile(File file,String
mode); //file是文件对象
◇ 文件指针的操作
long getFilePointer( ); //用于得到当前的文件指针
void seek( long pos ); //用于移动文件指针到指定的位置
int skipBytes( int n ); //使文件指针向前移动指定的n个字节
(五)过滤流
过滤流在读/写数据的同时可以对数据进行处理,它提供了同步机制,使得某一时刻只有一个线程可以访问一个I/O流,以防止多个线程同时对一个I/O流进行操作所带来的意想不到的结果。
类FilterInputStream和FilterOutputStream分别作为所有过滤输入流和输出流的父类。
为了使用一个过滤流,必须首先把过滤流连接到某个输入/出流上,通常通过在构造方法的参数中指定所要连接的输入/出流来实现。
例如:
FilterInputStream( InputStream in );
FilterOutputStream( OutputStream out );
常见的过滤流:
◇
BufferedInputStream和BufferedOutputStream 缓冲流,用于提高输入/输出处理的效率
◇ DataInputStream 和
DataOutputStream 不仅能读/写数据流,而且能读/写数据基本类型。
◇
LineNumberInputStream 除了提供对输入处理的支持外,还可以记录当前的行号。
◇
PushbackInputStream 提供方法可以把刚读过的字节退回到输入流中,以便重新再读一遍。
◇
PrintStream 打印流:把Java语言的内构类型以其字符表示形式送到相应的输出流。
(六)字符流的处理java中提供了处理以16位的Unicode码表示的字符流的类,即以Reader和Writer为基类派生出的一系列类。
1.Reader和Writer
Reader类是处理所有字符流输入类的父类、Writer类是处理所有字符流输出类的父类。
◇ 读取字符(输入流)
public int read() throws IOException; //读取一个字符,返回值为读取的字符
public int read(char cbuf[]) throws IOException;
//读取一系列字符到数组cbuf[]中
public abstract int read(char cbuf[],int off,int len) throws
IOException; //必须由子类实现
◇
写入字符(输出流)
public void write(int c) throws IOException;
public void write(char cbuf[]) throws IOException;
public abstract void write(char cbuf[],int off,int len) throws
IOException;
public void write(String str) throws IOException;
public void write(String str,int off,int len) throws
IOException;
◇ flush(输出流);刷空输出流,并输出(写入)所有被缓存的字节。
◇ 标记流(输入流)
public boolean markSupported();
public
void mark(int readAheadLimit) throws IOException;
public
void reset() throws IOException;
◇ 关闭流(输入输出流)
public abstract void close() throws IOException;
2.InputStreamReader 和
OutputStreamWriter:字节流和字符流的中介流◇
生成流对象 public InputStreamReader(InputStream
in); //InputStreamReader可以把字节流in转换成字符流
public InputStreamReader(InputStream in,String enc) throws
UnsupportedEncodingException;
public OutputStreamWriter(OutputStream out);
public OutputStreamWriter(OutputStream out,String enc) throws
UnsupportedEncodingException;
◇ 读入和写出字符
基本同Reader和Writer。
◇ 获取当前编码方式
public String getEncoding();
◇ 关闭流
public void close() throws IOException;
3.BufferedReader
和 BufferedWriter:◇
生成流对象
public BufferedReader(Reader in); //使用缺省的缓冲区大小
public BufferedReader(Reader in, int
sz); //sz为缓冲区的大小
public BufferedWriter(Writer out);
public BufferedWriter(Writer out, int sz);
◇
读入/写出字符
除了Reader和Writer中提供的基本的读写方法外,增加对整行字符的处理 public String readLine() throws
IOException; //读一行字符
public void newLine() throws
IOException; //写一行字符
【例子】从键盘接收数据输入:
import
java.io.*;
public class NumberInput{
public static void main(String args[]){
try{ InputStreamReader
ir;
BufferedReader in;
ir=new
InputStreamReader(System.in); //从键盘接收了一个字符串的输入,并创建了一个字符输入流的对象
in=new BufferedReader(ir);
String s=in.readLine();//从输入流in中读入一行,并将读取的值赋值给字符串变量s
System.out.println("Input value is: "+s);int
i = Integer.parseInt(s);//转换成int型
i*=2;
System.out.println("Input value changed after doubled:
"+i);
}catch(IOException e)
{System.out.println(e);}
}
}
【注意】在读取字符流时,如果不是来自于本地的,比如说来自于网络上某处的与本地编码方式不同的机器,那么我们在构造输入流时就不能简单地使用本地缺省的编码方式,否则读出的字符就不正确;为了正确地读出异种机上的字符,我们应该使用下述方式构造输入流对象:
ir = new
InputStreamReader(is, "8859_1");采用ISO
8859_1编码方式,这是一种映射到ASCII码的编码方式,可以在不同平台之间正确转换字符。(七)对象的串行化(Serialization)我们把对象的这种能记录自己的状态以便将来再生的能力,叫做对象的持续性(persistence)。对象通过写出描述自己状态的数值来记录自己,这个过程叫对象的串行化(Serialization)。
串行化目的:为java的运行环境提供一组特性,其主要任务是写出对象实例变量的数值。
串行化方法:在java.io包中,只有实现了Serializable类的对象才可以被串行化。
串行化只能保存的元素:非静态成员变量的值,不能保存变量任何修饰符。
transient关键字:暂时保存的对象
首先,定义一个可串行化对象
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;
}
}
其次,构造对象输入输出流:在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对象。
(八)其他常用流:
1.管道流:PipedInputStream、PipedOutputStream
管道输入流作为一个通信管道的接收端,管道输出流作为发送端。在使用管道之前,管道输出流和管道输入流必须进行连接。下面有两种连接的方法:构造方法中连接 PipedInputStream(PipedOutputStream src);
PipedOutputStream(PipedInputStream snk);
connect()方法进行连接
类PipedInputStream中定义为:
void connect(PipedOutputStream src);
类PipedOutputStream中定义为:
void connect(PipedInputStream snk);
2.内存的读/写:
ByteArrayInputStream和ByteArrayOutputStream 字节数组读取写入字节数据
StringBufferInputStream和StringBufferOutputStream字符串缓冲区中写入读取写入字符数据
3.顺序输入流:
SequenceInputStream
把几个输入流顺序连接起来。顺序输入流提供了把若干不同的流统一为同一个流的功能,使得程序变得更加简洁。