io流学习流程
说白了,本节的目的就是对 文件增删改查,先说一下学习顺序吧
- 定位文件 
  - File类可以定位文件:可以进行删除文件内容,读取文件本身信息等操作,但是不能读写文件内容
 
- 字符集 
  - 想要读取文件中的数据,就必须知道数据的底层形式(字节,字符)
 
- 读写文件内容 
  - IO流可以对硬盘中的文件进行读写
 
File类
File类学习流程
- 如何创建一个File类
- 使用File类,创建、删除、判断、获取
- 遍历文件夹
- 删除文件夹

File类概述和构造方法
-  File类介绍 - 它是文件和目录路径名的抽象表示
- 文件和目录是可以通过File封装成对象的
- 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已.它可以是存在的,也可以是不存在的.将来是要通过具体的操作把这个路径的内容转换为具体存在的
 
-  File类的构造方法 
| 方法名 | 说明 | 
|---|---|
| File(String pathname) | 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例 | 
| File(String parent, String child) | 从父路径名字符串和子路径名字符串创建新的 File实例 | 
| File(File parent, String child) | 从父抽象路径名和子路径名字符串创建新的 File实例 | 
- 示例代码
public class FileDemo01 {
    public static void main(String[] args) {
        //File(String pathname): 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例
        File f1 = new File("E:\\iotest\\java.txt");
        System.out.println(f1);
        //File(String parent, String child): 从父路径名字符串和子路径名字符串创建新的 File实例
        File f2 = new File("E:\\iotest","java.txt");
        System.out.println(f2);
        //File(File parent, String child): 从父抽象路径名和子路径名字符串创建新的 File实例
        File f3 = new File("E:\\iotest");
        File f4 = new File(f3,"java.txt");
        System.out.println(f4);
    }
}
绝对路径和相对路径
- 绝对路径
 是一个完整的路径,从盘符开始
- 相对路径
 是一个简化的路径,相对当前项目下的路径
- 示例代码
public class FileDemo02 {
    public static void main(String[] args) {
        // 是一个完整的路径,从盘符开始
        File file1 = new File("D:\\iotest\\a.txt");
        // 是一个简化的路径,从当前项目根目录开始
        File file2 = new File("a.txt");
        File file3 = new File("模块名\\a.txt");
    }
}
File类创建功能-通过File创建文件
- 方法分类
| 方法名 | 说明 | 
|---|---|
| public boolean createNewFile() | 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件 | 
| public boolean mkdir() | 创建由此抽象路径名命名的目录 | 
| public boolean mkdirs() | 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录 | 
- 示例代码
import java.io.File;
import java.io.IOException;
public class FileDemo02 {
    public static void main(String[] args) throws IOException {
        //需求1:我要在E:\\iotest目录下创建一个文件java.txt
        File f1 = new File("E:\\iotest\\java.txt");
        File parentFile = f1.getParentFile(); // E:\iotest 就是获取上一个文件夹/文件的内容
        System.out.println("创建文件夹,解决因为文件不存在发生的异常:" + parentFile.mkdirs());
        System.out.println("创建一个文件:" + f1.createNewFile());  //   //注意点:文件所在的文件夹必须要存在.
        System.out.println("--------");
        //需求2:我要在E:\\iotest目录下创建一个目录JavaSE,不能创建多级
        File f2 = new File("E:\\iotest\\JavaSE");
        System.out.println(f2.mkdir());
        System.out.println("--------");
        //需求3:我要在E:\\iotest目录下创建一个多级目录JavaWEB\\HTML
        File f3 = new File("E:\\iotest\\JavaWEB\\HTML");
        System.out.println(f3.mkdirs());
        System.out.println("--------");
        // 获取当前模块路径
        String currentPath=System.getProperty("user.dir");
      
        //需求4:我要在当前模块目录下创建一个多级目录JavaWEB\\HTML
        File f4 = new File("iotest4\\java.txt");
        if( f4.getParentFile() != null &&  f4.getParentFile().mkdirs()){
            System.out.println("成功创建文件夹");
        }
        System.out.println("创建一个文件:" + f4.createNewFile());
    }
}
File类删除功能
Java中File.delete删除当前文件或者文件夹,java删除的文件,将会直接删除,不会进入回收站。
如果删除的是文件夹,则该文件必须为空,如果要删除一个非空的文件夹,则需要首先删除该文件夹下面每个文件和文件夹,才可以删除。
- 方法分类
| 方法名 | 说明 | 
|---|---|
| public boolean delete() | delete方法只能删除文件和空文件夹 | 
- 示例代码
public class FileDemo03 {
    public static void main(String[] args) throws IOException {
        // 获取当前模块路径
        String currentPath=System.getProperty("user.dir");
        // 删除文件
        File f1 = new File("iotest\\java.txt");
        f1.delete();
        // 删除目录
        File f2 = new File("iotest");
        f2.delete();
    }
}
File类判断和获取功能
- 判断功能
| 方法名 | 说明 | 
|---|---|
| public boolean isDirectory() | 测试此抽象路径名表示的File是否为目录 | 
| public boolean isFile() | 测试此抽象路径名表示的File是否为文件 | 
| public boolean exists() | 测试此抽象路径名表示的File是否存在 | 
- 获取功能
| 方法名 | 说明 | 
|---|---|
| public String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 | 
| public String getPath() | 将此抽象路径名转换为路径名字符串 | 
| public String getName() | 返回由此抽象路径名表示的文件或目录的名称 | 
| public File[] listFiles() | 返回此抽象路径名表示的目录中的文件和目录的File对象数组 | 
- 示例代码
public class FileDemo04 {
    public static void main(String[] args) {
        //创建一个File对象
        File f = new File("myFile\\java.txt");
//        public boolean isDirectory():测试此抽象路径名表示的File是否为目录
//        public boolean isFile():测试此抽象路径名表示的File是否为文件
//        public boolean exists():测试此抽象路径名表示的File是否存在
        System.out.println(f.isDirectory());
        System.out.println(f.isFile());
        System.out.println(f.exists());
//        public String getAbsolutePath():返回此抽象路径名的绝对路径名字符串
//        public String getPath():将此抽象路径名转换为路径名字符串
//        public String getName():返回由此抽象路径名表示的文件或目录的名称
        System.out.println(f.getAbsolutePath());
        System.out.println(f.getPath());
        System.out.println(f.getName());
        System.out.println("--------");
//        public File[] listFiles():返回此抽象路径名表示的目录中的文件和目录的File对象数组
        File f2 = new File("E:\\iotest");
        File[] fileArray = f2.listFiles();
        for(File file : fileArray) {
//            System.out.println(file);
//            System.out.println(file.getName());
            if(file.isFile()) {
                System.out.println(file.getName());
            }
        }
    }
}
File类练习-遍历文件夹下所有文件-控制台以树形展示
- 案例需求
 遍历文件夹下所有文件
 打印格式如下:
filemodule
|_aaa
|__a.txt
|___111.txt					0MB
|__b.txt					0MB
- 代码实现
import java.io.File;
class FileSystem {
    public static void main(String[] args) {
        File f = new File("filemodule");// 指定文件位置
        System.out.println(f.getName());// 打印在这个文件下地文件夹;
        tree(f, 1);// 方法!进入子文件夹中 并打印子文件名
    }
    private static void tree(File f, int level) {
        File[] childs = f.listFiles();// 返回一个抽象路径名数组,这些路径名表示此抽象路径名所表示目录中地文件
        for (int i = 0; i < childs.length; i++) {
            // 打印前缀
            for (int j = 0; j < level; j++) {
                if (j == 0) {
                    System.out.print("|_");
                } else {
                    System.out.print("_");
                }
            }
            if (childs[i].isDirectory()) { //
                System.out.println(childs[i].getName());// 打印子文件地名字
                tree(childs[i], level + 1);
            } else {
                System.out.println(childs[i].getName() + "\t\t\t\t\t" + childs[i].length() / 1024 / 1024 + "MB");// 如果是文件把大小也打印出来
            }
        }
    }
}
File类练习-删除文件夹所有内容
- 案例需求
 删除一个多级文件夹
- 实现步骤
-  
  - 定义一个方法,接收一个File对象
- 遍历这个File对象,获取它下边的每个文件和文件夹对象
- 判断当前遍历到的File对象是文件还是文件夹
- 如果是文件夹,递归调用自己,将当前遍历到的File对象当做参数传递
- 将得到的文件或者空目录删除
- 参数传递过来的文件夹File对象已经处理完成,最后直接删除这个空文件夹
 
- 代码实现
import java.io.File;
public class Test2 {
    public static void main(String[] args) {
        /**
         * delete方法只能删除文件和空文件夹
         * 如果现在要删除一个有内容的文件夹
         *      先删掉这个文件夹里面所有的内容
         *      最后再删除这个文件夹
         */
        File src = new File("iotest2");
        remove(src);
    }
    /**
     * 删除指定文件夹下的全部内容
     *
     * @param file
     */
    public static void remove(File file) {
        File[] files = file.listFiles();//将file子目录及子文件放进文件数组
        if (files != null) {//如果包含文件进行删除操作
            for (int i = 0; i < files.length; i++) {
                if (files[i].isDirectory()) {//通过递归方法删除子目录的文件
                    remove(files[i]);
                }
                files[i].delete();//删除子目录/子文件
            }
            file.delete(); // 删除传过来的目录
        }
    }
}
字符集
字符集相关概念
我们是怎么把文字存储到计算机的
- 我们把文字转换成十进制的表现方式(比如“a”相当于97),然后转换成二进制,然后就可以存储到计算机。
什么是字符集?什么叫字符编码
字符集(Character set)是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等
- 字符集:规定了字符和字符码之间的对应关系。 可以将字符集理解成一个很大的表格,它列出了所有字符和二进制的对应关系,计算机显示文字或者存储文字,就是一个查表的过程。
- 字符编码:规定了一个字符码在计算机中如何存储。
常用字符集
-  ASCII字符集(American Standard Code for Information Interchange,美国信息交换标准代码) - 是最早产生的编码规范,包括数字、英文、符号
- 一个字节存储一个字符,也就是8位,总共可以表示128个字符信息(2^8=256,不包括负数)
 
-  GBK(即“国标”“扩展”汉语拼音的第一个字母) 字符集:全称《汉字内码扩展规范》 - window系统默认的码表。兼容ASCII码表,包括中日韩
- 一个中文以两个字节存储,英文1个字节
 
