转载

java基础(四):谈谈java中的IO流

  通过api查找output。找到很多,其中java.io.OutputStream,OutputStream: 输出字节流的超类。

  基本特点:

  1、操作的数据都是字节。

  2、定义了输出字节流的基本共性功能。

  3、输出流中定义都是写write方法。操作字节数组write(byte[]),操作单个字节write(byte)。

  子类有规律: 所有的子类名称后缀是父类名,前缀名是这个流对象功能

  想要操作文件: FileOutputStream

public class FileOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        //需求:将数据写入到文件中。
        //创建存储数据的文件。
        File file = new File("c://file.txt");
        //创建一个用于操作文件的字节输出流对象。一创建就必须明确数据存储目的地。
        //输出流目的是文件,会自动创建。如果文件存在,则覆盖。
        FileOutputStream fos = new FileOutputStream(file);
        //调用父类中的write方法。
        byte[] data = "abcde".getBytes();
        fos.write(data);
        //关闭流资源。
        fos.close();
    }
}
复制代码

1.1.2.给文件中续写和换行

  我们知道直接new FileOutputStream(file)这样创建对象,会覆盖原有的文件,那么我们想在原有的文件中续写内容怎么办呢?继续查阅FileOutputStream的API。 发现在FileOutputStream的构造函数中,可以接受一个boolean类型的值,如果值true,就会在文件末位继续添加

public class FileOutputStreamDemo2 {
    public static void main(String[] args) throws Exception {
        File file = new File("c://file.txt");
        FileOutputStream fos = new FileOutputStream(file, true);
        String str = "/r/n"+"itcast";
        fos.write(str.getBytes());
        fos.close();
    }
}
复制代码

1.1.3.IO异常的处理

  我们在开发中应该如何处理这些异常呢?

public class FileOutputStreamDemo3 {
    public static void main(String[] args) {

        File file = new File("c://file.txt");
        //定义FileOutputStream的引用
        FileOutputStream fos = null;
        try {
            //创建FileOutputStream对象
            fos = new FileOutputStream(file);
            //写出数据
            fos.write("abcde".getBytes());

        } catch (IOException e) {
            System.out.println(e.toString() + "----");
        } finally {
            //一定要判断fos是否为null,只有不为null时,才可以关闭资源
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException("");
                }
            }
        }
    }
}
复制代码

1.2.字节输入流Input

1.2.1.读取数据read方法

  通过api查找input。java.io.InputStream。InputStream:字节输入流的超类。

  常见功能:

int read():读取一个字节并返回,没有字节返回-1.

int read(byte[]): 读取一定量的字节数,并存储到字节数组中,返回读取到的字节数。

  用于读取文件的字节输入流对象:FileInputStream。

public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
        File file = new File("c://file.txt");
        //创建一个字节输入流对象,必须明确数据源,其实就是创建字节读取流和数据源相关联。
        FileInputStream fis = new FileInputStream(file);
        //读取数据。使用 read();一次读一个字节。
        int ch = 0;
        while((ch=fis.read())!=-1){
            System.out.println("ch="+(char)ch);
        }
        // 关闭资源。
        fis.close();
    }
}
复制代码

1.2.2.读取数据read(byte[])方法

  在读取文件中的数据时,调用read方法,每次只能读取一个,太麻烦了,于是我们可以定义数组作为临时的存储容器,这时可以调用重载的read方法,一次可以读取多个字符。

public class FileInputStreamDemo2 {
    public static void main(String[] args) throws IOException {
        /*
         * 演示第二个读取方法, read(byte[]);
         */
        File file = new File("c://file.txt");
        // 创建一个字节输入流对象,必须明确数据源,其实就是创建字节读取流和数据源相关联。
        FileInputStream fis = new FileInputStream(file);        
        //创建一个字节数组。
        byte[] buf = new byte[1024];//长度可以定义成1024的整数倍。        
        int len = 0;
        while((len=fis.read(buf))!=-1){
            System.out.println(new String(buf,0,len));
        }
        fis.close();
    }
}
复制代码

