06-IO流

IO流

异常

简介

  • 概述:java中把程序出现的所有不正常的情况 统称为 异常

  • 异常的体系图:

    • 顶层类:Throwable

      • Error:

        • 概述: 表示错误,一般指的是非代码级别的问题,大多数问题你也处理不了.
        • 举例:服务器宕机,数据库崩溃。
      • Exception:

        • 概述:这个才是我们常说的异常,一般都是代码级别的问题,需要我们(程序员)处理

        • 举例:

          • NullPointerException,
            StringIndexOutOfBoundsException,
            ConcurrentModificationException,
            ClassCastException,
            ArithmeticException,
            ParseException,
            .....
            
        • 分类:

          • 编译期异常
            • 指的是在代码编译期间就会发生的异常,非RuntimeException及其子类都是 编译期异常
          • 运行时异常
            • 指的是在代码运行期间才会发生的异常,编译期间没问题,RuntimeException及其子类都是 运行时异常
package com.ithiema.api.demo;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo01 {
    public static void main(String[] args) {
        System.out.println(10 / 0);			//运行期异常
        System.out.println("-----------");

        SimpleDateFormat sdf = new SimpleDateFormat("yyy/MM/dd");
        Date date = sdf.parse("2021/05/21");	//编译期异常
    }
}

JVM默认处理异常的方式

  1. 把异常的类型,异常出现的原因以及异常出现的位置打印到控制台上.
  2. 并终止程序的执行.

自己处理异常

方式一:捕获异常
格式:

try{
    //可能出问题的代码
} catch(Exception e) {		//异常类型 对象名
    //走这里, 说明程序出现异常了, 所以这里写的基本上都是解决方案.
    e.printStackTrace();        //该方法会将异常的类型, 描述信息及位置打印到控制台上.
} finally {
    //这里的代码正常情况下永远都会执行, 一般是用来释放资源的.
}

特点:处理完之后, 程序会继续运行.

package com.itheima.demo01_exception;

//案例: 演示异常的两种处理方式.
/*
    方式一: try.catch.finally语句
        格式:
            try{
                //可能出问题的代码
            } catch(Exception e) {
                //走这里, 说明程序出现异常了, 所以这里写的基本上都是解决方案.
                e.printStackTrace();        //该方法会将异常的类型, 描述信息及位置打印到控制台上.
            } finally {
                //这里的代码正常情况下永远都会执行, 一般是用来释放资源的.
            }
        特点:
            处理完之后, 代码会继续往下执行.
 */
public class Demo02 {
    public static void main(String[] args) {
        try{
            int a = 10;
            int b = 2;
            System.out.println(a / b);
        } catch (Exception e) {
            e.printStackTrace();//该方法会将异常的类型, 描述信息及位置打印到控制台上.
        }

        System.out.println("看看我执行了吗?");
    }
}
方式二:声明抛出异常

throws,声明抛出异常,把异常交给调用者 处理

格式:
	在定义方法的小括号后 throws 异常类型1,异常类型2...

特点:处理完之后,程序会终止执行.

package com.ithiema.api.demo;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo02 {
    public static void main(String[] args) /*throws Exception*/{
        try {
            show();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("看看我执行了吗");
    }
    public static void show() throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat();
        Date date = sdf.parse("2021/2/3/");     //编译期异常
    }
}

File类

简介

  • 概述:

    • 它是java.io包下的类,用之前需要先导包.
    • 它表示文件或者文件夹(目录),以 路径的形式来描述.
  • 路径分类:

    • 绝对路径:
      • 以盘符开头,固定的,写"死"的路径.
      • 例如: d:/abc/1.txt
    • 相对路径:
      • 指的是相对于某个路劲来讲,在java中,默认都是以 当前的项目路径来做参考路径的.
      • 例如: 你写了 1.txt, 其实是: 当前项目路径/1.txt
      • 举例:
        • 1.txt -> D:\workspace\bigdata_24_sz\JavaSE\1.txt

构造方法:

  • public File(String path); 创建File对象, 封装: 字符串形式的路径.

  • public File(String parent, String child); 创建File对象, 封装: 字符串形式的父目录子文件/子目录

  • public File(File parent, String child); 创建File对象, 封装: 父目录对象字符串形式的子文件/子目录

细节:

  1. 上述三种形式创建的文件(夹), 结果都是一样的.
  2. 之所以提供这么多的创建File对象的形式, 就是为了满足用户灵活多变的需求.
  3. 盘符不区分大小写, 写大写或者写小写都可以.
  4. 因为 \ 在Java中表示转移符, 所以如果写 右斜线需要写两个, 即: \\ 。左斜线写一个 / 就行了。
案例: File类入门
package com.itheima.demo02_file;

import java.io.File;

//案例: File类入门.
public class Demo01 {
    public static void main(String[] args) {
        //需求: 描述 D:\\abc\\1.txt
        //1. 演示  public File(String pathName); 根据传入的路径, 获取对应的File对象.
        File file1 = new File("d:\\abc\\1.txt");
        File file2 = new File("d:/abc/1.txt");
        System.out.println("file1: " + file1);
        System.out.println("file2: " + file2);
        System.out.println("-----------------");

        //2. 演示  public File(String parent, String child);  根据传入的父目录和子文件名获取其对应的File对象.
        File file3 = new File("d:/abc/","1.txt");
        System.out.println("file3: " + file3);
        System.out.println("-----------------");

        //3. 演示  public File(File parent, String child); 根据传入的父目录对象和子文件名获取其对应的File对象.
        File file4 = new File("d:/abc/");
        File file5 = new File(file4, "1.txt");
        System.out.println("file5: " + file5);
    }
}

成员方法之-创建功能

  • public boolean createNewFile(); 创建文件,文件不存在就创建返回true,否则返回false.

  • public boolean mkdir(); 创建目录, 只能创建单级目录. 不存在就创建返回true, 否则返回false.

  • public boolean mkdirs(); 创建目录, 可以创建单级目录, 也可以创建多级. 不存在就创建返回true, 否则返回false.

/(正)斜线,\ 反斜线

案例: 演示File类的创建功能.
package com.itheima.demo02_file;

import java.io.File;
import java.io.IOException;

//案例: 演示File类的创建功能.

public class Demo02 {
    public static void main(String[] args) throws IOException {
        //需求1: 在D:\ceshi文件夹下创建1.txt文件.
        File file1 = new File("d:/ceshi/1.txt");
        System.out.println(file1.createNewFile());
        System.out.println("-----------------------");

        //需求2: 在D:\ceshi文件夹下创建aa\bb文件夹
        File file2 = new File("d:/ceshi/aa/bb");
        //System.out.println(file2.mkdir());     结果为false
        System.out.println(file2.mkdirs());     //结果为true
        System.out.println("-----------------------");
    }
}

成员方法之-判断功能

  • public boolean isDirectory() 是否是目录, 存在的且是目录路径就返回true, 否则:false
  • public boolean isFile() 是否是文件, 存在的且是文件路径就返回true, 否则:false
  • public boolean exists() 测试此抽象路径名表示的File是否存在
案例: 演示File类的判断功能
package com.itheima.demo02_file;

import java.io.File;