- Unicode字符集(又叫万国码、统一码):是计算机科学领域里的一项业界字符编码规范 
  - Unicode包含了全世界所有的字符,兼容ASCII 。Unicode最多可以保存4个字节容量的字符。也就是说,要区分每个字符,每个字符的地址需要4个字节。这是十分浪费存储空间的,于是,程序员就设计了几种字符编码方式,比如:UTF-8,UTF-16,UTF-32。最广为程序员使用的就是UTF-8,UTF-8是一种变长字符编码,注意:UTF-8不是编码规范,而是编码方式。 
    - utf-8:一个中文以3个字节存储,英文1个字节
 
 
- Unicode包含了全世界所有的字符,兼容ASCII 。Unicode最多可以保存4个字节容量的字符。也就是说,要区分每个字符,每个字符的地址需要4个字节。这是十分浪费存储空间的,于是,程序员就设计了几种字符编码方式,比如:UTF-8,UTF-16,UTF-32。最广为程序员使用的就是UTF-8,UTF-8是一种变长字符编码,注意:UTF-8不是编码规范,而是编码方式。 
    
小结
- 英文和数字在任何国家的字符集中都占一个字节(在任何国家的编码中都不会乱码)
- GBK中一个中文字符2个字节
- UTF-8中一个中文字符3个字节
- 编码前的字符集和编码后的字符集必须一致,否则乱码
- 中文的字节存储方式
- 用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
- 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
字符集的编码、解码操作
代码如下:
class FileSystem {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String str = "a1爱";
        byte[] gbks1 = str.getBytes();
        System.out.println(gbks1.length);
        System.out.println("以当前默认字符集编码:" + Arrays.toString(gbks1));
        System.out.println("以当前默认字符集解码:" + new String(gbks1));
        byte[] gbks2 = str.getBytes("GBK");
        System.out.println(gbks2.length);
        System.out.println("指定以GBK编码:" + Arrays.toString(gbks2));
        System.out.println("以当前默认字符集解码GBK编码的数据:" +  new String(gbks2));
        System.out.println("指定以GBK解码:" +  new String(gbks2,"GBK"));
        
    }
}
输出如下
5
以当前默认字符集编码:[97, 49, -25, -120, -79]
以当前默认字符集解码:a1爱
4
指定以GBK编码:[97, 49, -80, -82]
以当前默认字符集解码GBK编码的数据:a1��
指定以GBK解码:a1爱
汉字的存储和解析过程

IO流
IO流的使用流程
- 创建输入/输出流
- 读/写 操作
- 关闭输入/输出流
IO流的概念与作用
I/O是Input/Output的缩写, 用于处理设备之间的数据传输。如读/写文件,网络通讯等。
流是一种抽象概念,它代表了数据的无结构化传递。
Java程序中,对于数据的输入/输出操作以”流(stream)” 的方式进行,所以叫io流。java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。常见的应用: 文件复制; 文件上传; 文件下载
IO流的分类
按照数据的流向(方向)
- 输入流:读数据,是数据从硬盘文件读入到内存的过程 =》 硬盘->内容
- 输出流:写数据,是内存程序的数据从内存写出到硬盘文件的过程=》内存->硬盘
按照数据类型(单位)
- 字节流:以字节为单位,可以读写所有数据
- 字符流:以字符为单位
使用场景
如果操作的是纯文本,比如json文件,txt文件,优先使用字符流
- 怎么判断是纯文本呢? 能用记事本打开并且能看懂的就是纯文本
如果是图片、视频、音频等二进制文件,优先使用字节流
如果不确认文件类型,优先使用字节流,字节流是万能的流
IO流体系结构图
| 字节流(byte) | 字符流(char) | |||
|---|---|---|---|---|
| 分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 | 
| 抽象基类 | InputStream | OutputStream | Reader | Writer | 
| 操作文件 | FileInputStream | FileOutputStream | FileReader | FileWriter | 
| 操作数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter | 
| 缓冲流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter | 
| 转换流 | InputStreamReader | OutputStreamWriter | ||
| 对象操作流 | ObjectInputStream | ObjectOutputStream | ||
| 打印流 | PrintStream | PrintWriter | 
其中,转换流都是字节流转字符流。
- OutputStreamWriter : 字节输出流 -> 字符输出流;
- InputStreamReader : 字节输入流 -> 字符输入流
对象操作流也叫序列化流
字节流/字符流基础操作
字节输入流InputStream主要方法:
-  read() :从此输入流中读取一个数据字节。 
-  read(byte[] b) :从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。 
-  read(byte[] b, int off, int len) :从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。 
-  close():关闭此输入流并释放与该流关联的所有系统资源。 
字节输出流OutputStream主要方法:
- write(byte[] b) :将 b.length 个字节从指定 byte 数组写入此文件输出流中。
- write(byte[] b, int off, int len) :将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。
- write(int b) :将指定字节写入此文件输出流。
- flush() :刷新流,之后还可以继续写数据
- close() :关闭此输入流并释放与该流关联的所有系统资源。
字符输入流Reader主要方法:
- read():读取单个字符。
- read(char[] cbuf) :将字符读入数组。
- read(char[] cbuf, int off, int len) : 将字符读入数组的某一部分。
- read(CharBuffer target) :试图将字符读入指定的字符缓冲区。
- close() :关闭此流,但要先刷新它。
字符输出流Writer主要方法:
-  write(char[] cbuf) :写入字符数组。 
-  write(char[] cbuf, int off, int len) :写入字符数组的某一部分。 
-  write(int c) :写入单个字符。 
-  write(String str) :写入字符串。 
-  write(String str, int off, int len) :写入字符串的某一部分。 
-  flush() :刷新流,之后还可以继续写数据 
-  close() :关闭此流,但再次之前刷新它。 一旦流已关闭,进一步的write()或flush()调用将导致抛出IOException。 关闭以前关闭的流无效。 
另外,字符缓冲流还有两个独特的方法:
- BufferedWriter类newLine() :写入一个行分隔符。这个方法会自动适配所在系统的行分隔符。
- BufferedReader类readLine() :读取一个文本行。
注意:输出流必须刷新才能写入,当然也可以直接调用
close(),他里面包含了flush,但是使用close()关闭流后就不能在使用了如上,常用方法
输入流都有read方法
输出流都有write,flush方法
字节输入流读文件
代码如下:
public class ByteInputStreamTest1 {
    public static void main(String[] args) throws IOException {
        /**
         * 字节输入流的使用方式
         */
        // 1、创建输入流 => InputStream
        InputStream fileInputStream = new FileInputStream("aa.txt"); // abc123测试
        // 2、读文件
        // read() :从此输入流中读取一个数据字节。
        int read = fileInputStream.read();
        System.out.println(read); // 97
        System.out.println((char) read); // a
        byte[] buffered = new byte[3];
        // read(byte[] b) :从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
        //int read1 = fileInputStream.read(buffered);
        //System.out.println(read1); // 读取的字符数量 => 3
        //System.out.println(Arrays.toString(buffered)); // [98, 99, 49]
        //System.out.println(new String(buffered)); // bc1
        // read(byte[] b, int off, int len) :从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。
        int read2 = fileInputStream.read(buffered, 0, 2);
        System.out.println(read2); // 2
        System.out.println(Arrays.toString(buffered)); // [98, 99, 0]
        System.out.println(new String(buffered)); // bc 
        // 如果没有读取到呢吗返回-1
        // 3、关闭资源
        fileInputStream.close();
    }
}
字符输入流读文件
- 介绍
 Reader: 用于读取字符流的抽象父类
 FileReader: 用于读取字符流的常用子类
