Java07:IO流(知识点+代码案例)

文章目录

IO流

1. IO流概述和分类

1.1 IO流介绍

  • IO:输入/输出(Input/Output)
  • 流:是一种抽象概念,是对数据传输的总称。也就是说数据在设备间的传输称为流,流的本质是数据传输
  • IO流就是用来处理设备间数据传输问题的。常见的应用:文件复制;文件上传;文件下载
  • 读数据是从文件中读取数据、写数据是写入某文件数据
  • 输入流,从什么文件输入;输出流,输出到什么文件

1.2 IO流的分类

  • 按照数据的流向
    • 输入流:读数据
    • 输出流:写数据
  • 按照数据类型来分
    • 字节流
      • 字节输入流
      • 字节输出流
    • 字符流
      • 字符输入流
      • 字符输出流

1.3 IO流的使用场景

  • 如果操作的是纯文本文件,优先使用字符流
  • 如果操作的是图片、视频、音频等二进制文件。优先使用字节流
  • 如果不确定文件类型,优先使用字节流。字节流是万能的流

1.4 IO流总体概述

在这里插入图片描述

2. 字节流

2.1 字节流概述

2.1.1 字节流抽象基类
  • InputStream:这个抽象类是表示字节输入流的所有类的超类
  • OutputStream:这个抽象类是表示字节输出流的所有类的超类
  • 子类名特点:子类名称都是以其父类名作为子类名的后缀
2.1.2 字节输出流
  • FileOutputStream(String name):创建文件输出流以指定的名称写入文件
2.1.3 使用字节输出流写数据的步骤
  • 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
  • 调用字节输出流对象的写数据方法
  • 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
        FileOutputStream fileOutputStream=new FileOutputStream("project01\\for.txt");
        //如果没有fos.txt文件则会自动创建这个文件
        fileOutputStream.write(97);//写入一个字节,数字对应着ASCII码表
        fileOutputStream.close();

2.2 字节流写数据(FileOutputStream)

2.2.1 FileOutputStream构造方法及其说明
  • 写数据的构造方法分类
    |方法名|说明 |
    | ----------------------------------------|------------------------------------------------------------|
    |FileOutputStream(String name) |创建文件输出流以指定的名称写入文件 |
    |FileOutputStream(File file) | 创建文件输出流以写入由指定的 File对象表示的文件 |

  • 示例代码

        //构造方法一
         FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");//推荐写法
        //底层代码相当于 FileOutputStream fos = new FileOutputStream(new File("myByteStream\\fos.txt"));

        //构造方法二
         File file = new File("myByteStream\\fos.txt");
         FileOutputStream fos2 = new FileOutputStream(file);
         //简洁版如下 
         FileOutputStream fos2 = new FileOutputStream(new File("myByteStream\\fos.txt"));
2.2.1 字节流写数据的三种方法
  • 写数据的方法分类

    方法名说明
    void write(int b)将指定的字节写入此文件输出流 一次写一个字节数据
    void write(byte[] b)将 b.length字节从指定的字节数组写入此文件输出流 一次写一个字节数组数据
    void write(byte[] b, int off, int len)将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流 一次写一个字节数组的部分数据
  • 示例代码

        FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
        //方法一: void write(int b):将指定的字节写入此文件输出流
        fos.write(97);
        fos.write(98);
        fos.write(99);
        
        //方法二: void write(byte[] b):将 b.length字节从指定的字节数组写入此文件输出流
        byte[] bys = {97, 98, 99, 100, 101};
        //byte[] getBytes():返回字符串对应的字节数组
        byte[] bys2 = "abcde".getBytes();
        fos.write(bys);

        //方法三: void write(byte[] b, int off, int len):将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流
        fos.write(bys,0,bys.length);//整个字符数组全部写入
        fos.write(bys,1,3);//指定字符数组的长度写入

        //释放资源
        fos.close();
2.2.1 字节流写数据的两个小问题
  • 问题一:字节流写数据如何实现换行

    • 用换行符实现,不同系统换行符不同,具体如下
    • windows:\r\n
    • linux:\n
    • mac:\r
  • 问题二:字节流写数据如何实现追加写入(一次运行内多次写入)

    • public FileOutputStream(String name,boolean append)
    • 创建文件输出流以指定的名称写入文件。如果第二个参数为true ,则字节将写入文件的末尾而不是开头
  • 示例代码

          //创建字节输出流对象
  //        FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
          FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt",true);
  
          //写数据
          for (int i = 0; i < 10; i++) {
              fos.write("hello".getBytes());
              fos.write("\r\n".getBytes());
          }
  
          //释放资源
          fos.close();
2.2.1 字节流写数据加异常处理
  • 异常处理格式

    • try-catch-finally

      try{
      	可能出现异常的代码;
      }catch(异常类名 变量名){
      	异常的处理代码;
      }finally{
      	执行所有清除操作;
      }
      
    • finally特点

      • 被finally控制的语句一定会执行,除非JVM退出
  • 示例代码

             //若出现异常异常后的代码就不会执行,但是IO流必须要释放资源
            //加入finally来实现释放资源
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream("myByteStream\\fos.txt");
                fos.write("hello".getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
    

2.2 字节流读数据

2.2.1 一次读一个字节数
  • 字节输入流

    • FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream ,该文件由文件系统中的路径名name命名
  • 字节输入流读取数据的步骤

    • 创建字节输入流对象
    • 调用字节输入流对象的读数据方法
    • 释放资源
  • 示例代码

    public class FileInputStreamDemo01 {
        public static void main(String[] args) throws IOException {
            //创建字节输入流对象
            //FileInputStream(String name)
            FileInputStream fis = new FileInputStream("myByteStream\\fos.txt");
    
            int by;
            /*
                fis.read():读数据
                by=fis.read():把读取到的数据赋值给by
                by != -1:判断读取到的数据是否是-1
             */
            while ((by=fis.read())!=-1) {
                System.out.print((char)by);
            }
    
            //释放资源
            fis.close();
        }
    }
    
2.2.1 一次读一个字节数组
  • 一次读一个字节数组的方法

    • public int read(byte[] b):从输入流读取最多b.length个字节的数据
    • 返回的是读入缓冲区的总字节数,也就是实际的读取字节个数
  • 示例代码

    public class FileInputStreamDemo02 {
        public static void main(String[] args) throws IOException {
            //创建字节输入流对象
            FileInputStream fis = new FileInputStream("myByteStream\\fos.txt");
    
            /*
                hello\r\n
                world\r\n
    
                第一次:hello
                第二次:\r\nwor
                第三次:ld\r\nr
    
             */
    
            byte[] bys = new byte[1024]; //1024及其整数倍
            int len;
            while ((len=fis.read(bys))!=-1) {
                System.out.print(new String(bys,0,len));
            }
    
            //释放资源
            fis.close();
        }
    }
    

2.3 字节缓冲流

2.3.1字节缓冲流概述
  • 字节缓冲流介绍

    • lBufferOutputStream:该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用

    • lBufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节

2.3.2字节缓冲流概述构造方法
方法名说明
BufferedOutputStream(OutputStream out)创建字节缓冲输出流对象
BufferedInputStream(InputStream in)创建字节缓冲输入流对象
  • 示例代码

    public class BufferStreamDemo {
        public static void main(String[] args) throws IOException {
            //字节缓冲输出流:BufferedOutputStream(OutputStream out)
     
            BufferedOutputStream bos = new BufferedOutputStream(new    FileOutputStream("myByteStream\\bos.txt"));
            //写数据
            bos.write("hello\r\n".getBytes());
            bos.write("world\r\n".getBytes());
            //释放资源
            bos.close();
        
    
            //字节缓冲输入流:BufferedInputStream(InputStream in)
            BufferedInputStream bis = new BufferedInputStream(new  FileInputStream("myByteStream\\bos.txt"));
    
            //一次读取一个字节数据
    //        int by;
    //        while ((by=bis.read())!=-1) {
    //            System.out.print((char)by);
    //        }
    
            //一次读取一个字节数组数据
            byte[] bys = new byte[1024];
            int len;
            while ((len=bis.read(bys))!=-1) {
                System.out.print(new String(bys,0,len));
            }
    
            //释放资源
            bis.close();
        }
    }
    

3. 字符流

3.1 字符流概述

3.1.1为什么会出现字符流
  • 字符流的介绍

    由于字节流操作中文不是特别的方便,一个中文三个字节,但读取的时候是一个字节一个字节读取的,会出现乱码,所以Java就提供字符流

    字符流 = 字节流 + 编码表

  • 中文的字节存储方式

    用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?

    汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数

3.1.2编码表
  • 什么是字符集

    是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等

    l计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等

  • 常见的字符集

    • ASCII字符集:

      lASCII:是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)

      基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等

    • GBXXX字符集:

      GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等

    • Unicode字符集:

      UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码

      编码规则:

      128个US-ASCII字符,只需一个字节编码

      拉丁文等字符,需要二个字节编码

      大部分常用字(含中文),使用三个字节编码

      其他极少使用的Unicode辅助字符,使用四字节编码

