编码方式

  • 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();
    }
}