Java I/O体系(三)
有关I/O的类虽然没有全部学习完,但是我们已经了解了一些比较重要的类,下面我们继续学习其他I/O类的使用:
1. 字节流和字符流的桥梁——转换流对象
当我们涉及到字符编码转换时,就会用到,转换流对象主要有主要有:
1) InputStreamReader:可以将字节输入流转换为字符输入流
2) OutputStreamWriter:可以将字节输出流转换为字符输出流
举个例子:
InputStreamReader isw=new InputStreamReader(new FileInputStream(“c\\1.txt”),”utf-8”);
OutputStreamWriter osw=new OutputStreamWriter(newFileOutputStream(“c\\1.txt”),”utf-8”);
InputStreamReader和OutputStreamWriter都可以在调用时直接指定要使用的字符编码,方便我们使用。
2. 对象流(ObjectInputStream,ObjectOutputStream)
这个流呢,可以将对内存中的对象实例数据存储到文件中或者是网络上,要使用它们来存储对象的话,要存储的对象一定要实现Serializable接口才行,因为Serializable接口中并没有任何药实现的方法,所以它只是一个标识接口,表示实现这个接口的类的对象可以被序列化。
下面就这两个对象来举一个例子吧:
package learn.io;
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;
class Book implements Serializable {
/* 序列化ID */
private static final long serialVersionUID =-3178520804420806893L;
private String bookName;// 书名
private float price;// 价格
/* 这里的transient关键字可以防止被其修饰的成员变量被序列化 */
private transient String authorName;// 作者名
/* 静态变量不存储在堆内存中,所以也不会被序列化 */
private static String paperSize = "A4";// 纸张大小
public Book(String bookName, float price, String authorName) {
this.bookName = bookName;
this.price = price;
this.authorName = authorName;
}
}
public class ObjectStreamDemo {
public static void main(String[] args) {
Book book = new Book("西游记", 58.5f, "吴承恩");
saveObj(book);
}
/*保存对象*/
public static void saveObj(Object obj) {
try {
/* 创建文件输出流 */
ObjectOutputStreamoos = new ObjectOutputStream(
new FileOutputStream("f:\\book.obj"));
/* 把book对象写入到文件中 */
oos.writeObject(obj);
oos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/*读取对象*/
public static Object readObj(String path) {
Object obj=null;
try {
/* 创建文件输出流 */
ObjectInputStreamois = new ObjectInputStream(new FileInputStream(
"f:\\book.obj"));
/* 把book对象写入到文件中 */
obj = ois.readObject();
ois.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundExceptione) {
e.printStackTrace();
}
return obj;
}
}
在上面的代码中也提到一个问题,就是ObjectOutputStream只会将堆内存中的对象数据保存起来,静态成员变量和被transient关键字修饰的成员变量是不会被保存的。
3. 随机读取文件(RandomAccessFile)
这个类比较特殊,它不是继承自前面讲过的In/OutStream或者是Reader/Writer,所以它不算是I/O体系中的子类;但是RandomAccessFile既具备读文件的功能也具备写文件的功能,并且可以获取文件指针位置,它只能操作文件。
还有一点,就是RandomAccessFile在操作文件时,要有文件访问模式,有以下几个模式:
模式名 意义
“r” 以只读方式打开文件,若在此模式下调用写文件的方法会抛出IO异常
“rw” 以读写方式打开文件,如果文件不存在的话会尝试创建该文件
“rws” 此模式相比”rw”模式,还要求对文件的内容或元数据的每个更新都同步更新到底层存储设备
“rwd” 此模式相比”rw”模式,还要求对文件的内容的每次更新都更新到底层存储设备
下面来看看例子:
package learn.io;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileDemo {
public static void main(String[] args) {
/*随机写入*/
randomWriteFile();
/*随机读取*/
//randomReadFile();
}
/*随机写入*/
public static void randomWriteFile() {
try {
/*以读写方式打开f:\random.txt文件*/
RandomAccessFileraf=new RandomAccessFile("f:\\random.txt", "rw");
/*RandomAccessFile中与其他的I/O操作类最大的区别,就是它可以调用seek方法跳过任意字节数再读写文件*/
raf.seek(1024);
raf.write("你在干嘛?10000".getBytes("gbk"));
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/*随机读取*/
public static void randomReadFile() {
try {
/*以读写方式打开f:\random.txt文件*/
RandomAccessFileraf=new RandomAccessFile("f:\\random.txt", "rw");
/*RandomAccessFile中与其他的I/O操作类最大的区别,就是它可以调用seek方法跳过任意字节数再读写文件*/
raf.seek(1024);
byte[] b=new byte[1024] ;
int len=raf.read(b);
String text=new String(b,0,len,"gbk");
System.out.println(text);
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
上面的例子提到了RadomAccessFile.seek()方法,它可以随意的跳过任意字节数(当然不能为负数)来读取文件,方便我们随机的操作文件。
下面我们对I/O的学习进行一些简单的总结:
4. I/流的操作规则
学习了前面的I/O类图我们知道,这个体系里有很多的类,在这里我画了一个操作和规则流程图:
依据以上原则,应该就可以选择好我们要使用的类是什么了。
例如:我们需要将一个文本文件里面的内容复制到另一个文本文件中,我们就可以进行如下选择和判断(橙色部分):
经过上述判断,最终得出使用的类有FileReader(读)、FilerWriter(写)。