事先说明I/O操作远不止这么几种,发挥想象力和去动手吧,这篇帖子记录仅仅是基础的I/O使用。
字符文本形式IO
-
读取
FileReader类,字符流,用来读取文本文件中的内容,常用BufferedReader辅助读取。可以通过文件名或者File类构造对象。
FileReader reader = new FileWriter("MyTest.txt"); File file = new File("MyTest.txt"); FileReader reader = new FileReader(file);
要抓IOException异常,编译器会检查,不抓不让过,但是如果是文件不存在(FileNotFoundException)的这类运行时异常(Runtime)不用抓。为什么要用BufferedReader类辅助读取呢?因为BufferedReader提供的API更丰富,是不是很神奇,功能从底层基本功能慢慢扩展到容易理解使用的地步。
import java.io.*; class ReadAFile{ public static void main(String[] args){ try{ FileReader fileReader = new FileReader("MyTest.txt"); BufferedReader reader = new BufferedReader(fileReader); String line = null; while((line = reader.readLine()) != null){ System.out.println(line); } reader.close(); }catch(IOException ex){ ex.printStackTrace(); } } }
-
写入
FileWriter类,字符流,常用String字符串类和File类创建输出流。第二个参数可选代表是否附加数据,否则覆盖文件原来数据,如果文件不存在自动创建。
FileWriter writer = new FileWriter("Foo.txt"); File file = new File("Foo.txt"); FileWriter writer = new FileWriter(file);
向文件中写入数据调用方法write,对流操作完成后关闭流。
import java.io.*; class WriteAFile{ public static void main(String[] args){ try{ FileWriter writer = new FileWriter("Foo.txt"); writer.write("hello."); writer.close(); } catch(IOException ex){ ex.printStackTrace(); } } }
序列化形式保存
-
前置知识
-
Serializable接口
与序列化至关重要的一点就是所有支持序列化的类必须要实现Serializable接口,没有声明实现Serializable接口的类一律不能被序列化。Serializable接口本身不具备任何需要实现的方法,此接口只被约定用来把实现了Serializable的类视作可序列化。
-
transient关键字
transient关键字放在变量名前表示此变量在序列化时跳过,在反序列化时赋予此变量默认值或者调用空参数构造函数。
-
-
序列化
- 利用文件名构建一个FileOutputStream类对象
- 利用刚才构建的FileOutputStream对象构建一个ObjectOutputStream类对象
FileOutputStream filerStream = new FileOutputStream("MyGame.ser"); ObjectOutputStream os = new ObjectOutputStream(fileStream);
- 调用writeObject方法输出数据到文件
- 关闭流
os.writeObject(object1); os.writeObject(object2); os.writeObject(object3); os.close();
序列化一个类的对象时,会自动把此类包含的所有变量进行序列化。举个例子,如果一个类中存在一种未实现Serializable的类的变量,即此变量不支持序列化。
import java.io.*; class A{} class B implements java.io.Serializable{ A a = new A(); } class C{ public static void main(String[] args){ try{ FileOutputStream fileStream = new FileOutputStream("MyGame.ser"); ObjectOutputStream os = new ObjectOutputStream(fileStream); B b = new B(); os.writeObject(b); } catch(IOException ex){ ex.printStackTrace(); } } }
以上代码虽然可以编译通过,但是运行时会出现java.io.NotSerializableException异常,原因在于B类中有A类的变量,而A是不支持序列化的。任何不支持序列化的类作为writeObject的参数都会报同样的异常。解决办法也很简单。将
A a = new A()
改成transient A a = new A()
。还记得吗,使用了transient关键字修饰的变量在序列化时会被跳过。 -
反序列化
- 使用文件名构建一个FileInputStream对象
- 使用第一步构建的对象构建ObjectInputStream对象
FileInputStream fileStream = new FileInputStream("MyGame.ser"); ObjectInputStream os = new ObjectInputStream(fileStream);
-
调用readObject函数从文件中读取实现序列化好的对象
-
将对象转换成正确的类型
-
关闭流
Object one = os.readObject(); //假设one的类型是A A a = (A)one; os.close();
反序列化时,需要让jvm知道正在反序列化的对象的类,如果找不到将会抛出异常。如果文件中的内容全部读取完还继续读取时,会抛出EOFExpecton异常,即遇到了文件尾。反序列化遇到被transient修饰的变量会赋予其默认值或调用无参构造函数。
-
serialVersionUID
什么是serialVersionUID?serialVersionUID用来标识类版本。用来干什么呢?假设有几个类A的对象已经序列化保存到了文件中去,之后出于一些原因修改了类A的内容,这时旧的A和新的A的serialVersionUID就不同了,serivalVersionUID不同就不能将旧A已经序列化的对象反序列化成新A。
serialVersionUID可以人工指定。出于对扩展性的考虑,你想以后新的类可以由旧的类反序列化而来,那么在类中定义
static final long serialVersionUID
。
需要注意的地方
- 文本形式I/O感兴趣可以了解FileOutputStream和FileInputStream类,较以上的方法更加底层,操作的是字节,更扩展可以去查询java.io包内的类关系图。
- 每次用完流后一定一定要记得关闭流。
原创文章,作者:,如若转载,请注明出处:https://blog.ytso.com/272041.html