2.字符流

  经过前面的学习,我们基本掌握的文件的读写操作,在操作过程中字节流可以操作所有数据,可是当我们操作的文件中有中文字符,并且需要对中文字符做出处理时怎么办呢?

2.1.字节流读取问题

  通过以下程序读取带有中文件的文件。

public class CharStreamDemo {
    public static void main(String[] args) throws IOException {
        //给文件中写中文
        writeCNText();
        //读取文件中的中文
        readCNText();
    }    
    //读取中文
    public static void readCNText() throws IOException {
        FileInputStream fis = new FileInputStream("c://cn.txt");
        int ch = 0;
        while((ch = fis.read())!=-1){
            System.out.println(ch);
        }
    }
    //写中文
    public static void writeCNText() throws IOException {
        FileOutputStream fos = new FileOutputStream("c://cn.txt");
        fos.write("a传智播客欢迎你".getBytes());
        fos.close();
    }
}
复制代码

  上面程序在读取含有中文的文件时,我们并没有看到具体的中文,而是看到一些数字,这是什么原因呢?既然看不到中文,那么我们如何对其中的中文做处理呢?要解决这个问题,我们必须研究下字符的编码过程。

2.2.编码表

  我们知道计算机底层数据存储的都是二进制数据,而我们生活中的各种各样的数据,如何才能和计算机中存储的二进制数据对应起来呢?这时老美他们就把每一个字符和一个整数对应起来,就形成了一张编码表,老美他们的编码表就是ASCII表。其中就是各种英文字符对应的编码。

编码表:其实就是生活中字符和计算机二进制的对应关系表。

  • ascii: 一个字节中的7位就可以表示。对应的字节都是正数。0-xxxxxxx

  • iso8859-1:拉丁码表 latin,用了一个字节用的8位。1-xxxxxxx 负数。

  • GB2312:简体中文码表。包含6000-7000中文和符号。用两个字节表示。两个字节都是开头为1 ,两个字节都是负数。

  • GBK:目前最常用的中文码表,2万的中文和符号。用两个字节表示,其中的一部分文字,第一个字节开头是1,第二字节开头是0

  • GB18030:最新的中文码表,目前还没有正式使用。

  • unicode:国际标准码表:无论是什么文字,都用两个字节存储。

   Java中的char类型用的就是这个码表。char c = 'a';占两个字节。

   Java中的字符串是按照系统默认码表来解析的。简体中文版 字符串默认的码表是GBK。

  • UTF-8 :基于unicode,一个字节就可以存储数据,不要用两个字节存储,而且这个码表更加的标准化,在每一个字节头加入了编码信息(后期到api中查找)。

   能识别中文的码表:GBK、UTF-8;正因为识别中文码表不唯一,涉及到了编码解码问题。对于我们开发而言;常见的编码 GBK UTF-8 ISO8859-1

    文字--->(数字) :编码。

    (数字)--->文字 : 解码。

2.3.FileReader类介绍

  上述程序中我们读取拥有中文的文件时,使用的字节流在读取,那么我们读取到的都是一个一个字节。只要把这些字节去查阅对应的编码表,就能够得到与之对应的字符。API中是否给我们已经提供了读取相应字符的功能流对象呢?

  查阅FileInputStream的API,发现FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。如果读取字符流,要使用FileReader,FileReader是什么呢?

  打开FileReader的API介绍。用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的,原来FileReader 用于读取字符流。使用FileReader时,了解它的功能,看它所属的体系顶层。

  Reader:读取字符流的抽象超类。

    read():读取单个字符并返回

    read(char[]):将数据读取到数组中,并返回读取的个数。