//案例: 演示File类的判断

public class Demo03 {
    public static void main(String[] args) {
        //1. 创建File对象.
        //演示  public boolean exists();            //判断指定的文件(夹)是否存在.
        File file1 = new File("d:/ceshi/1.txt");
        System.out.println(file1.exists());
        System.out.println("-------------------");

        //2. 演示  public boolean isFile();            //判断给定的路径是否是文件.
        File file2 = new File("d:/ceshi/1.txt");
        System.out.println(file1.isFile());
        System.out.println("-------------------");

        //3. 演示public boolean isDirectory();  //判断给定的路径是否是文件夹.
        File file3 = new File("d:/ceshi");
        //System.out.println(file1.isDirectory());        //false
        System.out.println(file3.isDirectory());          //true
    }
}

成员方法之-获取功能

  • public String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串
  • public String getPath() 将此抽象路径名转换为路径名字符串
  • public String getName() 返回由此抽象路径名表示的文件文件夹名称
  • public String[] list() 返回此抽象路径名表示的目录中的文件目录的名称字符串数组
  • public File[] listFiles() 返回此抽象路径名表示的目录中的文件目录的File对象数组
案例: 演示File类的获取功能
package com.itheima.demo02_file;

import java.io.File;

//案例: 演示File类的获取功能

public class Demo04 {
    public static void main(String[] args) {
        //1. 获取getAbsolutePath();    //获取绝对路径.
        File file1 = new File("day12/data/1.txt");          //相对路径.
        System.out.println(file1.getAbsolutePath());        //绝对路径.
        System.out.println(file1.getPath());                //相对路径, 一般是相对于当前项目的路径来讲的.
        System.out.println("-------------------");

        //2. public String getName();            //获取文件(夹)的名字.
        System.out.println(file1.getName());
        System.out.println("-------------------");

        //3. public String[] list(); //获取指定的目录下所有的 文件(夹) 的字符串数组形式.
        //理解即可.
        File file2 = new File("d:/ceshi");
        String[] fileNames = file2.list();
        for (String fileName : fileNames) {
            System.out.println(fileName);		//1.txt		aa
        }
        System.out.println("-------------------");

        //4.  public File[] listFiles();    //获取指定的目录下所有的 文件(夹) 的File对象数组形式.
        File[] listFiles = file2.listFiles();
        for (File listFile : listFiles) {
            System.out.println(listFile);	//d:\ceshi\1.txt	d:\ceshi\aa
        }

    }
}

成员方法之-删除功能

  • public boolean delete(); 成功true,false:失败

细节:
1.java的删除不走回收站.
2.要删除的文件夹必须是空文件夹.

案例: 演示File类的删除功能
package com.itheima.demo02_file;

import java.io.File;

//案例: 演示File类的删除功能

public class Demo05 {
    public static void main(String[] args) {
        //需求1: 删除 d:/ceshi/aa  文件夹
        File file1 = new File("d:/ceshi/aa");
        System.out.println(file1.delete());

       //需求2: 删除d:/ceshi/1.txt文件
        File file1 = new File("d:/ceshi/1.txt");
        System.out.println(file1.delete());

        //需求3: 删除d:/ceshi文件夹
        File file1 = new File("d:/ceshi");
        System.out.println(file1.delete());
    }
}

IO流简介

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j7Avm0ey-1669232987297)(F:\BigData\java\图片\day12图片\02. IO流解释.png)]

简介:

  • 概述:

    • IO流也叫输入/输出流,主要是用来做数据传输
    • I:input(输入),O:output(输出)
  • 作用:

    • 文件复制.
    • 文件上传.
    • 文件下载.

分类:

  • 按照流向分:

    • 输入流: 读取数据
    • 输出流: 写数据
  • 按照操作分:

    • 字节流:
      • 特点:也叫万能流,能拷贝任意类型的文件,即:以字节为单位操作数据
      • 分类:
        • 字节输入流:
          • 特点:以字节为单位读取数据, 顶层抽象类是: InputStream
          • 常用子类:
            • FileInputStream普通的字节输入流
            • BufferedInputStream高效的字节输入流
        • 字节输出流:
          • 特点:以字节为单位写数据, 顶层抽象类是: OutputStream
          • 常用子类:
            • FileOutputStream普通的字节输出流
            • BufferedOutputStream高效的字节输出流
    • 字符流:
      • 特点:以字符为单位操作数据, 只能操作 纯文本文件, 操作其他文件(例如图片, 音频, 视频)可能会导致乱码.
      • 分类:
        • 字符输入流:
          • 特点:以字符为单位读取数据, 顶层抽象类是: Reader
          • 常用子类:
            • FileReader普通的字符输入流
            • BufferedReader高效的字符输入流
        • 字符输出流:
          • 特点:以字符为单位写数据, 顶层抽象类是: Writer
          • 常用子类:
            • FileWriter普通的字符输出流
            • BufferedWriter高效的字符输出流
  • 细节:

    1. 电脑的工作原理简单理解就是: 从磁盘(外存)加载数据到内存中, 处理之后在把数据写回到磁盘(外存)中
    2. 能用字符流优先考虑使用字符流, 字符流解决不了再考虑使用字节流
    3. IO流涉及到的单词: InputStream, OutputStream, Reader, Writer, File, Buffered

字节输出流

  • 概述:

    • 它的顶层抽象类是 OutputStream, 以字节为单位往文件中写数据它不能new, 所以我们常用它的子类.
  • 常用子类:

    • FileOutputStream:普通的字节输出流,
    • BufferedOutputStream:高效的字节输出流.

FileInputStream简介:

  • 概述:
    • 它是基本字节输出流, 以字节为单位往数据中 写数据, 该流没有默认的缓冲区.

构造方法:

  • public FileOutputStream(String pathName); 创建基本的字节输出流, 关联: 目的地文件(字符串形式的路径)
  • public FileOutputStream(File pathName); 创建基本的字节输出流, 关联: 目的地文件(File对象形式)
  • public FileOutputStream(String pathName, boolean append); true:追加, false: 覆盖(默认的)
  • public FileOutputStream(File pathName, boolean append); true:追加, false: 覆盖(默认的)

成员方法:

  • public void write(int c); 一次写一个字节.
  • public void write(byte[] bys); 一次写一个字节数组.
  • public void write(byte[] bys, int start, int len); 一次写一个字节数组一部分, start:起始索引, len: 个数
  • public void close(); 释放资源.

细节:

  1. 默认是覆盖文件中的数据, 即:如果重新往文件中写数据, 则会覆盖文件中之前的数据.
  2. 目的地文件如果不存在程序会自动创建, 前提: 该文件的父目录必须存在.
案例: 演示字节输出流入门
package com.itheima.demo03_outputstream;

import java.io.FileOutputStream;
import java.io.IOException;

//案例: 演示字节输出流入门.

