你的位置:首页 > Java教程

[Java教程]I/O流——其他流


其他流

一、ObjectInputStream/ObjectOutputStream

① ObjectInputStream和ObjectOutputStream分别与FileInputStream和FileOutputStream一起使用时,可以对应用程序提供对对象的持久存储。我们把对象以某种特定的编码格式写入称之为“序列化”。把写入的编码格式内容还原成对象称之为“反序列化”。

② 被序列化的对象必须实现Serializable接口。

 

序列化示例:

 1 //必须实现Serializable接口 2 class Student implements Serializable{ 3   private String name; 4   private int age; 5   public Student(String name, int age) { 6     super(); 7     this.name = name; 8     this.age = age; 9   }10   public String getName() {11     return name;12   }13   public void setName(String name) {14     this.name = name;15   }16   public int getAge() {17     return age;18   }19   public void setAge(int age) {20     this.age = age;21   }22   @Override23   public String toString() {24     return "Student [name=" + name + ", age=" + age + "]";25   }26   27 }

主方法:

1     Student stu=new Student("zhangsan", 30);2     FileOutputStream fos=new FileOutputStream("d:/test.txt");3     ObjectOutputStream oos=new ObjectOutputStream(fos);4     oos.writeObject(stu);//把对象序列化到指定的文件输出流中5     oos.close();//释放资源

 

反序列化:

主方法:

1     ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/test.txt"));2     try {3       Student stu2= (Student)ois.readObject();4       System.out.println(stu2);5     } catch (ClassNotFoundException e) {6       e.printStackTrace();7     }8     ois.close();

 

反序列化成功之后,再在Student中添加一个地址属性。

private String address;

发现不能反序列化了,因为序列化的对象与反序列化的对象不一致。

解决办法:Eclipse的Student类行号左边有一个小灯,

添加一个版本号。再次进行序列化操作,新增属性后反序列化也能成功,它会自动取出序列化中不存在的属性。

 

二、InputStreamReader/OutputStreamWriter

①转换流是指将字节流与字符流之间的转换。

②转换流的出现方便了对文本的读写,她在字符流与字节流之间架起了一座桥梁,使原本毫无关联的两种流操作能进行转化,提高了程序的灵活性。

③ 节流中的数据都是字符时,转成字符流操作更高效。

④ 如果使用非默认编码保存文件或读取文件时,需要用到转换流。因为字节流的重载构造方法中有指定编码格式的参数,而FileReader与FileWriter是默认编码的文本文件。

⑤ 常见的编码表

a)     ASCII:美国标准信息交换码。用一个字节的7位可以表示。

b)     ISO8859-1:拉丁码表。欧洲码。表用一个字节的8位表示。

c)     GB2312:中国的中文编码表。

d)     GBK:中国的中文编码表升级,融合了更多的中文文字符号。

e)     Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是Unicode。

f)      UTF-8:最多用三个字节来表示一个字符。

g)     ……

 

将内容以指定编码的格式存入文件:

1     OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("d:/code.txt"), "utf-8");2     BufferedWriter bw=new BufferedWriter(osw);3     bw.write("你好");4     bw.close();

读取文件:

1     BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("d:/code.txt"), "utf-8"));2     String line=null;3     while((line=br.readLine())!=null){4       System.out.println(line);5     }6     br.close();

 

三、RandomAccessFile 随机访问文件

①支持对随机访问文件的读取和写入。

②随机访问文件的行为类似存储在文件系统中的一个大型byte数组。存在指向该隐含数组的光标或索引,称为文件指针。

③ 入操作从文件指针开始读取字节,随着对字节的读取而前移此文件指针。

④ 如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,随着对字节的写入而前移此文件指针。

⑤ 写入隐含数组末尾之后的输入操作导致该数组扩展。

⑥ 该文件指针可用通过getFilePointer方法读取,通过seek方法设置。

 

案例说明:创建一个Person类,内有name属性占15个字符,即30个字节,age属性4个字节,共34个字节。将多个Person对象放入RandomAccessFile中。然后在指定的位置上取出Person对象(名字,年龄)。

 

创建一个Person类

 1 class Person { 2   private String name; 3   private int age; 4  5   public Person() { 6  7   } 8  9   public Person(String name, int age) {10     StringBuilder builder = null;11     if (name != null) {12       builder = new StringBuilder(name);13     } else {14       builder = new StringBuilder(15);15     }16     builder.setLength(15);// 固定长度为15个字符,不满15时,'\u0000'17     this.name = builder.toString();18     this.age = age;19   }20 21   public String getName() {22     return name;23   }24 25   public void setName(String name) {26     StringBuilder builder = null;27     if (name != null) {28       builder = new StringBuilder(name);29     } else {30       builder = new StringBuilder(15);31     }32     builder.setLength(15);// 固定长度为15个字符,不满15时,'\u0000'33     this.name = builder.toString();34   }35 36   public int getAge() {37     return age;38   }39 40   public void setAge(int age) {41     this.age = age;42   }43 44   // 每个对象所占的字节数45   public static int size() {46     return 34;47   }48 49 }

读取名字时不用将全部字节都读取出来,所以应该替换空字节。方法如下,将其写在与主方法同一个类下即可

1   private static String readName(RandomAccessFile randomaccessFile)2       throws IOException {3     char[] name = new char[15];4     for (int i = 0; i < name.length; i++) {5       name[i] = randomaccessFile.readChar();6     }7     return new String(name).replace('\u0000', ' ');8   }

主方法:

 1     Person[] persons = { new Person("zhangsan", 10), 2         new Person("lisi", 24), new Person("wangwu", 36), 3         new Person("zhaoliu", 66) }; 4     RandomAccessFile randomaccessFile = new RandomAccessFile( 5         "d:/test2.txt", "rw"); 6     // 写入数据到RandomAccessFile这个对象中、 7     for (int i = 0; i < persons.length; i++) { 8       randomaccessFile.writeChars(persons[i].getName()); 9       randomaccessFile.writeInt(persons[i].getAge());10     }11     // 读取指定位置上的Person对象12     Scanner scanner = new Scanner(System.in);13     System.out.println("读取第几个Person对象数据");14     int num = scanner.nextInt();15     // 使用seek方法来操作存取位置16     randomaccessFile.seek((num - 1) * Person.size());17     Person person = new Person();18     person.setName(readName(randomaccessFile));19     person.setAge(randomaccessFile.readInt());20     System.out.println("姓名:" + person.getName());21     System.out.println("年龄:" + person.getAge());22     randomaccessFile.close();

运行后在Console窗口上输入要获取的位置即可,超出位置会抛异常。