public class CharStreamDemo {
    public static void main(String[] args) throws IOException {
        //给文件中写中文
        writeCNText();
        //读取文件中的中文
        readCNText();
    }    
    //读取中文
    public static void readCNText() throws IOException {
        FileReader fr = new FileReader("D://test//cn.txt");
        int ch = 0;
        while((ch = fr.read())!=-1){
            //输出的字符对应的编码值
            System.out.println(ch);
            //输出字符本身
            System.out.println((char)ch);
        }
    }
    //写中文
    public static void writeCNText() throws IOException {
        FileOutputStream fos = new FileOutputStream("D://test//cn.txt");
        fos.write("a传智播客欢迎你".getBytes());
        fos.close();
    }
}
复制代码

2.4.FileWriter类介绍

  既然有专门用于读取字符的流对象,那么肯定也有写的字符流对象,查阅API,发现有一个Writer类,Writer是写入字符流的抽象类。其中描述了相应的写的动作。

public class FileWriterDemo {
    public static void main(String[] args) throws IOException {
        //演示FileWriter 用于操作文件的便捷类。
        FileWriter fw = new FileWriter("d://text//fw.txt");
        
        fw.write("你好谢谢再见");//这些文字都要先编码。都写入到了流的缓冲区中。
        
        fw.flush();
        
        fw.close();
    }
}
复制代码

2.5.flush()和close()的区别

  flush():将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用。

  close():关闭资源,但在关闭前会将缓冲区中的数据先刷新到目的地,否则丢失数据,然后在关闭流。流不可以使用。如果写入数据多,一定要一边写一边刷新,最后一次可以不刷新,由close完成刷新并关闭。

3.转换流

  学习完了使用字符流对文件的简单操作后,在学习字符流的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputStreamWriter,这又是什么意思呢?

3.1.OutputSreamWriter类

  需求:既然识别中文的码表有两个,GBK、UTF-8等,能不能将中文数据按照utf-8的方式进行文件的存储呢?

  还能使用FileWriter吗? 不能使用了,因为FileWriter中默认的是GBK 。通过FileWriter的api描述,要指定编码表这些值,需要使用OutputStreamWriter

  OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它的作用的就是, 将字符串按照指定的编码表转成字节,在使用字节流将这些字节写出去

public static void writeCN() throws Exception {
        //创建与文件关联的字节输出流对象
        FileOutputStream fos = new FileOutputStream("c://cn8.txt");
        //创建可以把字符转成字节的转换流对象,并指定编码
        OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
        //调用转换流,把文字写出去,其实是写到转换流的缓冲区中
        osw.write("你好");//写入缓冲区。
        osw.close();
    }
复制代码

  OutputStreamWriter流对象,它到底如何把字符转成字节输出的呢?

  其实在OutputStreamWriter流中维护自己的缓冲区,当我们调用OutputStreamWriter对象的write方法时,会拿着字符到指定的码表中进行查询,把查到的字符编码值转成字节数存放到OutputStreamWriter缓冲区中。然后再调用刷新功能,或者关闭流,或者缓冲区存满后会把缓冲区中的字节数据使用字节流写到指定的文件中。

3.2.InputSreamReader类

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

public class InputStreamReaderDemo {
    public static void main(String[] args) throws IOException {
        //演示字节转字符流的转换流
        readCN();
    }
    public static void readCN() throws IOException{
        //创建读取文件的字节流对象
        InputStream in = new FileInputStream("c: //cn8.txt");
        //创建转换流对象 
        //InputStreamReader isr = new InputStreamReader(in);这样创建对象,会用本地默认码表读取,将会发生错误解码的错误
        InputStreamReader isr = new InputStreamReader(in,"utf-8");
        //使用转换流去读字节流中的字节
        int ch = 0;
        while((ch = isr.read())!=-1){
            System.out.println((char)ch);
        }
        //关闭流
        isr.close();
    }
}
复制代码

   注意:在读取指定的编码的文件时,一定要指定编码格式,否则就会发生解码错误,而发生乱码现象

