1-20 IO流_2

字符输入输出流

使用字节流操作汉字或特殊的符号语言的时候,容易乱码,建议使用字符流.

先有字节流,后有字符流,字符流是对字节流的补充.

使用记事本打开某个文件,可以看到内容的就是文本文件,否则可以理解二进制.

一般的,操作二进制文件(图片,音频,视频等)必须使用字节流.

一般的,操作文本文件使用字符流.

**Ps:如果不清楚是哪一类型文件,使用字节流.**

Reader【字符输入流】

Reader是所有字符输入流的父类,这个流中数据是字符【char】,所有字符输入流都需要继承于它,并且得到其方法

因为Reader是一个抽象类,所以不能直接创建对象,需要使用其子类来创建对象FileReader

FileReader【文件字符输入流】

它是Reader子类,可以读取【文本文件】,读取文件的时候使用时默认字符集【你的系统】

    • 常用构造方法
      FileReader(File file) 创建一个新的 FileReader,给予 File读。
      FileReader(String fileName) 创建一个新的 FileReader,给定要读取的文件的名称。
    • 常用方法
      voidclose() 关闭流并释放与它相关联的任何系统资源。
    • intread() 读取单个字符。
      intread(char[] cbuf) 将字符读入一个数组。
      intread(char[] cbuf, int off, int len) 将字符读入一个数组的一部分。

Writer【字符输出流】

Writer是所有字符输出流的父类,这个流中数据是字符【char】,所有字符输出流都需要继承于它,并且得到其方法

因为Writer是一个抽象类,所以不能直接创建对象,需要使用其子类来创建对象FileWriter

FileWriter【文件字符输出流】

它是Writer子类,可以写出【文本文件】,写出文件的时候使用时默认字符集【你的系统】

    • 常用构造方法
      FileWriter(File file) 构建了一个文件对象FileWriter对象。
      FileWriter(File file, boolean append) 构建了一个文件对象FileWriter对象。
      FileWriter(String fileName) 构造给定文件名的FileWriter对象。
      FileWriter(String fileName, boolean append) 构造FileWriter对象给出一个文件名与一个布尔值,指示是否附加写入的数据。
    • abstract voidclose() 关闭流,冲洗它。
      abstract voidflush() 冲流。
      voidwrite(char[] cbuf) 写一个字符数组。
      abstract voidwrite(char[] cbuf, int off, int len) 写入一个字符数组的一部分。
      voidwrite(int c) 写一个字符。
      voidwrite(String str) 写一个字符串。
      voidwrite(String str, int off, int len) 写入字符串的一部分。
package com.qfedu.Writer;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

/*
字符输出流
 */
