第五章 输入输出
又称为例外,是特殊的运行错误对象
Java中声明了很多异常类,每个异常类都代表了一种运行错误,类中包含了该运行错误的信息处理错误的方法。每当Java程序运行中出现错误有一个异常类与之相对应时,系统都会产生一个相应的该异常类的对象,即产生一个异常。

说明:
▫ try 语句
其后跟随可能产生异常的代码块。
▫ catch语句
其后跟随异常处理语句,通常都要用到两个方法:
getMessage() – 返回一个字符串,对发生的异常进行描述。
printStackTrace() – 给出方法的调用序列,一直到异常的产生位置 。
▫ finally语句
不论在try代码段是否产生异常,finally 后的程序代码段都会被执行。通常在这里释放内 存以外的其他资源。
• 注意事项 ▫ 如果并列有多个catch语句捕获多个异常,则一般的异常类型放在后面,特殊的放在前面。
try { throw new ArithmeticException(); }
catch(ArithmeticException ae){ System.out.println(ae); }
• 自定义的所有异常类都必须是Exception的子类
• 声明语法如下
public class MyExceptionName extends SuperclassOfMyException {
public MyExceptionName() {
super("Some string explaining the exception");
}
}
面向字符的抽象流类——Reader和Writer
• java.io包中所有字符流的抽象超类。
• Reader提供了输入字符的API。
• Writer提供了输出字符的API。
• 它们的子类又可分为两大类
▫ 节点流:从数据源读入数据或往目的地写出数据;(下面阴影部分)
▫ 处理流:对数据执行某种处理。(非阴影部分)
• 多数程序使用这两个抽象类的一系列子类来读入/写出文本信息
▫ 例如FileReader/FileWriter用来读/写文本文件。
- 面向字节

