读Java编程艺术之笔记(文件IO)(二)
在文件IO中,经常需要写出和读入整个对象。Java提供了在二进制文件中对对象的输出和输入的处理和操作。而对象序列化Serializable,是专门用来提供在二进制文件IO中,对对象的写出和读入技术。序列化的目的是为了在二进制文件执行对对象文件的IO中,保证对象写出和读入的一致性persistence。对输出对象序列化的结果是在输出文件中不仅记录有关对象类型及其状态信息,而且记录封装在对象中的数据及其类型。在读入对象的操作中,则按照对象序列化的信息,进行反序列化deserilazable处理,重新在内存中还原对象。
序列化的对象必须是实现了Serializable接口的实例。这个接口包括在java.io包中,虽然Serializable接口不提供任何方法需要完善,但提出如下规定:
序列化对象输出操作必须通过调用private void writeObject(ObjectOutputStream out)方法实现。这个方法将抛出IOException异常,代码中必须提供处理这个异常的机制;
反序列化对象输入的操作必须通过调用private void readObject(ObjectInputStream in)方法实现。这个方法将抛出IOException和ClassNotFoundException异常,代码中必须提供处理这两个异常的机制。
其中ObjectOutputStream以及ObjectInputStream为java.io包中提供的用来处理对象序列化IO的API类。
下面是一个对象序列化IO的例子
import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Scanner; public class ObjectOutput { /** * @param args * @throws IOException * @throws FileNotFoundException */ public static void main(String[] args) throws FileNotFoundException, IOException { // TODO Auto-generated method stub String fileName = "D:"+File.separator+"object.bat"; Scanner scanner = new Scanner(System.in); String choice = ""; System.out.println("read or write or quit? (r/w/q)"); while (!choice.equalsIgnoreCase("q")) { if (choice.equalsIgnoreCase("r")) { ObjectFileInput input = new ObjectFileInput(); input.createInputFile(fileName); input.showData(); input.closeInputFile(); System.out.println("read or write or quit? (r/w/q)"); } else if(choice.equalsIgnoreCase("w")) { ObjectFileOutput output = new ObjectFileOutput(); output.createOutputFile(fileName); output.createData(scanner); output.closeFileOutput(); System.out.println("read or write or quit? (r/w/q)"); } choice = scanner.next(); } } } /** * @author xhj * 实现了serializable接口的产品类,生成我们进行对象IO的对象 */ class IOProduct implements Serializable { private int ID; private String title; private double price; private int amount; public IOProduct() { // TODO Auto-generated constructor stub } public IOProduct(int ID, String title, double price, int amount) { this.ID = ID; this.title = title; this.price = price; this.amount = amount; } public int getID() { return ID; } public String getTitle() { return title; } public double getPrice() { return price; } public int getAmount() { return amount; } } /** * @author xhj * 利用获得数据实例化产品类IOProduct,并调用ObjectOut的方法序列化输出对象 */ class ObjectFileOutput { ObjectOut objectOut; Scanner scanner; public void createOutputFile(String fileName) { objectOut = new ObjectOut(fileName); } public void createData(Scanner scanner) { IOProduct product; int ID; String title; double price; int amount; String choice = "y"; while (choice.equalsIgnoreCase("y")) { try { System.out.print("Enter the IOProduct ID: "); ID = scanner.nextInt(); // This method returns the rest of the current line, excluding // any // line separator at the end. The position is set to the // beginning // of the next line. scanner.nextLine(); System.out.print("Enter the IOProduct title: "); title = scanner.nextLine(); System.out.print("Enter the IOProduct price: "); price = scanner.nextDouble(); scanner.nextLine(); System.out.print("Enter the IOProduct amount: "); amount = scanner.nextInt(); product = new IOProduct(ID, title, price, amount); objectOut.outObject(product);//将对象写入文件,即序列化 } catch (Exception e) {//如果输入有误 // TODO: handle exception scanner.nextLine();//清除输入 System.out.println("invalid input, try again!"); continue; //开始新循环 } System.out.print("Continue? (y/n): "); choice = scanner.next(); System.out.println(); } } public void closeFileOutput() { objectOut.closeFile(); } } /** * @author xhj * 利用ObjectIn的方法读入对象,转化为IOProduct对象并输出对象信息 */ class ObjectFileInput { ObjectIn objectIn; Object object; IOProduct product; public void createInputFile(String fileName) throws FileNotFoundException, IOException { objectIn = new ObjectIn(fileName); } public void showData() { while (objectIn.hasMore()) { object = objectIn.inObject();//读取对象,即反序列化操作 if (object instanceof IOProduct) { product = (IOProduct)object; System.out.println("Product ID: "+product.getID()); System.out.println("Product title: "+product.getTitle()); System.out.println("Product price: "+product.getPrice()); System.out.println("Product amount: "+product.getAmount()); } else { break; } } } public void closeInputFile() throws IOException { objectIn.closeFile(); } } /** * @author xhj * 定义了序列化输出的操作,利用ObjectOutputStream的writeObject和close方法 */ class ObjectOut { ObjectOutputStream out; ObjectOut(String name) { try { out = new ObjectOutputStream(new FileOutputStream(name)); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void outObject(Object obj) { try { //用ObjectOutputStream的writeObject方法写出 out.writeObject(obj); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void closeFile() { try { out.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * @author xhj * 定义了序列化输入的操作,利用ObjectInputStream的readObject和close方法 */ class ObjectIn { ObjectInputStream in; boolean status = true; public ObjectIn(String name) throws FileNotFoundException, IOException { in = new ObjectInputStream(new FileInputStream(name)); } public Object inObject() { Object obj = new Object(); try { //用ObjectInputStream的readObject方法读入 obj = in.readObject(); } catch (EOFException e) { // TODO: handle exception System.out.println("End of the file."); status = false; return null; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return obj; } public boolean hasMore() { return status; } public void closeFile() throws IOException { in.close(); } }
运行过程:
read or write or quit? (r/w/q) w Enter the IOProduct ID: 1 Enter the IOProduct title: first Enter the IOProduct price: 99.99 Enter the IOProduct amount: 7 Continue? (y/n): y Enter the IOProduct ID: 2 Enter the IOProduct title: second Enter the IOProduct price: 88.88 Enter the IOProduct amount: 9 Continue? (y/n): n read or write or quit? (r/w/q) r Product ID: 1 Product title: first Product price: 99.99 Product amount: 7 Product ID: 2 Product title: second Product price: 88.88 Product amount: 9 End of the file. read or write or quit? (r/w/q) q
上例中也小小体现了一下异常处理和异常传播。
Java提供专门用来处理随机文件IO的API类,与其他编程语言,例如C/C++相比,简化了随机文件IO的编程。Java的随机文件IO技术可用来对文本文件以及二进制文件的处理和操作。
RandomAccessFile支持如下访问模式:r——只读;rw——读写;rws——协调式读写,在多线程/多用户文件读写中,只有一个线程/用户可以访问文件,执行更新文件数据以及更新关于文件本身信息metadata的操作;rwd——协调式读写,与rws类似,当每次访问不涉及对文件本身信息metadata的更新操作。(文件本身信息包括:文件长度信息、更新日期以及其他文件信息)
关于RandomAccessFile的几个函数:
long java.io.RandomAccessFile.getFilePointer() throws IOException Returns the current offset in this file.
void java.io.RandomAccessFile.seek(long pos) throws IOException Sets the file-pointer offset, measured from the beginning of this file, at which the next read or write occurs.
int java.io.RandomAccessFile.skipBytes(int n) throws IOException Attempts to skip over n
bytes of input discarding the skipped bytes.
关于RandomAccessFile的实例,没有写了,可以参考http://jacobcookie.iteye.com/blog/1737818