3.3.转换流和子类的区别

  发现有如下继承关系:

    OutputStreamWriter:

      FileWriter:

    InputStreamReader:

      FileReader;

  父类和子类的功能有什么区别呢?

  OutputStreamWriter和InputStreamReader是字符和字节的桥梁:也可以称之为字符转换流。字符转换流原理:字节流+编码表。

  FileWriter和FileReader:作为子类,仅作为操作字符文件的便捷类存在。当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。

   InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默认字符集。

   InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。

   FileReader fr = new FileReader("a.txt");

  这三句代码的功能是一样的,其中第三句最为便捷。

  注意: 一旦要指定其他编码时,绝对不能用子类,必须使用字符转换 流。什么时候用子类呢?

  条件:

  1、操作的是文件。2、使用默认编码。3 纯文本

  总结:

  字节--->字符 : 看不懂的--->看的懂的。 需要读。输入流。 InputStreamReader

  字符--->字节 : 看的懂的--->看不懂的。 需要写。输出流。 OutputStreamWriter

4.缓冲流

  在我们学习字节流与字符流的时候,大家都进行过读取文件中数据的操作,读取数据量大的文件时,读取的速度会很慢,很影响我们程序的效率,那么, 我想提高速度,怎么办?Java中提高了一套缓冲流,它的存在,可提高IO流的读写速度缓冲流,根据流的分类分类字节缓冲流与字符缓冲流。

4.1.字节缓冲流

  字节缓冲流根据流的方向,共有2个

     写入数据到流中,字节缓冲输出流 BufferedOutputStream

    读取流中的数据,字节缓冲输入流 BufferedInputStream

  它们的内部都包含了一个缓冲区,通过缓冲区读写,就可以提高了IO流的读写速度

4.1.1.字节缓冲输出流BufferedOutputStream

  通过字节缓冲流,进行文件的读写操作 写数据到文件的操作

  构造方法public BufferedOutputStream(OutputStream out)创建一个新的缓冲输出流,以将数据写入指定的底层输出流。

public class BufferedOutputStreamDemo01 {
    public static void main(String[] args) throws IOException {
        
        //写数据到文件的方法
        write();
    }

    /*
     * 写数据到文件的方法
     * 1,创建流
     * 2,写数据
     * 3,关闭流
     */
    private static void write() throws IOException {
        //创建基本的字节输出流
        FileOutputStream fileOut = new FileOutputStream("abc.txt");
        //使用高效的流,把基本的流进行封装,实现速度的提升
        BufferedOutputStream out = new BufferedOutputStream(fileOut);
        //2,写数据
        out.write("hello".getBytes());
        //3,关闭流
        out.close();
    }
}
复制代码

4.1.2.字节缓冲输出流BufferedInputStream

  刚刚我们学习了输出流实现了向文件中写数据的操作,那么,现在我们完成读取文件中数据的操作

  构造方法

  public BufferedInputStream(InputStream in)

/*
     * 从文件中读取数据
     * 1,创建缓冲流对象
     * 2,读数据,打印
     * 3,关闭
     */
    private static void read() throws IOException {
        //1,创建缓冲流对象
        FileInputStream fileIn = new FileInputStream("abc.txt");
        //把基本的流包装成高效的流
        BufferedInputStream in = new BufferedInputStream(fileIn);
        //2,读数据
        int ch = -1;
        while ( (ch = in.read()) != -1 ) {
            //打印
            System.out.print((char)ch);
        }
        //3,关闭
        in.close();
    }
复制代码

4.1.3.使用基本的流与高效的流完成复制文件

  我们一直在说,高效的流速度快并高效,怎么体现呢?需要通过一个复制文件耗时的比较过程,来体验一下高效流带来的快感。

