Java:25-IO流

IO流

IO流的概念------------------------------
IO就是Input和Output的简写,也就是输入和输出的含义,也可以说读和写
IO流就是指读写数据时像流水一样从一端流到另外一端,因此得名为"流"
基本分类------------------------------
按照读写数据的基本单位不同,分为 字节流 和 字符流
其中字节流主要指以字节为单位进行数据读写的流,可以读写任意类型的文件,因为只要是字节形式的都可以接受
其中字符流主要指以字符(2个字节)为单位进行数据读写的流,只能读写文本文件,因为只接受字符
即有文字,没图片或者音频之类的
汉字一般占两个字节,即用字节流的话,一次只能读出来半个汉字,翻译时,若翻译不明白,就会乱码
即在读取有汉字或者其他特殊的这样的文件时,就用字符流,这样就可以读取完整的汉字或其他的内容,这就是字符流的意义
按照读写数据的方向不同,分为 输入流 和 输出流(站在程序的角度)
其中输入流主要指从文件中读取数据内容输入到程序中,也就是读文件
其中输出流主要指将程序中的数据内容输出到文件中,也就是写文件,所以我才说是站在程序的角度(不是文件的角度)

在这里插入图片描述

按照流的角色不同分为节点流和处理流
其中节点流主要指直接和输入输出源对接的流(看是否与文件直接关联)
其中处理流主要指需要建立在节点流的基础之上的流,如缓冲流
体系结构------------------------------

在这里插入图片描述

在这里插入图片描述

FileWriter类------------------------------
java.io.FileWriter类主要用于将文本内容写入到文本文件
public class FileWriter
extends OutputStreamWriter
public class OutputStreamWriter
extends Writer
public abstract class Writer
extends Object
implements Appendable, Closeable, Flushable
//能够被添加 char 序列和值的对象,如果某个类的实例打算接收取自 Formatter 的格式化输出
//那么该类必须实现 Appendable 接口
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
//Flushable是可以刷新的数据的目的地,调用flush方法将任何缓冲输出写入到依赖的流
常用的方法------------------------------
FileWriter(String fileName),根据参数指定的文件名构造对象
FileWriter(String fileName, boolean append),以追加的方式根据参数指定的文件名来构造对象
void write(int c),写入单个字符
void write(char[] cbuf, int off, int len),将指定字符数组中从偏移量off开始的len个字符写入此文件输出流
void write(char[] cbuf),将cbuf.length个字符从指定字符数组写入此文件输出流中
void flush(),刷新流
void close(),关闭流对象并释放有关的资源,自带刷新功能
package com.lagou.task17;

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

public class FileWriterTest {