3.2 字符串中的编码解码问题

3.2.1 相关方法
方法名说明
byte[] getBytes()使用平台的默认字符集将该 String编码为一系列字节
byte[] getBytes(String charsetName)使用指定的字符集将该 String编码为一系列字节
String(byte[] bytes)使用平台的默认字符集解码指定的字节数组来创建字符串
String(byte[] bytes, String charsetName)通过指定的字符集解码指定的字节数组来创建字符串
  • 代码演示

    public class StringDemo {
        public static void main(String[] args) throws UnsupportedEncodingException {
            //定义一个字符串
            String s = "中国";
    
            //byte[] bys = s.getBytes(); //[-28, -72, -83, -27, -101, -67]
            //byte[] bys = s.getBytes("UTF-8"); //[-28, -72, -83, -27, -101, -67]
            byte[] bys = s.getBytes("GBK"); //[-42, -48, -71, -6]
            System.out.println(Arrays.toString(bys));
    
            //String ss = new String(bys);
            //String ss = new String(bys,"UTF-8");
            String ss = new String(bys,"GBK");
            System.out.println(ss);
        }
    }
    
3.2.2 字符流中和编码解码问题相关的两个类
  • InputStreamReader:是从字节流到字符流的桥梁

    ​ 它读取字节,并使用指定的编码将其解码为字符

    ​ 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

  • OutputStreamWriter:是从字符流到字节流的桥梁

    ​ 是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节

    ​ 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

  • 构造方法

    方法名说明
    InputStreamReader(InputStream in)使用默认字符编码创建InputStreamReader对象
    InputStreamReader(InputStream in,String chatset)使用指定的字符编码创建InputStreamReader对象
    OutputStreamWriter(OutputStream out)使用默认字符编码创建OutputStreamWriter对象
    OutputStreamWriter(OutputStream out,String charset)使用指定的字符编码创建OutputStreamWriter对象
  • 代码演示

    public class ConversionStreamDemo {
        public static void main(String[] args) throws IOException {
            //OutputStreamWriter osw = new OutputStreamWriter(new                                             FileOutputStream("myCharStream\\osw.txt"));
            OutputStreamWriter osw = new OutputStreamWriter(new                                              FileOutputStream("myCharStream\\osw.txt"),"GBK");
            osw.write("中国");
            osw.close();
    
            //InputStreamReader isr = new InputStreamReader(new 	                                         FileInputStream("myCharStream\\osw.txt"));
            InputStreamReader isr = new InputStreamReader(new                                                 FileInputStream("myCharStream\\osw.txt"),"GBK");
            //一次读取一个字符数据
            int ch;
            while ((ch=isr.read())!=-1) {
                System.out.print((char)ch);
            }
            isr.close();
        }
    }
    

3.3 字符流写数据的5种方式

  • 方法介绍

    方法名说明
    void write(int c)写一个字符
    void write(char[] cbuf)写入一个字符数组
    void write(char[] cbuf, int off, int len)写入字符数组的一部分
    void write(String str)写一个字符串
    void write(String str, int off, int len)写一个字符串的一部分
  • 刷新和关闭的方法

    方法名说明
    flush()刷新流,调用写方法后必须冲刷一下才能真正写入文件,之后还可以继续写数据
    close()关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
  • 代码演示

    public class OutputStreamWriterDemo {
        public static void main(String[] args) throws IOException {
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"));
    
            //void write(int c):写一个字符
    //        osw.write(97);
    //        osw.write(98);
    //        osw.write(99);
    
            //void writ(char[] cbuf):写入一个字符数组
            char[] chs = {'a', 'b', 'c', 'd', 'e'};
    //        osw.write(chs);
    
            //void write(char[] cbuf, int off, int len):写入字符数组的一部分
    //        osw.write(chs, 0, chs.length);
    //        osw.write(chs, 1, 3);
    
            //void write(String str):写一个字符串
    //        osw.write("abcde");
    
            //void write(String str, int off, int len):写一个字符串的一部分
    //        osw.write("abcde", 0, "abcde".length());
            osw.write("abcde", 1, 3);
    
            //释放资源
            osw.close();
        }
    }
    

3.4 字符流读数据的2种方式

  • 方法介绍

    方法名说明
    int read()一次读一个字符数据
    int read(char[] cbuf)一次读一个字符数组数据
  • 代码演示

    public class InputStreamReaderDemo {
        public static void main(String[] args) throws IOException {
       
            InputStreamReader isr = new InputStreamReader(new FileInputStream("myCharStream\\ConversionStreamDemo.java"));
    
            //int read():一次读一个字符数据
    //        int ch;
    //        while ((ch=isr.read())!=-1) {
    //            System.out.print((char)ch);
    //        }
    
            //int read(char[] cbuf):一次读一个字符数组数据
            char[] chs = new char[1024];
            int len;
            while ((len = isr.read(chs)) != -1) {
                System.out.print(new String(chs, 0, len));
            }
    
            //释放资源
            isr.close();
        }
    }
    

3.5 字符缓冲流