public class Demo01 {
    public static void main(String[] args) throws IOException {
        //1.创建FileOutputStream对象, 关联指定的目的地文件.
        //解释: 往哪个文件中写数据, 哪个文件就叫: 目的地文件.
        //FileOutputStream fos = new FileOutputStream("day12/data/1.txt");    默认是覆盖
        FileOutputStream fos = new FileOutputStream("day12/data/1.txt", true);      //传true表示追加
        //FileOutputStream fos = new FileOutputStream(new File("day12/data/1.txt"));

        //2.往文件中写入字符'a', 'b', 'c'.
        //方式一:  一次写一个字节
        fos.write(97);
        fos.write(98);
        fos.write(99);

        //方式二: 一次写一个字节数组
        byte[] bys = {65, 66, 67};
        fos.write(bys);

        //方式三: 一次写一个字节数组的一部分
        byte[] bys = {65, 66, 67};
        fos.write(bys,1, 1);

        //3. 释放资源.
        fos.close();
    }
}
案例: 演示字节输出流写数据的两个小问题

细节:

  1. 如何往文件追加数据.
    public FileOutputStream(String pathName, boolean append); true:追加, false: 覆盖(默认的)
    public FileOutputStream(File pathName, boolean append); true:追加, false: 覆盖(默认的)
  2. 如何换行.
    转义符之 \r\n, 注意: 不同操作系统回车换行符不一样. windows: \r\n, Linux: \n, Mac: \r
  • String类中有一个方法, 可以把 字符串转成其对应的 字节数组形式.
    • public byte[] getBytes();
      字符串转成其对应的 字节数组形式, 采用: 默认编码.
    • public byte[] getBytes(String charsetName);
      字符串转成其对应的 字节数组形式, 采用: 指定编码.
package com.itheima.demo03_outputstream;

import java.io.FileOutputStream;
import java.io.IOException;

//案例: 演示字节输出流的两个小问题
/*
    问题1: 换行符, \r\n
    问题2: 追加数据,  FIleOutputStream的构造方法, 传入true.
 */
public class Demo02 {
    public static void main(String[] args) throws IOException {
        //1.创建FileOutputStream对象, 关联指定的目的地文件.
        FileOutputStream fos = new FileOutputStream("day12/data/2.txt", true);  //目的地文件不存在, JVM会自动创建.
        //2.往文件中写入10次hello, 每个hello占一行.
        for (int i = 0; i < 10; i++) {
            fos.write("hello".getBytes());
            //换行符
            fos.write("\r\n".getBytes());  //window操作系统: \r\n,  linux: \n   mac: \r
        }

        //3.往文件中追加一句话: 键盘敲烂, 月薪过万!
		//字符串转字节数组 -> getBytes()
        fos.write("键盘敲烂, 月薪过万!\r\n".getBytes());
        //4. 关闭流, 释放资源
        fos.close();
    }
}
案例: 通过try.catch.finally语句解决IO流的异常问题
package com.itheima.demo03_outputstream;

import java.io.FileOutputStream;
import java.io.IOException;

