Java从入门到精通十一(javaIO流)【下】

简介: 字符流说明字符输入流读数据的方法Reader是一个抽象类public abstract class Readerextends Objectimplements Readable, Closeable用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

字符流说明


字符输入流读数据的方法

Reader是一个抽象类


public abstract class Readerextends Objectimplements Readable, Closeable


用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。


我们主要演示的实现类就是InputStreamReader


public class InputStreamReaderextends Reader


InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。


每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。


为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:

BufferedReader in

= new BufferedReader(new InputStreamReader(System.in));


当然这个缓冲流我们在后面举例


InputStreamReader的主要构造方法有这么几个


构造方法摘要

1:InputStreamReader(InputStream in)

创建一个使用默认字符集的 InputStreamReader。

2:InputStreamReader(InputStream in, Charset cs)

创建使用给定字符集的 InputStreamReader。

3:InputStreamReader(InputStream in, CharsetDecoder dec)

4: 创建使用给定字符集解码器的 InputStreamReader。

InputStreamReader(InputStream in, String charsetName)

创建使用指定字符集的 InputStreamReader。


主要的方法


void close()

关闭该流并释放与之关联的所有资源。

String getEncoding()

返回此流使用的字符编码的名称。

int read()

读取单个字符。

int read(char[] cbuf, int offset, int length)

将字符读入数组中的某一部分。

boolean ready()

判断此流是否已经准备好用于读取。


从提供的构造方法的·参数我们可以了解到这个实现类基本的底层还是有字节流的实现。或者可以跟进源码。



我们可以认为这个字符输入流实现类基本实现的就是字节流加上编码的效果。


现在我们可以去看看它的读取数据的方法。(到源码中)




我们可以这样用字符流进行简单读取


package io_demo;
import java.io.*;
public class IODemo_10 {
    public static void main(String args[]) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
        int ch ;
        while((ch=isr.read())!=-1)
        {
            System.out.println((char)ch);
        }
        isr.close();
    }
}


当然还是可以进行一次读取一个字符数组的方法


package io_demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
public class IoDemo_12 {
    public static void main(String args[]) throws IOException {
        //字符流读数据
        InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
//        int ch;
//        while((ch=isr.read())!=-1)
//        {
//            System.out.println((char)ch);
        char[] chs = new char[1024];
        int len;
        while ((len = isr.read(chs)) != -1) {
            System.out.println(new String(chs,0,len));
        }
        isr.close();
    }
}


字符缓冲输入流


我们照样可以使用缓冲流


这个缓冲流也继承了Reader类,基本上也提供了父类有的read()等方法,另外还有readline()方法。我们在后面的例子中使用。



package io_demo;
import java.io.*;
public class DemoPratice01 {
    public static void main(String args[]) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_01"));
        BufferedReader br= new BufferedReader(isr);
        int ch;
        while((ch=br.read())!=-1)
        {
            System.out.print((char)ch);
        }
        char[] chars = new char[1024];
        int len;
        while ((len = br.read(chars))!=-1)
        {
            System.out.println(new String(chars,0,len));
        }
    }
}


使用字符缓输入流可以实现一行一行读取的操作。


BufferedReader br = new BufferedReader(new FileReader("bw.newLine();"));
         String line;
         while((line = br.readLine())!=null)
         {
             System.out.println(line);
         }


字符输出流写数据的方法


public abstract class Writerextends Objectimplements Appendable, Closeable, Flushable


写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。


还是首先选一个实现类开始一些说明


public class OutputStreamWriterextends Writer


OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。


每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。

为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。例如:

Writer out

= new BufferedWriter(new OutputStreamWriter(System.out));


主要的构造方法


1:OutputStreamWriter(OutputStream out)

创建使用默认字符编码的 OutputStreamWriter。

2:OutputStreamWriter(OutputStream out, Charset cs)

创建使用给定字符集的 OutputStreamWriter。

3:OutputStreamWriter(OutputStream out, CharsetEncoder enc)

创建使用给定字符集编码器的 OutputStreamWriter。

OutputStreamWriter(OutputStream out, String charsetName)

创建使用指定字符集的 OutputStreamWriter。


常用的方法


void close()