3.5.1 字符缓冲流介绍与构造方法
  • BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途

  • BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途

  • 构造方法

    方法名说明
    BufferedWriter(Writer out)创建字符缓冲输出流对象
    BufferedReader(Reader in)创建字符缓冲输入流对象
  • 代码演示

    public class BufferedStreamDemo01 {
        public static void main(String[] args) throws IOException {
            //BufferedWriter(Writer out)
            BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\bw.txt"));
            bw.write("hello\r\n");
            bw.write("world\r\n");
            bw.close();
    
            //BufferedReader(Reader in)
            BufferedReader br = new BufferedReader(new  FileReader("myCharStream\\bw.txt"));
    
            //一次读取一个字符数据
    //        int ch;
    //        while ((ch=br.read())!=-1) {
    //            System.out.print((char)ch);
    //        }
    
            //一次读取一个字符数组数据
            char[] chs = new char[1024];
            int len;
            while ((len=br.read(chs))!=-1) {
                System.out.print(new String(chs,0,len));
            }
    
            br.close();
        }
    }
    
3.5.2 字符缓冲流特有功能【应用】
  • 方法介绍

    BufferedWriter:

    方法名说明
    void newLine()写一行行分隔符,行分隔符字符串由系统属性定义

    BufferedReader:

    方法名说明
    String readLine()读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符如果流的结尾已经到达,则为null
  • 代码演示

    public class BufferedStreamDemo02 {
        public static void main(String[] args) throws IOException {
    
            //创建字符缓冲输出流
            BufferedWriter bw = new BufferedWriter(new                                                          FileWriter("myCharStream\\bw.txt"));
    
            //写数据
            for (int i = 0; i < 10; i++) {
                bw.write("hello" + i);
                //bw.write("\r\n");
                bw.newLine();
                bw.flush();
            }
    
            //释放资源
            bw.close();
    
            //创建字符缓冲输入流
            BufferedReader br = new BufferedReader(new                                                          FileReader("myCharStream\\bw.txt"));
    
            String line;
            while ((line=br.readLine())!=null) {
                System.out.println(line);
            }
    
            br.close();
        }
    }
    

4. 特殊字符流

4.1 标准输入输出流

4.1.1标准输入输出流概述
  • System类中有两个静态的成员变量

    • public static final InputStream in:标准输入流。通常该流对应于键盘输入或由主机环境或用户指定的另一个输入源【键盘输入】
    • public static final PrintStream out:标准输出流。通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标
4.1.2 标准输入流代码案例
  //       public static final InputStream in:标准输入流
  // 注解:final修饰说明是常量;static修饰说明可以直接用类名访问
           InputStream is = System.in;
  //注解 :这里是多态形式完成初始化,InputStream是字节输入流的抽象基类
  
  //需求1:一次读一个字节
          int by;
          while ((by=is.read())!=-1) {
              System.out.print((char)by);
          }
  
  //      需求1代码出现问题:读汉字不方便,因为一个汉字采用UTF-8编码,是由三个字节组成的,但需求1代码是没读到一个字节就把它强制转化为字符。会出现中文乱码问题。
  
  //      解决办法:如何把字节流转换为字符流?  用转换流!!!
         InputStreamReader isr = new InputStreamReader(is);
          //使用字符流能不能够实现一次读取一行数据呢?可以
          //但是,一次读取一行数据的方法是字符缓冲输入流的特有方法
          BufferedReader br = new BufferedReader(isr);
  
  // 最终根本读数据的是 is,字节流,通过转换流变为字符流,通过字符缓冲流包装成字符缓冲输入流
  
  //       上面三个步骤的简洁版:如下
          BufferedReader br = new BufferedReader(new InputSeamReader(System.in));
  
          System.out.println("请输入一个字符串:");
          String line = br.readLine();//接受变量是String类型
          System.out.println("你输入的字符串是:" + line);
  
          System.out.println("请输入一个整数:");
          int i = Integer.parseInt(br.readLine());//接受变量是int类型
          System.out.println("你输入的整数是:" + i);
  
          //综上,自己实现键盘录入数据太麻烦了,所以Java就提供了一个类供我们使用,包装System.in,Scanner类有很多便捷的方法提供给我们使用
          Scanner sc = new Scanner(System.in);
      }
  }
4.1.3 标准输出流代码案例
  • 输出语句的本质:是一个标准的输出流

    • PrintStream ps = System.out;
    • PrintStream类有的方法,System.out都可以使用
  • 示例代码

            //public static final PrintStream out:标准输出流
            //PrintStream是一个具体的类
            PrintStream ps = System.out;
    
            //能够方便地打印各种数据值
    //        ps.print("hello");
    //        ps.print(100);
    
    //   println方法输出内容到控制台并且换行
    //        ps.println("hello");
    //        ps.println(100);
    
            //System.out的本质是一个字节输出流
            System.out.println("hello");
            System.out.println(100);
    
            System.out.println();
    //        System.out.print();
    
    

4.2 打印流

4.2.1 打印流概述
  • 打印流分类

    • 字节打印流:PrintStream
    • 字符打印流:PrintWriter
  • 打印流的特点

    • 只负责输出数据,不负责读取数据
    • 永远不会抛出IOException
    • 有自己的特有方法
4.2.1 字节打印流
  • 字节打印流:PrintStream,继承字节输出流OutputStream

  • PrintStream(String fileName):构造方法,使用指定的文件名创建新的打印流

  • 使用继承父类的方法写数据,查看的时候会转码;使用自己的特有方法写数据,查看的数据原样输出

  • 可以改变输出语句的目的地

    ​ public static void setOut(PrintStream out):重新分配“标准”输出流

  • 示例代码

    
            //PrintStream(String fileName):使用指定的文件名创建新的打印流
            PrintStream ps = new PrintStream("myOtherStream\\ps.txt");
    
            //写数据
            //字节输出流有的方法
    //        ps.write(97);
    
            //使用特有方法写数据
    //        ps.print(97);//不换行
    //        ps.println();
    //        ps.print(98);
            ps.println(97);//换行
            ps.println(98);
            
            //释放资源
            ps.close();
    
4.2.3 字符打印流
  • 字符打印流PrintWriter,继承字符输出流Writer

  • 字符打印流构造方法

    方法名说明
    PrintWriter(String fileName)使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新
    PrintWriter(Writer out, boolean autoFlush)创建一个新的PrintWriter out:字符输出流 autoFlush: 一个布尔值,如果为真,则println , printf ,或format方法将刷新输出缓冲区
  • 示例代码

            //PrintWriter(String fileName) :使用指定的文件名创建一个新的PrintWriter,而不需要自动执行行刷新
            PrintWriter pw = new PrintWriter("myOtherStream\\pw.txt");
    
            pw.write("hello");
            pw.write("\r\n");    //换行
            pw.flush();
    
    
    //        pw.println("hello");
            /*相当于下面两步动作
                pw.write("hello");
                pw.write("\r\n");
             */
    //        pw.flush();
    
    
            pw.println("world");
            pw.flush();
    
           //需求能否能不自动flush呢
            //PrintWriter(Writer out, boolean autoFlush):创建一个新的PrintWriter
            PrintWriter pw = new PrintWriter(new FileWriter("myOtherStream\\pw.txt"),true);
    //        PrintWriter pw = new PrintWriter(new FileWriter("myOtherStream\\pw.txt"),false);
    
            pw.println("hello");
            /*相当于下面三步动作
                pw.write("hello");
                pw.write("\r\n");
                pw.flush();
             */
            pw.println("world");
    
            pw.close();
        }
    }
    