//案例: 通过try.catch.finally语句解决IO流的异常问题.
public class Demo03 {
    public static void main(String[] args) {
        //1.创建FileOutputStream对象, 关联指定的目的地文件.
        FileOutputStream fos = null;
        try{
            System.out.println(1/0);
            //可能会出现问题的代码
            fos = new FileOutputStream("day12/data/2.txt");
            //2.往文件中写入10次hello, 每个hello占一行.
            for (int i = 0; i < 10; i++) {
                fos.write("hello".getBytes());
                //换行符
                fos.write("\r\n".getBytes());
            }
        }catch(Exception e) {
            //出现问题的异常处理代码.
            e.printStackTrace();
        }finally{
            //3. 关闭流, 释放资源
            try {
                if (fos != null) {
                    fos.close();   //快捷键: alt + enter
                    fos = null;    //GC会优先回收null对象.
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

字节输入流

  • 概述:它是一个抽象类, 用 InputStream表示, 可以以字节为单位读取文件中的数据.

  • 常用子类:

    • FileInputStream: 基本的字节输入流.
    • BufferedInputStream: 高效的字节输入流.

FileInputStream类简介:

  • 概述:它表示基本字节输入流, 可以以字节为单位读取数据, 没有默认的缓冲区.

构造方法:

  • public FileInputStream(String path); 创建字节输入流对象, 封装: 数据源文件(字符串形式的路径)
  • public FileInputStream(File path); 创建字节输入流对象, 封装: 数据源文件(File形式的路径)
    成员方法:

成员方法:

  • public int read(); 一次读取一个字节, 并返回读取到的内容(即: 该字节的ASCII码值), 读不到返回-1
  • public int read(byte[] bys);
    一次读取一个字节数组, 并将读取到的内容存入到字节数组中, 返并回读取到的有效字节数, 读不到返回-1
  • public void close(); 释放资源
IO流的核心操作步骤
1. 创建输入流对象, 关联: 数据源文件.
2. 创建输出流对象, 关联: 目的地文件.
3. 定义变量, 记录读取到的数据(字节, 有效字节数, 字符, 有效字符数,字符串)
4. 循环读取, 只要条件满足就一直读, 并将读取到的数据赋值给 变量
5. 将读取到的内容写入到目的地文件中.
6. 释放资源.
案例: 字节输入流入,一次读取一个字节
package com.itheima.demo04_inputstream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

//案例: 字节输入流入门.

public class Demo01 {
    public static void main(String[] args) throws IOException {
        //1. 创建字节输入流对象, 封装数据源文件.    数据源文件必须存在, 不然报错.
        FileInputStream fis = new FileInputStream("day12/data/1.txt");
        //2. 一次读一个字节
        //2.1 定义变量, 记录读取到的内容.
        int len = 0;
        /*
            (len = fis.read()) != -1 这行代码做了3件事:
                1. 执行 fis.read(),            一次读取一个字节.
                2. 执行 len = fis.read()      将读取到的字节的ASCII码值赋值给变量len
                3. 执行 len != -1             如果len不等于-1, 说明读到内容了, 就进循环.
         */
        while((len = fis.read()) != -1) {
            System.out.println(len);
        }
        //3. 关流.
        fis.close();
    }
}
案例: 字节输入流,一次读取一个字节数组
package com.itheima.demo04_inputstream;

import java.io.FileInputStream;
import java.io.IOException;

//案例: 字节输入流 一次读取一个字节数组.

public class Demo02 {
    public static void main(String[] args) throws IOException {
        //1. 创建字节输入流对象, 封装数据源文件.    数据源文件必须存在, 不然报错.
        FileInputStream fis = new FileInputStream("day12/data/1.txt");
        //2. 一次读一个字节数组
        //合并版
        //2.1 定义字节数组, 记录读取到的数据.
        byte[] bys = new byte[3];       //字节数组的长度: 一般是1024的整数倍.  初始值: 0, 0, 0
        int len = 0;                    //记录读取到的有效字节数.

        /*
        (len = fis.read(bys)) != -1  这行代码做了3件事
         1. 执行fis.read(bys),         一次读取一个字节数组, 并把读取到的内容存储到bys数组中.
         2. 执行len = fis.read(bys)    将读取到的有效字节数赋值给变量len
         3. 执行len != -1              如果len不等于-1, 说明读到内容了, 进循环.
         */
        while((len = fis.read(bys)) != -1) {
            System.out.println(new String(bys,0, len ));
        }
        //3. 关流.
        fis.close();



        //分解版
        //第一次读取
        int len1 = fis.read(bys);       //bys: abc(97, 98, 99),  len1 = 3
        String s1 = new String(bys, 0, len1);
        System.out.println(s1);
        System.out.println(len1);
        System.out.println("----------------------");

        //第二次读取
        int len2 = fis.read(bys);       //bys: dbc(100, 98, 99),  len2 = 1
        String s2 = new String(bys, 0, len2);
        System.out.println(s2);
        System.out.println(len2);

    }
}
案例: 普通字节流拷贝文件的两种方式
package com.ithiema.api.demo;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo13 {
    public static void main(String[] args) throws IOException {
        //拷贝文件方式一: 一次读写一个字节.
        method01();

        //拷贝文件方式二: 一次读写一个字节数组.
        method02();
    }

    public static void method01() throws IOException {
        //1. 创建输入流对象, 关联数据源文件.
        FileInputStream fis = new FileInputStream("javaSE/data/1.txt");
        //2. 创建输出流对象, 关联目的地文件.
        FileOutputStream fos = new FileOutputStream("javaSE/data/2.txt");
        //3. 定义变量, 记录读取到的内容或者有效字节(符)数.
        int len = 0;
        //4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
        while ((len = fis.read()) != -1) {
            //5. 将读取到的内容写入到目的地文件中.
            fos.write(len);
        }
        //6. 关流, 释放资源.
        fis.close();
        fos.close();
        System.out.println("拷贝完成.");
    }

    public static void method02() throws IOException{
        //1. 创建输入流对象, 关联数据源文件.
        FileInputStream fis = new FileInputStream("javaSE/data/1.txt");
        //2. 创建输出流对象, 关联目的地文件.
        FileOutputStream fos = new FileOutputStream("javaSE/data/2.txt");
        //3. 定义变量, 记录读取到的内容或者有效字节(符)数.
        int len = 0;
        byte[] bys = new byte[1024];        //一般是8KB
        //4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
        while ((len = fis.read(bys)) != -1) {
            //5. 将读取到的内容写入到目的地文件中.
            fos.write(bys, 0, len);
        }
        //6. 关流, 释放资源.
        fis.close();
        fos.close();
        System.out.println("拷贝完成.");
    }
}
案例:图片加密

核心规则:
用位运算符之 位异或^, 它的规则是: 一个数字被同一个数字位异或两次, 值不变.
a ^ b ^ b = a

例如:

​ 5 ^ 10 ^ 5 = 10

​ 3 ^ 4 ^ 4 = 3

package com.itheima.demo05_exercise;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

//案例: 图片加密.

public class Demo02 {
    public static void main(String[] args) throws IOException {
        System.out.println(5 ^ 5 ^ 10);     //10
        System.out.println(2 ^ 3 ^ 3);      //2
        /*
            需求:
                1.对项目下的a.jpg图片进行加密, 获取到一个新的加密图片b.jpg.
                2.对加密后的图片b.jpg进行解密, 获取解密后的图片c.jpg.
         */
        // 1. 创建输入流对象, 关联数据源文件.
        FileInputStream fis = new FileInputStream("day12/data/b.jpg");
        // 2. 创建输出流对象, 关联目的地文件.
        FileOutputStream fos = new FileOutputStream("day12/data/c.jpg");
        // 3. 定义变量, 记录读取到的内容或者有效字节(符)数.
        int len = 0;
        // 4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
        while ((len = fis.read()) != -1) {
            // 5. 将读取到的内容写入到目的地文件中.
            fos.write(len ^ 5);             //这里的5就是密钥
        }
        // 6. 关流, 释放资源.
        fis.close();
        fos.close();
    }
}

高效字节流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YHftTOad-1669232987299)(F:\BigData\java\图片\day12图片\01.字节流拷贝文件的图解.png)]

简介:

​ 也叫: 字节高效流, 高效字节流, 缓冲字节流, 字节缓冲流.

  • 概述:
    • 高效的字节流指的是 BufferedInputStreamBufferedOutputStream, 它们自带有缓冲区.
  • 单词解释:
    • BufferedInputStream: 高效的字节输入流
    • BufferedOutputStream: 高效的字节输出流

构造方法

  • public BufferedInputStream(InputStream is);
  • public BufferedOutputStream(OutputStream os);

问题: 为什么高效流需要封装基本流, 而不是普通的文件路径或者File对象呢?
答案: 因为高效流仅仅提供缓冲区的功能, 是对普通流的功能做扩展和延伸, 具体的读写操作还是由基本流完成的.

字节高效流入门详解

package com.ithiema.api.demo;

import java.io.*;

public class Demo15 {
    public static void main(String[] args) throws IOException {
        //1.通过字节缓冲输出流往文件中写一句话
        //分解版
        FileOutputStream fos = new FileOutputStream("javaSE/data/1.txt");
        //高效版                                             基本流
        BufferedOutputStream bos = new BufferedOutputStream(fos);

        //合并版
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("javaSE/data/1.txt"));
        bos.write("hello".getBytes());
        bos.close();

        //2.通过字节缓冲输入流读取文件中的内容
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("javaSE/data/1.txt"));
        int len = 0;
        while ((len = bis.read()) != -1){
            System.out.println(len);
        }
         bis.close();
    }
}

四种拷贝方式的效率问题

  • 读写速度从高到低分别是:
    1. 高效的字节流一次读写一个 字节数组. 29 毫秒
    2. 基本的字节流一次读写一个 字节数组. 29 毫秒 //掌握
      如果字节数组的长度为 8192(8KB), 理论上来讲, 这种拷贝方式效率最高.
    3. 高效的字节流一次读写一个 字节. 633 毫秒 //掌握
    4. 基本的字节流一次读写一个 字节. 76675毫秒
  • 小细节:
    • 因为字节缓冲流提供的缓冲区的大小是: 8196个字节(8KB), 所以如果我们用 普通流一次读写一个字节数组的方式拷贝文件,且自定义的数组长度在 8196个字节, 那么可能会出现: 普通流一次读写一个字节数组的速度 > 高效流一次读写一个字节数组的速度。
package com.itheima.demo05_exercise;

import java.io.*;

//案例: 演示字节流拷贝文件的效率.
/*
    读写速度从高到底分别是:
        1. 高效流一次读写一个字节数组.         71毫秒
        2. 普通流一次读写一个字节数组.         70毫秒              掌握
        3. 高效流一次读写一个字节.             609毫秒             掌握
        4. 普通流一次读写一个字节.             138550毫秒
    小细节:
        因为字节缓冲流提供的缓冲区的大小是: 8196个字节(8KB), 所以如果我们用 普通流一次读写一个字节数组的方式拷贝文件,
        且自定义的数组长度在 8196个字节, 那么可能会出现: 普通流一次读写一个字节数组的速度 > 高效流一次读写一个字节数组的速度
 */
public class Demo03 {
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
        //1.分别通过四种方式拷贝视频文件, 并观察它们的效率.
        //2.通过普通的字节流一次读写一个字节的形式实现.
        method01();                   138550毫秒

        //3.通过普通的字节流一次读写一个字节数组的形式实现.
        method02();                     //70毫秒