/*
 * 需求:将d://test.avi文件进行复制
 *         采用4种方式复制
 *         方式1: 采用基本的流,一次一个字节的方式复制    共耗时 224613毫秒
 *         方式2: 采用基本的流,一个多个字节的方式赋值    共耗时     327毫秒
 *         方式3: 采用高效的流,一次一个字节的方式复制    共耗时    2047毫秒
 *         方式4: 采用高效的流,一个多个字节的方式赋值    共耗时      96毫秒
 * 
 * 数据源: d://test.avi
 * 目的地1: d://copy1.avi
 * 目的地2: d://copy2.avi
 * 目的地3: d://copy3.avi
 * 目的地4: d://copy4.avi
 * 
 * 实现的步骤:
 *     1,指定数据源
 *     2,指定目的地
 *     3,读数据
 *     4,写数据
 *     5,关闭流
 * 
 */
public class CopyAVI {
    public static void main(String[] args) throws IOException {
        //开始计时
        long start = System.currentTimeMillis();
        //方式1: 采用基本的流,一次一个字节的方式复制
        //method1("d://test.avi", "d://copy1.avi");
        //方式2: 采用基本的流,一个多个字节的方式赋值
        //method2("d://test.avi", "d://copy2.avi");
        //方式3: 采用高效的流,一次一个字节的方式复制
        //method3("d://test.avi", "d://copy3.avi");
        //方式4: 采用高效的流,一个多个字节的方式赋值
        method4("d://test.avi", "d://copy4.avi");
        
        //结束计时
        long end = System.currentTimeMillis();
        //打印耗时多少毫秒
        System.out.println("共耗时 " +(end - start)+ "毫秒");
    }
    
    //方式4: 采用高效的流,一个多个字节的方式赋值
    private static void method4(String src, String dest) throws IOException {
        //1,指定数据源
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(src));
         //2,指定目的地
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dest));
         //3,读数据
        byte[] buffer = new byte[1024];
        int len = -1;
        while ( (len = in.read(buffer)) != -1) {
            //4,写数据
            out.write(buffer, 0, len);
        }
         //5,关闭流
        in.close();
        out.close();
    }

    //方式3: 采用高效的流,一次一个字节的方式复制
    private static void method3(String src, String dest) throws IOException {
        //1,指定数据源
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(src));
         //2,指定目的地
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dest));
         //3,读数据
        int ch = -1;
        while ((ch=in.read()) != -1) {
            //4,写数据
            out.write(ch);    
        }        
         //5,关闭流
        in.close();
        out.close();
    }

    //方式2: 采用基本的流,一个多个字节的方式赋值
    private static void method2(String src, String dest) throws IOException {
        //1,指定数据源
        FileInputStream in = new FileInputStream(src);
        //2,指定目的地
        FileOutputStream out = new FileOutputStream(dest);
        //3,读数据
        byte[] buffer = new byte[1024];
        int len = -1;
        while ( (len=in.read(buffer)) != -1) {
            //4,写数据
            out.write(buffer, 0, len);
        }
        //5,关闭流
        in.close();
        out.close();
    }

    //方式1: 采用基本的流,一次一个字节的方式复制
    private static void method1(String src, String dest) throws IOException {
        //1,指定数据源
        FileInputStream in = new FileInputStream(src);
        //2,指定目的地
        FileOutputStream out = new FileOutputStream(dest);
        //3,读数据
        int ch = -1;
        while (( ch=in.read()) != -1) {
            //4,写数据
            out.write(ch);
        }
        //5,关闭流
        in.close();
        out.close();
    }
}
复制代码

4.2.字符缓冲流

  字符缓冲输入流 BufferedReader

  字符缓冲输出流 BufferedWriter

  完成文本数据的高效的写入与读取的操作

4.2.1.字符缓冲输出流BufferedWriter

  将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

  方法:void newLine() 根据当前的系统,写入一个换行符