- 构造方法
| 方法名 | 说明 | 
|---|---|
| FileReader(File file) | 在给定从中读取数据的 File 的情况下创建一个新 FileReader | 
| FileReader(String fileName) | 在给定从中读取数据的文件名的情况下创建一个新 FileReader | 
- 成员方法
| 方法名 | 说明 | 
|---|---|
| int read() | 一次读一个字符数据 | 
| int read(char[] cbuf) | 一次读一个字符数组数据 | 
- 代码演示
import java.io.FileReader;
import java.io.IOException;
public class InputStreamReaderDemo {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("myCharStream\\b.txt");
        
        //int read():一次读一个字符数据
        int ch;
        while ((ch = fr.read()) != -1) {
            System.out.print((char) ch);
        }
        
        // int read(char[] cbuf):一次读一个字符数组数据
        char[] chs = new char[1024];
        int len;
        while ((len = fr.read(chs)) != -1) {
            System.out.print(new String(chs, 0, len));
        }
        // 释放资源       
         fr.close();  
    }
}
字节输出流写数据
-  使用字节输出流写数据的步骤 - 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
- 调用字节输出流对象的写数据方法
- 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
 
-  写数据的方法分类 
| 方法名 | 说明 | 
|---|---|
| void write(int b) | 将指定的字节写入此文件输出流 一次写一个字节数据 | 
| void write(byte[] b) | 将 b.length字节从指定的字节数组写入此文件输出流 一次写一个字节数组数据 | 
| void write(byte[] b, int off, int len) | 将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流 一次写一个字节数组的部分数据 | 
- 示例代码
package ioreview.bytestream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamDemo02 {
    public static void main(String[] args) throws IOException {
        // 1、创建输出流
        //FileOutputStream(String name):创建文件输出流以指定的名称写入文件
        //FileOutputStream fos = new FileOutputStream("fos.txt");
        // FileOutputStream(File file):创建文件输出流以写入由指定的 File对象表示的文件
        FileOutputStream fos = new FileOutputStream(new File("fos.txt"));
        // 2、写入数据
        // void write(int b):将指定的字节写入此文件输出流
        fos.write(97);
        fos.write(98);
        fos.write(99);
        //void write(byte[] b):将 b.length字节从指定的字节数组写入此文件输出流
        byte[] bys = {97, 98, 99};
        fos.write(bys);
        //byte[] getBytes():返回字符串对应的字节数组
        byte[] bys2 = "abc".getBytes();
        fos.write(bys2);
        //   void write(byte[] b, int off, int len):将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流
        fos.write(bys, 0, bys.length);
        fos.write(bys, 1, 1);
        // fos.flush(); //	刷新流,之后还可以继续写数据
        // 3、关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
        fos.close();
    }
}
字符输出流写文件
- 介绍
 Writer: 用于写入字符流的抽象父类
 FileWriter: 用于写入字符流的常用子类