    public static void main(String[] args) {
        // 选中代码后可以使用 ctrl+alt+t 来生成异常的捕获代码等
        FileWriter fw = null;
        try {
            // 1.构造FileWrite类型的对象与d:/a.txt文件关联
            // 若文件不存在,该流会自动创建新的空文件
            // 若文件存在,该流会清空文件中的原有内容
            //FileWriter(String fileName),根据参数指定的文件名构造对象
            //fw = new FileWriter("d:/ggg/a.txt");
            //用fw流指向这个文件,称fw为管道(流)
            // 以追加的方式创建对象去关联文件
            // 若文件不存在则自动创建新的空文件,若文件存在则保留原有数据内容
            //FileWriter(String fileName, boolean append),以追加的方式根据参数指定的文件名来构造对象
            fw = new FileWriter("d:/ggg/a.txt", true); //设置false就不会追加了,即清空数据,然后写入
            // 而是清空文件中的原有内容,在写入数据,而true就不会而是清空文件中的原有内容
            // 2.通过流对象写入数据内容  每当写入一个字符后则文件中的读写位置向后移动一位
            //给管道倒水(自己放入一个数据),即给流数据'a',流将数据,流(写)到文件里
            //void write(int c),写入单个字符
            fw.write('a');

            // 准备一个字符数组
            char[] cArr = new char[]{'h', 'e', 'l', 'l', 'o'};
            // 将字符数组中的一部分内容写入进去
            //void write(char[] cbuf, int off, int len)
            //将指定字符数组中从偏移量off开始的len个字符写入此文件输出流
            fw.write(cArr, 1, 3);  // ell
            // 将整个字符数组写进去
            //void write(char[] cbuf),将cbuf.length个字符从指定字符数组写入此文件输出流中
            fw.write(cArr); // hello

            // 刷新流
            //往文件写数据时,要通过流,在流的内部还会残留一部分数据
            //而他可以将该数据控干净,即刷新出去,扔到文件里
            //即将上一次没过去的数据放入文件
            //void flush(),刷新流
            fw.flush(); //一般有8192个字符的缓存区(一般其他的io基本都是这样的,当然可能也并非全是,因为是人写的代码,具体可以百度),如果没有这么多,那么不好写入文件,所以需要flush,注意:一般要输出的需要他,因为输入不需要,即输入流一般没有该方法
                     //一般只有缓冲流或者其他流需要缓冲(存)区,这里只是一个机制
            System.out.println("写入数据成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 3.关闭流对象并释放有关的资源
            if (null != fw) {
                try {
                    //断开流与文件的联系,自带刷新功能,但有可能会关不了,即还会用刷新流flush(),一般是类似的功能,或者里面调用了,这里了解即可
                    //void close(),关闭流对象并释放有关的资源,自带刷新功能
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

FileReader类------------------------------
java.io.FileReader类主要用于从文本文件读取文本数据内容
public class FileReader
extends InputStreamReader
public class InputStreamReader
extends Reader
public abstract class Reader
extends Object
implements Readable, Closeable
//Readable接口就是为了Scanner类专门创建的一个接口,使得Scanner的入口参数不必限于某个类
//实现Readable接口要只需是实现public int read(CharBuffer cb)方法,当方法返回-1时候Scanner类停止读取
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
常用的方法------------------------------
FileReader(String fileName),根据参数指定的文件名构造对象
int read(),读取单个字符的数据并返回,返回-1表示读取到末尾
int read(char[] cbuf, int offset, int length),从输入流中将最多len个字符的数据读入一个字符数组中
返回读取到的字符个数,返回-1表示读取到末尾,offset是数组的下标,依次往下放
int read(char[] cbuf),从此输入流中将最多 cbuf.length 个字符的数据读入字符数组中
返回读取到的字符个数,返回-1表示读取到末尾
void close(),关闭流对象并释放有关的资源
package com.lagou.task17;

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

public class FileCharCopyTest {

    public static void main(String[] args) {
        FileReader fr = null;
        FileWriter fw = null;
        //try,catch,finally里面创建的变量,不能用于他们之外,但之外的变量可以用
        //即可以说是他们三个空间不与其他人共享,就是相当于if语句的变量一样
        // 那么就可以说finally的值也影响不到他们两个
        try {
            // 1.创建FileReader类型的对象与d:/a.txt文件关联
            fr = new FileReader("d:/a.txt");
            //fr = new FileReader("d:/03 IO流的框架图.png");
            // 2.创建FileWriter类型的对象与d:/b.txt文件关联
            fw = new FileWriter("d:/b.txt");
            //fw = new FileWriter("d:/IO流的框架图.png");   拷贝图片文件失败!!!
            //虽然拷贝成功,但还是有些会损失,因为对于图片或视频等,字符流无法将他们变为可以知道的字符
            //即会有损失
            // 3.不断地从输入流中读取数据内容并写入到输出流中
            System.out.println("正在玩命地拷贝...");
            int res = 0;
            while ((res = fr.read()) != -1) {
                fw.write(res);
            }
            System.out.println("拷贝文件成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4.关闭流对象并释放有关的资源
            if (null != fw) {
                try {
                    fw.close();
                    //先创建后关闭,先创建的一般用到最后
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != fr) {
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

package com.lagou.task17;

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

public class FileReaderTest {

    public static void main(String[] args) {
        FileReader fr = null;

        try {
            // 1.构造FileReader类型的对象与d:/a.txt文件关联
            //fr = new FileReader("d:/a.txt");
            //FileReader(String fileName),根据参数指定的文件名构造对象
      //对于输出流来说,若文件存在,该流会清空文件中的原有内容(假设有内容b),而输入流不好
            fr = new FileReader("d:/b.txt");
            // 2.读取数据内容并打印
            /*
            //int read(),读取单个字符的数据并返回,返回-1表示读取到末尾
            int res = fr.read();
            System.out.println("读取到的单个字符是:" + (char)res); // 'a'
             */
               //注意:读取字符是从文件第一个内容开始的哦,且他们是直接当成字符的,而不是字符变成的十进制
            int res = 0;
            while ((res = fr.read()) != -1) {
                System.out.println("读取到的单个字符是:" + (char)res + ",对应的编号是:" + res);
            }

            // 准备一个字符数组来保存读取到的数据内容
            // char[] cArr = new char[5];
            // 期望读满字符数组中的一部分空间,也就是读取3个字符放入数组cArr中下标从1开始的位置上
            //int read(char[] cbuf, int offset, int length)
            //从输入流中将最多len个字符的数据读入一个字符数组中
            //返回读取到的字符个数,返回-1表示读取到末尾,offset是数组的下标,依次往下放
            /*int res = fr.read(cArr, 1, 3);
            System.out.println("实际读取到的字符个数是:" + res); // 3
            for (char cv : cArr) {
                System.out.println("读取到的单个字符是:" + (char)cv); // 啥也没有 a e l 啥也没有
            }*/

            // 期望读满整个字符数组
            //int read(char[] cbuf),从此输入流中将最多 cbuf.length 个字符的数据读入字符数组中
            //返回读取到的字符个数,返回-1表示读取到末尾
            /*int res = fr.read(cArr);
            System.out.println("实际读取到的字符个数是:" + res); // 5
            for (char cv : cArr) {
                System.out.println("读取到的单个字符是:" + (char)cv); // a e l l h
            }*/
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 3.关闭流对象并释放有关的资源
            if (null != fr) {
                try {
                    //void close(),关闭流对象并释放有关的资源
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

FileOutputStream类------------------------------
java.io.FileOutputStream类主要用于将图像数据之类的原始字节流写入到输出流中
public class FileOutputStream
extends OutputStream
public abstract class OutputStream
extends Object
implements Closeable, Flushable
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
//Flushable是可以刷新的数据的目的地,调用flush方法将任何缓冲输出写入到依赖的流
常用的方法------------------------------
FileOutputStream(String name),根据参数指定的文件名来构造对象
FileOutputStream(String name,boolean append),以追加的方式根据参数指定的文件名来构造对象
void write(int b),将指定字节写入此文件输出流
void write(byte[] b, int off, int len),将指定字节数组中从偏移量off开始的len个字节写入此文件输出流
void write(byte[] b),将 b.length 个字节从指定字节数组写入此文件输出流中
void flush(),刷新此输出流并强制写出任何缓冲的输出字节,注意是缓冲的,一般来说对于缓冲流来说需要他,否则需要封顶才会自动输出,或者操作关闭也行,那么也会操作写出
void close(),关闭流对象并释放有关的资源
FileInputStream类------------------------------
java.io.FileInputStream类主要用于从输入流中以字节流的方式读取图像数据等
public class FileInputStream
extends InputStream
public abstract class InputStream
extends Object
implements Closeable
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
常用的方法------------------------------
FileInputStream(String name),根据参数指定的文件路径名来构造对象
int read(),从输入流中读取单个字节的数据并返回,返回-1表示读取到末尾
int read(byte[] b, intoff, int len),从此输入流中将最多len个字节的数据读入字节数组中
返回读取到的字节个数(如果在读取过程中到顶端,结束读取,返回读取的字节数,下次就是-1了),返回-1表示读取到末尾
int read(byte[] b),从此输入流中将最多 b.length 个字节的数据读入字节数组中
返回读取到的字节个数,返回-1表示读取到末尾
void close(),关闭流对象并释放有关的资源
int available(),获取输入流所关联文件的大小
案例题目------------------------------
编程实现两个文件之间的拷贝功能
package com.lagou.task17;

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

public class FileByteCopyTest {

    public static void main(String[] args) {
        // 获取当前系统时间距离1970年1月1日0时0分0秒的毫秒数
        long g1 = System.currentTimeMillis();

        FileInputStream fis = null;
        FileOutputStream fos = null;

        try {
            //FileInputStream(Stringname),根据参数指定的文件路径名来构造对象
            // 1.创建FileInputStream类型的对象与d:/03 IO流的框架图.png文件关联
            fis = new FileInputStream("d:/03 IO流的框架图.png");
            //fis = new FileInputStream("d:/02_IO流的框架结构.mp4");
            // 2.创建FileOutputStream类型的对象与d:/IO流的框架图.png文件关联
            fos = new FileOutputStream("d:/IO流的框架图.png");
            //fos = new FileOutputStream("d:/IO流的框架结构.mp4");
            // 3.不断地从输入流中读取数据内容并写入到输出流中
            //若拷贝一半停止,那么有可能会播放,但有些损坏,也有可能播放不了,因为损坏到关键位置(一般是该关键位置)
            //而拷贝不同类型的,那么就会出错,因为解析不了(一般看后缀)
            System.out.println("正在玩命地拷贝...");
            // 方式一:以单个字节为单位进行拷贝,也就是每次读取一个字节后再写入一个字节
            // 缺点:文件稍大时,拷贝的效率很低,因为得到一个就要判断
            /*int res = 0;
            //int read(),从输入流中读取单个字节的数据并返回,返回-1表示读取到末尾
            while ((res = fis.read()) != -1) {
                fos.write(res);
            }*/
            // 方式二:准备一个和文件大小一样的缓冲区,一次性将文件中的所有内容取出到缓冲区然后一次性写入进去
             //这样就少了很多判断了
            // 缺点:若文件过大时,无法申请和文件大小一样的缓冲区,真实物理内存不足
            /*
            //int available(),获取输入流所关联文件的大小
            int len = fis.available();
            System.out.println("获取到的文件大小是:" + len);
            byte[] bArr = new byte[len];
            //int read(byte[] b),从此输入流中将最多 b.length 个字节的数据读入字节数组中
            //返回读取到的字节个数,返回-1表示读取到末尾
            int res = fis.read(bArr);
            System.out.println("实际读取到的文件大小是:" + res);
            fos.write(bArr);*/
            // 方式三:准备一个相对适当的缓冲区,分多次将文件拷贝完成
            byte[] bArr = new byte[1024];
            int res = 0;
            while ((res = fis.read(bArr)) != -1) {
                fos.write(bArr, 0, res);
            }

            System.out.println("拷贝文件成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4.关闭流对象并释放有关的资源
            if (null != fos) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != fis) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        long g2 = System.currentTimeMillis();
        System.out.println("使用文件流拷贝视频文件消耗的时间为:" + (g2-g1));  // 165
    }
}

接下来是非常重要的一点:
乱码:
乱码数据是当数据在显示设备(如计算机屏幕、手机屏幕、打印机等)上无法被正确解析或显示时,显示设备可能会用一些特殊的符号或图案来代替未能正确显示的字符,也就是说,乱码是系统自身存在的,并且可能通过某种运行指定到一个乱码
其他乱码:溢出问题,比如-1等等
要理解字节流和字符流的区别,只需要注意byte即可,一般情况下,字节流是将数据直接保存为byte的,并且按照java的系统编码,通常可能是ascii,而字符流则是根据字符的编码来进行
比如字节流写入一个字节,都会将数据变成byte后进行处理,这个时候可能由于编码不同,导致出现乱码数据,但是大多数情况下只是其他乱码,也就是说可能会识别成-1,一般当识别成-1时,就会终止写入,所以会造成文件变小
也有可能会识别成多个字节,那么就会造成拷贝时,可能会使得文件变大
当然,既然是乱码问题,那么字符流自然也是如此,也由于这样,导致反过来必然不会对应原来的,因为他本来就不能回到,因为是不可识别的,所以在这种情况下,就算你使用相同编码编码回去,可能也会操作文件大小的改变
即还是看你编码的方式,即可以说:字符流 = 字节流 + 编码方式(若是字节的默认编码表,读取的有些东西可能识别不了(一般指汉字咯),就会找相似,一般存在隐藏的-1)
这里的编码方式:JVM识别,可以对字符流的编码进行改变,在转换流后面可以指定编码
字节是程序最小单位(虽然有bit存在),虽然存在编码方式,但是这些并不能提供安全,因为某个黑客拿取你的数据,是可以测试出来的,所以需要进行加密,通常情况下我们可以使用自定义编码来操作,即部分操作编码,意思是:假设我有10个数据,前3个数据操作一种编码,后6个数据操作一种编码,由于数据是我们可以加上的,那么可以在服务器中加上数据时进行一个一个的加,那么在读取时可以选择反过来(写入使用字节,将字符变成字节存储,读取时读取字节,将字节变成字符显示,这个变成字节和字符的过程是我们自定义的,即手动将那几个部分进行处理),而黑客并不知道里面是如何反过来操作,只能统一进行处理,那么你要想反过来是基本不可能的,就算一个个的测试也不行,这比弱口令还要困难,所以大多数应用(包括网站或者APP等等)可能是操作自定义的编码的,当然,这还只是一层,可以选择再次进行同样的处理,并且还可以加密信息进行加盐等等,这个时候是非常难以破解,或者几乎不可能破解(之所以是几乎,因为内部人员或者代码是可以的,要不然数据是怎么给你的,源代码的处理不就是一种破解方式吗,只要有,那么就没有不可能破解的情况)
当然,无论是什么编码问题,只需要解码和编码一致即可,一开始的编码格式或者显示(统一编码作为称呼的)由当前写上的数(或者说文本)和当前编码影响的,也更加证明编码一致即可
通过上面的说明,那么我们也就可以知道,像图像什么的其实可以使用字符来处理,只需要编码方式完全参照系统编码,而不是通过对应的编码方式来变成字节,即只需要解码和编码一致即可
BufferedOutputStream类------------------------------
java.io.BufferedOutputStream类主要用于描述缓冲输出流,此时不用为写入的每个字节调用底层系统
public class BufferedOutputStream
extends FilterOutputStream
public class FilterOutputStream
extends OutputStream
public abstract class OutputStream
extends Object
implements Closeable, Flushable
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
//Flushable是可以刷新的数据的目的地,调用flush方法将任何缓冲输出写入到依赖的流
常用的方法------------------------------
BufferedOutputStream(OutputStream out),根据参数指定的引用来构造对象
BufferedOutputStream(OutputStream out, int size),根据参数指定的引用和缓冲区大小来构造对象
void write(int b)写入单个字节
void write(byte[] b, int off, int len),写入字节数组中的一部分数据
void write(byte[] b),写入参数指定的整个字节数组
void flush(),刷新流
void close(),关闭流对象并释放有关的资源
BufferedInputStream类------------------------------
java.io.BufferedInputStream类主要用于描述缓冲输入流
public class BufferedInputStream
extends FilterInputStream
public class FilterInputStream
extends InputStream
public abstract class InputStream
extends Object
implements Closeable
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
常用的方法------------------------------
BufferedInputStream(InputStream in),根据参数指定的引用构造对象
BufferedInputStream(InputStream in, int size),根据参数指定的引用和缓冲区大小构造对象
int read(),读取单个字节
int read(byte[] b, int off, int len),读取len个字节
int read(byte[] b),读取b.length个字节
void close(),关闭流对象并释放有关的资源
package com.lagou.task17;

import java.io.*;

public class BufferedByteCopyTest {

    public static void main(String[] args) {

        // 获取当前系统时间距离1970年1月1日0时0分0秒的毫秒数
        long g1 = System.currentTimeMillis();

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            // 1.创建BufferedInputStream类型的对象与d:/02_IO流的框架结构.mp4文件关联
            //BufferedOutputStream(OutputStream out),根据参数指定的引用来构造对象
            bis = new BufferedInputStream(new FileInputStream("d:/03 IO流的框架图.png"));
            /*
            public BufferedInputStream(InputStream in) {
                    this(in, DEFAULT_BUFFER_SIZE);
             }
              private static int DEFAULT_BUFFER_SIZE = 8192;
               public BufferedInputStream(InputStream in, int size) {
                super(in);
                if (size <= 0) {
                   throw new IllegalArgumentException("Buffer size <= 0");
               }
                buf = new byte[size];//这里放8192,1024的8倍
    }


             */
            // 2.创建BufferedOuputStream类型的对象与d:/IO流的框架结构.mp4文件关联
            bos = new BufferedOutputStream(new FileOutputStream("d:/IO流的框架结构.png"));

            // 3.不断地从输入流中读取数据并写入到输出流中
            System.out.println("正在玩命地拷贝...");

            byte[] bArr = new byte[1024];
            int res = 0;
            while ((res = bis.read(bArr)) != -1) {
                bos.write(bArr, 0, res);
            }
             //缓冲区(包括字符的缓冲区)自带8192字符(一般字节流没有缓冲区(或者说机制)),若你设置的字节数组长度没有超过这个数,那么就用8192覆盖掉
            //若超过了,就用超过的

            System.out.println("拷贝文件成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4.关闭流对象并释放有关的资源
            if (null != bos) {
                try {
                    bos.close();
                    //带着参数一起关闭
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != bis) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        long g2 = System.currentTimeMillis();
        System.out.println("使用缓冲区拷贝视频文件消耗的时间为:" + (g2-g1)); // 44
    }
}

实际上不用缓冲流要比用缓冲流慢,即我们最好用缓冲流
一般对应的FileWriter是操作字符的,所以一般会进行编码,即将字节变成字符,这也是为什么在前面说,字符流=字节流 + 编码方式,所以,这里(代表操作缓冲的)也操作了编码,主要的区别就是,FileWriter是写入缓冲一次编码一次(字节先变字符),而这里是缓冲区满了才会进行(他们是存放字节的哦,因为是字节缓冲流,而不是字符哦,也就是8192的字节数组大小)写入,这就是主要的优势,即速度快的原因(字节处理一般都比较快,因为不需要处理编码),具体可以看这个博客:https://blog.csdn.net/ninewolfyan/article/details/112882614,由于字节流(不是缓冲流)自身不需要编码,所以他本身不会操作缓冲区(机制)
简单来说,他之所以快,就是因为他是改变原有缓冲的规则的
BufferedWriter类------------------------------
java.io.BufferedWriter类主要用于写入单个字符、字符数组以及字符串到输出流中
public class BufferedWriter
extends Writer
public abstract class Writer
extends Object
implements Appendable, Closeable, Flushable
//能够被添加 char 序列和值的对象,如果某个类的实例打算接收取自 Formatter 的格式化输出
//那么该类必须实现 Appendable 接口
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
//Flushable是可以刷新的数据的目的地,调用flush方法将任何缓冲输出写入到依赖的流
常用的方法------------------------------
BufferedWriter(Writer out),根据参数指定的引用来构造对象
BufferedWriter(Writer out, int sz),根据参数指定的引用和缓冲区大小来构造对象
void write(int c),写入单个字符到输出流中
void write(char[] cbuf, int off, intlen),将字符数组cbuf中从下标off开始的len个字符写入输出流中
void write(char[] cbuf),将字符串数组cbuf中所有内容写入输出流中
void write(String s, int off, int len),将参数s中下标从off开始的len个字符写入输出流中
void write(String str),将参数指定的字符串内容写入输出流中
void newLine(),用于写入行分隔符到输出流中,可以到下一行
void flush(),刷新流
void close(),关闭流对象并释放有关的资源
BufferedReader类------------------------------
java.io.BufferedReader类用于从输入流中读取单个字符、字符数组以及字符串
public class BufferedReader
extends Reader
public abstract class Reader
extends Object
implements Readable, Closeable
//Readable接口就是为了Scanner类专门创建的一个接口,使得Scanner的入口参数不必限于某个类
//实现Readable接口要只需是实现public int read(CharBuffer cb)方法,当方法返回-1时候Scanner类停止读取
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
常用的方法------------------------------
BufferedReader(Reader in),根据参数指定的引用来构造对象
BufferedReader(Reader in, int sz),根据参数指定的引用和缓冲区大小来构造对象
int read(),从输入流读取单个字符,读取到末尾则返回-1,否则返回实际读取到的字符内容
int read(char[] cbuf, intoff, int len),从输入流中读取len个字符放入数组cbuf中下标从off开始的位置上
若读取到末尾则返回-1,否则返回实际读取到的字符个数
int read(char[] cbuf),从输入流中读满整个数组cbuf
String readLine(),读取一行字符串并返回,返回null表示读取到末尾,读取一行字符串,下次一般到下一行
void close(),关闭流对象并释放有关的资源

在这里插入图片描述

package com.lagou.task17;

import java.io.*;

public class BufferedCharCopyTest {

    public static void main(String[] args) {
        BufferedReader br = null;
        BufferedWriter bw = null;

        try {
            // 1.创建BufferedReader类型的对象与d:/a.txt文件关联
            //BufferedReader(Readerin),根据参数指定的引用来构造对象
            br = new BufferedReader(new FileReader("d:/a.txt"));
            // 2.创建BufferedWriter类型的对象与d:/b.txt文件关联
            //BufferedWriter(Writer out),根据参数指定的引用来构造对象
            bw = new BufferedWriter(new FileWriter("d:/b.txt"));
            // 3.不断地从输入流中读取一行字符串并写入到输出流中
            System.out.println("正在玩命地拷贝...");
            String str = null;
            while ((str = br.readLine()) != null) {
                bw.write(str);
                bw.newLine(); // 当前系统中的行分隔符是:通常是\r\n,如果不加的话,写入的都是一个行里面的
            }
            System.out.println("拷贝文件成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4.关闭流对象并释放有关的资源
            if (null != bw) {
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

\n和\r的区别,前者\n可以理解为换行,后者,就将回到当前的开头,若在回到开头之前,有其他数,那么就会清除
案例------------------------------
System.out.print("啊\n和");
System.out.print("奖金\r哦");
//输出结果
//啊
//哦
//"哦"前面的"和奖金"都没了,并且你到下一行时,是开头,通常都会以为与\n没区别,但其实清除东西了
//一般的,\r的清除只是再打印中出现,在文件中,代表一行的首部,即不清除,所以文件中的一行末尾一般是\r\n(特别的,不同的操作系统中,对于换行可能是不同的处理,并且不同的软件也是如此,比如这里的打印,或者\r\n是一个整体,代表换行,在windows的流中一般就是如此)
/*
在 Unix/Linux 系统中,通常使用 \n 表示换行
在 Windows 系统中,通常使用 \r\n 整体表示换行,即回车符加换行符
在老式的 Mac 系统中,通常使用 \r 表示换行
*/

//最后,我们说明一下\t,\t是制表符号,他默认占用8字符,这个8字符代表8倍数位置,\t自身也占有一个字符,所以如果是这样:
System.out.print("1111111\t");
System.out.println("11111111\t");
//其中第一个1111111是7个字符,加上\t后8字符,占满了,那么跳转到下一个8位置,所以打印是:
1111111	11111111 //(其中空格是\t),而如果上面都是:
System.out.print("11111111\t");
System.out.println("11111111\t");
//那么结果是:11111111	11111111,中间是四个空格(通常代表8字符),一般一个空格在打印时代表两个字符,当然,这受打印的影响,所以忽略即可,因为一般是固定的宽度,只是打印时在考虑\t是这样,所以这是特殊情况,其他情况下,基本是一样的大小的(宽)

//所以\t在打印99乘法表时是非常好的,因为9x9=81没有大于等于8(考虑\t也占有一个,所以不能等于8)


PrintStream类------------------------------
java.io.PrintStream类主要用于更加方便地打印各种数据内容
public class PrintStream
extends FilterOutputStream
implements Appendable, Closeable
public class FilterOutputStream
extends OutputStream
public abstract class OutputStream
extends Object
implements Closeable, Flushable
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
//Flushable是可以刷新的数据的目的地,调用flush方法将任何缓冲输出写入到依赖的流
常用的方法------------------------------
PrintStream(OutputStream out),根据参数指定的引用来构造对象
void print(String s),用于将参数指定的字符串内容打印出来
void println(String x),用于打印字符串后并终止该行
void flush(),刷新流
void close(),用于关闭输出流并释放有关的资源
PrintWriter类------------------------------
java.io.PrintWriter类主要用于将对象的格式化形式打印到文本输出流
public class PrintWriter
extends Writer
public abstract class Writer
extends Object
implements Appendable, Closeable, Flushable
//能够被添加 char 序列和值的对象,如果某个类的实例打算接收取自 Formatter 的格式化输出
//那么该类必须实现 Appendable 接口
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
//Flushable是可以刷新的数据的目的地,调用flush方法将任何缓冲输出写入到依赖的流
常用的方法------------------------------
PrintWriter(Writer out),根据参数指定的引用来构造对象
void print(String s),将参数指定的字符串内容打印出来
void println(String x),打印字符串后并终止该行
void flush(),刷新流
void close(),关闭流对象并释放有关的资源
案例题目------------------------------
不断地提示用户输入要发送的内容,若发送的内容是"bye"则聊天结束,否则将用户输入的内容写入到文件d:/a.txt中
要求使用BufferedReader类来读取键盘的输入
System.in代表键盘输入,前提是一般需要结合(具体原因,可能是jvm启动操作了初始化并且需要结合操作,因为InputStream inputStream = System.in;并不会阻塞),比如这里,还有他自身操作read方法(这里了解即可)
要求使用PrintStream类负责将数据写入文件
package com.lagou.task17;

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

public class PrintStreamChatTest {

    public static void main(String[] args) {

        // 由手册可知:构造方法需要的是Reader类型的引用,但Reader类是个抽象类
        //实参只能传递子类的对象  字符流
        // 由手册可知: System.in代表键盘输入, 而且是InputStream类型的 字节流
        //public static final InputStream in = null;
        //这里说明一下手册:可以到这个地址下获取:
        /*
        链接:https://pan.baidu.com/s/1uvNLLwoLWQVLlDq60FnKQg 
提取码:alsk 
--来自百度网盘超级会员V1的分享
        */
        BufferedReader br = null;
        PrintStream ps = null;
        try {
            //转换流
            //Reader a = new InputStreamReader(System.in);
            //public class InputStreamReader extends Reader{...}
            //FileReader b = (FileReader)a;
            //InputStreamReader(InputStream in),根据参数指定的引用来构造对象
            br = new BufferedReader(new InputStreamReader(System.in));
            //PrintWriter(Writer out),根据参数指定的引用来构造对象
            ps = new PrintStream(new FileOutputStream("d:/a.txt", true));

            // 声明一个boolean类型的变量作为发送方的代表
            boolean flag = true;

            while(true) {
                // 1.提示用户输入要发送的聊天内容并使用变量记录
                System.out.println("请" + (flag? "张三": "李四") + "输入要发送的聊天内容:");
                String str = br.readLine();
                // 2.判断用户输入的内容是否为"bye",若是则聊天结束
                if ("bye".equals(str)) {
                    System.out.println("聊天结束!");
                    break;
                }
                // 3.若不是则将用户输入的内容写入到文件d:/a.txt中
                //else {
                // 获取当前系统时间并调整格式
                Date d1 = new Date();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                ps.println(sdf.format(d1) + (flag?" 张三说:":" 李四说:") + str);
                //}
                flag = !flag;
            }
            //void println(String x),打印字符串后并终止该行
            ps.println(); // 写入空行(相当于代表回车,自己看文件信息就知道了) 与之前的聊天记录隔开
            ps.println();
            ps.println();
              //一般来说打印流在输出时,会自动刷新,且他有其他的功能,实际上大多数的流的作用基本都是一样的,只是根据偏向权重不同,或者在某些方面有特殊的好处,所以才会将他们划分为某些流的,所以也可以说他是一些流的补充,一般来说像打印流,和缓冲区这些补充,都会操作缓冲,以及他们有自己的功能(打印流写入空行,缓冲流操作一行等等),由于可以操作行行对应,所以称为缓冲,而这里可以更好的操作结果的出现,所以称为打印,具体其他的流的解释就不做说明了,因为他们的偏向很明显,如对象流,特殊流等等
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4.关闭流对象并释放有关的资源
            if (null != ps) {
                ps.close();
            }
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

OutputStreamWriter类------------------------------
java.io.OutputStreamWriter类主要用于实现从字符流到字节流的转换,对于这样的转换,简单来说,就是将数据必然的通过对应编码过去
public class OutputStreamWriter
extends Writer
public abstract class Writer
extends Object
implements Appendable, Closeable, Flushable
//能够被添加 char 序列和值的对象,如果某个类的实例打算接收取自 Formatter 的格式化输出
//那么该类必须实现 Appendable 接口
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
//Flushable是可以刷新的数据的目的地,调用flush方法将任何缓冲输出写入到依赖的流
常用的方法------------------------------
OutputStreamWriter(OutputStream out),根据参数指定的引用来构造对象
OutputStreamWriter(OutputStream out, StringcharsetName),根据参数指定的引用和编码构造对象
void write(String str),将参数指定的字符串写入
void flush(),刷新流
void close(),用于关闭输出流并释放有关的资源
InputStreamReader类------------------------------
java.io.InputStreamReader类主要用于实现从字节流到字符流的转换
public class InputStreamReader
extends Reader
public abstract class Reader
extends Object
implements Readable, Closeable
//Readable接口就是为了Scanner类专门创建的一个接口,使得Scanner的入口参数不必限于某个类
//实现Readable接口要只需是实现public int read(CharBuffer cb)方法,当方法返回-1时候Scanner类停止读取
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
常用的方法------------------------------
InputStreamReader(InputStream in),根据参数指定的引用来构造对象
InputStreamReader(InputStream in, StringcharsetName),根据参数指定的引用和编码来构造对象
int read(char[] cbuf),读取字符数据到参数指定的数组
void close(),用于关闭输出流并释放有关的资源
字符编码------------------------------
计算机只能识别二进制数据,早期就是电信号
为了方便计算机可以识别各个国家的文字,就需要将各个国家的文字采用数字编号的方式进行描述并建立对应的关系表
该表就叫做编码表
常见的编码表------------------------------
ASCII:美国标准信息交换码, 使用一个字节的低7位二位进制进行表示
ISO8859-1:拉丁码表,欧洲码表,使用一个字节的8位二进制进行表示
GB2312:中国的中文编码表,最多使用两个字节16位二进制为进行表示
GBK:中国的中文编码表升级,融合了更多的中文文字符号,最多使用两个字节16位二进制位表示
Unicode:国际标准码,融合了目前人类使用的所有字符,为每个字符分配唯一的字符码
所有的文字都用两个字节16位二进制位来表示
编码的发展------------------------------
面向传输的众多 UTF(UCS Transfer Format)标准出现了,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位
这是为传输而设计的编码并使编码无国界,这样就可以显示全世界上所有文化的字符了
Unicode只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号
具体存储成什么样的字节流,取决于字符编码方案
推荐的Unicode编码是UTF-8和UTF-16
UTF-8:变长的编码方式,可用1-4个字节来表示一个字符

在这里插入图片描述

DataOutputStream类------------------------------
java.io.DataOutputStream类主要用于以适当的方式将基本数据类型写入输出流中
public class DataOutputStream
extends FilterOutputStream
implements DataOutput
public class FilterOutputStream
extends OutputStream
public abstract class OutputStream
extends Object
implements Closeable, Flushable
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
//Flushable是可以刷新的数据的目的地,调用flush方法将任何缓冲输出写入到依赖的流
常用的方法------------------------------
DataOutputStream(OutputStream out),根据参数指定的引用构造对象
OutputStream类是个抽象类,实参需要传递子类对象
void writeInt(int v),用于将参数指定的整数一次性写入输出流,优先写入高字节
void close(),用于关闭文件输出流并释放有关的资源
package com.lagou.task17;

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

public class DataOutputStreamTest {

    public static void main(String[] args) {
        DataOutputStream dos = null;

        try {
            // 1.创建DataOutputStream类型的对象与d:/a.txt文件关联
            //DataOutputStream(OutputStream out),根据参数指定的引用构造对象
            dos = new DataOutputStream(new FileOutputStream("d:/a.txt"));
            // 2.准备一个整数数据66并写入输出流
            // 66: 0000 0000 ... 0100 0010    =>   B
            int num = 66;
            //void writeInt(int v),用于将参数指定的整数一次性写入输出流,优先写入高字节
            //dos.writeInt(num);  // 写入4个字节,即由于66 => 'B',那么在写的时候
            //结果:   B,B前面有三个格子,即三个格子对应三个字节,即0 => ''(要注意:对应代表的是编码的空,具体可以参考Ascii的对应的空,而不会没有哦,就算是String的""他也是存在的,只是显示时忽略而已),对应的二进制是空的,占一格子
             //其中:dos.writeByte(128),那么就会出现数据问题,根据底层操作可以发现,最终是FileOutputStream进行的,而他操作128输出在Ascii没有找到,这里就需要注意一个大大的问题了,字节流是不会出现乱码的,乱码的出现是因为文件的读取的原因,并非输出字节流并不能操作128,且经过我的测试,会发现,只有字节流才会出现这样的情况,那么为什么呢,这是因为字节流,由于是操作一个字节,而一个字节的基本都会参照类似于Ascii的表,而该表没有128,那么自然就会分开,当被读取时,就容易可能他的操作会出现某些变化,主要看对应的编码,所以会出现乱码或者不同的显示,找个相似的,这里还要注意的一点就是,相似的并非是自己的,可能有保留相似的表,使得所有找相似的会考虑他们
            // 因为num是int类型,即会写四个字节,会有三个格子,然后在写'B'
            dos.write(num);       // 写入1个字节,这里代表从最后一个字节开始,不操作最高位了
            System.out.println("写入数据成功!");
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 3.关闭流对象并释放有关的资源
            if (null != dos) {
                try {
                    dos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

DataInputStream类------------------------------
java.io.DataInputStream类主要用于从输入流中读取基本数据类型的数据
public class DataInputStream
extends FilterInputStream
implements DataInput
public class FilterInputStream
extends InputStream
public abstract class InputStream
extends Object
implements Closeable
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
常用的方法------------------------------
DataInputStream(InputStreamin),根据参数指定的引用来构造对象
InputStream类是抽象类,实参需要传递子类对象
int readInt(),用于从输入流中一次性读取一个整数数据并返回
void close(),用于关闭文件输出流并释放有关的资源
package com.lagou.task17;

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

public class DataInputStreamTest {

    public static void main(String[] args) {
        DataInputStream dis = null;

        try {
            // 1.创建DataInputStream类型的对象与d:/a.txt文件关联
            //DataInputStream(InputStreamin),根据参数指定的引用来构造对象
            dis = new DataInputStream(new FileInputStream("d:/a.txt"));
            // 2.从输入流中读取一个整数并打印
            //int readInt(),用于从输入流中一次性读取一个整数数据并返回
            //int res = dis.readInt(); // 读取4个字节,其中若只有一个
            //那么就会报错,因为还没读取4个字节,就到末尾了
            int res = dis.read();      // 读取1个字节,从开头开始读取,读取都是操作开头的,前面的输出是自己操作位有所不同哦
            System.out.println("读取到的整数数据是:" + res); // 66
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 3.关闭流对象并释放有关的资源
            if (null != dis) {
                try {
                    dis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

上述66会变成B,是因为文件给我们看的不是二进制代码,而是编码表的图案,而图案需要二进制转化为对应编号,即66
所以66 => ‘B’,这个B是给我们看的,即我们在读取时,也是将B对应的二进制,即66的二进制读取出来
然后,在变成对应的内容,即读出66对应的二进制,当然,在打印时,还是会将该二进制变为66,给你看的
然后将该66的整数类型变成字符串,通过字符串,将’‘66’'变成两个’6’一个一个的打出(若有6666,总不能保存6666吧,这也是为什么有些看起来连接的,实际上是操作了多次,虽然这个66不是),而不是找66对应的字符,即打出两个6,即66
即我们看的基本都是图案
ObjectOutputStream类------------------------------
public class ObjectOutputStream
extends OutputStream
implements ObjectOutput, ObjectStreamConstants
public abstract class OutputStream
extends Object
implements Closeable, Flushable
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
//Flushable是可以刷新的数据的目的地,调用flush方法将任何缓冲输出写入到依赖的流
java.io.ObjectOutputStream类主要用于将一个对象的所有内容整体写入到输出流中
只能将支持 java.io.Serializable 接口的对象写入流中
类通过实现 java.io.Serializable 接口以启用其序列化功能
所谓序列化主要指将一个对象需要存储的相关信息有效组织成字节序列的转化过程
如一个图片里面有多个信息,如颜色,长度等,那么就将他们有规则的组织
常用的方法------------------------------
ObjectOutputStream(OutputStream out),根据参数指定的引用来构造对象
void writeObject(Object obj),用于将参数指定的对象整体写入到输出流中
void close(),用于关闭输出流并释放有关的资源
ObjectInputStream类------------------------------
public class ObjectInputStream
extends InputStream
implements ObjectInput, ObjectStreamConstants
public abstract class InputStream
extends Object
implements Closeable
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
java.io.ObjectInputStream类主要用于从输入流中一次性将对象整体读取出来
所谓反序列化主要指将有效组织的字节序列恢复为一个对象及相关信息的转化过程
常用的方法------------------------------
ObjectInputStream(InputStreamin),根据参数指定的引用来构造对象
Object readObject(),主要用于从输入流中读取一个对象并返回 无法通过返回值来判断是否读取到文件的末尾
void close(),用于关闭输入流并释放有关的资源
package com.lagou.task17;

public class User implements java.io.Serializable {
    private static final long serialVersionUID = -5814716593800822421L;  //必须是static final的才算,以及该变量名,有一个不对就不是序列号了,自然操作时会报错
    //在进行序列化时,这个版本号会当作唯一标识,即也会存入文件里
    //那么,在反序列化时,就会将你本地的相应实体类的版本号比对(若你没有改动,那么就不会出问题)
    //若不一致,则会报错

    private String userName;  // 用户名
    private String password;  // 密码
    private transient String phoneNum;  // 手机号  表示该成员变量不参与序列化操作

    public User() {
    }
    public User(String userName, String password, String phoneNum) {
        this.userName = userName;
        this.password = password;
        this.phoneNum = phoneNum;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhoneNum() {
        return phoneNum;
    }

    public void setPhoneNum(String phoneNum) {
        this.phoneNum = phoneNum;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                ", phoneNum='" + phoneNum + '\'' +
                '}';
    }
}

package com.lagou.task17;

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

public class ObjectOutputStreamTest {

    public static void main(String[] args) {
        ObjectOutputStream oos = null;

        try {
            // 1.创建ObjectOutputStream类型的对象与d:/a.txt文件关联
            //ObjectOutputStream(OutputStream out),根据参数指定的引用来构造对象
            oos = new ObjectOutputStream(new FileOutputStream("d:/a.txt"));
            // 2.准备一个User类型的对象并初始化
            User user = new User("qidian", "123456", "13511258688");
            // 3.将整个User类型的对象写入输出流
            //void writeObject(Object obj),用于将参数指定的对象整体写入到输出流中
            oos.writeObject(user);
            System.out.println("写入对象成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4.关闭流对象并释放有关的资源
            if (null != oos) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

package com.lagou.task17;

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

public class ObjectInputStreamTest {

    public static void main(String[] args) {
        ObjectInputStream ois = null;

        try {
            // 1.创建ObjectInputStream类型的对象与d:/a.txt文件关联
            //ObjectInputStream(InputStreamin),根据参数指定的引用来构造对象
            ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
            // 2.从输入流中读取一个对象并打印
            //Object readObject(),主要用于从输入流中读取一个对象并返回 
            //一般无法通过返回值来判断是否读取到文件的末尾,实际上序列号就决定了你存在,所以一般可以用Object,且一般只能用他(返回值就是Object)
            Object obj = ois.readObject();
            System.out.println("读取到的对象是:" + obj); // qidian 123456 13511258688  null
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 3.关闭流对象并释放有关的资源
            if (null != ois) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

序列化版本号------------------------------
序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的
在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较
如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)
当然,若反序列化出现的类(包括serialVersionUID)
与当前项目的相同的serialVersionUID对应的类的包名,或者类名不一致(相当于没有对应类),或者其没有实现java.io.Serializable接口(我删除,再操作得到会报错)
那么就算对应的serialVersionUID的值相同,也会报错
若有多个serialVersionUID,那么只要有一个符合了,就不会报错
最后也要注意,操作序列化时,需要实现java.io.Serializable接口,否则报错,若序列化不写,他会默认加上了,所以也不会报错哦(根据类信息加上,具体是什么,可以百度,且默认私有,所以基本得不到,并且也不能直接的使用,因为是类似于Class的保存的,一般明面上的得不到)
注意:只要两个项目能够相互访问,那么序列化和反序列化是可以在不同项目中实现的(无论是否远程),但是前提对应的对象在项目的路径是需要相同的(这里参考79章博客)
transient关键字------------------------------
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分
当一个对象被串行化(序列化)的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的,而没有包括进去的,得到的数据,默认为默认值,即给过默认值进去
经验的分享------------------------------
当希望将多个对象写入文件时,通常建议将多个对象放入一个集合中,因为在读取对象时
不知道读多少(因为没有返回值来返回具体数值),即由于文件里对象太多(有些是乱码的)
那么你判断不出有几个对象,即不知道循环多少次
Object readObject(),主要用于从输入流中读取一个对象并返回 无法通过返回值来判断是否读取到文件的末尾
//这只读取一个对象
然后将集合这个整体看做一个对象写入输出流中,此时只需要调用一次readObject方法就可以将整个集合的数据读取出来
从而避免了通过返回值进行是否达到文件末尾的判断,由于是数组,且对应的是常量,所以他们都是共享一个序列号的,即可以看成一个序列号的判断就行了,虽然他们可能都会判断或者判断一次
RandomAccessFile类------------------------------
public class RandomAccessFile
extends Object
implements DataOutput, DataInput, Closeable
//DataOutput接口定义了一系列方法用于将任何Java类型的数据转换为一系列字节,并将这些字节写入二进制流
//DataInput接口提供了一系列的方法从二进制流中读取字节,并将读取出来的字节转换成任意的java基本类型
//包括转换成UTF-8类型的字符串
//Closeable 是可以关闭的数据源或目标,调用 close 方法可释放对象保存的资源(如打开文件)
java.io.RandomAccessFile类主要支持对随机访问文件的读写操作
常用的方法------------------------------
RandomAccessFile(String name, Stringmode),根据参数指定的名称和模式构造对象
r: 以只读方式打开
rw:打开以便读取和写入
rwd:打开以便读取和写入,同步文件内容的更新
rws:打开以便读取和写入,同步文件内容和元数据的更新
int read(),读取单个字节的数据
void seek(long pos),用于设置从此文件的开头开始测量的文件指针偏移量
void write(int b),将参数指定的单个字节写入
void close(),用于关闭流并释放有关的资源
package com.lagou.task17;

import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomAccessFileTest {

    public static void main(String[] args) {
        RandomAccessFile raf = null;

        try {
            // 1.创建RandomAccessFile类型的对象与d:/a.txt文件关联
            //RandomAccessFile(String name, Stringmode),根据参数指定的名称和模式构造对象
            //r: 以只读方式打开
            //rw:打开以便读取和写入
            //rwd:打开以便读取和写入,同步文件内容的更新
            //rws:打开以便读取和写入,同步文件内容和元数据的更新
            raf = new RandomAccessFile("d:/a.txt", "rw");
            // 2.对文件内容进行随机读写操作
            // 设置距离文件开头位置的偏移量,从文件开头位置向后偏移3个字节    aellhello
            //void seek(long pos),用于设置从此文件的开头开始测量的文件指针偏移量
            raf.seek(3);
            int res = raf.read();
            System.out.println("读取到的单个字符是:" + (char)res); // a l
            res = raf.read();
            System.out.println("读取到的单个字符是:" + (char)res); // h 指向了e
            raf.write('2'); // 执行该行代码后覆盖了字符'e',如果没有,自然加上,如果没有数据,那么不移动,直接返回空数据(在ascii中一般代表0位置的字符)
            System.out.println("写入数据成功!");
            //实际上对应的位置可以认为是直接的指向或者在前方,指向认为是覆盖,前方也是,而write就是在该位置添加,而一般返回-1的就是到这个最后的,当然有些是读取多个,那么这个最后代表结束,下次使得-1退出

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 3.关闭流对象并释放有关的资源
            if (null != raf) {
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

对于偏移来说,可以看成下标,如albn
开始下标为0(当成1也可,但需要将1认为是最小的,但最好还是0,因为大多数下标是从零开始的),指向a,然后偏移2,指向b
当写入时,会覆盖所指向的数
也可以用光标来说,如"l",在文件里都有,可以将他的下一个当作要读的数,即开头指向a,然后偏移2,指向b
且光标在"l"和"b"中间,即光标的下一个就是要读取的数,当写入时,会覆盖下一个数
我们最好用第一个解释
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值