4.4 对象序列化流

4.4.1 对象序列化流概述
  • 对象序列化介绍

    • 对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
    • 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息
    • 字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
    • 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
4.5 对象序列化流
  • 对象序列化流: ObjectOutputStream

    • 将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象
    • 对象序列化流和对象反序列化流类似于编码和解码的过程,也是一种输入输出流,包装的对象是类对象
  • 构造方法

    方法名说明
    ObjectOutputStream(OutputStream out)创建一个写入指定的OutputStream的ObjectOutputStream
  • 序列化对象的方法

    方法名说明
    void writeObject(Object obj)将指定的对象写入ObjectOutputStream
  • 示例代码

    • 学生类

      public class Student implements Serializable {
      //实现Serializable接口,里面没有方法需要重写
          private String name;
          private int age;
      
          public Student() {
          }
      
          public Student(String name, int age) {
              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 "Student{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      '}';
          }
      }
      
    • 测试类

      public class ObjectOutputStreamDemo {
          public static void main(String[] args) throws IOException {
              //ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
              ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));
      
              //创建对象
              Student s = new Student("林青霞",30);
      
              //void writeObject(Object obj):将指定的对象写入ObjectOutputStream
              oos.writeObject(s);
      
              //释放资源
              oos.close();
          }
      }
      
  • 注意事项

    • 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口,不然会报错。NotSerializableException
    • Serializable是一个标记接口,实现该接口,不需要重写任何方法
4.5 对象反序列化流
  • 对象反序列化流: ObjectInputStream

    • ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
  • 构造方法

    方法名说明
    ObjectInputStream(InputStream in)创建从指定的InputStream读取的ObjectInputStream
  • 反序列化对象的方法

    方法名说明
    Object readObject()从ObjectInputStream读取一个对象
  • 示例代码

    public class ObjectInputStreamDemo {
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            //ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream,该文件是ObjectOutputStream写的文件
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));
    
            //Object readObject():从ObjectInputStream读取一个对象
            Object obj = ois.readObject();
    
            Student s = (Student) obj;
            System.out.println(s.getName() + "," + s.getAge());
    
            ois.close();
        }
    }
    
4.5 serialVersionUID&transient
  • 对象序列化流可能产生的三个问题

    • 用对象序列化流序列化一个对象后,假设我们修改了对象所属的类文件,读数据会不会出现问题呢?
    • 如果出问题了,如何解决呢?
    • 如果一个对象中的某个成员变量的值不想被序列化,又如何实现呢?
  • serialVersionUID

    • 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
      • 会出问题,因为要被序列化流的类继承了Serializable接口,若修改类,类的版本serialVersionUID变了,前后两者不匹配,会抛出InvalidClassException异常
    • 如果出问题了,如何解决呢?
      • 重新序列化
      • 给对象所属的类加一个serialVersionUID
        • private static final long serialVersionUID = 42L;
  • transient

    • 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
      • 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
  • 示例代码

    • 学生类

      public class Student implements Serializable {
          private static final long serialVersionUID = 42L;
          //解决序列化流问题,具体看开发文档serialVersionUID
          
          private String name;
      //    private int age;
          private transient int age;
      // transient关键字修饰,该关键字标记的成员变量不参与序列化过程
      
          public Student() {
          }
      
          public Student(String name, int age) {
              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 "Student{" +
      //                "name='" + name + '\'' +
      //                ", age=" + age +
      //                '}';
      //    }
      }
      
    • 测试类

      public class ObjectStreamDemo {
          public static void main(String[] args) throws IOException, ClassNotFoundException {
      //        write();
              read();
          }
      
          //反序列化
          private static void read() throws IOException, ClassNotFoundException {
              ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));
              Object obj = ois.readObject();
              Student s = (Student) obj;
              System.out.println(s.getName() + "," + s.getAge());
              ois.close();
          }
      
          //序列化
          private static void write() throws IOException {
              ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));
              Student s = new Student("林青霞", 30);
              oos.writeObject(s);
              oos.close();
          }
      }
      

4.6 Properties类

4.Properties类概述
  • Properties介绍

    • 是一个Map体系的集合类,继承Hashtable
    • Properties可以保存到流中或从流中加载
    • 属性列表中的每个键及其对应的值都是一个字符串
  • Properties作为map集合的基本使用

    public class PropertiesDemo01 {
        public static void main(String[] args) {
            //创建集合对象
    //        Properties<String,String> prop = new Properties<String,String>(); 
    //       上面代码错误,虽然是map集合但不能有泛型
            Properties prop = new Properties();
    
            //存储元素
            prop.put("itheima001", "林青霞");
            prop.put("itheima002", "张曼玉");
            prop.put("itheima003", "王祖贤");
    
            //遍历集合,按照map集合遍历
            Set<Object> keySet = prop.keySet();
            for (Object key : keySet) {
                Object value = prop.get(key);
                System.out.println(key + "," + value);
            }
        }
    }
    
作为集合的特有方法
方法名说明
Object setProperty(String key, String value)设置赋值集合的键和值,都是String类型,底层调用 Hashtable方法 put
String getProperty(String key)使用此属性列表中指定的键搜索属性
Set stringPropertyNames()从该属性列表中返回一个不可修改的键集,得到所有的键,键集合,可以用增强for循环遍历,其中键及其对应的值是字符串
  • 示例代码

    public class PropertiesDemo02 {
        public static void main(String[] args) {
            //创建集合对象
            Properties prop = new Properties();
    
            //方法1:Object setProperty(String key, String value):设置集合的键和值,都是String类型,底层调用Hashtable方法put
            prop.setProperty("itheima001", "林青霞");
            /*   源码:这种设计了解一下
                Object setProperty(String key, String value) {
                    return put(key, value);
                }
    
                Object put(Object key, Object value) {
                    return map.put(key, value);
                }
             */
            prop.setProperty("itheima002", "张曼玉");
            prop.setProperty("itheima003", "王祖贤");
    
            //方法2:String getProperty(String key):使用此属性列表中指定的键搜索属性
    //        System.out.println(prop.getProperty("itheima001"));
    //        System.out.println(prop.getProperty("itheima0011")); //找不到,返回null
    
    //        System.out.println(prop);
    
            //方法3:Set<String> stringPropertyNames():从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
            Set<String> names = prop.stringPropertyNames();
            for (String key : names) {
    //            System.out.println(key);
                String value = prop.getProperty(key);
                System.out.println(key + "," + value);
            }
        }
    }
    