- 构造方法
| 方法名 | 说明 | 
|---|---|
| FileWriter(File file) | 根据给定的 File 对象构造一个 FileWriter 对象 | 
| FileWriter(File file, boolean append) | 根据给定的 File 对象构造一个 FileWriter 对象 | 
| FileWriter(String fileName) | 根据给定的文件名构造一个 FileWriter 对象 | 
| FileWriter(String fileName, boolean append) | 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象 | 
- 成员方法
| 方法名 | 说明 | 
|---|---|
| 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() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 | 
- 代码演示
import java.io.FileWriter;
import java.io.IOException;
public class OutputStreamWriterDemo {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("myCharStream\\a.txt");
        //void write(int c):写一个字符
        fw.write(97);
        fw.write(98);
        fw.write(99);
        // void writ(char[] cbuf):写入一个字符数组        
        char[] chs = {'a', 'b', 'c', 'd', 'e'};
        fw.write(chs);
        // void write(char[] cbuf, int off, int len):写入字符数组的一部分
        fw.write(chs, 0, chs.length);
        fw.write(chs, 1, 3);
        // void write(String str):写一个字符串
        fw.write("abcde");
        // void write(String str, int off, int len):写一个字符串的一部分
        fw.write("abcde", 0, "abcde".length());
        fw.write("abcde", 1, 3);
        // 释放资源
        fw.close();
    }
}
异常处理-处理释放资源
try-catch-finally
- 异常处理格式 
  - try-catch-finally
 