public class WriterDemo {
    //FileWriter 里面的三个特殊方法
    public static void main(String[] args) {
        //1.常见字符输出流对象【明确流的流向(要读还是要写)】
        //2.读取或写入文件的数据类型【明确流中要流动什么数据,区分读取或写入的式 二进制文件还是文本文件】
        //PS: 二进制的流破一切,操作问题
        Writer w  = null;

        try {
            w = new FileWriter(new File("dir/File2.txt"),true);
            //创建字符串
            String str1 = "今天是周五了";
            String str2 = "要留作业了,不是很多,但是不少";
            String str3 = "够两天时间一个下午\n";
            String str4 = "呵呵~~~!!!!";

            //w.write(str1);
            //w.write(str2,0,str2.length());
            //这个方法的作用和write的作用是一样的,但是就是参数不用
            //w.append(str3);
            w.append(str4,0,str4.length());

            w.flush();
            System.out.println("老子写完了!!!");


        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(w != null){
                try {
                    w.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

练习:使用字符输入输出流,完成图片拷贝。然后观察图片!

需求:使用字符流复制图片

package com.qfedu.ReaderAndWriter;

import java.io.*;

//文件字符输入输出流的操作
public class ReaderAndWriter {
    public static void main(String[] args) {
        //使用字符输入输出流创建对象
        Reader r = null;
        Writer w = null;

        try {
            r = new FileReader(new File("dir/gamersky_01origin_01_201851918405E3.jpg"));
            w = new FileWriter(new File("desc/gamersky_01origin_01_201851918405E3.jpg"));
            //此时使用字符输入输出流, 流中数据是 字符 所以使用字符数组,因为所有读取是经过计算讨论 所以定义大小为1024
            // 在扩充读取大小的时候 要以1024的倍数
            char[] buf = new  char[1024];
            //它循环读取和字节流是一样的
            int len;
            while((len = r.read(buf))!=-1){
                w.write(buf,0,len);
            }
            w.flush();
            System.out.println("老子写完!!!");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(w != null){
                try {
                    w.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(r != null){
                try {
                    r.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

字符流的总结:

字符流主要作用,是操作**【文本文件】**使用,基本上可以解决字节流读取文件中如果是汉字出现的乱码问题,但是因为流中的数据类型是char,所以不可以与用来读取二进制文件【音频、视频、图片等等】

字节流和字符流的区别

字节流与字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同呢?

实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区在操作文件

演示:

package com.qfedu.ReaderAndWriter;

import java.io.*;

public class ByteAndCharStream {
    public static void main(String[] args) throws Exception {
        //字节流是不使用缓冲区,字符流是使用缓冲区
        //1.同时使用字节流和字符流写出数据,都关闭流对象,都可以将数据写出
        //2.同时使用字节流和字符流写出数据, 都不关闭流对象,字节流会将数据写出,但是字符流不会将数据写出
        //原因在于字符流使用缓冲区

        OutputStream os = new FileOutputStream(new File("dir/ByteFile.txt"));
        os.write("Hello World".getBytes());
       // os.close();

        Writer writer = new FileWriter(new File("dir/CharFile.txt"));
        writer.write("Hello world");
        writer.flush();
      //  writer.close();

    }
}

程序运行后可以发现文件中没有任何内容【字符流】,这是因为字符流使用了缓冲区【而在关闭字符流时会强制性地将缓冲区中的内容进行输出】,但是如果程序没有关闭,则缓冲区中的内容是无法输出的,所以**【字符流使用了缓冲区,而字节流有使用缓冲区】字符流更加偏向使用flush**

什么是缓冲区?

缓冲区可以理解为一段特殊的内容

某些情况下,如果一个程序频繁地操作一个资源(如文件或数据库),则性能会很低,此时为了提升新能,【就可以将一部分数据暂时读入到内存的一块区域之中】,以后直接从此区域中读取数据即可,因为读取内存速度会比较快,这样可以提升程序的性能。

在字符流操作中,所有的字符都是在**【内存中形成的】,在输出前会将【所有的内容暂时保存在内存中】,所以使用了【缓冲区暂存数据。】**

PS:如果在不关闭流时可以将字节流的内容全部输出,如果实际字符流可以使用Writer类中的【flush】方法完成

使用字节流还是字符流?

【理论上还是建议根据不同的场景选用不同流进行处理较好】,但所有的文件在硬盘或在传输时都是以字节的方式进行的,包括图片等都是按照字节的方式存储的,而字符是只有在内存中才会形成,所以在开发中**【字节流使用较为广泛】。**

字符编码的发展历程

阶段1:

计算机只认识数字,我们在计算机里一切数据都是以数字来表示,因为英文符号有限,

所以规定使用的字节的最高位是0.每一个字节都是以**【0~127之间的数字来表示】**,比如A对应65,a对应97.

这就是美国标准信息交换码**【ASCII】.**

阶段2:

随着计算机在全球的普及,很多国家和地区都把自己的字符引入了计算机,比如汉字.

此时发现一个字节能表示数字范围太小,不能包含所有的中文汉字那么就规定使用【两个字节】来表示一个汉字.

规定:原有的ASCII字符的编码保持不变,仍然使用一个字节表示,为了区别一个中文字符与两个ASCII码字符,

中文字符的每个字节最高位规定为1(中文的二进制是负数).这个规范就是**【GB2312】**编码,

后来在GB2312的基础上增加了更多的中文字符,比如汉字,也就出现了**【GBK】**.

阶段3:

新的问题,在中国是认识汉字的,但是如果把汉字传递给其他国家,该国家的码表中没有收录汉字,其实就显示另一个符号或者乱码.

为了解决各个国家因为本地化字符编码带来的影响,咱们就把全世界所有的符号统一进行编码-**【Unicode万国码】**编码.

此时某一个字符在全世界任何地方都是固定的,比如**【哥】,在任何地方都是以十六进制的【54E5】**来表示.

Unicode的编码字符都占有**【2个字节大小】.**

常用字符编码集

ASCII码: 占一个字节,只能包含128个符号,不能表示汉字

ISO-8859-1【latin-1】占一个字节,收录的是西欧语言,不能表示汉字

ANSI【GB2312】占两个字节,在简体中文操作系统中ANSI就是值的GB2312

GB18030/GBK 占两个字节,支持中文

UTF-8:是一种针对Unicode的可变长字符编码,又称万国码,UTF-8是Unicode的实现方式之一,这个编码兼容ASCII码

ps:在UTF家族中提供了其他编码 UTF-8E ,UTF-16 UTF-32, 这里有一个特殊编码集千万不要使用,这个编码集是MS公司搞出来

UTF-8 BOM,默认3个字节一个汉字【不要使用】

转换流

PS:提供字符流的目的是解决字节流读取文件出现乱码问题【一次读取一个字节的汉字】,但是只能解决普通这种乱码,如果在指定编码集前下,字符流就无法完成对乱码的处理

转换流的作用:

1.修改编码集

2.将字符流和字节流互相转换

转换输入流InputStreamReader

PS:字节字符输入流作用:将字节流转换为字符流【桥梁 字节转换字符】

InputStreamReader的父类是Reader即字符流

    • 常用构造方法
      InputStreamReader(InputStream in) 创建一个inputstreamreader使用默认字符集。
      InputStreamReader(InputStream in, Charset cs) 创建一个inputstreamreader使用给定的字符集。
      InputStreamReader(InputStream in, String charsetName) 创建一个inputstreamreader使用指定的字符集。
    • 常用方法
      voidclose() 关闭流并释放与它相关联的任何系统资源。
      StringgetEncoding() 返回此流使用的字符编码的名称。
      intread() 读取单个字符。
      intread(char[] cbuf, int offset, int length) 将字符读入一个数组的一部分。
      intread(char[] cbuf) 将读取的数据存储到字符数组中
package com.qfedu.InputStreamReader;

import java.io.*;

//字节字符输入转换流
public class InputStreamReaderDemo {

    public static void main(String[] args) {
        //Java7之后提供的一个语法,这个语法可以帮组流自动释放资源
        //这个语法需要使用try-catch语句, 这个操作必须实现AutoCloseable
        try(InputStreamReader isr =
                    new InputStreamReader(
                            new FileInputStream(
                                    new File("C:\\IdeaProjects\\Java2003_Day_20\\src\\com\\qfedu\\ReaderAndWriter\\ReaderAndWriter.java")))){
           //获取当前文件编码集
            System.out.println("当前文件编码集:"+isr.getEncoding());

            //因为是转换流,相当于将字节流转换为字符流,所以流中数据是char
            char[] buf = new char[1024];
            int len;
            while ((len = isr.read(buf))!=-1){
                System.out.println(new String(buf,0,len));
            }
        }catch(IOException e){
            e.printStackTrace();
        }

    }
}

使用编码集

在于构建 当前转换输入流对象
   //当前是以字符串指定编码集,此时读取文件时使用就是这个编码集
 InputStreamReader isr = new InputStreamReader(new FileInputStream(new File("")),"UTF-8")

   //当前是以CharSet设置编码集,此时读取文件使用的就是这个编码集
 InputStreamReader isr = new InputStreamReader(new FileInputStream(new File("")),CharSet.forName("GBK"))
转换输出流OutputStreamWriter

PS:字节字符输出流作用:将字符流转换为字节流【桥梁 字符转换字节】

OutputSteamWriter的父类是Writer即字符输出流

    • 常用构造方法
      OutputStreamWriter(OutputStream out) 创建一个outputstreamwriter使用默认的字符编码。
      OutputStreamWriter(OutputStream out, Charset cs) 创建一个outputstreamwriter使用给定的字符集。
      OutputStreamWriter(OutputStream out, String charsetName) 创建一个outputstreamwriter使用指定的字符集。
    • 常用方法
      voidclose() 关闭流,冲洗它。
      voidflush() 冲流。
      StringgetEncoding() 返回此流使用的字符编码的名称。
      voidwrite(char[] cbuf, int off, int len) 写入一个字符数组的一部分。
      voidwrite(int c) 写一个字符。
      voidwrite(String str, int off, int len) 写入字符串的一部分。

需求:使用转换流进行文件读写,写一个通用方法,可以接受任何编码集作为读取和输出文件的编码集

1.源文件的编码集是UTF-8 输出文件的编码集要是GBK

2.源文件的比编码集是GBK ,输出文件的编码集是UTF-8

在开发过程中,你的同桌不小心将原本是UTF-8文件读取成GBK,你需要不就将当前文件可以输出查看

package com.qfedu.InputStreamReaderAndOutputStreamWriter;

import jdk.internal.util.xml.impl.Input;

import java.io.*;

/*
 读取文件修改文件编码集操作
 */
public class InputStreamReaderAndOutputStreamWriterDemo {

    public static void main(String[] args) throws  Exception {
       // 1.源文件的编码集是UTF-8  输出文件的编码集要是GBK
//        TransFormationFileCharSet(new File("dir/File2.txt"),"UTF-8",
//                new File("TransFormationFile/GBKFile.txt"),"GBK");
        //2.源文件的比编码集是GBK ,输出文件的编码集是UTF-8
//        TransFormationFileCharSet(new File("TransFormationFile/GBKFile.txt"),"GBK",
//                new File("TransFormationFile/UTFFile.txt"),"UTF-8");

                TransFormationFileCharSet(new File("dir/File2.txt"),"GBK",
                new File("TransFormationFile/XXXXX.txt"),"UTF-8");

    }

    /**
     * 文件编码转换操作
     * @param srcFile 源文件File对象
     * @param srcCharSet 源文件编码集【String类型】
     * @param desFile 目标文件File对象
     * @param desCharSet 目标文件编码集【String类型】
     */
    public static void TransFormationFileCharSet(File srcFile,String srcCharSet,File desFile,String desCharSet) throws  Exception{
           //1.需要创建转换流对象【输入流【读取数据】和输出流【写出数据】】
        InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFile),srcCharSet);
        OutputStreamWriter osw  = new OutputStreamWriter(new FileOutputStream(desFile),desCharSet);

           //2.循环读写数据【这里流动数据是char】
        char[] buf = new char[1024];
        int len;
        while((len = isr.read(buf))!=-1){
            osw.write(buf,0,len);
        }
        osw.flush();
        System.out.println("写完了!!!!");

        osw.close();
        isr.close();

    }
}

PS: 如果读取文件时字符编码集是正确,那么输出文件时就可以随意更编码集
    如果读取文件时字符编码集是错误,那么除数文件时无论如何更改都是错误的【错误体现就是乱码】

总结:

1.操作文件时,如果文件存在编码转换的需求,可以使用转换流完成

PS:一定要读取文件时字符编码正确,写出文件时才会正确,否则会出现乱码

2.转换流可以设置编码,除此之外, 可以将字节流转换为字符流【InputStreamReader】

​ 可以将字符流转换为字节流【OutputStreamWriter】

3.这个转换流的父类是字符流,并且这个流接收的参数是一个字节输入、输出流对象

请添加图片描述

请添加图片描述

字节缓冲流和字符缓冲流

无论是字节缓冲流还是字符缓冲流,读取数据和写出数据都是操作一个内存区域**【缓冲区】**

在读取数据的时候都是先将数据写入到缓冲区,然后输出数据时在从缓冲区中将数据写出

PS:缓冲流在操作数据时效率要远远高于原始流,在项目中过多使用缓冲流

字节缓冲流

PS:这个流式支持mark标记和reset重启**【但是没有任何效果】**

BufferedInputStream 缓冲字节输入流

BufferedInputStream自带缓冲区的流,将一个**字节输入流包装成带有缓冲区的流,**所以参数需要是一个字节输入流对象

这个流父类是InputStream,父类中提供方法,他都有并给支持mark和reset操作

    • 常用构造方法
      BufferedInputStream(InputStream in) 创建一个 BufferedInputStream和保存它的参数,输入流 in,供以后使用。
      BufferedInputStream(InputStream in, int size) 创建一个具有指定的缓冲区大小 BufferedInputStream,并保存它的参数,输入流 in,供以后使用。
    • 常用方法
      voidclose() 关闭此输入流并释放与流关联的任何系统资源。
      intread() 看到的 InputStreamread方法一般合同。
      intread(byte[] b) 读取文件存储到数组中。【常用】
      intread(byte[] b, int off, int len) 从这个字节的输入流读取到指定的字节数组中的字节,从给定的偏移量开始。
      booleanmarkSupported() 如果输入流的支持 markreset方法。 【无用】
      voidmark(int readlimit) 看到的 InputStreammark方法一般合同。 【无用】
      voidreset() 看到的 InputStreamreset方法一般合同。 【无用】

BufferedOutputStream 缓冲字节输出流

BufferedOutputStream自带缓冲区的流,将一个字节输出流包装成带有缓冲区的流,所以参数需要是一个字节输出流对象

这个流父类是OutputStream,父类中提供方法,他都有

    • 常用构造方法
      BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,将数据写入到指定的基本输出流中。
      BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,用指定的缓冲区大小写数据到指定的基本输出流中。
    • 常用方法
      voidflush() 刷新缓冲输出流。
      voidwrite(byte[] b, int off, int len)len字节指定字节数组中的起始偏移 off这个缓冲输出流。
      voidwrite(int b) 将指定的字节写入该缓冲输出流中。
      voidclose() 关闭此输出流并释放与此流关联的任何系统资源。

PS:这个缓冲流所有操作参考字节输入输出流操作即可

性能测试

需求:使用字节输入输出流读取一个超过2.2G文件,计算读取和写出时间,同样文件使用缓冲字节输入输出流进行读取和写出,计算时间

package com.qfedu.Buffered.InputAndOutput;

import java.io.*;

//使用字节缓冲流和字节流进行文件拷贝
public class TimeTest {
    public static void main(String[] args) throws Exception {
          long begin =  System.currentTimeMillis();
          //普通流读取【字节】拷贝文件的时间:14877毫秒
       // copyFileByByteStream(new File("dir/1.zip"),new File("desc/1.zip"));
         //缓冲流读取【缓冲】拷贝文件的时间:4443毫秒
        copyFileByBufferedStream(new File("dir/1.zip"),new File("desc/1.zip"));
          long end = System.currentTimeMillis();
        System.out.println("拷贝文件的时间:"+(end - begin)+"毫秒");
    }
    //普通流
    public static void copyFileByByteStream(File srcFile, File desFile)throws  Exception{
        InputStream is = new FileInputStream(srcFile);
        OutputStream os = new FileOutputStream(desFile);
        byte[] bs = new byte[1024];
        int len ;
        while((len = is.read(bs))!=-1){
            os.write(bs,0,len);
        }
        os.flush();
        os.close();
        is.close();
    }
    //缓冲流
    public static void copyFileByBufferedStream(File srcFile, File desFile)throws  Exception{
             //缓冲流是默认操作的缓冲区,也就是说将数据直接写入到了缓冲区【默认大小是1024*8 --》 8192】
        //缓冲字节输入流对象创建
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        //缓冲字节输出流对象创建
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(desFile));
        //因为它自带缓冲,所以接下来的操作和 当前原始流即普通字节输入输出流一样
        byte[] bs = new byte[1024];
        int len ;
        while((len = bis.read(bs))!=-1){
            bos.write(bs,0,len);
        }
        bos.flush();
        bos.close();
        bis.close();

    }


}


需求:用字节缓冲流实现用户在控台读取,将数据写到一个磁盘文件啊,用户不输入886之前一直获取,否则停止

package com.qfedu.Buffered.InputAndOutput;
//字节缓冲流获取控制输出,并且写到文件中

import java.io.*;
import java.util.Scanner;

public class SystemInDemo {
    public static void main(String[] args) throws Exception {
        //1.从控制台上获取数据
        System.out.println("请输入一句话");
        BufferedInputStream  bis = new BufferedInputStream(System.in);
        //2.将数据写入文件中
       // BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("dir/控制台上输入的数据.txt")));
        //使用System.out将数据写入到文件中
        //2.1创建打印流对象【修改System.out的输出】
        PrintStream ps = new PrintStream(new FileOutputStream(new File("dir/控制台上输入的数据.txt")));
        //输出定向【改变原有系统流【System.in 和 System.out】】
        System.setOut(ps);//参数是一个打印流的对象,会根据当前流所指定位置进行重新输出

        //3.创建字节数组,存储数据
        byte[] bs = new  byte[1024];
        int  len;
        while(true){
             len = bis.read(bs);
             //需要将字节数组数据转换原有的字符串
            String  content = new String(bs,0,len);
            //字节流所以886 并不是一个单纯字串,它会获取到换行
            if("886\n".equals(content)){
                break;
            }
            System.out.println(content); //因为System.out已经被改变了流量,所以会直接输出文件中
        }
        bis.close();



    }
}

//思考:现阶System.out已经被改变到文件中,那么如何修改成到控制?
字符缓冲流

PS:字符缓冲流也是自带缓冲区,大小也是【8192k】,它是读取文本文件中数据最好使用一个流

BufferedReader【字符缓冲输入流】

BufferedReader父类是Reader,需要传入一个Reader对象,会将当前对象进行包装,让字符流存在缓冲区,提高效率

PS:BufferedReader基本操作参看Reader即可使用 read(char[] buf)读取数据

    • 构造方法
      BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流。
      BufferedReader(Reader in, int sz) 创建一个使用指定大小的输入缓冲区的缓冲字符输入流。
    • 常用方法
      StringreadLine() 读一行文本。
package com.qfedu.Buffered.ReaderAndWriter;



import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;

/**
 * 字符缓冲输入流的使用
 */
public class BufferedReaderDemo {
    public static void main(String[] args) throws Exception {
        //1.构建字符缓冲输入流对象
        BufferedReader  br = new BufferedReader(new FileReader("dir/ByteAndCharStream.java"));
        //原始读取数据
//        char[] buf  = new  char[1024];
//        int len;
//        while((len = br.read(buf))!=-1){
//            System.out.println(new String(buf,0,len));
//        }
        //强烈建议使用下面方式进行数据读取操作【非常方便】
        //这个操作读取出来数据是一个字符
        String content = null;
        //readLine这个方法的时候需要注意,判断读取到文件末尾使用 null
        while((content = br.readLine())!=null){
            System.out.println(content);
        }
        
        br.close();

    }
}


BufferedWriter【字符缓冲输出流】

BufferedWriter的父类是Writer,构建对象时需要一个Writer对象,并且会将当前Writer包装带有缓冲区的流

    • 常用构造方法
      BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流。
      BufferedWriter(Writer out, int sz) 创建一个新的缓冲字符输出流,该流使用给定大小的输出缓冲区。
    • voidnewLine() 写行分隔符。
      voidwrite(String str) 写一个字符串。 【这个方法是专门应对readLine方法写出的】

ps:剩余的基本操作可以参考Writer,包括写出数据write(char[] buf,int off,int len) 和关闭close

需求:使用字符缓冲流读取文件并写入到des文件夹中

package com.qfedu.Buffered.ReaderAndWriter;



import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;

/**
 * 字符缓冲输入流的+字符缓冲输出流使用
 */
public class BufferedReaderAndBufferedWriterDemo {
    public static void main(String[] args) throws Exception {
        //1.构建字符缓冲输入流对象
        BufferedReader  br = new BufferedReader(new FileReader("dir/ByteAndCharStream.java"));
        BufferedWriter bw =  new BufferedWriter(new FileWriter("desc/ByteAndCharStream.java"));
        //强烈建议使用下面方式进行数据读取操作【非常方便】
        //这个操作读取出来数据是一个字符
        String content = null;
        //readLine这个方法的时候需要注意,判断读取到文件末尾使用 null
        //readLine读取数据的时候,是不会读取换行,所以为了保证,数据写出的完整 BufferedWriter这个类中就提供了换行操作
        while((content = br.readLine())!=null){
             bw.write(content);
             bw.newLine();  //就相当于是回车了
        }
        bw.flush();
        System.out.println("老子写完了!!!");

        bw.close();
        br.close();

    }
}



需求:使用字符缓冲流重构转换流案例

package com.qfedu.Buffered.ReaderAndWriter;

import java.io.*;

/**
 * 工具类
 * 提供缓存字符流处理转换流案例
 */
public class TransformationFlowsDemo {
    private TransformationFlowsDemo(){};
    
    public static void cpoyFile(File srcFile, String srcCharName,File desFile,String desCharName) throws Exception {
        BufferedReader br = new BufferedReader(
                new InputStreamReader(
                        new FileInputStream(srcFile),srcCharName));
        
        BufferedWriter bw = new BufferedWriter(
                new OutputStreamWriter(
                        new FileOutputStream(desFile),desCharName));
        
        String content = null;
        while((content = br.readLine())!=null){
            bw.write(content);
            bw.newLine();
        }
        bw.flush();
        bw.close();
        br.close();
    }
}


Properties文件

Properties继承于Hashtable

Hashtable是Map的实现类,和HashMap是兄弟

Hashtable特点就是线程安全,但是效率低,已经很少使用,但是这个类提供一个文件类Properties

这个文件类中是典型键值对即 一个key 对应 一个value,以字符串的形式进行存储的

Properties多用于后期数据参数的存储,配置文件信息,轻量数据的传输,基本上接近于JSON

如何写

需求:通过控制台创建Properties文件,并存储数据,存储完毕之后在读取文件中数据

PS:Properties文件默认编码集是【ISO-8859-1】,这个编码集是不支持中文

package com.qfedu.Properties;

import java.io.*;
import java.util.Properties;

/**
 * 创建资源文件,写入数据
 */
public class PropertiesFileDemo {
    public static void main(String[] args) throws Exception {
        //使用字符缓冲输入输出流,读取控制台数据并写入到文件中
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        //如果需要这个资源文件,文件的后缀名必须是properties 全小写不能写错
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("dir/config.properties")));

        //核心:
        // 1.创建Properties对象来存储数据
        Properties p = new Properties(); //这个文件中存储的式kv键值对

        //2.循环在控制台获取数据写入操文件中
        while(true){
            System.out.println("请输入一个key:");
            String key = br.readLine();
            //约定当前用书输出 over 就停止输入
            if(key.equals("over")){
                break;
            }
            System.out.println("请输入一个value:");
            String  value = br.readLine();

            //以键值对的形式存储数据到Properties对象中
            p.setProperty(key,value);
        }
        //3.将存Properties对象中数据写入到文件中
        //这个注释会出现在文件的最上面
        p.store(bw,"我是注释!!!");
        bw.close();
        br.close();

    }
}


如何读取

package com.qfedu.Properties;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

//工具类,这个工具类将是以后读取Properties文件模板
public class PropertiesUtil {
    private  PropertiesUtil(){}

    //1.创建文件对象
    private  static Properties properties;
    static{
        properties = new Properties();
        //读取文件中内容
        try {
            //这里的异常只能抓取
            //会通过流将文件中键值对数据读取到这个对象中
            properties.load(new FileInputStream("dir/config.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //获取里面的value值需要提供key
    public static String getValue(String key){
        return properties.getProperty(key);
    }
}













总结核心方法:

1.创建对象 new Properties()

2.设置key 和value值的存储 setProperty(key,value)

3.写成文件store(【字节输出流或字符输出流】,“文件注释”)

4.读取文件load(【字节输入流或字符输入流】)

5.获取value值 getProperty(key)

对象流

序列化和反序列化

对象的序列化:把一个【对象变成二进制数据流】的一种方式,通过对象的序列化可以方便的实现对象**【传输和存储】**

对象反序列化:把一个【**二进制数据转换为原有对象】**的一种方法,就可以读取原有对象中存储数据数据类型

Serializable接口

是类序列化必要的一个实现接口,只有实现了这个接口的类才能进行序列化和反序列化操作,这个接口没有任何方法只需要实现即可。

请添加图片描述

对象字节输入输出流

使用对象字节输出流输出对象称为**【序列化】,从内存中将一个对象固化到当前磁盘文件当中,使用流是【ObjectOutputStream】**

使用对象字节输入流读取对象称为**【反序列化】,从磁盘文件中将对象读取到内中,使用流是【ObjectInputStream】**

ObjectOutputStream常用构造方法和常用方法

    • 常用构造方法
      publicObjectOutputStream(OutputStream out) 创建一个对象写入到指定的输出流,参数需要是一个字节输出流对象
    • 常用方法
      voidwriteObject(Object obj) 将对象写入到对象流中【对象的序列化】。

ObjectInputStream常用的构造方法和常用方法

    • 常用构造方法
      public ObjectInputStream(InputStream in) 创建一个对象输入流读取从指定的输入流,参数需要一个字节输入流对象
    • 常用方法
      ObjectreadObject() 从对象输入流读取对象【反序列化】。

PS:无论怎么样他们都是流,所以需要关闭【close方法】

单个对象序列化操作

package com.qfedu.SerializableObject;

import java.io.Serializable;

//需要对这个类的对象进行序列化和反序列化,此时就需要让这个类实现Serializable
//这个接口只需要实现,不需要重写任何方法【主要是它没有定义任何方法】
public class Student implements Serializable {
    //如果需要做JDK版本的区分
    //1 显示声明序列号
   // private  static  final  long serialVersionUID = 1L;
    //2. 如果不写系统自动生成
    //序列化序列的是当前类的对象,这个对象会将类中属性值一起带走
    private String  name;
    private String  gender;
    private Integer age;
    public Student() {

    }
    public Student(String name, String gender, Integer age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                '}';
    }
}

package com.qfedu.SerializableObject;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

/**
 * 进行对象的序列化操作
 */
public class ObjectOutputStreamDemo {
    public static void main(String[] args) throws Exception {
        //1.先创建序列化流的对象
        //序列化的文件给与不给文件后缀名都是一样,它没有一个统一的后缀,毕竟这个文件不是给程序猿看
        ObjectOutputStream  oos = new ObjectOutputStream(new FileOutputStream("dir/对象序列化文件.txt"));
        //2.提供序列化中的对象【这个对象的类必须实现 Serializable接口】
        Student stu = new Student("小白","男",10);
        oos.writeObject(stu);//对象序列化了
        System.out.println("完成序列化!");
        oos.close();
    }
}
package com.qfedu.SerializableObject;

import java.io.FileInputStream;
import java.io.ObjectInputStream;

/**
 * 对象的反序列化
 */
public class ObjectInputStreamDemo {
    public static void main(String[] args) throws Exception{
        //1.创建对象反序列化流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dir/对象序列化文件.txt"));
        //2.进行反序列化
        Object o = ois.readObject();
        System.out.println("当前反序列化对象是Student类型吗?"+(o instanceof Student));
        System.out.println(o);
        ois.close();
    }
}



多个对象序列化和反序列化操作

版本一:直接序列化存储对象集合,反序列化集合获取到对象【取巧】

package com.qfedu.SerializableObject;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 多个对象序列化和反序列化
 */
public class SerializableMoreStudent {
    public static void main(String[] args) throws Exception {
        //1.集合对象序列化和反序列化
        List<Student> list = new ArrayList<>();
        //向集合中存储Student对象
        Collections.addAll(list
                ,new Student("张三","男",18)
                ,new Student("李四","男",19)
                ,new Student("王五","男",20)
                ,new Student("赵六","男",21)
        );
        //CollectionSerializable(list);
        CollectionDeserializable();


    }
    //集合对象序列化
    public static void  CollectionSerializable(List<Student> list)throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dir/集合对象序列化"));
        oos.writeObject(list);
        oos.close();
        System.out.println("序列化完毕!");

    }
    //反序列化
    public static void  CollectionDeserializable()throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dir/集合对象序列化"));
        Object o = ois.readObject();
        ArrayList<Student> list = (ArrayList<Student>)o;
        for (Student  stu: list) {
            System.out.println(stu);

        }
        ois.close();

    }













}


方式二:多个对象进行序列化和反序列化

package com.qfedu.SerializableObject;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 多个对象序列化和反序列化
 */
public class SerializableMoreStudent2 {
    public static void main(String[] args) throws Exception {
        //1.集合对象中存储对象进行序列化和反序列化
        List<Student> list = new ArrayList<>();
        //向集合中存储Student对象
        Collections.addAll(list
                ,new Student("张三","男",18)
                ,new Student("李四","男",19)
                ,new Student("王五","男",20)
                ,new Student("赵六","男",21)
                ,null
        );
       //ObjectSerializable(list);
        ObjectDeserializable();


    }
    //每个对象序列化
    public static void  ObjectSerializable(List<Student> list)throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dir/集合对象序列化2"));
        for(Student stu :list) {
            oos.writeObject(stu);
        }
        oos.close();
        System.out.println("序列化完毕!");

    }
    //反序列化
    public static void  ObjectDeserializable()throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dir/集合对象序列化2"));
        //此时序列化文件中存储的式一个一个对象,反序列化读取文件中对象,
        // 如果判断是最后一个位置,如果要是读取到没有对象肯定返回值应该是一个null
        //如果这样直接读取文件会出现EOFException,这个异常是一个正确异常,说明读取到文件末尾了,但是并没有按照正常方式停止
        //所以为了避免这个EOFException,我们建议在实现多个对象序列化的时候在所有对象的最后添加一个 null,以作为结尾
        Object o = null;
        while((o = ois.readObject())!= null){
            System.out.println(o);
        }

        ois.close();

    }













}


总结:

序列化和反序列化是对象在网络流中传递最方便的一种方式,只有类实现了Serializable接口才可以序列化和反序列化

ObjectOutputStream是序列化形式

ObjectInputStream是反序列化形式

自定义类中如果某个属性不需要进行序列化操作,可以使用**【transient】**进行修饰 ,此属性就不会序列化了

例如:private transient Integer age;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值