3.3Properties和IO流相结合的方法【应用】

  • 和IO流结合的方法【字节流、字符流都可】

    方法名说明
    void load(InputStream inStream)从输入字节流读取属性列表(键和元素对)
    void load(Reader reader)从输入字符流读取属性列表(键和元素对)
    void store(OutputStream out, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流
    void store(Writer writer, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流
  • 示例代码

    public class PropertiesDemo03 {
        public static void main(String[] args) throws IOException {
            //把集合中的数据保存到文件
    //        myStore();
    
            //把文件中的数据加载到集合
            myLoad();
    
        }
        //把文件中的数据加载到集合
        private static void myLoad() throws IOException {
            Properties prop = new Properties();//创建集合对象,数据来自于文件
    
            //void load(Reader reader):
            FileReader fr = new FileReader("myOtherStream\\fw.txt");
            prop.load(fr);
            fr.close();
    
            System.out.println(prop);
        }
    
        private static void myStore() throws IOException {
            Properties prop = new Properties();//集合
    
            prop.setProperty("itheima001","林青霞");
            prop.setProperty("itheima002","张曼玉");
            prop.setProperty("itheima003","王祖贤");
    
    //       集合中的数据保存到文件
            //void store(Writer writer, String comments):
            FileWriter fw = new FileWriter("myOtherStream\\fw.txt");
            prop.store(fw,null);//通过集合调用store方法,第二个变量是描述信息,没有就null
            fw.close();
        }
    }
    

4. IO练习案例

4.1 图片加密

/*
 * 加密操作
 * 解密操作
 *
 * 原理:一个数据异或两次,数据不变
 * */
public class Case01 {
    public static void main(String[] args) throws IOException {

//        1、输入输出流
        FileInputStream fis = new FileInputStream("C:\\Users\\19605\\Desktop\\p.jpg");
        FileOutputStream fio = new FileOutputStream("C:\\Users\\19605\\Desktop\\p2.jpg");

        //        解密的时候只需把p2再异或一遍,写到新的文件
//        FileInputStream fis = new FileInputStream("C:\\Users\\19605\\Desktop\\p2.jpg");
//        FileOutputStream fio = new FileOutputStream("C:\\Users\\19605\\Desktop\\p3.jpg");
//          2、读写数据

        int b = 0;
        while ((b = fis.read()) != -1) {
            fio.write(b ^ 5);
            System.out.print(b);
        }

        fis.close();
        fio.close();

        System.out.println("图像加密ok");
 
    }
}

4.2 根据用户输入文件路径拷贝文件到当前项目目录

public class Demo01 {
    public static void main(String[] args) throws IOException {
//根据用户输入文件[图片]拷贝文件到当前目录

//1.获取用户输入的文件路径
        System.out.println("请输入文件路径名:");
        Scanner scanner = new Scanner(System.in);
        String spath = scanner.nextLine();
        File file = new File(spath);

//2.判断这个路径是否为文件
        if (file.isDirectory()) {
            System.out.println("您输入的不是一个文件!");
            return;
        }
//3.如果是文件就拷贝
//3.1 输入流
        FileInputStream fio = new FileInputStream(file);
//3.2 输出流
        FileOutputStream fos = new FileOutputStream(file.getName());
        System.out.println(file.getName());
//3.3 读写【性能】
        byte[] buf = new byte[8 * 1024];
        int len = 0;
        while ((len = fio.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
//3.4 关流
        fio.close();
        fos.close();

        System.out.println("文件拷贝完成...");
    }
}

4.3 录入数据拷贝到文件

public class Demo02 {
    public static void main(String[] args) throws IOException {
        //练习:录入数据拷贝到文件
        /*
         *根据用户在键盘中输入内容,然后写到一个文件中去
         *
         */
        //1、创建一个文件输出流
        FileOutputStream fos = new FileOutputStream("new.txt");

        //2、创建一个Scanner对象
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入需要录入内容...输入quit代表退出");

        //3、通过死循环来获取用户输入【当输入quit,代表不录入】
        while (true) {
            //获取用户输入,一次读取一行
            String line = sc.nextLine();
            //退出循环
            if (line.equals("quit")) {
                break;
            }
            //写入文件
            fos.write(line.getBytes());
            //换行
            fos.write("\r\n".getBytes());
        }
        //4、关流
        fos.close();

    }
}

4.4 复制图片、文本文件

public class Demo03 {
    public static void main(String[] args)throws IOException {
        try {
            copyP();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            copyTxt();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //拷贝文件
    public static void copyTxt() throws IOException {
        FileInputStream fio = new FileInputStream("new.txt");
        FileOutputStream fos = new FileOutputStream("new2.txt");

        int by;
        while ((by = fio.read()) != -1) {
            fos.write(by);
        }

        fos.close();
        System.out.println("拷贝文件成功");

    }

    //拷贝图片
    public static void copyP() throws IOException {

        FileInputStream fio = new FileInputStream("G:\\1.png");
        FileOutputStream fos = new FileOutputStream("copy.png");

        //读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)
        byte[] bys=new byte[1024];
        int len;
        while ((len = fio.read(bys)) != -1) {
            fos.write(bys,0,len);
        }
        //释放资源
        fos.close();
        System.out.println("拷贝图片成功");

    }
}

4.5 复制视频(4种方式)

/*
    需求:
        把E:\\1.avi 复制到模块目录下的 1.avi

    思路:
        1:根据数据源创建字节输入流对象
        2:根据目的地创建字节输出流对象
        3:读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)
        4:释放资源

    四种方式实现复制视频,并记录每种方式复制视频的时间
        1:基本字节流一次读写一个字节             共耗时:64565毫秒
        2:基本字节流一次读写一个字节数组          共耗时:107毫秒
        3:字节缓冲流一次读写一个字节             共耗时:405毫秒
        4:字节缓冲流一次读写一个字节数组          共耗时:60毫秒
 */
public class CopyAviDemo {
    public static void main(String[] args) throws IOException {
        //记录开始时间
        long startTime = System.currentTimeMillis();

        //复制视频
//        method1();
//        method2();
//        method3();
        method4();

        //记录结束时间
        long endTime = System.currentTimeMillis();
        System.out.println("共耗时:" + (endTime - startTime) + "毫秒");
    }

    //字节缓冲流一次读写一个字节数组
    public static void method4() throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\1.avi"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\1.avi"));

        byte[] bys = new byte[1024];
        int len;
        while ((len=bis.read(bys))!=-1) {
            bos.write(bys,0,len);
        }

        bos.close();
        bis.close();
    }

    //字节缓冲流一次读写一个字节
    public static void method3() throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\1.avi"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\1.avi"));

        int by;
        while ((by=bis.read())!=-1) {
            bos.write(by);
        }

        bos.close();
        bis.close();
    }


    //基本字节流一次读写一个字节数组
    public static void method2() throws IOException {
        //E:\\itcast\\字节流复制图片.avi
        //模块目录下的 字节流复制图片.avi
        FileInputStream fis = new FileInputStream("E:\\1.avi");
        FileOutputStream fos = new FileOutputStream("myByteStream\\1.avi");

        byte[] bys = new byte[1024];
        int len;
        while ((len=fis.read(bys))!=-1) {
            fos.write(bys,0,len);
        }

        fos.close();
        fis.close();
    }

    //基本字节流一次读写一个字节
    public static void method1() throws IOException {
        //E:\\itcast\\字节流复制图片.avi
        //模块目录下的 字节流复制图片.avi
        FileInputStream fis = new FileInputStream("E:\\1.avi");
        FileOutputStream fos = new FileOutputStream("myByteStream\\1.avi");

        int by;
        while ((by=fis.read())!=-1) {
            fos.write(by);
        }

        fos.close();
        fis.close();
    }
}