        //4.通过高效的字节流一次读写一个字节的形式实现.
        method03();                      //609毫秒

        //5.通过高效的字节流一次读写一个字节数组的形式实现.
        method04();                     71毫秒


        long end = System.currentTimeMillis();
        System.out.println("拷贝时间为: " + (end - start));
    }

    public static void method04() throws IOException {
        //1. 创建输入流对象, 关联数据源文件.
        BufferedInputStream bis = new BufferedInputStream( new FileInputStream("day12/data/1.avi"));
        //2. 创建输出流对象, 关联目的地文件.
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day12/data/5.avi"));
        //3. 定义变量, 记录读取到的内容或者有效字节(符)数.
        int len = 0;
        byte[] bys = new byte[8196];
        //4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
        while ((len = bis.read(bys)) != -1) {
            //5. 将读取到的内容写入到目的地文件中.
            bos.write(bys, 0, len);
        }
        //6. 关流, 释放资源.
        bis.close();
        bos.close();
    }

    public static void method03() throws IOException {
        //1. 创建输入流对象, 关联数据源文件.
        FileInputStream fis = new FileInputStream("day12/data/1.avi");
        BufferedInputStream bis = new BufferedInputStream(fis);
        //2. 创建输出流对象, 关联目的地文件.
        FileOutputStream fos = new FileOutputStream("day12/data/4.avi");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //3. 定义变量, 记录读取到的内容或者有效字节(符)数.
        int len = 0;
        //4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
        while ((len = bis.read()) != -1) {
            //5. 将读取到的内容写入到目的地文件中.
            bos.write(len);
        }
        //6. 关流, 释放资源.
        bis.close();
        bos.close();
    }

    public static void method02() throws IOException {
        //1. 创建输入流对象, 关联数据源文件.
        FileInputStream fis = new FileInputStream("day12/data/1.avi");
        //2. 创建输出流对象, 关联目的地文件.
        FileOutputStream fos = new FileOutputStream("day12/data/3.avi");
        //3. 定义变量, 记录读取到的内容或者有效字节(符)数.
        int len = 0;
        byte[] bys = new byte[8196];
        //4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
        while ((len = fis.read(bys)) != -1) {
            //5. 将读取到的内容写入到目的地文件中.
            fos.write(bys, 0, len);
        }
        //6. 关流, 释放资源.
        fis.close();
        fos.close();
    }

    public static void method01() throws IOException {
        //1. 创建输入流对象, 关联数据源文件.
        FileInputStream fis = new FileInputStream("day12/data/1.avi");
        //2. 创建输出流对象, 关联目的地文件.
        FileOutputStream fos = new FileOutputStream("day12/data/2.avi");
        //3. 定义变量, 记录读取到的内容或者有效字节(符)数.
        int len = 0;
        //4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
        while ((len = fis.read()) != -1) {
            //5. 将读取到的内容写入到目的地文件中.
            fos.write(len);
        }
        //6. 关流, 释放资源.
        fis.close();
        fos.close();
    }
}

铺垫知识

为什么会出现字符流

  • 问题1: 字节流也叫万能流, 能拷贝任意类型的数据, 那为什么要学习字符流?

    • 因为中文在不同的码表中占用的字节数不一样, 所以如果是用字节流操作中文, 有可能出现中文乱码问题.
  • 问题2: 那到底什么是字符流呢?

    • 字符流 = 字节流 + 编码表, 即: 字节流按照指定的编码规则读取数据, 即为: 字符流.
      看起来好像是我们在一个字符一个字符的读取, 其实是底层的字节流根据码表规则读取.
  • 结论:

    1. 中文第一个字节肯定是负数, 英文字母, 数字, 特殊符号的字节肯定是正数.
    2. 中文UTF-8码表中占3个字节, 在GBK码表中占2个字节, 英文字母, 数字, 特殊符号不管在什么码表中都只占1个字节.
    3. "abc中国"在UTF-8码表下的内容是: 97, 98, 99, -28, -72, -83, -27, -101, -67
    4. 字符流 = 字节流 + 编码表
package com.ithiema.api.demo;

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

public class Demo17 {
    public static void main(String[] args) throws IOException {
        /*
            1.已知项目下的data文件夹下的1.txt文件中包含数据"abc中国".
            2.请通过字节流一次读取一个字节的形式, 将上述文件中的内容读取出来.
            3.将上述读取到的数据打印到控制台上.
            解释:
            1.字节流操作中文不是特别方便.
            2.通过String#getBytes()方法, 可以查看中文在不同码表下占用的字节内容.
         */

        //需求: 请通过字节流一次读取一个字节的形式, 将上述文件中的内容读取出来.
        FileInputStream fis = new FileInputStream("javaSE/data/1.txt");
        int len = 0;
        while ((len = fis.read()) != -1) {
            System.out.println((byte) len);
        }
        fis.close();


        //演示不同码表下的中文占用的字节数.
        //utf-8码表:  中国, -28,-72,-83,-27,-101,-67
        String str = "中国";
        byte[] bys = str.getBytes();            //默认码表: UTF-8
        byte[] bys = str.getBytes("utf-8");   //-28,-72,-83,-27,-101,-67
        byte[] bys = str.getBytes("gbk");    //-42, -48, -71, -6
        System.out.println(Arrays.toString(bys));
    }
}

字符串的编解码问题

名词解释:

  • 编码:指的是把 字符串 按照指定的码表 转换成其对应的 整数形式.

    • “abc中国” --> 按照utf-8, 转成: 97, 98, 99, -28, -72, -83, -27, -101, -67
  • 解码:指的是把 整数 按照指定的码表 解析成其对应的 字符串形式.

    • 97, 98, 99, -28, -72, -83, -27, -101, -67 -> 按照utf-8, 解析成: “abc中国”
  • 字符集

    • 就使用来描述 charint 之间的关系的, 各个国家都有自己的字符集.
      拿咱们国内举例, 咱们用的是GB系列的字符集, 它记录了 国内的汉字, 数字, 特殊符号等 和 int之间的关系.
    • 例如: ASCII字符集规定 'a’ -> 97‘A’ -> 65‘0’ -> 48
  • 编码表:

    • 可以把它理解为就是字符集的不同版本

      • 例如:

        • GB字符集分为 ->
          • GB2313 (收录了7000左右个汉字)
          • GBK (收录20000左右个汉字)
          • GB18030 (收录了70000左右个汉字)
      • 常用的编码表有:

        • ASCII:美国信息标准代码, 记录的最基础的数据, 后续的所有的码表基本上都兼容它.
        • ISO-8859-1:欧洲统一标准码.
        • GBK:国内用的.
        • UTF-8:万国码, 统一码.
  • 演示 编解码动作:

    • 编码: String -> byte[]
      • public byte[] getBytes(); 按照默认码表.
      • public byte[] getBytes(String charsetName); 按照指定码表.
    • 解码: byte[] -> String
      • public String(byte[] bys); 按照默认码表
      • public String(byte[] bys, String charsetName); 按照指定码表
  • 结论:

    1. 中文在UTF-8码表中占3个字节, 在GBK码表中占2个字节, 英文字母, 数字, 特殊符号不管在什么码表中都只占1个字节.
    2. 中文的第一个字节肯定是负数, 英文字母, 数字, 特殊符号的字节肯定是正数.
    3. 只要出现乱码问题, 原因只有一个, 即: 编解码不一致.
