java文件输入输出和序列化


事先说明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关键字放在变量名前表示此变量在序列化时跳过,在反序列化时赋予此变量默认值或者调用空参数构造函数。

  • 序列化

    1. 利用文件名构建一个FileOutputStream类对象
    2. 利用刚才构建的FileOutputStream对象构建一个ObjectOutputStream类对象
    FileOutputStream filerStream = new FileOutputStream("MyGame.ser");
    ObjectOutputStream os = new ObjectOutputStream(fileStream);
    
    1. 调用writeObject方法输出数据到文件
    2. 关闭流
     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关键字修饰的变量在序列化时会被跳过。

  • 反序列化

    1. 使用文件名构建一个FileInputStream对象
    2. 使用第一步构建的对象构建ObjectInputStream对象
    FileInputStream fileStream = new FileInputStream("MyGame.ser");
    ObjectInputStream os = new ObjectInputStream(fileStream);
    
    1. 调用readObject函数从文件中读取实现序列化好的对象

    2. 将对象转换成正确的类型

    3. 关闭流

    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

需要注意的地方

  1. 文本形式I/O感兴趣可以了解FileOutputStream和FileInputStream类,较以上的方法更加底层,操作的是字节,更扩展可以去查询java.io包内的类关系图。
  2. 每次用完流后一定一定要记得关闭流。

原创文章,作者:,如若转载,请注明出处:https://blog.ytso.com/272041.html

(0)
上一篇 2022年7月8日
下一篇 2022年7月8日

相关推荐

发表回复

登录后才能评论