java常用类解析六:IO系统文件读写工具类
几个文件读写的工具类:文本文件读写、二进制文件读写、对象读写。其中对象读写工具类有错误,在试图进行多个对象读取时,读第二个对象就抛出异常,这是为什么?此外怎样把一个存放对象的文件中所有的对象读出来?
这个问题已经解决,非常感谢Aguo的文章:自定义ObjectOutputStream,解决追加写入后,读取错误的问题 。在这篇文章中我找到了答案,同时对作者的源代码添加了一些注解。解决方案请看文章最后。
1、文本文件读写工具类
package mine.util;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;/** * 此工具类用于文本文件的读写 * * @author Touch */public class TextFile {// 读取指定路径文本文件public static String read(String filePath) {StringBuilder str = new StringBuilder();BufferedReader in = null;try {in = new BufferedReader(new FileReader(filePath));String s;try {while ((s = in.readLine()) != null)str.append(s + '\n');} finally {in.close();}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return str.toString();}// 写入指定的文本文件,append为true表示追加,false表示重头开始写,//text是要写入的文本字符串,text为null时直接返回public static void write(String filePath, boolean append, String text) {if (text == null)return;try {BufferedWriter out = new BufferedWriter(new FileWriter(filePath,append));try {out.write(text);} finally {out.close();}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}?package mine.util;public class TestTextFile {public static void main(String[] args) {TextFile.write("file/textfile.txt", false,"这是一个文本文件的读写测试\nTouch\n刘海房\n男\n");TextFile.write("file/textfile.txt", true, "武汉工业学院\n软件工程");System.out.println(TextFile.read("file/textfile.txt"));}}?2、二进制文件读写工具类
package mine.util;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;/** * 此工具类用于二进制文件的读写 * * @author Touch */public class BinaryFile {// 把二进制文件读入字节数组,如果没有内容,字节数组为nullpublic static byte[] read(String filePath) {byte[] data = null;try {BufferedInputStream in = new BufferedInputStream(new FileInputStream(filePath));try {data = new byte[in.available()];in.read(data);} finally {in.close();}} catch (IOException e) {e.printStackTrace();}return data;}// 把字节数组为写入二进制文件,数组为null时直接返回public static void write(String filePath, byte[] data) {if (data == null)return;try {BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(filePath));try {out.write(data);} finally {out.close();}} catch (IOException e) {e.printStackTrace();}}}?package mine.util;import java.util.Arrays;public class TestBinaryFile {public static void main(String[] args) {BinaryFile.write("file/binaryfile.dat", new byte[] { 1, 2, 3, 4, 5, 6,7, 8, 9, 10, 'a', 'b', 'c' });byte[] data = BinaryFile.read("file/binaryfile.dat");System.out.println(Arrays.toString(data));}}?3、对象读写工具类(有问题,在读取多个对象时有问题,怎样把一个对象文件中的所有对象读出来?)
package mine.util;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;/** * 此类用于对象的读写 * * @author Touch */public class ObjectFile {// 把一个对象写入文件,isAppend为true表示追加方式写,false表示重新写public static void write(String filePath, Object o, boolean isAppend) {if (o == null)return;try {ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filePath, isAppend));try {out.writeObject(o);} finally {out.close();}} catch (IOException e) {e.printStackTrace();}}// 把一个对象数组写入文件,isAppend为true表示追加方式写,false表示重新写public static void write(String filePath, Object[] objects, boolean isAppend) {if (objects == null)return;try {ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filePath, isAppend));try {for (Object o : objects)out.writeObject(o);} finally {out.close();}} catch (IOException e) {e.printStackTrace();}}// 读取对象,返回一个对象public static Object read(String filePath) {Object o = null;try {ObjectInputStream in = new ObjectInputStream(new FileInputStream(filePath));try {o = in.readObject();} finally {in.close();}} catch (Exception e) {e.printStackTrace();}return o;}// 读取对象,返回一个对象数组,count表示要读的对象的个数public static Object[] read(String filePath, int count) {Object[] objects = new Object[count];try {ObjectInputStream in = new ObjectInputStream(new FileInputStream(filePath));try {for (int i = 0; i < count; i++) {//第二次调用in.readObject()就抛出异常,这是为什么?objects[i] = in.readObject();}} finally {in.close();}} catch (Exception e) {e.printStackTrace();}return objects;}}?4、对象读写工具类(解决了3中的问题,能够写入及读取多个对象)
3中到底问题出在哪呢?先来看一段ObjectOutputStream构造方法的源代码,此源代码来自jdk1.6版。
public ObjectOutputStream(OutputStream out) throws IOException {verifySubclass();bout = new BlockDataOutputStream(out);handles = new HandleTable(10, (float) 3.00);subs = new ReplaceTable(10, (float) 3.00);enableOverride = false;</span><span style="font-size:16px;"><span style="color:#ff0000;">writeStreamHeader();</span>bout.setBlockDataMode(true); if (extendedDebugInfo) { debugInfoStack = new DebugTraceInfoStack();} else { debugInfoStack = null; } }?这段代码中我们只需要关注writeStreamHeader();方法,这个方法在源代码中的解释是
/** * The writeStreamHeader method is provided so subclasses can append or * prepend their own header to the stream. It writes the magic number and * version to the stream. * * @throwsIOException if I/O errors occur while writing to the underlying * stream */?
也就是说我们打开(new)一个ObjectOutputStream的时候,这个ObjectOutputStream流中就已经被写入了一些信息,这些信息会写入到我们的文件中。在第一次写入文件时,这些头部信息时需要的,因为ObjectInputStream读的时候会帮我们过滤掉。但是当我们追加写入一个文件时,这些头部信息也会写入文件中,读取的时候只会把文件第一次出现的头部信息过滤掉,并不会把文件中间的头部信息也过滤掉,这就是问题的根源所在。
怎么解决呢?正如package mine.util.io;import java.io.File;import java.io.IOException;import java.io.ObjectOutputStream;import java.io.OutputStream;/** * 此类继承ObjectOutputStream,重写writeStreamHeader()方法,以实现追加写入时去掉头部信息 */public class MyObjectOutputStream extends ObjectOutputStream {private static File f;// writeStreamHeader()方法是在ObjectOutputStream的构造方法里调用的// 由于覆盖后的writeStreamHeader()方法用到了f。如果直接用此构造方法创建// 一个MyObjectOutputStream对象,那么writeStreamHeader()中的f是空指针// 因为f还没有初始化。所以这里采用单态模式private MyObjectOutputStream(OutputStream out, File f) throws IOException,SecurityException {super(out);}// 返回一个MyObjectOutputStream对象,这里保证了new MyObjectOutputStream(out, f)// 之前f已经指向一个File对象public static MyObjectOutputStream newInstance(File file, OutputStream out)throws IOException {f = file;// 本方法最重要的地方:构建文件对象,两个引用指向同一个文件对象return new MyObjectOutputStream(out, f);}@Overrideprotected void writeStreamHeader() throws IOException {// 文件不存在或文件为空,此时是第一次写入文件,所以要把头部信息写入。if (!f.exists() || (f.exists() && f.length() == 0)) {super.writeStreamHeader();} else {// 不需要做任何事情}}}?
package mine.util.io;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;/** * 此类用于对象的读写 * * @author Touch */public class ObjectFile {// 把一个对象写入文件,isAppend为true表示追加方式写,false表示重新写public static void write(String filePath, Object o, boolean isAppend) {if (o == null)return;try {File f = new File(filePath);MyObjectOutputStream out = MyObjectOutputStream.newInstance(f,new FileOutputStream(f, isAppend));try {out.writeObject(o);} finally {out.close();}} catch (IOException e) {e.printStackTrace();}}// 把一个对象数组写入文件,isAppend为true表示追加方式写,false表示重新写public static void write(String filePath, Object[] objects, boolean isAppend) {if (objects == null)return;try {File f = new File(filePath);MyObjectOutputStream out = MyObjectOutputStream.newInstance(f,new FileOutputStream(f, isAppend));try {for (Object o : objects)out.writeObject(o);} finally {out.close();}} catch (IOException e) {e.printStackTrace();}}// 读取对象,返回一个对象public static Object read(String filePath) {Object o = null;try {ObjectInputStream in = new ObjectInputStream(new FileInputStream(filePath));try {o = in.readObject();} finally {in.close();}} catch (Exception e) {e.printStackTrace();}return o;}// 读取对象,返回一个对象数组,count表示要读的对象的个数public static Object[] read(String filePath, int count) {Object[] objects = new Object[count];try {ObjectInputStream in = new ObjectInputStream(new FileInputStream(filePath));try {for (int i = 0; i < count; i++) {objects[i] = in.readObject();}} finally {in.close();}} catch (Exception e) {e.printStackTrace();}return objects;}}?package mine.util.io;import java.io.Serializable;public class TestObjectFile {public static void main(String[] args) {ObjectFile.write("file/t.dat", new Person(), false);ObjectFile.write("file/t.dat", new Person(), true);ObjectFile.write("file/t.dat", new Person[] { new Person("Touch", 1),new Person("Rainbow", 0), new Person() }, true);for (Object o : ObjectFile.read("file/t.dat", 5))((Person) o).display();}}class Person implements Serializable {private static final long serialVersionUID = 1L;private String name = "刘海房";private int sex = 0;Person(String name, int sex) {this.name = name;this.sex = sex;}Person() {}void display() {System.out.println("my name is :" + name);String s = (sex == 0) ? "男" : "女";System.out.println("性别:" + s);}}?运行结果:
my name is :刘海房
性别:男
my name is :刘海房
性别:男
my name is :Touch
性别:女
my name is :Rainbow
性别:男
my name is :刘海房
性别:男
?