package com.ithiema.api.demo;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Demo18 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        //演示编码
        String s1 = "abc中国";
        System.out.println(Arrays.toString(s1.getBytes()));     //utf-8
        System.out.println(Arrays.toString(s1.getBytes("utf-8")));  //不区分大小写
        System.out.println(Arrays.toString(s1.getBytes("gbk")));    //不区分大小写
        System.out.println("----------------");

        //演示解码
        byte[] bys = {97, 98, 99, -42, -48, -71, -6};
        byte[] bys = {97, 98, 99, -28, -72, -83, -27, -101, -67};
        String s = new String(bys);
        String s = new String(bys, "gbk");  //指定码表
        String s = new String(bys, "utf-8");  //指定码表
        System.out.println(s);
    }
}

字符流

转换流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TFXom826-1669232987299)(F:\BigData\java\图片\day12图片\02.转换流解释.png)]

简介:
  • 概述:

    • 转换流指的是InputStreamReaderOutputStreamWriter, 它们都是属于: 字符流的.
  • 作用:用来在 字节流字符流 之间做转换的.

  • 解释:

    • InputStreamReader:
      • 概述:它叫转换输入流, 是 字节流 通向 字符流的桥梁.
      • 构造方法:
        • public InputStreamReader(InputStream is);
    • OutputStreamWriter:
      • 概述:它叫转换输出流, 是 字符流 通向 字节流的桥梁
      • 构造方法:
        • public OutputStreamWriter(OutputStream os);
  • 结论:

    1. 字符流 = 字节流 + 编码表, 这一点我们可以通过 转换流 实现.
    2. 区分一个流到底是字节流还是字符流的 小技巧:看该流以什么单词结尾.
      如果是以 Stream结尾, 它就是: 字节流.
      如果是以 Reader 或者 Writer 结尾, 它就是: 字符流.
演示转换流入门
package com.ithiema.api.demo;

import java.io.*;

public class Demo19 {
    public static void main(String[] args) throws IOException {
        //method01();
        method02();
    }

    private static void method01() throws IOException {
        //转换流一次读取一个字节
        //1. 创建输入流对象, 关联: 数据源文件.
        //字符流                 =                               字节流                                        编码表
        InputStreamReader isr = new InputStreamReader(new FileInputStream("javaSE/data/1.txt"), "utf-8");
        //2. 创建输出流对象, 关联: 目的地文件.
        //字符流                 =                               字节流                                        编码表
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("javaSE/data/2.txt"), "utf-8");
        //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("javaSE/data/2.txt"));    如果是默认码表可以这样写
        //3. 定义变量, 记录读取到的数据(字节, 有效字节数, 字符, 有效字符数,字符串)
        int len;
        //4. 循环读取, 只要条件满足就一直读, 并将读取到的数据赋值给 变量
        while ((len = isr.read()) != -1) {
            //5. 将读取到的内容写入到目的地文件中.
            osw.write(len);
        }
        //6. 释放资源.
        isr.close();
        osw.close();
    }

    private static void method02() throws IOException {
        //转换流一次读取一个字符数组
        //1. 创建输入流对象, 关联: 数据源文件.
        //字符流                 =                               字节流                                        编码表
        InputStreamReader isr = new InputStreamReader(new FileInputStream("javaSE/data/1.txt"), "utf-8");
        //2. 创建输出流对象, 关联: 目的地文件.
        //字符流                 =                               字节流                                        编码表
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("javaSE/data/3.txt"), "utf-8");
        //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("javaSE/data/2.txt"));    如果是默认码表可以这样写

        //3. 定义变量, 记录读取到的数据(字节, 有效字节数, 字符, 有效字符数,字符串)
        int len;
        char[] chs = new char[8192];
        //4. 循环读取, 只要条件满足就一直读, 并将读取到的数据赋值给 变量
        while ((len = isr.read(chs)) != -1) {
            //5. 将读取到的内容写入到目的地文件中.
            osw.write(chs, 0, len);
        }
        //6. 释放资源.
        isr.close();
        osw.close();
    }
}
演示普通的字符流

即:FileReaderFileWriter

  • 解读:
    • 虽然我们通过 “字符流 = 字节流 + 编码表” 的规则,结合 转换流 实现了 获取字符流的操作,但这样写太麻烦了,我们可以直接创建字符流对象通过 FileReader 和 FileWriter 直接实现
      • 即:FileReader 和 FileWriter 就是基本的字符流
package com.ithiema.api.demo;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Demo20 {
    public static void main(String[] args) throws IOException {
        method01();
        method02();
    }

    public static void method02() throws IOException {
        FileReader fr = new FileReader("javaSE/data/1.txt");
        FileWriter fw = new FileWriter("javaSE/data/2.txt");
        int len;
        char[] chs = new char[1024];
        while ((len = fr.read(chs)) != -1) {
            fw.write(chs, 0, len);
        }
        fr.close();
        fw.close();
    }

    private static void method01() throws IOException {
        FileReader fr = new FileReader("javaSE/data/1.txt");
        FileWriter fw = new FileWriter("javaSE/data/2.txt");
        int len;
        while ((len = fr.read()) != -1) {
            fw.write(len);
        }
        fr.close();
        fw.close();
    }
}

演示不同码表文件的拷贝

细节:

  1. 如果以后我们是拷贝 同码表的文件尽量不用去使用 转换流,因为写法繁琐
  2. 如果以后我们是拷贝 不同码表的文件只能用:转换流来实现
package com.ithiema.api.demo;

import java.io.*;

public class Demo21 {
    public static void main(String[] args) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("javaSE/data/1.txt"), "utf-8");
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("javaSE/data/2.txt"), "GBK");
        int len;
        char[] chs = new char[1024];
        while ((len = isr.read(chs)) != -1) {
            osw.write(chs, 0, len);
        }
        isr.close();
        osw.close();
    }
}

字符高效流

简介:

  • 概述:

    • 它指的是 BufferedReaderBufferedWriter,
    • 即: 可以以字符为单位操作文件, 它提供有缓冲区功能
  • 类解释:

    • BufferedReader:
      • 概述:
        • 字符高效输入流, 高效字符输入流, 字符缓冲输入流, 缓冲字符输入流.
      • 构造方法:
        • public BufferedReader(Reader r);
    • BufferedWriter:
      • 概述:
        • 字符高效输出流, 高效字符输出流, 字符缓冲输出流, 缓冲字符输出流.
      • 构造方法:
        • public BufferedWriter(Writer w);
  • 问: 为什么字符高效流封装的不是文件的路径, 而是一个基本的字符流?
    答: 因为高效流仅仅提供缓冲区的功能, 具体的读写还是由基本的流实现的.

字节高效流拷贝文件
package com.ithiema.api.demo;

import java.io.*;