try{	可能出现异常的代码;}catch(异常类名 变量名){	异常的处理代码;}finally{	执行所有清除操作;}
- 示例代码
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamDemo04 {
    public static void main(String[] args) {
        //加入finally来实现释放资源      
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("myByteStream\\fos.txt");
            fos.write("hello".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //最后先关闭输出,在关闭输入,防止空指针异常加入if
                if (fos != null) fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
try-catch-resource
jdk1.7后增加了try-with-resources,他是一个声明一个或多个资源的try语句,且()中只能放置资源对象,否则报错。一个资源作为一个对象,必须在程序结束之后关闭。try-with-resources语句确保在语句的最后每个资源都被关闭,任何实现了java.lang.AutoCloseable和java.io.Closeable的对象都可以使用try-with-resource来实现异常处理和关闭资源。
- 异常处理格式 
  - try-catch-resource
 
try(定义流对象){
}catch(){
}
- 示例代码
public class Test9 {
//JDK7改进方案
    public static void main(String[] args) {
        try (//这里只能放置资源对象,用完会自动关闭,自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
             InputStream io = new FileInputStream("src/aaa.txt");
             OutputStream ot = new FileOutputStream("D:/www.txt");
             //int a=1;//报错这里只能写资源
        ) {
            byte[] arr = new byte[1024];
            int len;
            while ((len = io.read(arr)) != -1) {
                ot.write(arr, 0, len);
            }
            System.out.println("文件拷贝成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
输出流写数据-换行/追加数据
-  写数据如何实现换行 - windows:\r\n
- linux:\n
- mac:\r
 
-  写数据如何实现追加写入 - public FileOutputStream(String name,boolean append)
- 创建文件输出流以指定的名称写入文件。如果第二个参数为true ,则字节将写入文件的末尾而不是开头
 
-  示例代码 
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamDemo03 {
    public static void main(String[] args) throws IOException {
        //创建字节输出流对象
        //        FileOutputStream fos = new FileOutputStream("fos.txt");
        FileOutputStream fos = new FileOutputStream("fos.txt", true);
        // 写数据
        for (int i = 0; i < 10; i++) {
            fos.write("hello".getBytes());
            fos.write("\r\n".getBytes());
        }
        // 释放资源
        fos.close();
    }
}
如何使用字节输入流读取中文内容输出不乱码?
- 定义一个与文件一样大的字节数组,一次性读取文件全部字节,但是如果文件过大,字节数据可能溢出
字节流复制练习
字节文件输入/输出流
代码
package ioreview.bytestream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
 * @author 韦存滨
 * @className Test1
 * @description 文件字节输入/输出流 完成文件复制
 * @date 2023年02月11日 23:17
 */
public class Test1 {
    public static void main(String[] args) {
        // 复制 - 其实下载也相当于一个复制,从网下下载到本地
        // 1、创建输出流/输出流
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            fileInputStream = new FileInputStream("aa.txt");
            fileOutputStream = new FileOutputStream("bb.txt");
            // 2、读/写数据
            String data = "";
            byte[] bytes = new byte[1024];
            int length;
            while ((length = fileInputStream.read(bytes)) != -1) {
                // 输出文件到
                fileOutputStream.write(bytes, 0, length);
                data += new String(bytes);
            }
            System.out.println("输出的文件内容为:" + data);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 3、关闭流
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
            } catch (Exception e) {
            }
        }
    }
}
字节数组输入/输出流
代码
package ioreview.bytestream;
import java.io.*;
/**
 * @author 韦存滨
 * @className Test1
 * @description 字节数组输入/输出流 完成文件复制
 * @date 2023年02月11日 23:17
 */
public class Test2 {
    public static void main(String[] args) {
        // 1、创建输出流/输出流
        ByteArrayInputStream byteArrayInputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            String data = "abc123测试";
            byte[] dataBytes = data.getBytes();
            byteArrayInputStream = new ByteArrayInputStream(dataBytes);
            byteArrayOutputStream = new ByteArrayOutputStream();
            // 2、读/写数据
            byte[] bytes = new byte[3];
            int length;
            while ((length = byteArrayInputStream.read(bytes)) != -1) {
                byteArrayOutputStream.write(bytes, 0, length);
                System.out.println(byteArrayOutputStream);
            }
            // 2.1、获取文件内容
            byte[] bytes1 = byteArrayOutputStream.toByteArray();
            String s = new String(bytes1);
            System.out.println("读取的内容是:" + s);
            // 或者直接输出
            System.out.println("读取的内容是:" + byteArrayInputStream.toString());
            int size = byteArrayOutputStream.size();
            // 将此 byte 数组输出流的
            //    count 字段重置为零,从而丢弃输出流中目前已累积的所有输出。通过重新使用已分配的缓冲区空间,
            //    可以再次使用该输出流
            //byteArrayOutputStream.reset();
            // 2.2、将此数组输出流的全部内容写入到指定的输出流参数中
            FileOutputStream fileOutputStream = new FileOutputStream("cc88.txt");
            byteArrayOutputStream.writeTo(fileOutputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (byteArrayInputStream != null) {
                    byteArrayInputStream.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
            } catch (Exception e) {
            }
        }
    }
}
字节缓冲输入/输出流
代码
package ioreview.bytestream;
import java.io.*;
/**
 * @author 韦存滨
 * @className Test1
 * @description 字节缓冲输入/输出流 完成文件复制
 * @date 2023年02月11日 23:17
 */
public class Test3 {
    public static void main(String[] args) {
        // 1、创建输出流/输出流
        BufferedInputStream bufferedInputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        try {
            bufferedInputStream = new BufferedInputStream(new FileInputStream("aa.txt"));
            bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("bb.txt"));
            String data = "";
            byte[] bytes = new byte[1024];
            int length;
            while ((length = bufferedInputStream.read(bytes)) != -1) {
                data += new String(bytes);
                bufferedOutputStream.write(bytes, 0, length);
            }
            System.out.println("读取的内容是:" + data);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 3、关闭流
            try {
                if (bufferedInputStream != null) {
                    bufferedInputStream.close();
                }
                if (bufferedOutputStream != null) {
                    bufferedOutputStream.close();
                }
            } catch (Exception e) {
            }
        }
    }
}
字节缓冲流——old
缓冲流的主要作用就是提高流的读取,写入效率。
- 操作方式:字节流字节操作文件,字节缓冲流先将数据添加到缓冲区,再将数据写入到文件中(或者读取文件);
- 效率:字节缓冲流的效率要高于字节流;(例子:把一堆砖头从A地搬往B地,一块一块的搬(字节流)的效率要远低于 先把砖头装进小推车再运往B地(缓冲流))
字节缓冲流构造方法
- 构造方法:
| 方法名 | 说明 | 
|---|---|
| BufferedOutputStream(OutputStream out) | 该类实现缓冲输出流.通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用 | 
| BufferedInputStream(InputStream in) | 创建BufferedInputStream将创建一个内部缓冲区数组.当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节 | 
- 示例代码
public class CopyAviDemo {
    public static void main(String[] args) throws IOException {
        //复制视频
//        method1();
      	 method2();
    }
    //字节缓冲流一次读写一个字节数组
    public static void method2() throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\iotest\\字节流复制图片.avi"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\字节流复制图片.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 method1() throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\iotest\\字节流复制图片.avi"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\字节流复制图片.avi"));
        int by;
        while ((by=bis.read())!=-1) {
            bos.write(by);
        }
        bos.close();
        bis.close();
    }
}
案例-字节缓冲流复制视频
-  案例需求 
 把“E:\itcast\字节流复制图片.avi”复制到模块目录下的“字节流复制图片.avi”
-  实现步骤 
-  
  - 根据数据源创建字节输入流对象
- 根据目的地创建字节输出流对象
- 读写数据,复制视频
- 释放资源
 
-  代码实现 
import java.io.*;
public class FileCopy {
    public static void main(String[] args) {
        /**
         * 复制文件
         */
        BufferedInputStream in = null;
        BufferedOutputStream out = null;
        try {
            //abc.txt ->内存
            in = new BufferedInputStream(new FileInputStream("aa.jpg"));
            //内存 ->xyz.txt
            out = new BufferedOutputStream(new FileOutputStream("cc.jpg"));
            //开辟缓冲区 数组 将文件内容在数组中读取
            byte[] buf = new byte[1024];
            int len;
            // int read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
            //在不等于-1 说明文件里还有字节 一直输入直到 返回值为-1
            while ((len = in.read(buf)) != -1) {
                // void write(byte[]b,int off,int len)将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流 一次写一个字节数组的部分数据
                out.write(buf, 0, len);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //最后先关闭输出,在关闭输入,防止空指针异常加入if
                if (out != null) out.close();
                if (in != null) in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
字符流
为什么会出现字符流
- 字符流的介绍 
  - 由于字节流操作中文不是特别的方便,所以Java就提供字符流
- 字符流 = 字节流 + 编码表
 
字符串中的编码解码问题
- 相关方法
| 方法名 | 说明 | 
|---|---|
| byte[] getBytes() | 使用平台的默认字符集将该 String编码为一系列字节 | 
| byte[] getBytes(String charsetName) | 使用指定的字符集将该 String编码为一系列字节 | 
| String(byte[] bytes) | 使用平台的默认字符集解码指定的字节数组来创建字符串 | 
| String(byte[] bytes, String charsetName) | 通过指定的字符集解码指定的字节数组来创建字符串 | 
- 代码演示
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class StringDemo {
    public static void main(String[] args) throws UnsupportedEncodingException {
        //定义一个字符串
        String s = "123中";
        byte[] bys1 = s.getBytes();  // 当前默认编码方式
        byte[] bys2 = s.getBytes("UTF-8");
        byte[] bys3 = s.getBytes("GBK");
        System.out.println(Arrays.toString(bys1)); // [49, 50, 51, -28, -72, -83]
        System.out.println(Arrays.toString(bys2)); // [49, 50, 51, -28, -72, -83]
        System.out.println(Arrays.toString(bys3)); // [49, 50, 51, -42, -48]
        String ss1 = new String(bys1); // 123中
        String ss2 = new String(bys1, "UTF-8"); // 123中
        String ss3 = new String(bys1, "GBK"); // 123涓�
        System.out.println(ss1);
        System.out.println(ss2);
        System.out.println(ss3);
    }
}
字符缓冲流
-  字符缓冲流介绍 - BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途
- BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途
 
-  构造方法 
| 方法名 | 说明 | 
|---|---|
| BufferedWriter(Writer out) | 创建字符缓冲输出流对象 | 
| BufferedReader(Reader in) | 创建字符缓冲输入流对象 | 
- 代码演示
import java.io.*;
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();
    }
}
字符缓冲流特有功能
- 方法介绍
 BufferedWriter:
 BufferedReader:
| 方法名 | 说明 | 
|---|---|
| void newLine() | 写一行行分隔符,行分隔符字符串由系统属性定义 | 
| 方法名 | 说明 | 
|---|---|
| String readLine() | 读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符如果流的结尾已经到达,则为null | 
- 代码演示
import java.io.*;
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();
    }
}
转换流
字符流中和编码解码问题相关的两个类
- InputStreamReader:是从字节流到字符流的桥梁,父类是Reader
 它读取字节,并使用指定的编码将其解码为字符
 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
- OutputStreamWriter:是从字符流到字节流的桥梁,父类是Writer
 是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
转换流读写数据
- 构造方法
| 方法名 | 说明 | 
|---|---|
| InputStreamReader(InputStream in) | 使用默认字符编码创建InputStreamReader对象 | 
| InputStreamReader(InputStream in,String chatset) | 使用指定的字符编码创建InputStreamReader对象 | 
| OutputStreamWriter(OutputStream out) | 使用默认字符编码创建OutputStreamWriter对象 | 
| OutputStreamWriter(OutputStream out,String charset) | 使用指定的字符编码创建OutputStreamWriter对象 | 
- 代码演示
import java.io.*;
public class ConversionStreamDemo {
    public static void main(String[] args) throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("fos.txt"));
        //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("fos.txt"), "GBK");
        osw.write("中国");
        osw.close();
        InputStreamReader isr = new InputStreamReader(new FileInputStream("fos.txt"));
        //InputStreamReader isr = new InputStreamReader(new FileInputStream("fos.txt"), "GBK");
        // 一次读取一个字符数据
        int ch;
        while ((ch = isr.read()) != -1) {
            System.out.print((char) ch);
        }
        isr.close();
    }
}
对象操作流
对象序列化流
序列化:将对象的状态信息转换为可以存储或传输的形式的过程。程序通过序列化把Java对象转换成二进制字节流,然后就可以把二进制字节流写入网络或磁盘。
反序列化:读取磁盘或网络中的二进制字节流数据,转化为Java对象
序列化流:ObjectOutputStream
反序列化流:ObjectInputStream
注意事项
- 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口
- Serializable是一个标记接口,实现该接口,不需要重写任何方法
-  对象序列化介绍 - 对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
- 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息
- 字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
- 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
 
-  对象序列化流: ObjectOutputStream - 将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象
 
-  构造方法 
| 方法名 | 说明 | 
|---|---|
| ObjectOutputStream(OutputStream out) | 创建一个写入指定的OutputStream的ObjectOutputStream | 
- 序列化对象的方法
| 方法名 | 说明 | 
|---|---|
| void writeObject(Object obj) | 将指定的对象写入ObjectOutputStream | 
- 示例代码
 学生类
 测试类
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;
    }
    @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("a.txt"));
        // 创建对象
        Student s = new Student("佟丽娅", 30);
        // void writeObject(Object obj):将指定的对象写入ObjectOutputStream
        oos.writeObject(s);
        // 释放资源
        oos.close();
    }
}
对象反序列化流
-  对象反序列化流: 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
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
        //Object readObject():从ObjectInputStream读取一个对象
        Object obj = ois.readObject();
        Student s = (Student) obj;
        System.out.println(s.getName() + "," + s.getAge());
        ois.close();
    }
}
serialVersionUID&transient
-  serialVersionUID -  用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢? - 会出问题,会抛出InvalidClassException异常
 
