编码方式
- FileReader可以读取IDE默认编码格式(UTF-8,三个字节一个中文字)的文件
- FileReader读取系统默认编码(中文GBK,两个字节一个中文)会产生乱码
可以用OutputStreamWriter和InputStreamReader实现编码格式的转换,OutputStreamWriter对象构造方法如下,charsetName用于指定解码方式:
OutputStreamWriter osw = new OutputStreamReader(FileOutputStream osw, String charsetName)
OutputStreamWriter:
public class Demo01InputStreamWriter {
public static void main(String[] args) throws IOException {
String fileName = "D:\\work_space\\Project01\\src\\File16IO\\example_GBK.txt";
method1(fileName);
/*
java.io.OutputStreamWriter extends Writer
构造方法:
OutputStreamWriter(OutputStream out, String charsetName)创建指定字符集的OutputStreamWriter,默认使用UTF-8
*/
method2();
method3();
return;
}
private static void method3() throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\work_space\\Project01\\src\\File16IO\\example_GBK.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
osw.write("GBK编码");
osw.flush();
osw.close();
}
private static void method2() throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\work_space\\Project01\\src\\File16IO\\example_GBK.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");
osw.write("UTF编码字段");
osw.flush();
osw.close();
return;
}
/*
FileReader可以读取IDE默认编码格式(UTF-8,三个字节一个中文)的文件
FileReader读取系统默认编码(中文GBK,两个字节一个中文)会产生乱码
*/
private static void method1(String fileName) throws IOException {
FileReader fr = new FileReader(fileName);
int len = 0;
while((len = fr.read()) != -1){
System.out.print((char)len); //GBK������ı�
}
fr.close();
}
}
InputStreamReader可以指定读入的解码方式:
public class Demo02InputStreamReader {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("D:\\work_space\\Project01\\src\\File16IO\\example_GBK.txt");
InputStreamReader isr = new InputStreamReader(fis, "GBK");
int len = 0;
while((len = isr.read()) != -1){
System.out.print((char)len);
}
isr.close();
}
}
编码方式的转换
利用InputStreamReader和OutputStreamWriter可以实现文本编码格式的转换:
public class FormatTransfer {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("D:\\work_swpace\\Project01\\src\\File16IO\\example_GBK.txt");
InputStreamReader isr = new InputStreamReader(fis, "GBK");
FileOutputStream fos = new FileOutputStream("D:\\work_space\\Project01\\src\\File16IO\\example_utf-8.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");
osw.write("转换为utf-8编码:\r\n");
int len = 0;
while((len = isr.read()) != -1){
osw.write(len);
}
osw.close();
isr.close();
}
}
对象的序列化和反序列化
java.io.ObjectOutputStream extends OutputStream把对象以流的方式写入到文件中保存
特有的成员方法:
**void writeObject(Object obj)**将指定的对象写入OutputStream中
序列化和反序列化的时候,会抛出NotSerializableException,类通过实现java.io.serializable接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
serializable接口也叫标记型接口,实现serializable接口就会给类添加标记,当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记。
注意:
当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象后发生了修改,那么反序列化操作也会失败,并抛出InvalidClassException,原因如下:
- 该类的序列版本号与从流中读取的类描述符的版本号不匹配
- 该类包含未知数据类型
- 该类没有可访问的无参数构造方法
serializable接口给需要序列化的类提供一个序列版本号serialVersionUID,该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
假设有一个Person类:
public class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString(){
return this.name + this.age;
}
}
ObjectOutputStream的使用如下:
public class Demo03ObjectOutputStream {
public static void main(String[] args) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\work_space\\Project01\\src\\File16IO\\person.txt"));
oos.writeObject(new Person("Bob", 20));
}
}
ObjectInputStream的使用如下:
public class Demo04ObjectInputStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("D:\\work_space\\Project01\\src\\File16IO\\person.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Object obj = ois.readObject();
System.out.println(obj);
ois.close();
}
}
每次修改类的定义,类重新编译生成一个新的序列号,这时对文件反序列化会抛出InvalidClassException。
解决方法:给类手动添加一个序列号,只要加入一个成员变量serialVersionUID即可:
private static final long serialVersionUID = 1L;
transient关键字
static:
静态优先于非静态加载到内存中
被static修饰的成员变量不能被序列化
transient:
被transient修饰的成员变量,不能被序列化
将某一成员变量声明为transient,对象的值将不被序列化:
private transient int age;
测试代码如下:
public class Demo01Transient {
public static void main(String[] args) throws Exception {
serialize();
deserialize(); //对象的成员变量age值变为0
}
private static void serialize() throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\work_space\\Project01\\src\\Demo17IO\\person"));
oos.writeObject(new Person("Bob", 20));
}
private static void deserialize() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\work_space\\Project01\\src\\Demo17IO\\person"));
Object obj = ois.readObject();
System.out.println(obj);
}
}
将成批对象序列化
将一系列对象放在容器中,然后用ObjectOutputStream对容器进行序列化。
public class Demo02SerializeSet {
public static void main(String[] args) throws IOException, ClassNotFoundException {
serialize();
deserialize();
}
private static void deserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\work_space\\Project01\\src\\Demo17IO\\objList"));
Object obj = ois.readObject();
ArrayList<Person> list = (ArrayList<Person>) obj;
for (Person p : list) {
System.out.println(p);
}
}
private static void serialize() throws IOException {
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("Bob", 18));
list.add(new Person("小明", 19));
list.add(new Person("John", 20));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\work_space\\Project01\\src\\Demo17IO\\objList"));
oos.writeObject(list);
}
}
PrintStream类
java.io.PrintStream:打印流
PrintStream为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式
PrintStream特点:
- 只负责数据的输出,不负责数据的读取
- 与其他输出流不同,printStream永远不会抛出IOException
- 有特有方法print(), println()
构造方法:
- PrintStream(File file):输出的目的地是一个文件
- PrintStream(OutputStream out):输出的目的地是一个字节输出流
- PrintStream(String fileName):输出的目的地是一个文件路径
PrintStream extends OutputStream
注意:
- 如果使用继承自父类的write()方法写数据,那么查看数据的时候会查询编码表
- 如果使用自己特有的方法print()写的数据原样输出 可以改变输出语句的目的地
- 使用System.setOut方法将输出语句的目的地改为参数中传递的打印流的目的地
- static void setOut(PrintStream out) 重新分配“标准”输出流:控制台输出/打印流文件输出
public class Demo03PrintStream {
public static void main(String[] args) throws FileNotFoundException {
PrintStream ps = new PrintStream("D:\\work_space\\Project01\\src\\Demo17IO\\printStream.txt");
ps.write(97);
ps.println();
ps.println(97);
System.out.println("控制台输出");
System.setOut(ps); //将打印流输出转到PrintStream对象的路径
System.out.println("打印流文件输出");
ps.close();
}
}