/*
 * BufferedWriter 字符缓冲输出流
 * 方法
 *     public void newLine()写入一个行分隔符
 * 
 * 需求: 通过缓冲输出流写入数据到文件
 * 分析:
 *     1,创建流对象
 *     2,写数据
 *     3,关闭流
 * 
 */
public class BufferedWriterDemo {
    public static void main(String[] args) throws IOException {
        //创建流
        //基本字符输出流
        FileWriter fileOut = new FileWriter("file.txt");
        //把基本的流进行包装
        BufferedWriter out = new BufferedWriter(fileOut);
        //2,写数据
        for (int i=0; i<5; i++) {
            out.write("hello");
            out.newLine();
        }
        //3,关闭流
        out.close();
    }
}
复制代码

4.2.2.字符缓冲输入流BufferedReader

  从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

  方法public String readLine() 读取一个文本行,包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null

/*
 * BufferedReader 字符缓冲输入流
 * 
 * 方法:
 *     String readLine() 
 * 需求:从文件中读取数据,并显示数据
 */
public class BufferedReaderDemo {
    public static void main(String[] args) throws IOException {
        //1,创建流
        BufferedReader in = new BufferedReader(new FileReader("file.txt"));
        //2,读数据
        //一次一个字符
        //一次一个字符数组
        //一次读取文本中一行的字符串内容
        String line = null;
        while( (line = in.readLine()) != null ){
            System.out.println(line);
        }
        
        //3,关闭流
        in.close();
    }
}
复制代码

4.2.3.使用字符缓冲流完成文本文件的复制

  刚刚我们学习完了缓冲流,现在我们就使用字符缓冲流的特有功能,完成文本文件的复制

/*
 * 采用高效的字符缓冲流,完成文本文件的赋值
 * 
 * 数据源: file.txt
 * 目的地: copyFile.txt
 * 
 * 分析:
 *     1,指定数据源, 是数据源中读数据,采用输入流
 *     2,指定目的地,是把数据写入目的地,采用输出流
 *     3,读数据
 *     4,写数据
 *     5,关闭流
 */
public class CopyTextFile {
    public static void main(String[] args) throws IOException {
        //1,指定数据源, 是数据源中读数据,采用输入流
        BufferedReader in = new BufferedReader(new FileReader("file.txt"));
        //2,指定目的地,是把数据写入目的地,采用输出流
        BufferedWriter out = new BufferedWriter(new FileWriter("copyFile.txt"));
        //3,读数据
        String line = null;
        while ( (line = in.readLine()) != null ) {
            //4,写数据
            out.write(line);
            //写入换行符号
            out.newLine();
        }
        //5,关闭流
        out.close();
        in.close();
    }
}
复制代码

5.Properties类

5.1.Properties类介绍

  Properties特点:

    1、Hashtable的子类,map集合中的方法都可以用。

    2、该集合没有泛型。键值都是字符串。

    3、它是一个可以持久化的属性集。键值可以存储到集合中,也可以存储到持久化的设备(硬盘、U盘、光盘)上。键值的来源也可以是持久化的设备。

    4、有和流技术相结合的方法。

      a.load(InputStream) 把指定流所对应的文件中的数据,读取出来,保存到Propertie集合中

      b. load(Reader)

      c. store(OutputStream,commonts)把集合中的数据,保存到指定的流所对应的文件中,参数commonts代表对描述信息

      d.stroe(Writer,comments);

/*
 * 
 * Properties集合,它是唯一一个能与IO流交互的集合
 * 
 * 需求:向Properties集合中添加元素,并遍历
 * 
 * 方法:
 * public Object setProperty(String key, String value)调用 Hashtable 的方法 put。
 * public Set<String> stringPropertyNames()返回此属性列表中的键集,
 * public String getProperty(String key)用指定的键在此属性列表中搜索属性
 */