关闭此流,但要先刷新它。

void flush()

刷新该流的缓冲。

String getEncoding()

返回此流使用的字符编码的名称。

void write(char[] cbuf, int off, int len)

写入字符数组的某一部分。

void write(int c)

写入单个字符。

void write(String str, int off, int len)

写入字符串的某一部分。


很明显这样的设计和输入流还是十分对称的。

下面还是简单的去使用它。


package io_demo;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class IoDemo__ {
    public static void main(String args[]) throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice1"));
        osw.write("hello");
        char[] chars = new char[]{'a','b','c'};
        osw.write(chars,0,chars.length);
    }
}


字符缓冲输出流


同样可以使用缓冲流


public class BufferedWriter extends Writer {
    private Writer out;
    private char cb[];
    private int nChars, nextChar;
    private static int defaultCharBufferSize = 8192;


缓冲流使用到的一些方法


主要的构造方法




一些普通方法


BufferedWriter bw = new BufferedWriter(osw);





同样还有进行读入行的方法



使用的时候这样将字符输出流对象进行传入就可以


BufferedWriter bw = new BufferedWriter(osw);


用输入输出实现数据的复制

package io_demo;
import java.io.*;
public class IoDemo13 {
    //字符流进行复制文件
    public static void main(String args[]) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\IoDemo_12.java"));
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\java_doc\\src\\io_demo\\Demo_Copy.java"));
        int ch;
//        while((ch = isr.read())!=-1)
//        {
//            osw.write(ch);
//        }
        //一次读取一个字符数组
//        char[] chars = new char[1024];
//        int len;
//        while((len= isr.read(chars))!=-1)
//        {
//            osw.write(chars,0,len);
//        }
        osw.close();
        isr.close();
    }
}


但是其实我们还可以使用这个两个类下的子类,更方便的进行操作。


package io_demo;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class IoDemo_14 {
    public static void main(String args[]) throws IOException {
        //使用下面的子类(可以简化书写长度)
        FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\demo_pratice");
        FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\demo_01");
        int ch;
        while((ch = fr.read())!=-1)
        {
            fw.write(ch);
        }
        char[] chars = new char[1024];
        int len;
        while((len= fr.read(chars))!=-1)
        {
            fw.write(chars,0,len);
        }
        fw.close();
        fr.close();
    }
}


同样该子类也可以结合字符缓冲流


package io_demo;
import java.io.*;
public class IoDemo15 {
    public static void main(String args[]) throws IOException {
        //字符缓冲流
        FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\demo_pratice");
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write("Hello");
        bw.close();
        BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\demo_01"));
        // 一次读一个字符数据
        int ch;
        while((ch=br.read())!=-1)
        {
            System.out.println((char)ch);
        }
        //一次读取一个字符数组
        char[] chars = new char[1024];
        int len;
        while((len=br.read(chars))!=-1)
        {
          System.out.println(new String(chars,0,len));
        }
    }
}


做一个随机点名器



假设文件中有这些名字,然后我们从中读取信息,并随机输出。这样就时一个随机点名器了。


package io_demo;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
//从文件中读取数据,做一个随机点名器
public class IoDemo18 {
    public static void main(String args[]) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\demo_pratice"));
        //创建ArrayList对象
        ArrayList<String> array = new ArrayList<String>();
        String line;
        while((line= br.readLine())!=null)
        {
            array.add(line);
        }
        br.close();
        Random r = new Random();
        int i = r.nextInt(array.size());
        String name = array.get(i);
        System.out.println("幸运者是"+name);
    }
}



集合到文件(将对象信息拼接输入到文件)

首先我们创建一个学术类