4.6 集合到文件(3种方式)

class ArrayListToTxtDemo {
    public static void main(String[] args) throws IOException {

        Test1();
        Test2();
        Test3();
    }

    //方式一:String+ArrayList集合+字符缓冲流

    public static void Test1() throws IOException {
/*

    需求:
        把ArrayList集合中的字符串数据写入到文本文件。要求:每一个字符串元素作为文件中的一行数据
    思路:
        1:创建ArrayList集合
        2:往集合中存储字符串元素
        3:创建字符缓冲输出流对象
        4:遍历集合,得到每一个字符串数据
        5:调用字符缓冲输出流对象的方法写数据
        6:释放资源
*/

        //创建ArrayList集合
        ArrayList<String> array = new ArrayList<String>();

        //往集合中存储字符串元素
        array.add("hello");
        array.add("world");
        array.add("java");

        //创建字符缓冲输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("project01\\array.txt"));

        //遍历集合,得到每一个字符串数据
        for (String s : array) {
            //调用字符缓冲输出流对象的方法写数据
            bw.write(s);
            bw.newLine();
            bw.flush();
        }

        //释放资源
        bw.close();

    }

    //方式二:Student类+StringBuilder+ArrayList集合+字符缓冲流

    /*
    需求:
        把ArrayList集合中的学生数据写入到文本文件。要求:每一个学生对象的数据作为文件中的一行数据
        格式:学号,姓名,年龄,居住地	举例:201707060330,阿隆,22,济南

    思路:
        1:定义学生类
        2:创建ArrayList集合
        3:创建学生对象
        4:把学生对象添加到集合中
        5:创建字符缓冲输出流对象
        6:遍历集合,得到每一个学生对象
        7:把学生对象的数据拼接成指定格式的字符串
        8:调用字符缓冲输出流对象的方法写数据
        9:释放资源
 */


    static class Student {
        private String sid;
        private String name;
        private int age;
        private String address;

        // 语文成绩
        private int chinese;
        // 数学成绩
        private int math;
        // 英语成绩
        private int english;

        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;
        }


        public Student(String name, int chinese, int math, int english) {
            super();
            this.name = name;
            this.chinese = chinese;
            this.math = math;
            this.english = english;
        }


        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 int getSum() {
            return this.chinese + this.math + this.english;
        }

    }


    public static void Test2() throws IOException {
        //创建ArrayList集合
        ArrayList<Student> array = new ArrayList<Student>();

        //创建学生对象
        Student s1 = new Student("2000", "aa", 30, "青岛");
        Student s2 = new Student("2001", "ss", 35, "济南");
        Student s3 = new Student("2002", "dd", 33, "山东");

        //把学生对象添加到集合中
        array.add(s1);
        array.add(s2);
        array.add(s3);

        //创建字符缓冲输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("project01\\array2.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();
    }


    //方式三:

    /*
    需求:键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩)。要求按照成绩总分从高到低写入文本文件
          格式:姓名,语文成绩,数学成绩,英语成绩	举例:林青霞,98,99,100

    思路:
        1:定义学生类
        2:创建TreeSet集合,通过比较器排序进行排序
        3:键盘录入学生数据
        4:创建学生对象,把键盘录入的数据对应赋值给学生对象的成员变量
        5:把学生对象添加到TreeSet集合
        6:创建字符缓冲输出流对象
        7:遍历集合,得到每一个学生对象
        8:把学生对象的数据拼接成指定格式的字符串
        9:调用字符缓冲输出流对象的方法写数据
        10:释放资源
 */
    public static void Test3() throws IOException {
//创建TreeSet集合,通过比较器排序进行排序
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student 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;
            }
        });

        //键盘录入学生数据
        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();

            //创建学生对象,把键盘录入的数据对应赋值给学生对象的成员变量
            Student s = new Student();
            s.setName(name);
            s.setChinese(chinese);
            s.setMath(math);
            s.setEnglish(english);

            //把学生对象添加到TreeSet集合
            ts.add(s);
        }

        //创建字符缓冲输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("project01\\array3.txt"));

        //遍历集合,得到每一个学生对象
        for (Student 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();
    }
}

4.7 文件到集合

/*
    需求:把文本文件中的数据读取到集合中,并遍历集合。要求:文件中每一行数据是一个学生对象的成员变量值
          举例:2222,zcl,30,济南

    思路:
        1:定义学生类
        2:创建字符缓冲输入流对象
        3:创建ArrayList集合对象
        4:调用字符缓冲输入流对象的方法读数据
        5:把读取到的字符串数据用split()进行分割,得到一个字符串数组
        6:创建学生对象
        7:把字符串数组中的每一个元素取出来对应的赋值给学生对象的成员变量值
        8:把学生对象添加到集合
        9:释放资源
        10:遍历集合
 */
public class FileToArrayListDemo {
    public static void main(String[] args) throws IOException {
        //创建字符缓冲输入流对象
        BufferedReader br = new BufferedReader(new FileReader("project01\\students.txt"));

        //创建ArrayList集合对象
        ArrayList<Student> array = new ArrayList<Student>();

        //调用字符缓冲输入流对象的方法读数据
        String line;
        while ((line = br.readLine()) != null) {
            //把读取到的字符串数据用split()进行分割,得到一个字符串数组
            String[] strArray = line.split(",");

            //创建学生对象
            Student s = new Student();
            //把字符串数组中的每一个元素取出来对应的赋值给学生对象的成员变量值
            //itheima001,林青霞,30,西安
            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());
        }
    }
}

4.8 点名器【Random、字符缓冲流】

  • 文件(names.txt)已经存在,在当前目录下, 里面有名字数据,一行一个名字。
/*
    需求:
        我有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随点名器

    思路:
        1:创建字符缓冲输入流对象
        2:创建ArrayList集合对象
        3:调用字符缓冲输入流对象的方法读数据
        4:把读取到的字符串数据存储到集合中
        5:释放资源
        6:使用Random产生一个随机数,随机数的范围在:[0,集合的长度)
        7:把第6步产生的随机数作为索引到ArrayList集合中获取值
        8:把第7步得到的数据输出在控制台
 */
public class CallNameDemo {
    public static void main(String[] args) throws IOException {
        //创建字符缓冲输入流对象
        BufferedReader br = new BufferedReader(new FileReader("project01\\names.txt"));

        //创建ArrayList集合对象
        ArrayList<String> array = new ArrayList<String>();

        //调用字符缓冲输入流对象的方法读数据
        String line;
        while ((line=br.readLine())!=null) {
            //把读取到的字符串数据存储到集合中
            array.add(line);
        }

        //释放资源
        br.close();

        //使用Random产生一个随机数,随机数的范围在:[0,集合的长度)
        Random r = new Random();
        int index = r.nextInt(array.size());

        //把第6步产生的随机数作为索引到ArrayList集合中获取值
        String name = array.get(index);

        //把第7步得到的数据输出在控制台
        System.out.println("幸运者是:" + name);
    }
}