public class Demo22 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("javaSE/data/1.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("javaSE/data/2.txt"));

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

字符高效流的用法之 一次读写一行

成员方法
  • BufferedReader:

    • public String readLine(); 一次读取一行的数据,并返回读取到的内容,读不到返回:null,它只接受行内容不接收回车换行符
  • BufferedWrite:

    • public void newLine(); 根据当前操作系统,给出对应的换行符
案例:字符高效流 一次读取一行
package com.ithiema.api.demo;

import java.io.*;

public class Demo23 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("javaSE/data/1.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("javaSE/data/2.txt"));
        String line;
        while ((line = br.readLine()) != null){
            bw.write(line);
            bw.newLine();       //细节:记得写换行符
        }
        br.close();
        bw.close();
    }
}
案例:随机点名器
package com.ithiema.api.demo;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

public class Demo24 {
    public static void main(String[] args) throws IOException {
        //1.定义ArrayList<String>,用来存储电影名
        ArrayList<String> list = new ArrayList<>();
        //2.创建字符高效输入流,关联:数据源文件:
        BufferedReader br = new BufferedReader(new FileReader("javaSE/data/name.txt"));
        //3.定义变量,记录读取到的数据
        String line;
        //4.循环读取,只要条件满足就一直读,并将读取到的数据赋值给变量
        while ((line = br.readLine()) != null) {
            //5.将读取到的数据,存储到集合中
            list.add(line);
        }
        //6.搞一个随机数,从集合中堆积获取 一个元素
        int num = (int) (Math.random() * list.size());
        String name = list.get(num);
        System.out.println("随机点名,抽到的是:" + name);
        //7.打印结果
        //合并版
        System.out.println(list.get((int) (Math.random() * list.size())));
    }
}

序列化流

对象序列化流

  • 概述:

    • 对象序列化流: ObjectOutputStream, 它可以把对象序列化到文件中.
  • 名词解释:
    对象 写入到 文件中的动作就叫:序列化

  • ObjectOutputStream

    • 构造方法:

      • public ObjectOutputStream(OutputStream os);
      • 问: 为什么序列化流要封装字节流?
        答: 因为它只提供序列化操作, 具体的读写是由基本的字节流实现的.
    • 成员方法:

      • public void writeObject(Object obj); 序列化对象到文件中.
      • public void close(); 释放资源.
  • 细节:

    • 一个类的对象要想实现序列化和反序列化操作, 则该类必须实现: Serializable接口.
    • Serializable接口, 它是一个标记接口, 充当标记的作用, 里边没有任何的成员.
案例:演示对象的 序列化操作

JavaBean类,学生类

package com.ithiema.api.pojo;

import java.io.Serializable;

//实现Serializable接口
public class Student implements 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;
    }

    public void study(){
        System.out.println("学习");
    }
    
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类,对象序列化操作

package com.ithiema.api.demo;

import com.ithiema.api.pojo.Student;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Demo25 {
    public static void main(String[] args) throws IOException {
        //创建 对象序列化流 关联 目的地文件
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("javaSE/data/2.txt"));

        //创建JavaBean对象
        Student s = new Student("李四", 23);

        //把对象写入到文件中,即:序列化操作
        oos.writeObject(s);

        //释放资源
        oos.close();
    }
}

对象反序列化流

  • 概述:

    • 对象反序列化流: ObjectInputStream, 它可以从文件中 反序列化出对象.
  • 名词解释:

    • 文件 中加载出 对象 的操作就叫:反序列化
  • ObjectInputStream:

    • 构造方法:
      • public ObjectInputStream(InputStream is);
      • 问: 为什么反序列化流要封装字节流?
        答: 因为它只提供反序列化操作, 具体的读写是由基本的字节流实现的.
    • 成员方法:
      • public Object readObject(Object obj); 从文件中读取对象.
      • public void close(); 释放资源.
案例:演示对象的 反序列化操作
package com.ithiema.api.demo;

import com.ithiema.api.pojo.Student;

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

public class Demo26 {
    public static void main(String[] args) throws Exception {
        //创建对象反序列化流,关联:数据源文件
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("javaSE/data/2.txt"));
        //具体的反序列化操作,记得向下转型
        Student s = (Student) ois.readObject();
        System.out.println(s);
        ois.close();
    }
}
进行序列化和反序列化时候的三个小问题:
  1. 问: 当序列化之后, 我们修改了JavaBean类中的成员, 然后再进行反序列化, 是否会出问题?
    答: 肯定会出问题, 因为 序列化版本号不一样, 如果一个类实现了Serializable接口, 没有手动给 序列化号,
    程序会自动给一个, 且当JavaBean类修改的时候, 该序列化号也同步随机修改.
  2. 如何解决上一步出现的问题?
    方案1: 重新序列化一次, 然后再反序列化.
    方案2: 我们手动给一个序列化版本号.
    private static final long serialVersionUID = 1L;
  3. 如果类中的某个属性不想实现序列化怎么办?
    例如: Student s = new Student(“刘亦菲”, 33); 该对象的年龄不要序列化到文件中.
    答案: 可以通过 transient 关键字实现, 它表示"瞬态"的意思, 被它标记的内容不能被实现序列化和反序列操作.

JavaBean类

package com.itheima.pojo;

import java.io.Serializable;

//JavaBean类, 叫: 学生类
public class Student implements Serializable {
    //给类加一个 序列化版本号, 如果不加, 系统会默认生成一个.
    private static final long serialVersionUID = 2L;        //值根据需求自己加

    private String name;
    private transient int age;    //transient: 学名叫 瞬态关键字.
    private String email;
    private double salary;


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

测试类

package com.itheima.demo08_serializable;

import com.itheima.pojo.Student;

import java.io.*;

//案例: 演示serialVersionUID 和  transient关键字.

public class Demo03 {
    public static void main(String[] args) throws Exception {
        //需求1: 序列化操作.
        //write();

        //需求2: 反序列化操作.
        read();
    }

    public static void read() throws IOException, ClassNotFoundException {
        //1. 创建序列化流, 封装数据源文件.
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day12/data/1.txt"));
        //2. 进行反序列化操作.
        Student s = (Student)(ois.readObject());
        //3. 打印对象.
        System.out.println(s);
        //4. 关流.
        ois.close();
    }

    public static void write() throws IOException {
        //1. 创建序列化流, 封装目的地文件.
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day12/data/1.txt"));
        //2. 创建JavaBean对象.
        Student s = new Student("刘亦菲", 33);
        //3. 进行序列化操作.
        oos.writeObject(s);
        //4. 关流.
        oos.close();
    }
}

Lambda表达式

  • 概述:

  • 它是JDK1.8的特性, 体现的是面向函数式编程思想, 即: 强调做什么, 而不是怎么做. 需要哪些对象, 重写哪些方法, 由系统自己实现.

  • 格式:

     (形式参数) -> {
            方法体;
    }
    
  • 细节:

    1. Lambda表达式只针对于有且只能有一个抽象方法接口 有效.
    2. Lambda表达式和匿名内部类之间的区别是什么?
      • A. 所需类型不同
        匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
        Lambda表达式:只能是接口
      • B. 使用限制不同
        如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
        如果接口中多于一个抽象方法只能使用匿名内部类,而不能使用Lambda表达式
      • C. 实现原理不同
        匿名内部类:编译之后,产生一个单独的.class字节码文件
        Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
Lambda表达式入门案例
package com.ithiema.api.demo;
//接口
public interface Animal {
    //抽象方法
    public abstract void eat();
}
package com.ithiema.api.demo;

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