面向字节的流—InputStream和OutputStream
数据源或目标中含有非字符数据,必须用字节流来输入/输出
• 通常被用来读写诸如图片、声音之类的二进制数据
• 绝大多数数据是被存储为二进制文件的,通常二进制文件要比含有相同数据量的文本文件小得多
System类的静态成员变量
▫ System.in: InputStream类型的,代表标准输入流,默认状态对应于键盘输入。
▫ System.out:PrintStream类型的,代表标准输出流,默认状态对应于显示器输出。
▫ System.err:PrintStream类型的,代表标准错误信息输出流,默认状态对应于显示器输出。
• setIn(InputStream): 设置标准输入流
• setOut(PrintStream):设置标准输出流
• setErr(PrintStream):设置标准错误输出流
按类型输入/输出数据-printf和Scanner
System.out.printf(“%-12s is %2d long”, name, l);
System.out.printf(“value = %2.2F”, value); # %n 是平台无关的换行标志。
Scanner s = new Scanner(System.in);
int n = s.nextInt();
▫ 还有下列方法:nextByte(),nextDouble(),nextFloat,nextInt(),nextLine(),nextLong(),nextShort()。
import java.io.*;
public class Redirecting {
public static void main(String[] args) throws IOException {
BufferedInputStream in = new BufferedInputStream(new FileInputStream( "Redirecting.java"));
PrintStream out = new PrintStream( new BufferedOutputStream( new FileOutputStream("test.out")));
System.setIn(in); System.setOut(out); System.setErr(out);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s;
while((s = br.readLine()) != null) System.out.println(s);
in.close();
out.close();
}
}
多数IO方法在遇到错误时会抛出异常,因此调用这些方法时必须
▫ 在方法头声明抛出IOException异常;
▫ 或者在try块中执行IO,然后在catch块中捕获IOException异常。
- FileWriter创建文件并写入若干行文本 (面向字符)
#通过FileWriter流创建一个文本文件,并写入若干行文本。
import java.io.*;
class FileWriterTester {
public static void main ( String[] args ) throws IOException {
String fileName = "C:\\Hello.txt";
FileWriter writer = new FileWriter( fileName );
writer.write( "Hello!\n");
writer.write( "This is my first text file,\n" );
writer.write( "You can see how this is done.\n" );
writer.write("输入一行中文也可以\n");
writer.close();
}
}
#使用这个类,换行有些问题,此外每次运行都会删除旧文件生成新文件。
#演示了捕获和处理I/O异常
import java.io.*;
class FileWriterTester {
public static void main ( String[] args ) {
String fileName = "c:\\Hello.txt" ;
try {
FileWriter writer = new FileWriter( fileName ,true );
writer.write( "Hello!\n");
writer.write( "This is my first text file,\n" );
writer.write( "You can see how this is done. \n" );
writer.write("输入一行中文也可以\n");
writer.close();
}
catch ( IOException iox) { System.out.println("Problem writing" + fileName ); }
}
}
#运行此程序,会发现在原文件内容后面又追加了重复的内容,这就是将构
造方法的第二个参数设为true的效果。
#如果将文件属性改为只读属性,再运行本程序,就会出现IO错误,程序将
转入catch块中,给出出错信息。
#如果需要写入的内容很多,就应该使用更为高效的缓冲器流类BufferedWriter。
#FileWriter和BufferedWriter类都用于输出字符流,包含的方法几乎完全一样,但BufferedWriter多提供了一个newLine()方法用于换行。
import java.io.*;
class BufferedWriterTester {
public static void main ( String[] args ) throws IOException {
String fileName = "C:/newHello.txt" ;
BufferedWriter out = new BufferedWriter(new FileWriter( fileName ) );
out.write( "Hello!" );
out.newLine() ;
out.write( "This is another text file using BufferedWriter," );
out.newLine(); ;
out.write( "So I can use a common way to start a newline" );
out.close();
}
}
- Reader 类、FileReader 类、BufferedReader类和readLine() 、文本文件复制
#同写的操作方法
import java.io.*;
class BufferedReaderTester {
public static void main ( String[] args ) {
String fileName = "C:/Hello.txt" , line;
try {
BufferedReader in = new BufferedReader(new FileReader( fileName ) );
line = in.readLine(); //读取一行内容
while ( line != null ) {
System.out.println( line ); line = in.readLine();
}
in.close();
}
catch ( IOException iox ) { System.out.println("Problem reading " + fileName ); }
}
}
#运行该程序,屏幕上将逐行显示出Hello.txt文件中的内容。
FileReader对象:创建后将打开文件,如果文件不存在,会抛出一个IOException
BufferedReader类的readLine()方法:从一个面向字符的输入流中读取一行文本。如果其中
不再有数据,返回null
Reader类的read()方法:也可用来判别文件结束。该方法返回的一个表示某个字符的int型整
数,如果读到文件末尾,返回 -1。据此,可修改本例中的读文件部分:
int c;
while((c=in.read())!= -1) System.out.print((char)c);
close()方法:为了操作系统可以更为有效地利用有限的资源,应该在读取完毕后,调用该方
法
#指定源文件和目标文件名,将源文件的内容复制到目标文件。调用方式为:
java FileCopy sourceFile destinationFile
#CopyMaker
– private boolean openFiles()
– private boolean copyFiles()
– private boolean closeFiles()
– public boolean copy(String src, String dst )
#FileCopy
– main()
import java.io.*;
class CopyMaker {
String sourceName, destName;
BufferedReader source;
BufferedWriter dest;
String line;
private boolean openFiles() {
try {
source = new BufferedReader(new FileReader( sourceName ));
}
catch ( IOException iox ) {
System.out.println("Problem opening " + sourceName );
return false;
}
try {
dest = new BufferedWriter(new FileWriter( destName ));
}
catch ( IOException iox )
{
System.out.println("Problem opening " + destName );
return false;
}
return true;
}
private boolean copyFiles() {
try {
line = source.readLine();
while ( line != null ) {
dest.write(line);
dest.newLine();
line = source.readLine();
}
}
catch ( IOException iox ) {
System.out.println("Problem reading or writing" );
return false;
}
return true;
}
private boolean closeFiles() {
boolean retVal=true;
try { source.close(); }
catch ( IOException iox ) {
System.out.println("Problem closing " + sourceName );
retVal = false;
}
try { dest.close(); }
catch ( IOException iox ) {
System.out.println("Problem closing " + destName );
retVal = false;
}
return retVal;
}
public boolean copy(String src, String dst ) {
sourceName = src ;
destName = dst ;
return openFiles() && copyFiles() && closeFiles();
}
}
public class FileCopy
{
public static void main ( String[] args ) {
if ( args.length == 2 ) new CopyMaker().copy(args[0], args[1]);
else System.out.println("Please Enter File names");
}
}
#在命令行方式下执行如下命令:java FileCopy c:/Hello.txt c:/CopyHello.txt
- 抽象类 OutputStream 、派生类FileOutputStream 、派生类DataOutputStream 写二进制件
名称 |
说明 |
public DataOutputStream(outputStream out) |
构造函数 |
protected int written |
私有属性,代表当前已写出的字节数 |
public void flush () |
冲刷此数据流,使流内的数据都被写出 |
public final int size () |
返回私有变量written的值,即已经写出的字节 |
public void write(int b) |
向底层输出流输出int变量的低八位。执行后,计数器+1 |
public final void writeBoolean (boolean b) |
写一个布尔数,计数器+1 |
public final void writeByte(int b) |
将int参数的低八位写入,舍弃高24位,计数器+1 |
public void writeBytes(String s) |
每个字符被丢掉高八位写入流中,计数器+字符个数 |
public final void writeChar(int c) |
将16-bit字符写入流中,高位在前,计数器增加2 |
public void writeDouble(double v) |
写双精度数,计数器增加8 |
public void writeFloat (float f) |
写单精度数,计数器增加4 |
public void writelnt (int I) |
写整数,计数器增加4 |
public void writeLong (long l) |
写长整数,计数器增加8 |
public final void writeShort(int s) |
写短整数,计数器增加2 |
#将三个int型数字255/0/-1写入数据文件data1.dat
import java.io.*;
class FileOutputstreamTester {
public static void main ( String[] args ) {
String fileName = "c:/data1.dat" ;
int value0 = 255, value1 = 0, value2 = -1;
try {
DataOutputStream out = new DataOutputStream(
new FileOutputStream( fileName ) );
out.writeInt( value0 );
out.writeInt( value1 );
out.writeInt( value2 );
out.close();
}
catch ( IOException iox ){
System.out.println("Problem writing " + fileName ); }
}
}
DataOutputStream out = new DataOutputStream(new BufferedOutputStream( new FileOutputStream( fileName ) ) );
#通过DataOutputStream流向文件中写多种类型的数据,并统计字节数
import java.io.*;
class BufferedOutputStreamTester {
public static void main ( String[] args ) throws IOException {
String fileName = "mixedTypes.dat" ;
DataOutputStream dataOut = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream( fileName ) ) );
dataOut.writeInt( 0 );
System.out.println( dataOut.size() + " bytes have been written.");
dataOut.writeDouble( 31.2 );
System.out.println( dataOut.size() + " bytes have been written.");
dataOut.writeBytes("JAVA");
System.out.println( dataOut.size() + " bytes have been written.");
dataOut.close();
}
}
#运行结果
– 4 bytes have been written
– 12 bytes have been written
– 16 bytes have been written
import java.io.*;
class DataInputStreamTester {
public static void main ( String[] args ) {
String fileName = "c:/data1.dat" ; long sum = 0;
try {
DataInputStream instr = new DataInputStream(new BufferedInputStream(
new ileInputStream(fileName)));
try {
while ( true )
sum += instr.readInt();
}
catch ( EOFException eof ) {
System.out.println( "The sum is: " + sum );
instr.close();
}
}
catch ( IOException iox ) {
System.out.println("IO Problems with " + fileName ); }
}
}
}
import java.io.*;
public class InputStreamTester {
public static void main(String[] args) throws IOException {
FileInputStream s=new FileInputStream("c:/Hello.txt");
int c;
while ((c = s.read()) != -1)
System.out.write(c);
s.close();
}
}
– read()方法读取一个字节,转化为[0 ,255]的之间的一个整数,返回一个int。如果读到
了文件末尾,则返回-1。
– write(int)方法写一个字节的低8位,忽略高24位。
压缩流类
• java.util.zip包中提供了一些类,使我们可以以压缩格式对流进行读写
• 都继承自字节流类OutputStream和InputStream
• GZIPOutputStream和ZipOutputStream
▫ 可分别把数据压缩成GZIP格式和Zip格式
• GZIPInputStream和ZipInputStream
▫ 可以分别把压缩成GZIP格式或Zip的数据解压缩恢复原状
#将文本文件“Hello.txt” 压缩为文件“test.gz”,再解压该文件,显示其中内容,并另存为“newHello.txt”
import java.io.*;
import java.util.zip.*;
public class GZIPTester {
public static void main(String[] args) throws IOException {
FileInputStream in = new FileInputStream("c:/Hello.txt");
GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream("c:/test.gz"));
System.out.println("Writing compressing file from c:/Hello.txt to c:/test.gz");
int c;
while((c = in.read()) != -1) out.write(c);
in.close();
out.close();
System.out.println("Reading file form c:/test.gz to monitor");
BufferedReader in2 = new BufferedReader( new InputStreamReader(new GZIPInputStream(new FileInputStream("c:/test.gz"))));
String s;
while((s = in2.readLine()) != null) System.out.println(s);
in2.close();
System.out.println("Writing decompression to c:/newHello.txt");
GZIPInputStream in3=new GZIPInputStream(new FileInputStream("c:/test.gz"));
FileOutputStream out2=new FileOutputStream("c:/newHello.txt");
while((c=in3.read())!=-1) out2.write(c);
in3.close();
out2.close();
}
}
– read()方法读取一个字节,转化为[0 ,255]的之间的一个整数,返回一个int。如果读到
了文件末尾,则返回-1。
– write(int)方法写一个字节的低8位,忽略了高24位。
Zip文件
▫ 可能含有多个文件,所以有多个入口(Entry)
▫ 每个入口用一个ZipEntity对象表示,该对象的getName()方法返回文件的最初名称
• ZipOutputStream
▫ 父类是DeflaterOutputStream
▫ 可以把数据压缩成ZIP格式
• ZipInputStream
▫ 父类是InflaterInputStream
▫ 可以把压缩成ZIP格式的数据解压缩
#从命令行输入若干个文件名,将所有文件压缩为“c:/test.zip”,再从此压缩文件中解压并显示
import java.io.*;
import java.util.*;
import java.util.zip.*;
public class ZipOutputStreamTester {
public static void main(String[] args) throws IOException {
ZipOutputStream out=new ZipOutputStream(new BufferedOutputStream(new FileOutputStream("c:/test.zip")));
for(int i = 0; i < args.length; i++) {
System.out.println("Writing file " + args[i]);
BufferedInputStream in =new BufferedInputStream(new FileInputStream(args[i]));
out.putNextEntry(new ZipEntry(args[i]));
int c;
while((c = in.read()) != -1) out.write(c);
in.close();
}
out.close();
System.out.println("Reading file");
ZipInputStream in2 =new ZipInputStream(new BufferedInputStream(new FileInputStream("c:/test.zip")));
ZipEntry ze;
while((ze = in2.getNextEntry()) != null) {
System.out.println("Reading file " + ze.getName());
int x;
while((x = in2.read()) != -1) System.out.write(x);
System.out.println();
}
in2.close();
}
}
– 在命令行输入两个文本文件名后,将生成c:/test.zip文件
– 在屏幕上显示出解压后每个文件的内容
– 在资源管理器窗口中,使用winzip软件可解压缩该文件,恢复出和原来文件相同的两个文本文件
import java.util.*;
import java.util.zip.*;
import java.lang.*;
import java.io.*;
class Unzip {
byte doc[]=null;
String Filename=null;
String UnZipPath=null;
public Unzip(String filename,String unZipPath) {
this.Filename=filename;
this.UnZipPath=unZipPath;
this.setUnZipPath (this.UnZipPath);
}
public Unzip(String filename) {
this.Filename=new String(filename);
this.UnZipPath=null;
this.setUnZipPath (this.UnZipPath);
}
private void setUnZipPath(String unZipPath) {
if(unZipPath.endsWith("\\"))
this.UnZipPath=new String(unZipPath);
else
this.UnZipPath=new String(unZipPath+"\\");
}
public void doUnZip() {
try {
ZipInputStream zipis=new ZipInputStream(
new FileInputStream(Filename));
ZipEntry fEntry=null;
while((fEntry=zipis.getNextEntry())!=null) {
if (fEntry.isDirectory())
checkFilePath(UnZipPath+fEntry.getName());
else {
String fname=new String(UnZipPath+fEntry.getName());
try{
FileOutputStream out = new FileOutputStream(fname);
doc=new byte[512];
int n;
while ((n = zipis.read(doc,0,512)) != -1)
out.write(doc, 0, n);
out.close();
out=null;
doc=null;
}
catch (Exception ex) { }
}
}
zipis.close();
}
catch(IOException ioe) { System.out.println(ioe); }
}
private void checkFilePath(String dirName) throws IOException {
File dir = new File(dirName);
if(!dir.exists())
dir.mkdirs();
}
}
public class UnZipTester {
public static void main(String [] args) {
String zipFile=args[0];
String unZipPath=args[1]+"\\";
Unzip myZip=new Unzip(zipFile,unZipPath);
myZip.doUnZip();
}
}
ObjectInputStream/ObjectOutputStream类
• 不保存对象的transient和static类型的变量
• 对象要想实现序列化,其所属的类必须实现Serializable接口
FileOutputStream out = new FileOutputStream("theTime");
ObjectOutputStream s = new ObjectOutputStream(out);#ObjectOutputStream是一个处理流类,非节点类
s.writeObject("Today");
s.writeObject(new Date());
s.flush();
FileInputStream in = new FileInputStream("theTime");
ObjectInputStream s = new ObjectInputStream(in);
String today = (String)s.readObject();
Date date = (Date)s.readObject();
Seriealizable
• 空接口,使类的对象可实现序列化
• Serializable 接口的定义
package java.io;
public interface Serializable {
// there's nothing in here!
};
• 实现Serializable接口的语句
public class MyClass implements Serializable {
...
}
• 使用关键字transient可以阻止对象的某些成员被自动写入文件
#创建一个书籍对象,并把它输出到一个文件book.dat中,然后再把该对象读出来,在屏幕上显示对象信息
class Book implements Serializable {
int id;
String name;
String author;
float price;
public Book(int id,String name,String author,float price) {
this.id=id;
this.name=name;
this.author=author;
this.price=price;
}
}
import java.io.*;
public class SerializableTester {
public static void main(String args[]) throws
IOException,ClassNotFoundException {
Book book=new Book(100032,"Java Programming
Skills","Wang Sir",30);
ObjectOutputStream oos=new ObjectOutputStream(
new FileOutputStream("c:/book.dat"));
oos.writeObject(book);
oos.close();
book=null;
ObjectInputStream ois=new ObjectInputStream(
new FileInputStream("c:/book.dat"));
book=(Book)ois.readObject();
ois.close();
System.out.println("ID is:"+book.id);
System.out.println("name is:"+book.name);
System.out.println("author is:"+book.author);
System.out.println("price is:"+book.price);
}
}
API中的说明为
public interface Externalizable extends Serializable
• 其中有两个方法writeExternal()和readExternal(),因此实现该接口的类必须实现
这两个方法
• ObjectOutputStream的writeObject()方法只写入对象的标识,然后调用对象所属
类的writeExternal()
• ObjectInputStream的readObject()方法调用对象所属类的readExternal()
RandomAccessFile类
• 可跳转到文件的任意位置读/写数据
• 可在随机文件中插入数据,而不破坏该文件的其他数据
• 实现了DataInput 和 DataOutput 接口,可使用普通的读写方法
• 有个位置指示器,指向当前读写处的位置。刚打开文件时,文件指示器指向文件的开
头处。对文件指针显式操作的方法有:
▫ int skipBytes(int n):把文件指针向前移动指定的n个字节
▫ void seek(long):移动文件指针到指定的位置。
▫ long getFilePointer():得到当前的文件指针。
• 在等长记录格式文件的随机读取时有很大的优势,但仅限于操作文件,不能访问其它
IO设备,如网络、内存映像等
• **构造方法**
public RandomAccessFile(File file,String mode)
throws FileNotFoundException
public RandomAccessFile(String name, String mode)
throws FileNotFoundException
• 构造RandomAccessFile对象时,要指出操作:仅读,还是读写
new RandomAccessFile("farrago.txt", "r");
new RandomAccessFile("farrago.txt", "rw");

#创建一个雇员类,包括姓名、年龄。姓名不超过8个字符,年龄是int类型。每条记录固定为20个字节。使用RandomAccessFile向文件添加、修改、读取雇员信息
import java.io.*;
class Employee {
char name[]={'\u0000','\u0000','\u0000','\u0000',
'\u0000','\u0000','\u0000','\u0000'};
int age;
public Employee(String name,int age) throws Exception {
if(name.toCharArray ().length>8)
System.arraycopy(name.toCharArray(),0,this.name,0,8);
else
System.arraycopy(name.toCharArray(),0,this.name,
0,name.toCharArray().length);
this.age=age;
}
}
public class RandomAccessFileTester {
String Filename;
public RandomAccessFileTester (String Filename) {
this.Filename=Filename;
}
public void writeEmployee(Employee e,int n) throws Exception {
RandomAccessFile ra=new RandomAccessFile(Filename,"rw");
ra.seek(n*20);
for(int I=0;I<8;I++) ra.writeChar (e.name[I]);
ra.writeInt(e.age);
ra.close();
}
public void readEmployee(int n) throws Exception {
char buf[]=new char[8];
RandomAccessFile ra=new RandomAccessFile(Filename,"r");
ra.seek(n*20);
for(int I=0;I<8;I++) buf[I]=ra.readChar();
System.out.print("name:");
System.out.println(buf);
System.out.println("age:"+ra.readInt());
ra.close();
}
public static void main(String[] args) throws Exception {
RandomAccessFileTester t=new RandomAccessFileTester ("d:/temp/1.txt");
Employee e1=new Employee("ZhangSantt",23);
Employee e2=new Employee(“李晓珊”,33);
Employee e3=new Employee(“王华”,19);
t.writeEmployee(e1,0);
t.writeEmployee(e3,2);
System.out.println("第一个雇员信息");
t.readEmployee(0);
System.out.println("第三个雇员信息");
t.readEmployee(2);
t.writeEmployee (e2,1);
System.out.println("第二个雇员信息");
t.readEmployee (1);
}
}