4.9 复制单级文件夹【File、字节流】

  • 单级文件夹:该文件夹里面只有文件没有文件夹
/*
    需求:
        把E:\\itcast 这个文件夹复制到模块目录下

    思路:
        1:创建数据源目录File对象,路径是E:\\itcast
        2:获取数据源目录File对象的名称(itcast)
        3:创建目的地目录File对象,路径名是模块名+itcast组成(myCharStream\\itcast)
        4:判断目的地目录对应的File是否存在,如果不存在,就创建
        5:获取数据源目录下所有文件的File数组
        6:遍历File数组,得到每一个File对象,该File对象,其实就是数据源文件
            数据源文件:E:\\itcast\\mn.jpg
        7:获取数据源文件File对象的名称(mn.jpg)
        8:创建目的地文件File对象,路径名是目的地目录+mn.jpg组成(myCharStream\\itcast\\mn.jpg)
        9:复制文件
            由于文件不仅仅是文本文件,还有图片,视频等文件,所以采用字节流复制文件
 */
public class CopyFolderDemo {
    public static void main(String[] args) throws IOException {
        //创建数据源目录File对象,路径是E:\\itcast
        File srcFolder = new File("E:\\itcast");

        //获取数据源目录File对象的名称(itcast)
        String srcFolderName = srcFolder.getName();

        //创建目的地目录File对象,路径名是模块名+itcast组成(myCharStream\\itcast)
        File destFolder = new File("project",srcFolderName);

        //判断目的地目录对应的File是否存在,如果不存在,就创建
        if(!destFolder.exists()) {
            destFolder.mkdir();
        }

        //获取数据源目录下所有文件的File数组
        File[] listFiles = srcFolder.listFiles();

        //遍历File数组,得到每一个File对象,该File对象,其实就是数据源文件
        for(File srcFile : listFiles) {
            //数据源文件:E:\\itcast\\mn.jpg
            //获取数据源文件File对象的名称(mn.jpg)
            String srcFileName = srcFile.getName();
            //创建目的地文件File对象,路径名是目的地目录+mn.jpg组成(myCharStream\\itcast\\mn.jpg)
            File destFile = new File(destFolder,srcFileName);
            //复制文件
            copyFile(srcFile,destFile);
        }
    }

    private static void copyFile(File srcFile, File destFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));

        byte[] bys = new byte[1024];
        int len;
        while ((len=bis.read(bys))!=-1) {
            bos.write(bys,0,len);
        }

        bos.close();
        bis.close();
    }
}

4.10 复制多级文件夹【File、递归、字节流】

/*
    需求:
        把“E:\\itcast”复制到 F盘目录下

    思路:
        1:创建数据源File对象,路径是E:\\itcast
        2:创建目的地File对象,路径是F:\\
        3:写方法实现文件夹的复制,参数为数据源File对象和目的地File对象
        4:判断数据源File是否是目录
            是:
                A:在目的地下创建和数据源File名称一样的目录
                B:获取数据源File下所有文件或者目录的File数组
                C:遍历该File数组,得到每一个File对象
                D:把该File作为数据源File对象,递归调用复制文件夹的方法
            不是:说明是文件,直接复制,用字节流
 */
public class CopyFoldersDemo {
    public static void main(String[] args) throws IOException {
        //创建数据源File对象,路径是E:\\itcast
        File srcFile = new File("E:\\itcast");
        //创建目的地File对象,路径是F:\\
        File destFile = new File("F:\\");

        //写方法实现文件夹的复制,参数为数据源File对象和目的地File对象
        copyFolder(srcFile,destFile);
    }

    //复制文件夹
    private static void copyFolder(File srcFile, File destFile) throws IOException {
        //判断数据源File是否是目录
        if(srcFile.isDirectory()) {
            //在目的地下创建和数据源File名称一样的目录
            String srcFileName = srcFile.getName();
            File newFolder = new File(destFile,srcFileName); //F:\\itcast
            if(!newFolder.exists()) {
                newFolder.mkdir();
            }

            //获取数据源File下所有文件或者目录的File数组
            File[] fileArray = srcFile.listFiles();

            //遍历该File数组,得到每一个File对象
            for(File file : fileArray) {
                //把该File作为数据源File对象,递归调用复制文件夹的方法
                copyFolder(file,newFolder);
            }
        } else {
            //说明是文件,直接复制,用字节流
            File newFile = new File(destFile,srcFile.getName());
            copyFile(srcFile,newFile);
        }
    }

    //字节缓冲流复制文件
    private static void copyFile(File srcFile, File destFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));

        byte[] bys = new byte[1024];
        int len;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }

        bos.close();
        bis.close();
    }

}

4.11 获取文本上字符出现的次数

public class Demo01 {
    //获取文本上字符出现的次数,把数据写入文件
/*
     思路:
        1、遍历文本的每一个字符
        2、字符出现的次数存在Map中
        Map<Character,Integer> map=new HashMap<Character, Integer>() ;
        map.put('a',18);
        map.put('您',2);
        3、把map写入文件

*/
    public static void main(String[] args) throws IOException {
//      1、创建Map集合
        Map<Character,Integer> map=new HashMap<Character, Integer>() ;

//      2、遍历每一个字符,每一个字符出现的次数放到Map中
        FileReader reader=new FileReader("a.txt");
        int c=0;
        while ((c = reader.read()) != -1) {
            //int 换元 char
            char ch=(char)c;
            //判断char是否在map中第一次出现
            if(map.get(ch)==null){
                map.put(ch,1);
            }else{
                map.put(ch,map.get(ch)+1);
            }
//      3、把map中数据存在文件count.txt中
//          3.1 创建Writer
            BufferedWriter bw=new BufferedWriter(new FileWriter("count.txt"));

//          3.2 遍历map,写入数据
            for(Map.Entry<Character,Integer> entry:map.entrySet()){
                switch (entry.getKey()){
                    case' ':
                        bw.write("空格="+entry.getKey());
                        break;
                    case'\t':
                        bw.write("tab键="+entry.getKey());
                        break;
                    case'\r':
                        bw.write("回车="+entry.getKey());
                        break;
                    case'\n':
                        bw.write("换行="+entry.getKey());
                        break;
                    default:
                        bw.write(entry.getKey()+"="+entry.getValue());
                        break;
                }
                bw.newLine();
            }
//      4、关流
            reader.close();
            bw.close();
        }

    }
}

4.12 用户输入文件夹路径打印出该文件夹下所有的.jpg文件名

public class Demo02 {
    //从键盘输入接收一个文件夹路径,打印出该文件夹下所有的.jpg文件名

    public static void main(String[] args) {
//     1、获取文件路径
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个文件夹路径...");
        String path = scanner.nextLine();

//      2、把路径封装成File对象
        File file = new File(path);
        if (!file.exists()) {
            System.out.println("此路径文件夹不存在");
            return;
        }

        if (!file.isDirectory()) {
            System.out.println("请输入文件夹路径,而不是文件路径");
            return;
        }

//      3、遍历文件夹下的子文件
        printSubFile(file);
    }