package io_demo;
public class Student {
    private String sid;
    private String name;
    private int age;
    private String address;
    public Student()
    {
    }
    public Student(String sid, String name, int age, String address) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public String getSid() {
        return sid;
    }
    public void setSid(String sid) {
        this.sid = sid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}


测试代码


package io_demo;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
public class IoDemo19 {
    //集合到文件,改进版
    public static void main(String args[]) throws IOException {
        //创建ArrayList集合
        ArrayList<Student> array = new ArrayList<>();
        Student s1 = new Student("001", "jgdabc", 19, "山西");
        Student s2 = new Student("002", "兰舟千帆", 20, "山西");
        Student s3 = new Student("003", "潇潇", 18, "湖北");
        Student s4 = new Student("004", "小雨", 21, "河北");
        Student s5 = new Student("005", "叶灵芸", 22, "浙江");
        array.add(s1);
        array.add(s2);
        array.add(s3);
        array.add(s4);
        array.add(s5);
        //创建字符流输出对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\\\java_doc\\\\src\\\\io_demo\\\\Student.txt"));
        for (Student s : array) {
            StringBuilder sb = new StringBuilder();
            sb.append(s.getSid()).append(",").append(s.getName())
                    .append(",").append(s.getAge()).append(",").append(s.getAddress());
            bw.write(sb.toString());
            bw.newLine();
            bw.flush();
        }
        //释放资源
        bw.close();
    }
}



文件到集合(将文件中的人物属性通过对象存放于集合中,然后进行遍历)

还是基本的学术类


package io_demo;
public class Student {
    private String sid;
    private String name;
    private int age;
    private String address;
    public Student()
    {
    }
    public Student(String sid, String name, int age, String address) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public String getSid() {
        return sid;
    }
    public void setSid(String sid) {
        this.sid = sid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}


测试类


package io_demo;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
//将文本文件当中的数据督导集合当中
public class IoDemo_20 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\Student.txt"));
        ArrayList<Student> array = new ArrayList<>();
        //调用字符缓冲输入流对象的方法读取数据
        String line;
        while ((line = br.readLine()) != null) {
            String[] strArray = line.split(",");
            Student s = new Student();
            //将字符串数组的每一个元素取出来对应的复制给学生对象的成员变量值
            s.setSid(strArray[0]);
            s.setName(strArray[1]);
            s.setAge(Integer.parseInt(strArray[2]));
            s.setAddress(strArray[3]);
            array.add(s);
        }
        br.close();
        for (Student s : array) {
            System.out.println(s.getSid() + "," + s.getName() + "," + s.getAge() + "," + s.getAddress());
        }
    }
}



将学生成绩排序,并将数据写入文件中(排序采用多级条件)

我们可以先创建一个学生类