-  如果出问题了,如何解决呢? - 重新序列化
 
-  给对象所属的类加一个serialVersionUID - private static final long serialVersionUID = 42L;
 
 
-  
-  transient - 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢? 
    - 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
 
 
- 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢? 
    
-  示例代码 
 学生类
 测试类
public class Student implements Serializable {
    //private static final long serialVersionUID = 42L;
    private String name;
    //private int age;
    private transient 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 ObjectStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        write();
        read();
    }
    //序列化
    private static void write() throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt"));
        Student s = new Student("佟丽娅", 20);
        oos.writeObject(s);
        oos.close();
    }
    //反序列化
    private static void read() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt"));
        Object obj = ois.readObject();
        Student s = (Student) obj;
        System.out.println(s.getName() + "," + s.getAge());
        ois.close();
    }
}
对象操作流练习
-  案例需求 
 创建多个学生类对象写到文件中,再次读取到内存中
-  实现步骤 
-  
  - 创建序列化流对象
- 创建多个学生对象
- 将学生对象添加到集合中
- 将集合对象序列化到文件中
- 创建反序列化流对象
- 将文件中的对象数据,读取到内存中
 
-  代码实现 
 学生类
 测试类
public class Student implements Serializable{
    
    private static final long serialVersionUID = 2L;
    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 class Demo03 {
    /**
     *  read():
     *      读取到文件末尾返回值是 -1
     *  readLine():
     *      读取到文件的末尾返回值 null
     *  readObject():
     *      读取到文件的末尾 直接抛出异常
     *  如果要序列化的对象有多个,不建议直接将多个对象序列化到文件中,因为反序列化时容易出异常
     *      建议: 将要序列化的多个对象存储到集合中,然后将集合序列化到文件中
     */
    public static void main(String[] args) throws Exception {
        /*// 序列化
        //1.创建序列化流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myCode\\oos.txt"));
        ArrayList<Student> arrayList = new ArrayList<>();
        //2.创建多个学生对象
        Student s = new Student("佟丽娅",30);
        Student s01 = new Student("佟丽娅",30);
        //3.将学生对象添加到集合中
        arrayList.add(s);
        arrayList.add(s01);
        //4.将集合对象序列化到文件中
        oos.writeObject(arrayList);
        oos.close();*/
        // 反序列化
      	//5.创建反序列化流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myCode\\oos.txt"));
      	//6.将文件中的对象数据,读取到内存中
        Object obj = ois.readObject();
        ArrayList<Student> arrayList = (ArrayList<Student>)obj;
        ois.close();
        for (Student s : arrayList) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}
打印流
Properties集合
Properties作为Map集合的使用
-  Properties介绍 - 不属于io流体系,
- 是一个Map体系的集合类
- Properties可以保存到流中或从流中加载
- 属性列表中的每个键及其对应的值都是一个字符串
 
-  Properties基本使用 
package cc;
import java.util.Properties;
import java.util.Set;
public class PropertiesDemo01 {
    public static void main(String[] args) {
        //创建集合对象
//        Properties<String,String> prop = new Properties<String,String>(); //错误
        Properties prop = new Properties();
        //存储元素
        prop.put("itheima001", "佟丽娅");
        prop.put("itheima002", "赵丽颖");
        prop.put("itheima003", "刘诗诗");
 
        //遍历集合
        Set<Object> keySet = prop.keySet();
        for (Object key : keySet) {
            Object value = prop.get(key);
            System.out.println(key + "," + value);
        }
    }
}
Properties作为Map集合的特有方法
- 特有方法
| 方法名 | 说明 | 
|---|---|
| Object setProperty(String key, String value) | 设置集合的键和值,都是String类型,底层调用 Hashtable方法 put | 
| String getProperty(String key) | 使用此属性列表中指定的键搜索属性 | 
| Set stringPropertyNames() | 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 | 
- 示例代码
package cc;
import java.util.Properties;
import java.util.Set;
public class PropertiesDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        Properties prop = new Properties();
        //Object setProperty(String key, String value):设置集合的键和值,都是String类型
        prop.setProperty("itheima001", "佟丽娅");
        prop.setProperty("itheima002", "赵丽颖");
        prop.setProperty("itheima003", "刘诗诗");
        //String getProperty(String key):使用此属性列表中指定的键搜索属性
//        System.out.println(prop.getProperty("itheima001"));
//        System.out.println(prop.getProperty("itheima0011"));
//        System.out.println(prop);
        //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);
        }
    }
}
Properties和IO流相结合的方法
- 和IO流结合的方法
| 方法名 | 说明 | 
|---|---|
| void load(Reader reader) | 从输入字符流读取属性列表(键和元素对) | 
| void store(Writer writer, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流 | 
- 示例代码
package cc;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
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("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("fw.txt");
        prop.store(fw, null);
        fw.close();
    }
}
Properties集合练习
-  案例需求 
 在Properties文件中手动写上姓名和年龄,读取到集合中,将该数据封装成学生对象,写到本地文件
-  实现步骤 
-  
  - 创建Properties集合,将本地文件中的数据加载到集合中
- 获取集合中的键值对数据,封装到学生对象中
- 创建序列化流对象,将学生对象序列化到本地文件中
 
-  代码实现 
 学生类
 测试类
public class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    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 Test {
    public static void main(String[] args) throws IOException {
      	//1.创建Properties集合,将本地文件中的数据加载到集合中
        Properties prop = new Properties();
        FileReader fr = new FileReader("prop.properties");
        prop.load(fr);
        fr.close();
		//2.获取集合中的键值对数据,封装到学生对象中
        String name = prop.getProperty("name");
        int age = Integer.parseInt(prop.getProperty("age"));
        Student s = new Student(name,age);
		//3.创建序列化流对象,将学生对象序列化到本地文件中
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
        oos.writeObject(s);
        oos.close();
    }
}



