public class PropertiesDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        Properties prop = new Properties();
        //添加元素到集合
        //prop.put(key, value);
        prop.setProperty("周迅", "张学友");
        prop.setProperty("李小璐", "贾乃亮");
        prop.setProperty("杨幂", "刘恺威");
        
        //System.out.println(prop);//测试的使用
        //遍历集合
        Set<String> keys = prop.stringPropertyNames();
        for (String key : keys) {
            //通过键 找值
            //prop.get(key)
            String value = prop.getProperty(key);
            System.out.println(key+"==" +value);
        }
    }
}
复制代码

将配置文件中的数据存储到文件中

/**
 * 需求:使用Properties集合,完成把集合内容存储到IO流所对应文件中的操作
 * 
 * 分析:
 *     1,创建Properties集合
 *     2,添加元素到集合
 *     3,创建流
 *     4,把集合中的数据存储到流所对应的文件中
 *        store(OutputStream,commonts)把集合中的数据,保存到指定的流所对应的文件中,参数commonts代表对描述信息
 *        stroe(Writer,comments);
 *     5,关闭流
 */
public class PropertiesDemo02 {
    public static void main(String[] args) throws IOException {
        //1,创建Properties集合
        Properties prop = new Properties();
        //2,添加元素到集合
        prop.setProperty("周迅", "张学友");
        prop.setProperty("李小璐", "贾乃亮");
        prop.setProperty("杨幂", "刘恺威");
        
        //3,创建流
        FileWriter out = new FileWriter("prop.properties");
        //4,把集合中的数据存储到流所对应的文件中
        prop.store(out, "save data");
        //5,关闭流
        out.close();
    }
}
复制代码

读取配置文件中的数据,同时更新数据,并保存

/*
 * 需求:从属性集文件prop.properties 中取出数据,保存到集合中
 * 分析:
 *     1,创建集合
 *     2,创建流对象
 *     3,把流所对应文件中的数据 读取到集合中
 *        load(InputStream)  把指定流所对应的文件中的数据,读取出来,保存到Propertie集合中
        load(Reader)  
 *     4,关闭流
 *     5,显示集合中的数据
 */
public class PropertiesDemo03 {
    public static void main(String[] args) throws IOException {
        //1,创建集合
        Properties prop = new Properties();
        //2,创建流对象
        FileInputStream in = new FileInputStream("prop.properties");
//FileReader in = new FileReader("prop.properties");
        //3,把流所对应文件中的数据 读取到集合中
        prop.load(in);
        //4,关闭流
        in.close();
        //5,显示集合中的数据
        System.out.println(prop);
        
    }
复制代码

注意: 使用字符流FileReader就可以完成文件中文的读取操作了

6.序列化流与烦序列化流

6.1.对象序列化流

   用于从流中读取对象的操作流 ObjectInputStream 称为 反序列化流

   用于向流中写入对象的操作流 ObjectOutputStream 称为 序列化流

  特点:用于操作对象。

  解决问题: 可以将对象进行序列化和反序列化

  注意: 对象序列化一定要实现Serializable接口。为了给类定义一个serialVersionUID。

  功能:

   ObjectInputStream readObject()

   ObjectOutputStream writeObject()

public class ObjectStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        /*
         * 将一个对象存储到持久化(硬盘)的设备上。
         */
        writeObj();//对象的序列化。
    }
    public static void writeObj() throws IOException {
        //1,明确存储对象的文件。
        FileOutputStream fos = new FileOutputStream("tempfile//obj.object");
        //2,给操作文件对象加入写入对象功能。
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        //3,调用了写入对象的方法。
        oos.writeObject(new Person("wangcai",20));
        //关闭资源。
        oos.close();
    }
}
复制代码

6.2.对象反序列化流

  当把一个对象持久化存储起来之后,需要使用反序列化技术获取存储起来的对象。使用此ObjectInputStream对象就可以完成反序列化动作