    public static void printSubFile(File dir) {
//      打印目录子文件
        File[] subfiles = dir.listFiles();
        for (File f : subfiles) {
            if (f.isDirectory()) {
                printSubFile(f);
            } else {
                if (f.getName().endsWith((".jpg"))) {
                    System.out.println(f.getAbsoluteFile());
                }
            }
        }

    }
}

4.13 复制Java文件(4种方式)

//需求: 把模块目录下的ConversionStreamDemo.java 复制到模块目录下的 Copy.java
public class CopyJavaDemo {
    public static void main(String[] args) throws IOException {

    }

    //01初级版字符流实现复制Java文件

    /*    思路:
        1:根据数据源创建字符输入流对象
        2:根据目的地创建字符输出流对象
        3:读写数据,复制文件
        4:释放资源
 */
    private static void fun1() throws IOException {
        //根据数据源创建字符输入流对象
        InputStreamReader isr = new InputStreamReader(new FileInputStream("project01\\ConversionStreamDemo.java"));
        //根据目的地创建字符输出流对象
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("project01\\Copy.java"));

        //读写数据,复制文件
        //一次读写一个字符数据
//        int ch;
//        while ((ch=isr.read())!=-1) {
//            osw.write(ch);
//        }

        //一次读写一个字符数组数据
        char[] chs = new char[1024];
        int len;
        while ((len = isr.read(chs)) != -1) {
            osw.write(chs, 0, len);
        }

        //释放资源
        osw.close();
        isr.close();
    }

    //02升级版字符流子类【便捷类】实现复制Java文件
    //转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化书写,转换流提供了对应的子类
    //FileReader:用于读取字符文件的便捷类
    //FileWriter:用于写入字符文件的便捷类
/*    数据源和目的地的分析
    数据源:project01\\ConversionStreamDemo.java --- 读数据 --- Reader --- InputStreamReader --- FileReader
    目的地: project01\\ Copy.java --- 写数据 --- Writer(写数据用字符输出流,是抽象类) --- OutputStreamWriter(这个名字太长,应用转换流具体的直接子类) --- FileWriter
    //涉及到编码和解码问题还是要用转换流InputStreamReader和OutputStreamWriter
    思路:
        1:根据数据源创建字符输入流对象
        2:根据目的地创建字符输出流对象
        3:读写数据,复制文件
        4:释放资源
        */
    private static void fun2() throws IOException {
        //根据数据源创建字符输入流对象
        FileReader fr = new FileReader("project01\\ConversionStreamDemo.java");
        //根据目的地创建字符输出流对象
        FileWriter fw = new FileWriter("project01\\Copy.java");

        //读写数据,复制文件
//        int ch;
//        while ((ch=fr.read())!=-1) {
//            fw.write(ch);
//        }

        char[] chs = new char[1024];
        int len;
        while ((len = fr.read(chs)) != -1) {
            fw.write(chs, 0, len);
        }

        //释放资源
        fw.close();
        fr.close();
    }

    //03字符缓冲流实现复制Java文件
    /*
    思路:
        1:根据数据源创建字符缓冲输入流对象
        2:根据目的地创建字符缓冲输出流对象
        3:读写数据,复制文件
        4:释放资源
 */
    private static void fun3() throws IOException {
//根据数据源创建字符缓冲输入流对象
        BufferedReader br = new BufferedReader(new FileReader("project01\\ConversionStreamDemo.java"));
        //根据目的地创建字符缓冲输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("project01\\Copy.java"));

        //读写数据,复制文件
        //一次读写一个字符数据
//        int ch;
//        while ((ch=br.read())!=-1) {
//            bw.write(ch);
//        }

        //一次读写一个字符数组数据
        char[] chs = new char[1024];
        int len;
        while ((len = br.read(chs)) != -1) {
            bw.write(chs, 0, len);
        }

        //释放资源
        bw.close();
        br.close();
    }

    //04使用字符缓冲流特有功能实现复制Java文件
    private static void fun4() throws IOException {
//根据数据源创建字符缓冲输入流对象
        BufferedReader br = new BufferedReader(new FileReader("project01\\ConversionStreamDemo.java"));
        //根据目的地创建字符缓冲输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("project01\\Copy.java"));

        //读写数据,复制文件
        //使用字符缓冲流特有功能实现
        String line;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        //释放资源
        bw.close();
        br.close();
    }


4.14 游戏次数【Properties集合、字符流】

  • 案例需求

    • 实现猜数字小游戏只能试玩3次,如果还想玩,提示:游戏试玩已结束,想玩请充值
  • 分析步骤

    1. 写一个游戏类,里面有一个猜数字的小游戏

    2. 写一个测试类,测试类中有main()方法,main()方法中写如下代码:
      利用Properties类中的lood和store方法,实现数据的保存和数据的加载
      ​ A、从文件中读取数据到Properties集合,用load()方法实现

         文件已经存在:game.txt
      
         里面有一个数据值:count=3
      

      ​ B、通过Properties集合获取到玩游戏的次数

      ​ 判断次数是否到到3次了

         如果到了,给出提示:游戏试玩已结束,想玩请充值
      
         如果不到3次:
      
         	次数-1,重新写回文件,用Properties的store()方法实现玩游戏
      
  • 代码实现

public class GuessNumber {
    private GuessNumber() {
    }

    public static void start() {
        //要完成猜数字的游戏,首先需要有一个要猜的数字,使用随机数生成该数字,范围1到100
        Random r = new Random();
        int number = r.nextInt(100) + 1;

        while (true) {
            //使用程序实现猜数字,每次均要输入猜测的数字值,需要使用键盘录入实现
            Scanner sc = new Scanner(System.in);

            System.out.println("请输入你要猜的数字:");
            int guessNumber = sc.nextInt();

            //比较输入的数字和系统产生的数据,需要使用分支语句。这里使用if..else..if..格式,根据不同情况进行猜测结果显示
            if (guessNumber > number) {
                System.out.println("你猜的数字" + guessNumber + "大了");
            } else if (guessNumber < number) {
                System.out.println("你猜的数字" + guessNumber + "小了");
            } else {
                System.out.println("恭喜你猜中了");
                break;
            }
        }
    }
}

public class PropertiesTest {
    public static void main(String[] args) throws IOException {
        //从文件中读取数据到Properties集合,用load()方法实现
        Properties prop = new Properties();

        FileReader fr = new FileReader("project\\game.txt");
        prop.load(fr);//读数据
        fr.close();//读到了就可以关流了

        //通过Properties集合获取到玩游戏的次数
        String count = prop.getProperty("count");
        int number = Integer.parseInt(count);

        //判断次数是否到到3次了
        if(number <= 0) {
            //如果到了,给出提示:游戏试玩已结束,想玩请充值
            System.out.println("游戏试玩已结束,想玩请充值");
        } else {
            //玩游戏
            GuessNumber.start();

            //次数-1,重新写回文件,用Properties的store()方法实现
            number--;
            prop.setProperty("count",String.valueOf(number));
            FileWriter fw = new FileWriter("project\\game.txt");
            prop.store(fw,null);
            fw.close();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值