        //方式1:匿名内部类,体现的是面向对象编程思想,需要我们创建对象,重写指定的当打,还需要我们指定要做的事(逻辑代码)
        printAnimal(new Animal() {
            @Override
            public void eat() {
                System.out.println("匿名内部类,动物会吃");
            }
        });
        //方式2:Lambda表达式,体现的是面向函数式编程,即:只关心做什么,不关心怎么做,需要创建什么对象,重写什么方法由系统保证。
        printAnimal(() -> {
            System.out.println("Lambda表达式,动物会吃");
        });
    }

    public static void printAnimal(Animal an) {
        an.eat();
    }
}
无参无返回值的方法
package com.ithiema.api.demo;
//Eatable 接口 
public interface Eatable {
    void eat();
}

package com.ithiema.api.demo;

public class Demo28 {
    public static void main(String[] args) {
        //方式1:匿名内部类
        useEatable(new Eatable(){
            @Override
            public void eat() {
                System.out.println("匿名内部类,吃饭");
            }
        });
        //方式2:Lambda表达式
        useEatable(() -> {
            System.out.println("吃饭");
        });
    }
    public static void useEatable(Eatable e){
        e.eat();
    }
}
有参无返回值的方法
package com.ithiema.api.demo;

public interface Flyable {
    void fly(String s);
}

package com.ithiema.api.demo;

public class Demo29 {
    public static void main(String[] args) {
        //匿名内部类
        useFlyable(new Flyable() {
            @Override
            public void fly(String s) {
                System.out.println("匿名内部类");
                System.out.println(s);
            }
        });

        System.out.println("--------------------");

        //Lambda表达式
        useFlyable((String s) -> {
            System.out.println("Lambda表达式");
            System.out.println(s);
        });

        //扩展:了解即可 ,调用:useFlyable(Flyable f,String s);
        useFlyable((String s) -> {
            System.out.println("Lambda表达式");
            System.out.println(s);
        }, "好好学习");
		
        useFlyable("好好学习",
                (String s) -> {
                    System.out.println("Lambda表达式");
                    System.out.println(s);
                }
        );
    }

    //入门版
    public static void useFlyable(Flyable f) {
        String s = "晴空万里";
        f.fly(s);
    }

    //进阶版
    public static void useFlyable(Flyable f, String s) {
        f.fly(s);
    }

    //再进阶版
    public static void useFlyable(String s, Flyable f) {
        f.fly(s);
    }
}
有参有返回值的方法
package com.ithiema.api.demo;

public interface Addable {
    int getSum(int x, int y);
}

package com.ithiema.api.demo;

public class Demo30 {
    public static void main(String[] args) {
        //匿名内部类
        useAddable(new Addable() {
            @Override
            public int getSum(int x, int y) {
                return x + y;
            }
        });
        //Lambda表达式
        useAddable((int x, int y) -> {
            System.out.println("Lambda表达式");
            return x + y;
        });

        //进阶版
        useAddable((int x, int y) -> {
                    System.out.println("Lambda表达式");
                    return x + y;
                },
                100,
                200
        );
    }

    public static void useAddable(Addable a) {
        int x = 10, y = 20;
        int sum = a.getSum(x, y);
        System.out.println(sum);
    }
    
	//进阶版
    public static void useAddable(Addable a, int x, int y) {
        int sum = a.getSum(x, y);
        System.out.println(sum);
    }
}
Lambda表达式的省略模式
  • 省略模式:
    1. 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
    2. 如果参数有且仅有一个,那么小括号可以省略
    3. 如果代码块的语句只有一条,可以省略大括号分号,和return关键字
package com.ithiema.api.demo;
//接口
public interface Eatable {
    void eat();
}

package com.ithiema.api.demo;

public interface Flyable {
    void fly(String s);
}

package com.ithiema.api.demo;

public interface Addable {
    //抽象方法 求和
    int getSum(int x, int y);
}


package com.ithiema.api.demo;

public class Demo31 {
    public static void main(String[] args) {
        //1. 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
        //不省略
        useFlyable((String s) -> {
            System.out.println(s);
        });

        useAddable((int x, int y) -> {
            return x + y;
        });

        //省略
        useFlyable((s) -> {
            System.out.println(s);
        });

        useAddable((x, y) -> {
            return x + y;
        });

        System.out.println("-------------------");

        //2. 如果参数有且仅有一个,那么小括号可以省略
        useFlyable(s -> {
            System.out.println(s);
        });
        //3. 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
        //不省略
        useEatable(() -> {
            System.out.println("吃饭");
        });

        useFlyable((String s) -> {
            System.out.println(s);
        });

        useAddable((int x, int y) -> {
            return x + y;
        });

        System.out.println("-------------------");

        //省略写法,要求如下三种写法能看懂,且尽量能写出来
        useEatable(() -> System.out.println("吃饭"));

        useFlyable(s -> System.out.println(s));

        useAddable((x, y) -> x + y);
    }

    public static void useEatable(Eatable e) {
        e.eat();
    }

    public static void useFlyable(Flyable f) {
        String s = "晴空万里";
        f.fly(s);
    }

    public static void useAddable(Addable a) {
        int x = 10, y = 20;
        int sum = a.getSum(x, y);
        System.out.println(sum);
    }
}

小括号可以省略
3. 如果代码块的语句只有一条,可以省略大括号分号,和return关键字

package com.ithiema.api.demo;
//接口
public interface Eatable {
    void eat();
}

package com.ithiema.api.demo;

public interface Flyable {
    void fly(String s);
}

package com.ithiema.api.demo;

public interface Addable {
    //抽象方法 求和
    int getSum(int x, int y);
}


package com.ithiema.api.demo;

public class Demo31 {
    public static void main(String[] args) {
        //1. 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
        //不省略
        useFlyable((String s) -> {
            System.out.println(s);
        });

        useAddable((int x, int y) -> {
            return x + y;
        });

        //省略
        useFlyable((s) -> {
            System.out.println(s);
        });

        useAddable((x, y) -> {
            return x + y;
        });

        System.out.println("-------------------");

        //2. 如果参数有且仅有一个,那么小括号可以省略
        useFlyable(s -> {
            System.out.println(s);
        });
        //3. 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
        //不省略
        useEatable(() -> {
            System.out.println("吃饭");
        });

        useFlyable((String s) -> {
            System.out.println(s);
        });

        useAddable((int x, int y) -> {
            return x + y;
        });

        System.out.println("-------------------");

        //省略写法,要求如下三种写法能看懂,且尽量能写出来
        useEatable(() -> System.out.println("吃饭"));

        useFlyable(s -> System.out.println(s));

        useAddable((x, y) -> x + y);
    }

    public static void useEatable(Eatable e) {
        e.eat();
    }

    public static void useFlyable(Flyable f) {
        String s = "晴空万里";
        f.fly(s);
    }

    public static void useAddable(Addable a) {
        int x = 10, y = 20;
        int sum = a.getSum(x, y);
        System.out.println(sum);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值