public class ObjectStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        readObj();//对象的反序列化。
    }
    public static void readObj() throws IOException, ClassNotFoundException {
        
        //1,定义流对象关联存储了对象文件。
        FileInputStream fis = new FileInputStream("tempfile//obj.object");
        
        //2,建立用于读取对象的功能对象。
        ObjectInputStream ois = new ObjectInputStream(fis);
        
        Person obj = (Person)ois.readObject();
        
        System.out.println(obj.toString());
        
    }
}
复制代码

6.3.序列化接口

  当一个对象要能被序列化,这个对象所属的类必须实现Serializable接口。否则会发生异常NotSerializableException异常。

  同时当反序列化对象时,如果对象所属的class文件在序列化之后进行的修改,那么进行反序列化也会发生异常InvalidClassException。发生这个异常的原因如下:

     该类的序列版本号与从流中读取的类描述符的版本号不匹配

     该类包含未知数据类型

     该类没有可访问的无参数构造方法

  Serializable标记接口。该接口给需要序列化的类,提供了一个序列版本号。serialVersionUID. 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。

public class Person implements Serializable {

    //给类显示声明一个序列版本号。
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    public Person() {
        super();
        
    }
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    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;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}
复制代码

7.总结

  • 字节流

   字节输入流 InputStream

    FileInputStream 操作文件的字节输入流

    BufferedInputStream高效的字节输入流

     ObjectInputStream 反序列化流(操作对象的字节输入流)

   字节输出流 OutputStram

     FileOutputStream 操作文件的字节输出流

    BufferedOutputStream 高效的字节输出流

     ObjectOuputStream 序列化流(操作对象的字节输出流)

  • 字符流

   字符输入流 Reader

    FileReader 操作文件的字符输入流

     BufferedReader 高效的字符输入流

     InputStreamReader 输入操作的转换流(把字节流封装成字符流)

   字符输出流 Writer

    FileWriter 操作文件的字符输出流

     BufferedWriter 高效的字符输出流

     OutputStreamWriter 输出操作的转换流(把字节流封装成字符流)

              将字符流转换成字节流去存储

  • 方法:

  读数据方法:

    read() 一次读一个字节或字符的方法

    read(byte[] char[]) 一次读一个数组数据的方法

    readLine() 一次读一行字符串的方法(BufferedReader类特有方法)

     readObject() 从流中读取对象(ObjectInputStream特有方法)

  写数据方法:

    write(int) 一次写一个字节或字符到文件中

    write(byte[] char[]) 一次写一个数组数据到文件中

     write(String) 一次写一个字符串内容到文件中

     writeObject(Object ) 写对象到流中(ObjectOutputStream类特有方法)

    newLine() 写一个换行符号(BufferedWriter类特有方法)

  • 向文件中写入数据的过程

  1,创建输出流对象

  2,写数据到文件

  3,关闭输出流

  • 从文件中读数据的过程

  1, 创建输入流对象

  2, 从文件中读数据

  3, 关闭输入流

  • 文件复制的过程

  1, 创建输入流(数据源)

  2, 创建输出流(目的地)

  3, 从输入流中读数据

  4, 通过输出流,把数据写入目的地

  5, 关闭流

  • Properties:Map集合的一种,它是Hashtable集合的子集合,它键与值都是String类型,它是唯一能与IO流结合使用的集合

  方法

  load( InputStream in ) 从流所对应的文件中,读数据到集合中

   load( Reader in ) 从流所对应的文件中,读数据到集合中

  store( OutputStream out , String message ) 把集合中的数据,写入到流所对应的文件中

  store( Writer out , String message) 把集合中的数据,写入到流所对应的文件中

实现文件内容的自动追加

构造方法

   FileOutputStream(File file, boolean append)

   FileOutputStream(String fileName, boolean append)

   FileWriter(File, boolean append)

   FileWriter(String fileName, boolean append)

参考:

java编程思想

黑马教学视频

www.cnblogs.com/dz-boss/p/1…

原文  https://juejin.im/post/5c74c7ad51882562c955f698
正文到此结束
Loading...