package io_demo;
public class Student01 {
    private String name;
    private int chinese;
    private int math;
    private int english;
    public Student01() {
        super();
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getChinese() {
        return chinese;
    }
    public void setChinese(int chinese) {
        this.chinese = chinese;
    }
    public int getMath() {
        return math;
    }
    public void setMath(int math) {
        this.math = math;
    }
    public int getEnglish() {
        return english;
    }
    public void setEnglish(int english) {
        this.english = english;
    }
    public Student01(String name, int chinese, int math, int english) {
        this.name = name;
        this.chinese = chinese;
        this.math = math;
        this.english = english;
    }
    public int getSum() {
        return this.chinese + this.english + this.math;
    }
}


创建学生类的唯一作用就是来获取对象,存放相关的属性。


然后我们再创建一个测主类


package io_demo;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
public class Student01_Demo {
    public static void main(String[] args) throws IOException {
        TreeSet<Student01> ts = new TreeSet<>(new Comparator<Student01>() {
            @Override
            public int compare(Student01 s1, Student01 s2) {
                int num = s2.getSum() - s1.getSum();
                int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
                int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;
                int num4 = num3 == 0 ? s1.getName().compareTo(s2.getName()) : num3;
                return num4;
                //return 0;
            }
        });
        for (int i = 0; i < 5; i++) {
            Scanner sc = new Scanner(System.in);
            System.out.println("请录入第" + (i + 1) + "个学生信息");
            System.out.println("姓名:");
            String name = sc.nextLine();
            System.out.println("语文成绩:");
            int chinese = sc.nextInt();
            System.out.println("数学成绩:");
            int math = sc.nextInt();
            System.out.println("英语成绩:");
            int english = sc.nextInt();
            Student01 s = new Student01();
            s.setName(name);
            s.setChinese(chinese);
            s.setMath(math);
            s.setEnglish(english);
            ts.add(s);
        }
        BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\java_doc\\src\\io_demo\\Student01.txt"));
        for (Student01 s : ts) {
            StringBuilder sb = new StringBuilder();
            sb.append(s.getName()).append(",").append(s.getChinese())
                    .append(",").append(s.getMath()).append(",").append(s.getEnglish()).append(",").append(s.getSum());
            bw.write(sb.toString());
            bw.newLine();
            bw.flush();
        }
        bw.close();
    }
}



标准输入输出流

无论是在api中查看,还是在Ststem中查看,我们都可以了解到System有标准输入和输出流的定义,并且声明为static,于是肯定是可以直接进行按照类名调用的。


api对System类有以下说明


在 System 类提供的设施中,有标准输入、标准输出和错误输出流;对外部定义的属性和环境变量的访问;加载文件和库的方法;还有快速复制数组的一部分的实用方法。


作为了System类的两个成员变量。这两个流的主要是进行键盘和内存的交互。


标准输入流

public abstract class InputStreamextends Objectimplements Closeable此抽象类是表示字节输入流的所有类的超类。


这个抽象类唯一的构造方法就是InputStream()这个无参构造方法。


简单了解下


package io_demo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Scanner;
public class IoDemo_21 {
    public static void main(String args[]) throws IOException {
       //字节流
        InputStream in = System.in;
        int by;
        while((by=in.read())!=-1)
        {
            System.out.print((char)by);
        }
        //使用字符流
        InputStreamReader isr = new InputStreamReader(in);
        //使用字符流实现一次读取一次数据(使用字符缓冲输入流)
        BufferedReader br = new BufferedReader(isr);
        System.out.println("请输入一个字符串:");
        String line = br.readLine();
        System.out.println("你输入的字符串是"+line);
        System.out.println("请输入一个整数:");
        int i = Integer.parseInt(br.readLine());
        System.out.println("你输入的整数是:"+i);
        //java提供了一个类实现键盘录入
        Scanner sc = new Scanner(System.in);//既可以接收整数也可以接收字符串
    }
}


平常我们都会使用Scanner这个下面的方法去进行键盘输入数据,但是弄清楚它的本质也是一件非常有意义的事情。



它的底层还是使用了字节输入流。



这样就与流的知识衔接起来了。


标准输出流

public class PrintStreamextends FilterOutputStreamimplements Appendable, Closeable


PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。它还提供其他两项功能。与其他输出流不同,PrintStream 永远不会抛出 IOException;而是,异常情况仅设置可通过 checkError 方法测试的内部标志。另外,为了自动刷新,可以创建一个 PrintStream;这意味着可在写入 byte 数组之后自动调用 flush 方法,可调用其中一个 println 方法,或写入一个换行符或字节 (’\n’)。

PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。


所具有的一些构造方法


PrintStream(File file)

创建具有指定文件且不带自动行刷新的新打印流。

PrintStream(File file, String csn)

创建具有指定文件名称和字符集且不带自动行刷新的新打印流。

PrintStream(OutputStream out)

创建新的打印流。

PrintStream(OutputStream out, boolean autoFlush)

创建新的打印流。

PrintStream(OutputStream out, boolean autoFlush, String encoding)

创建新的打印流。

PrintStream(String fileName)

创建具有指定文件名称且不带自动行刷新的新打印流。

PrintStream(String fileName, String csn)

创建具有指定文件名称和字符集且不带自动行刷新的新打印流。


这样我们得到的信息是,我么可以在参数中按照布尔值确定是否进行自动刷新,另外还可以进行指定编码。


这个和标准输入流在System类中定义的格式是一样的。底层话,其实同样也是字节流。简单看下怎么使用。


//标准输出流
        PrintStream out = System.out;
        out.print("hello");//输出在控制台


但是你其实也可以向文件中进行输出,也就是将数据输入到文件中。


 

//字节打印流
        PrintStream ps = new PrintStream("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
        ps.write(97);//使用父类方法
        ps.print(97);//使用自己特有方法
        ps.close();
       //字符打印流
        PrintWriter pW = new PrintWriter("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
        pW.write("hello");
        pW.flush();
        pW.println("Hello");
        pW.flush();
        PrintWriter pw = new PrintWriter(new FileWriter("E:\\java_doc\\src\\io_demo\\jgdabc.txt"),true);//会自动刷新
        pw.println("Hello");
        pw.println("world"


对象序列化流于反序列化流

什么是序列化与反序列化

=java序列化,就是指吧java对象转换为字节序列的过程。而反序列自然就是将字节对象恢复为java对象。==


这样做的意义在哪呢?对象进行序列化,会转换为字节流,这样在网络上传输,或者是进行保存为本地文件都是非常方便的。反序列很明显就是进行对象的重构。


其实你可以和通信联系在一起。网络上的文本,图片,视频,音频都是通过二进制进行传输的,我们的java所创建的对象在传输的时候也应该进行序列化,转换为字节流,然后通过网络,io传入,当我们的对象序列传输完成后,对方进行反序列化,就可以读取到数据内容。


对象序列化流( ObjectOutputStream)

api对这个类有很多的说明,主要说明


public class ObjectOutputStreamextends OutputStreamimplements ObjectOutput, ObjectStreamConstants


ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。


看一下简单使用


首先我们需要一个对象类


package io_demo;
import java.io.Serializable;
public class Student implements Serializable {
    private String sid;
    private String name;
    int age;
    private String address;
    public Student()
    {
    }
    public Student(String sid, String name, int age, String address) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public String getSid() {
        return sid;
    }
    public void setSid(String sid) {
        this.sid = sid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}


然后测试主类


package io_demo;
import java.io.*;
public class IoDemo_23 {
    //对象序列化流
//    构造方法
   // ObjectOutputStream(OutputStream out)
    public static void main(String args[]) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\java_doc\\src\\io_demo\\jgdabc.txt"));
        Student s = new Student("001","刘备",28,"汉中");
        oos.writeObject(s);//将对象序列化()
        oos.close();
    }
}


为什么Student需要实现Serializable,api有说明


只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。


为什么没有具体实现?



因为这个接口本身没有具体实现,我们不要要实现什么,这个只是作为一个标识而已。


对象反序列化流(.ObjectInputStream)

public class ObjectInputStream

extends InputStream

implements ObjectInput, ObjectStreamConstants


说明如下


ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象。

ObjectOutputStream和ObjectInputStream可以分别为与FileOutputStream和FileInputStream一起使用的对象图提供持久性存储的应用程序。 ObjectInputStream用于恢复先前序列化的对象。 其他用途包括使用套接字流在主机之间传递对象,或者在远程通信系统中进行封送和解组参数和参数。


首先还是原来的Student对象类


package io_demo;
import java.io.Serializable;
public class Student implements Serializable {
    private String sid;
    private String name;
    int age;
    private String address;
    public Student()
    {
    }
    public Student(String sid, String name, int age, String address) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public String getSid() {
        return sid;
    }
    public void setSid(String sid) {
        this.sid = sid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}


对的,你虽然是从文件中读取,但是如果你不使用构建的对象的话。是绝对无法成功进行反序列化的。


主要的测试代码


package io_demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
//反序列化ObiectreadObject() 进行读取来反序列化
public class IoDemo_24 {
    public static void main(String args[]) throws IOException, ClassNotFoundException {
        //实现对象反序列化流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\java_doc\\src\\io_demo\\jgdabc.txt"));
        Object o = ois.readObject();
        Student s = (Student)o;
        System.out.println(s.getName()+","+s.getAge());
        ois.close();
    }
}



这样感觉有时候很像加密与解密的样子。


如果在反序列话的时候原来对象类有修改?

我现在就修改一个属性。我让age属性权限有所变化,变为私有属性



**结果反序列化就报错了,为什么?你看报错 **



再清晰一点就是


local class incompatible: stream classdesc serialVersionUID = -8624680451455869930, local class serialVersionUID = 6474701452341618882


serialVersionUID是什么?(为什么修改对象类会反序列化失败?)

serialVersionUID是序列化的时候会生成的一个的版本标识,。当我们对对象进行修改后,那么这个序列化的版本标识的值就会与之前的不一样了。这样会导致字节流的版本标识与本地的版本标识不一样,不一样就会反序列化失败。


解决修改对象类修改反序列化失败的问题

我们就自己定义一个常量。让这个版本标识符不再变化



注意,一定是在对象类中,不是在测试类。


不想让某些属性被序列化?

这样就可以



集合(Properties)

介绍这个集合的原因就是这个集合可以和流结合使用。

这个集合的具体说明就不在本文详细说明了,具体的在之前的集合文中再补充吧。简单说明一下和其用法。


这个集合首先是Map下的子类,所以也是双列集合。但是不支持泛型。用代码来看看具体使用。简单演示。


package io_demo;
import java.util.Properties;
import java.util.Set;
public class IoDemo_25 {
    public static void main(String[] args) {
        //创建集合对象
        Properties pro = new Properties();
        //存储元素
        pro.put(001,"jgdabc");
        pro.put(002,"兰舟千帆");
        pro.put(002,"lym");
        //遍历集合
        Set<Object> KeySet = pro.keySet();
        for(Object key:KeySet)
        {
            Object value = pro.get(key);
            System.out.print(key+","+value);
        }
        pro.setProperty("000","jgdabc");
        pro.getProperty("000");
        //获取键值
        Set<String> names = pro.stringPropertyNames();//获取到键集合
        //于是可以这样遍历
        for(String key:names)
        {
            String value = pro.getProperty(key);
            System.out.println(key+value);
        }
    }
}


==它的put()方法键是Object类型的,setProperty()方法的键必须是String类型。==这个需要注意。


集合到文件,文件到集合

package io_demo;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class IoDemo_26 {
    public static void main(String[] args) throws IOException {
        //把集合中的数据保存到文件
        myStore();
        //文件数据加载到集合
        myLoad();
    }
    private static void myLoad() throws IOException {
        Properties prop = new Properties();
        FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
        prop.load(fr);
        fr.close();
        System.out.print(prop);
    }
    private static void myStore() throws IOException {
        Properties prop = new Properties();
        prop.setProperty("000","jack");
        prop.setProperty("001","jgdabc");
        prop.setProperty("002","john");
        FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
        prop.store(fw,null);//null代表不描述信息
    }
}


做一个猜数字游戏

文件中给一个count=0,然后用这个集合读取到数据(需要转换为int类型,然后根据值得限制,限制玩的次数。当然没每次运行完毕都会count+1,运行三次就不能玩了。这个和一直在交互是不一样的。)

首先一个猜数字游戏类


package java_practice;
import java.util.Scanner;
public  class GuessNumber {
    public static  void start()
    {
        Scanner input = new Scanner(System.in);
        int number = (int) (Math.random() * 100)+1; // 产生随机数
        int guess;// 用户猜的数字
        int count = 0;// 猜测次数
        System.out.println("我心里有一个0到100之间的整数,你猜是什么?");
        // 用户猜测随机数
        do {
            guess = input.nextInt();
            if (number < guess) {
                System.out.println("大了点,再猜!");
                count++;
            } else if (number > guess) {
                System.out.println("小了点,再猜!");
                count++;
            } else {
                count++;
                break;
            }
        } while (true);
        System.out.println("这个数字是"+number);
        System.out.println("您猜的次数是"+count);
        // 根据猜测次数给出评价
        if (count == 1) {
            System.out.println("你太聪明了!");
        } else if (count >= 2 && count <= 5) {
            System.out.println("不错,再接再厉!");
        } else {
            System.out.println("要努力啊!");
        }
    }
}


然后一个测试主类


package io_demo;
import java_practice.GuessNumber;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.SQLOutput;
import java.util.Properties;
public class IoDemo_27 {
    public static void main(String args[]) throws IOException {
        GuessNumber guessNumber = new GuessNumber();
        Properties prop = new Properties();
        FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\count.txt");
        prop.load(fr);
        fr.close();
        String count = prop.getProperty("count");
        int number = Integer.parseInt(count);
        if(number>=3)
        {
            System.out.println("游戏试玩结束,想玩请充值");
        }else
        {
           guessNumber.start();
            prop.setProperty("count",String.valueOf(number));
            number++;
            FileWriter fw =  new FileWriter("E:\\java_doc\\src\\io_demo\\count.txt");
            prop.store(fw,null);
            fw.close();
        }
    }
}


注意一定要自己建立一个文件,里面存一个标记值。这样功能就可以实现了。


IO异常处理

可以根据自己的需要选择将数据输入到文件中还是将信息输出到控制台。


抛出异常

package io_demo;
import java.io.*;
public class IODemo_10 {
    public static void main(String args[]) throws IOException {
        FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
        OutputStreamWriter osw = new OutputStreamWriter(fos);
        InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
        //
        osw.write("中国");
        osw.close();
        int ch ;
        while((ch=isr.read())!=-1)
        {
            System.out.println((char)ch);
        }
        isr.close();
    }
}


标准捕获

package io_demo;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class IoDemoException {
    public static void main(String args[])
    {
        FileReader fr=null;
        FileWriter fw=null;
        try {
             fr= new FileReader("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
             fw= new FileWriter("E:\\java_doc\\src\\io_demo\\demo_pratice");
            char[] chs = new char[1024];
            int len;
            while((len = fr.read())!=-1)
            {
                fw.write(chs,0,len);
            }
        }catch (IOException e)
        {
            e.printStackTrace();
        }finally {
            if(fw!=null)
            try {
                fw.close();
            }catch (IOException e)
            {
                e.printStackTrace();
            }
           if(fr!=null){
               try {
                   fr.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
        }
    }
}


但是这样的处理还是比较麻烦的


捕获处理改进版(jdk7特性)

package io_demo;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//jdk7的改进
public class IoDemoException01 {
    public static void main(String args[])
    {
        try (  FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
               FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\Student.txt");) {
            char[] chars = new char[1024];
            int len;
            while ((len = fr.read()) != -1) {
                fw.write(chars, 0, len);
            }
        }catch (IOException e)
        {
            e.printStackTrace();
        }
        }
           ;
        }
//自动释放资源


这样的处理还是会自动释放资源,不需要人为关闭。

相关文章
|
1月前
|
存储 安全 Java
从入门到精通:Java Map全攻略,一篇文章就够了!
【10月更文挑战第17天】本文详细介绍了Java编程中Map的使用,涵盖Map的基本概念、创建、访问与修改、遍历方法、常用实现类(如HashMap、TreeMap、LinkedHashMap)及其特点,以及Map在多线程环境下的并发处理和性能优化技巧,适合初学者和进阶者学习。
50 3
|
11天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
17天前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
|
23天前
|
存储 安全 Java
🌟Java零基础-反序列化:从入门到精通
【10月更文挑战第21天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
63 5
|
21天前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
32 1
|
27天前
|
Java 数据处理 开发者
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
44 3
|
29天前
|
Java
Java中的多线程编程:从入门到精通
本文将带你深入了解Java中的多线程编程。我们将从基础概念开始,逐步深入探讨线程的创建、启动、同步和通信等关键知识点。通过阅读本文,你将能够掌握Java多线程编程的基本技能,为进一步学习和应用打下坚实的基础。
|
1月前
|
开发框架 IDE Java
java制作游戏,如何使用libgdx,入门级别教学
本文是一篇入门级教程,介绍了如何使用libgdx游戏开发框架创建一个简单的游戏项目,包括访问libgdx官网、设置项目、下载项目生成工具,并在IDE中运行生成的项目。
50 1
java制作游戏,如何使用libgdx,入门级别教学
|
30天前
|
存储 安全 Java
从入门到精通:Java Map全攻略,一篇文章就够了!
【10月更文挑战第19天】本文介绍了Java编程中重要的数据结构——Map,通过问答形式讲解了Map的基本概念、创建、访问与修改、遍历方法、常用实现类(如HashMap、TreeMap、LinkedHashMap)及其特点,以及Map在多线程环境下的使用和性能优化技巧,适合初学者和进阶者学习。
45 4
|
29天前
|
Java
[Java]Socket套接字(网络编程入门)
本文介绍了基于Java Socket实现的一对一和多对多聊天模式。一对一模式通过Server和Client类实现简单的消息收发;多对多模式则通过Server类维护客户端集合,并使用多线程实现实时消息广播。文章旨在帮助读者理解Socket的基本原理和应用。
23 1
下一篇
无影云桌面