IO流笔记

目录

  • 1. 概述
  • 2. IO流的分类
  • 3. IO四大家族
    • 3.1 需要掌握的流
  • 4 初识 FileInputStream
  • 5. FileOutputStream
    • 5.1 文件拷贝
  • 6. 字符流
    • 6.1 拷贝普通文本文件
  • 7.缓冲流
  • 8. 数据流
  • 9. 标准输出流
  • 10. File 类
  • 11. 拷贝整个目录及文件
  • 12. 序列化与反序列化
    • 12.1 序列化对象
    • 12.2 反序列化
    • 12.3 序列化集合
    • 12.4 反序列化集合
    • 12.5 transient关键字
    • 12.6 序列化版本号
  • 13. Io+ Properties联合使用

1. 概述

IO流就是文件的输入和输出(以内存为参照物)
IO流笔记_第1张图片

2. IO流的分类

有多种分类方式:

  • 一种方式是按照流的方向进行分类:
    (1)以内存作为参照物,往内存中去,叫做输入(Input)。或者叫做读(Read)。
    (2)从内存中出来,叫做输出(output)。或者叫做写(write) 。
  • 另一种方式是按照读取数据方式不同进行分类:
    (1)有的流是按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位,这种流是方能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频等
    (2)有的流是按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件 (.txt) 而存在的,这种流不能读取:图片、声音、视频等文件。只能读取纯文本文件,连 word 文件都无法读取。

综上所述:流的分类为

  • 输入流、输出流(按照流的方向)
  • 字节流、字符流(按照读取数据方式)

Java中的IO流都已经写好了,我们程序员不需要关心,我们最主要还是掌握,在 java 中已经提供了哪些流,每个流的特点是什么,每个流对象上的常用方法有哪些。

java中所有的流都是在:java.io.* ; 下。
java中主要还是研究:怎么new流对象,调用流对象的哪个方法是读,哪个方法是写。

3. IO四大家族

java IO流这块有四大家族:(都是抽象类)

  • java.io.Inputstream 字节输入流
  • java.io.Outputstream 字节输出流
  • java.io.Reader 字符输入流
  • java.io. Writer 字符输出流

所有的流都实现了:
       java.io.Closeable接口,都是可关闭的,都有close()方法。
流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费(占用)很多资源。养成好习惯,用完流一定要关闭。

所有的输出流都实现了:
       java.io.Flushable接口,都是可刷新的,都有flush()方法。养成一个好习惯,输出流在最终输出之后,一定要记得flush()刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道!)刷新的作用就是清空管道。
       注意:如果没有flush()可能会导致丢失数据。

注意:在 java 中只要"类名"以 stream 结尾的都是字节流,以"Reader / Writer"结尾的都是字符流。

3.1 需要掌握的流

java.io包下需要掌握的流有16个:

文件专属:

java.io.FileInputstream
java.io.Fileoutputstream
java.io.FileReader
java.io.Filewriter

转换流:(将字节流转换成字符流)

java.io.InputstreamReader
java.io.OutputstreamWriter

缓冲流专属:

java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputstream
java.io.BufferedOutputstream

数据流专属:

java.io.DataInputstream
java.io.DataOutputstream

标准输出流:

java.io.PrintWriter
java.io.PrintStream

对象专属流:

java.io.ObjectInputstream
java.io.ObjectOutputstream

4 初识 FileInputStream

package com.dljd.io;

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

/**
 * java.io.FileInputStream :
 *      1、文件字节输入流,万能的,任何类型的文件都可以采用这个流来读。
 *      2、以字节的方式,完成输入的操作,完成读的操作(从硬盘--->内存)
 */
public class FileInputstreamTest01 {

    public static void main(String[] args){
        FileInputStream fis = null;
        //文件路径是 D:\test.txt ,IDEA会自动把 "\" 编成 "\\",因为Java中"\"表示转义
        try {
            fis = new FileInputStream("D:\\test.txt");//写成"D:/test.txt"也行
            /**
             * read()方法自带一个指针,初始时指向首个字节的前方空白处,
             * 每次调用 read(),指针向后移一个字节(8个二进制位),当指向末尾字节的后面空白处时,返回-1,
             * 表示没有读取到数据
             *
             * read()这个方法完成的事情就是从数据源中读取8个二进制位,并将这8个 0或1 转换成十进制的整数,然后将其返回。
             * 8个二进制位转换为十进制范围是 0~255
             */
            int readData = fis.read();//这个方法的返回值是:读取到的"字节"本身。
            System.out.println(readData);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //在finally语句块当中确保流一定关闭。
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

分析上面这个程序的缺点:
一次读取一个字节byte,这样内存和硬盘交互太频繁,基本上时间/资源都耗费在交互上面了。

package com.dljd.io;

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

/**
 * int read( byte[] b)
 *      将读到的字节存到 byte[]数组当中,每次调用都会覆盖 byte[] 重新存一遍
 *      一次最多读取 b.Length个字节。
 *      减少硬盘和内存的交互,提高程序的执行效率。
 */
public class FileInputstreamTest02 {

    public static void main(String[] args) {
        FileInputStream fis = null;

        try {
            /**
             * 1.相对路径一定是从当前所在的位置作为起点开始找!
             * 2.IDEA默认的当前路径是哪里?工程Project的根就是IDEA的默认当前路径。
             * 3.test.txt中的内容为:abcdef
             */
            fis = new FileInputStream("test.txt");//相对路径
            byte[] bytes = new byte[4];//4个字节

            int readCount = fis.read(bytes);//返回读到了多少个字节
            System.out.println(readCount);//第一次可以读到4个字节
            System.out.println(new String(bytes));//读到abcd放到bytes数组中

            readCount = fis.read(bytes);
            System.out.println(readCount);//第二次只能读到2个字节
            /**
             * 重要!
             *      1.第二次只能读到 ef ,会放到 bytes数组的前面,
             *      覆盖掉 ab,所以此时 bytes={efcd}
             *      2.每次覆盖都是从下标0开始,所以可以用 new String(bytes, 0, readCount)
             *      打印读到的字节数据,读到多少就打印多少
             */
            System.out.println(new String(bytes, 0, readCount));

            readCount = fis.read(bytes);
            System.out.println(readCount);//第三次读不到字节了,返回-1
            System.out.println(new String(bytes));

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

将上面的代码改为训循环:

public class FileInputstreamTest02 {

    public static void main(String[] args) {
        FileInputStream fis = null;

        try {
            fis = new FileInputStream("test.txt");//相对路径
            byte[] bytes = new byte[4];//4个字节
            int readCount = 0;
            while ((readCount = fis.read(bytes)) != -1) {
                System.out.println(readCount);
                System.out.println(new String(bytes, 0, readCount));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

其他方法:
available()

public class FileInputstreamTest03 {

    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("test.txt");
        //int data = fis.read();//读取一个字节
        //System.out.println("剩下多少个字节没有读: " + fis.available());

        //这个方法有啥用?直接指定 bytes 长度,读一次就全读完了
        byte[] bytes = new byte[fis.available()];//不适合太大的文件,因为bytes数组不能太大
        int readCount = fis.read(bytes);
        System.out.println(new String(bytes));
    }
}

skip()

public class FileInputstreamTest03 {

    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("test.txt");//abcdef
        fis.skip(3);//跳过3个字节不读,此时 read()的指针指向c
        int data = fis.read();//100--->d
        System.out.println(data);
    }
}

5. FileOutputStream

package com.dljd.io;

import java.io.FileOutputStream;

/**
 * 文件字节输出流,负责写。
 * 从内存到硬盘。
 */
public class FileOutputstreamTest01 {

    public static void main(String[] args) throws Exception {
        //true表示"追加"写入,不会将目标文件先清空再写入
        FileOutputStream fos = new FileOutputStream("1.txt", true);

        byte[] datas = {97, 98, 99, 100};
        //将数组全部写出去
        fos.write(datas);//这种方式会将目标文件先清空再写入
        //将数组指定位置的数据写出
        fos.write(datas, 0, 2);

        String s = "鸡你太美!";
        fos.write(s.getBytes());//将字符串转为字节数组写出去

        //写完之后一定要刷新
        fos.flush();
        fos.close();
    }
}

5.1 文件拷贝

IO流笔记_第2张图片

/**
 * 使用 FileInputStream + FileOutputStream完成文件的拷贝。拷贝的过程应该是一边读,一边写。
 * 使用以上的字节流拷贝文件的时候,文件类型随意,万能的。什么样的文件都能拷贝。
 */
public class Copy01 {

    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("1.txt");
        FileOutputStream fos = new FileOutputStream("1-copy.txt");
        byte[] bytes = new byte[1024];//一次最多拷贝1k
        int readCount = 0;
        while ((readCount = fis.read(bytes)) != -1) {
            fos.write(bytes, 0, readCount);
        }

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

6. 字符流

/**
 * FiLeReader :
 * 文件字符输入流,只能读取普通文本。
 * 读取文本内容时,比较方便,快捷。
 */
public class FileReaderTest {

    public static void main(String[] args) throws Exception {
        FileReader read = new FileReader("1.txt");
        char[] chars = new char[4];//一次读取4个字符
        int readCount = 0;
        while ((readCount = read.read(chars)) != -1) {
            System.out.println(new String(chars, 0, readCount));
        }
        read.close();
    }
}
/**
 * Filewriter :
 * 文件字符输出流。写。只能输出普通文本。
 */
public class FileWriterTest {

    public static void main(String[] args) throws IOException {
        FileWriter writer = new FileWriter("1.txt");
        char[] data = {'我', '是', '中', '国', '人'};
        writer.write(data, 2, 3);//从下标2开始,写3个字符
        writer.write("我是一个码农");

        writer.flush();
        writer.close();
    }
}

6.1 拷贝普通文本文件

能用记事本编辑的都是普通文本文件

public class Copy02 {

    public static void main(String[] args) throws Exception {
        FileReader reader = new FileReader("test.txt");//.java 也是普通文本
        FileWriter writer = new FileWriter("test-copy.txt");
        char[] data = new char[4];
        int readCount = 0;
        while ((readCount = reader.read(data)) != -1) {
            writer.write(data, 0, readCount);
        }
        writer.flush();

        writer.close();
        reader.close();
    }
}

7.缓冲流

/**
 * BufferedReader :
 * 带有缓冲区的字符输入流。
 * 使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组。自带缓冲。|
 */
public class BufferedReaderTest01 {

    public static void main(String[] args) throws Exception {
        FileReader reader = new FileReader("test.txt");
        //当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做∶节点流。
        //外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流。
        //像当前这个程序来说:FileReader就是一个节点流。BufferedReader就是包装流/处理流。
        BufferedReader br = new BufferedReader(reader);//构造方法只能传字符流对象
        //String line = br.readLine();
        //System.out.println(line);

        String line = null;
        //br.readLine()方法读取一个文本行,但不带换行符。
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        //对于包装流来说,只需要关闭最外层流就行,里面的节点流会自动关闭。(可以看源代码)
        br.close();
    }
}
/**
 * 字节输入流也想用BufferedReader怎么办?
 *       用转换流 InputStreamReader转换为 字符输入流
 */
public class BufferedReaderTest02 {

    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("1.txt");//字节流
        //此时fis是节点流,reader是包装流
        InputStreamReader reader = new InputStreamReader(fis);//转换为字符流
        //reader是节点流,br是包装流
        BufferedReader br = new BufferedReader(reader);
    }
}
public class BufferedWriterTest {

    public static void main(String[] args) throws IOException {
        //带有缓冲区的字符输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("11.txt"));
        bw.write("hello,world");
        bw.flush();
        //关闭最外层
        bw.close();
    }
}

8. 数据流

/**
 * java.io.DataOutputStream :数据专属的流。这个流可以将数据连同数据的类型一并写入文件。
 * 注意:这个文件不是普通文本文档。(这个文件使用记事本打不开)
 */
public class DataOutputStreamTest {

    public static void main(String[] args) throws Exception {
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("data"));
        //准备数据
        byte b = 100;
        short s = 20;
        int i = 90;
        long l = 400L;
        float f = 1.0F;
        double d = 3.14;
        boolean bl = false;
        char c = 'a';
        //写出数据
        dos.writeByte(b);//把数据以及数据的类型一并写入到文件当中。
        dos.writeShort(s);
        dos.writeInt(i);
        dos.writeLong(l);
        dos.writeFloat(f);
        dos.writeDouble(d);
        dos.writeBoolean(bl);
        dos.writeChar(c);

        dos.flush();
        //包装流,关闭最外层即可
        dos.close();
    }
}
/**
 * DataInputStream:数据字节输入流。
 * DataOutputStream写的文件,只能使用DataInputStream去读。
 * 并且读的时候你需要提前知道写入的顺序。读的顺序需要和写的顺序一致。才可以正常取出数据。
 */
public class DataInputStreamTest {

    public static void main(String[] args) throws Exception {
        DataInputStream dis = new DataInputStream(new FileInputStream("data"));
        byte b = dis.readByte();
        short s = dis.readShort();
        int i = dis.readInt();
        long l = dis.readLong();
        float f = dis.readFloat();
        double d = dis.readDouble();
        boolean bl = dis.readBoolean();
        char c = dis.readChar();

        System.out.println(b);
        System.out.println(s);
        System.out.println(i);
        System.out.println(l);
        System.out.println(f);
        System.out.println(d);
        System.out.println(bl);
        System.out.println(c);
    }
}

9. 标准输出流

/**
 * java.io.PrintStream :标准的字节输出流。默认输出到控制台。
 */
public class PrintStreamTest {

    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("hello");

        PrintStream ps = System.out;
        ps.println("张三");
        //标准输出流不需要关闭
        //可以改变标准输出流的输出方向吗?可以
        //标准输出流不再指向控制台,指向"log"文件。
        PrintStream ps2 = new PrintStream(new FileOutputStream("log"));
        System.setOut(ps2);
        System.out.println("你好张三");//不会再输出到控制台
    }
}

10. File 类

File 是文件和目录路径名的抽象表示形式。

/**
 * File
 *      1、File类和四大家族没有关系,所以File类不能完成文件的读和写。
 *      2、File对象代表什么?
 *          文件和目录路径名的抽象表示形式。
 *          C:\Drivers 这是一个File对象
 *          C:\Drivers\Lan\ReaLtek\Readme.txt也是FiLe对象。一个File对象有可能对应的是目录,也可能是文件。
 *          File只是一个路径名的抽象表示形式。
 *      3、需要掌握ile类中常用的方法
 */
public class FileTest01 {

    public static void main(String[] args) throws IOException {
        File f1 = new File("D://file");
        //判断是否存在
        System.out.println(f1.exists());

        //如果不存在
        if (!f1.exists()) {
            //f1.createNewFile();//则以文件的形式创建
            f1.mkdir();//则以目录的形式创建
        }

        File f2 = new File("D://a//b//c");
        if (!f2.exists()) {
            //以多重目录的形式新建
            f2.mkdirs();
        }
    }
}
public static void main(String[] args) throws IOException {
    File f = new File("C:\\Users\\15642\\Desktop\\论文相关\\大论文\\注意.txt");
    String parentPath = f.getParent();//获取文件父路径
    System.out.println(parentPath);
    File parentFile = f.getParentFile();//返回File对象
    System.out.println(parentFile.getAbsolutePath());
}
public class FileTest01 {

    public static void main(String[] args) throws IOException {
        File f = new File("C:\\Users\\15642\\Desktop\\论文相关\\大论文\\注意.txt");
        //获取文件名
        System.out.println("文件名:" + f.getName());

        //判断是否是个目录
        System.out.println(f.isDirectory());

        //判断是否是个文件
        System.out.println(f.isFile());

        //获取文件最后一次修改时间
        long ms = f.lastModified();//返回毫秒:从1970年到现在的毫秒数
        Date time = new Date(ms);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        String strTime = sdf.format(time);
        System.out.println("最后修改于:" + strTime);

        //获取文件大小
        System.out.println("文件大小为:" + f.length() + "字节");
    }
}

listFiles()

public class FileTest01 {

    public static void main(String[] args) throws IOException {
        File f = new File("C:\\Users\\15642\\Desktop\\狂神说笔记");
        File[] files = f.listFiles();//获取当前目录下的所有子文件(文件或文件夹)
        for (File file : files) {
            //System.out.println(file.getAbsolutePath());
            System.out.println(file.getName());
        }
    }
}

11. 拷贝整个目录及文件

package com.dljd.io;

import java.io.*;

/**
 * 拷贝目录
 */
public class CopyAll {

    public static void main(String[] args) {
        //拷贝源,从哪拷贝
        File srcFile = new File("E:\\Java项目\\04_乐字节Java项目中信CRM客户管理系统\\crm");
        //拷贝目标,拷贝到哪
        File destFile = new File("D:\\");
        copyDir(srcFile, destFile);
    }

    private static void copyDir(File srcFile, File destFile) {
        if (srcFile.isFile()) {
            FileInputStream fis = null;
            FileOutputStream fos = null;
            try {
                fis = new FileInputStream(srcFile);
                String destPath = destFile.getAbsolutePath();
                String path = (destPath.endsWith("\\") ? destPath : destPath + "\\") + srcFile.getAbsolutePath().substring(3);
                fos = new FileOutputStream(path);
                byte[] bytes = new byte[1024 * 1024];
                int readCount = 0;
                while ((readCount = fis.read(bytes)) != -1) {
                    fos.write(bytes, 0, readCount);
                }
                fos.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            return;//如果是文件,就不用递归了
        }
        //获取源下面的子目录
        File[] files = srcFile.listFiles();
        for (File file : files) {
            //获取所有文件的(包括目录和文件)绝对路径
            //System.out.println(file.getAbsolutePath());
            if (file.isDirectory()) {
                String srcDir = file.getAbsolutePath();
                String destPath = destFile.getAbsolutePath();
                //三目运算符加个括号,避免出现优先级问题,从下标3开始截,去掉D:\
                String destDir = (destPath.endsWith("\\") ? destPath : destPath + "\\") + srcDir.substring(3);
                File newFile = new File(destDir);
                if (!newFile.exists()) {
                    newFile.mkdirs();
                }
            }
            //递归
            copyDir(file, destFile);
        }
    }
}

12. 序列化与反序列化

IO流笔记_第3张图片
准备一个 Student 类

package com.dljd.bean;

import java.io.Serializable;

public class Student implements Serializable {//实现序列化接口才能序列化
    private int no;
    private String name;

    public Student() {
    }

    public Student(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

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

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

12.1 序列化对象

package com.dljd.io;

import com.dljd.bean.Student;

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

/**
 * 1.参与序列化和反序列化的对象,必须实现Serializable接口。
 * 2.注意:通过源代码发现,Serializable接口只是一个标志接口∶
 *      public interface Serializable { }
 *      这个接口当中什么代码都没有。那么它起到一个什么作用呢?
 *      起到标识的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。
 *      Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类自动生成—个序列化版本号。
 */
public class ObjectOutputStreamTest01 {
    public static void main(String[] args) throws IOException {
        Student s = new Student(1, "张三");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("stu"));
        //序列化对象
        oos.writeObject(s);//将对象 s 写到 stu 文件
        oos.flush();
        oos.close();
    }
}

12.2 反序列化

package com.dljd.io;

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

public class ObjectInputStreamTest {
    public static void main(String[] args) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("stu"));
        //开始反序列化
        Object s = ois.readObject();
        System.out.println(s);
        ois.close();
    }
}

12.3 序列化集合

package com.dljd.io;

import com.dljd.bean.Student;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * 一次序列化多个对象呢?
 * 可以,可以将对象放到集合当中,序列化集合。
 * 提示:
 *    参与序列化的ArrayList集合以及集合中的元素都需要实现 java.io.SeriaLizable接口
 */
public class ObjectOutputStreamTest02 {
    public static void main(String[] args) throws Exception {
        List<Student> stus = new ArrayList<>();
        stus.add(new Student(1, "张三"));
        stus.add(new Student(2, "李四"));
        stus.add(new Student(3, "王五"));
        stus.add(new Student(4, "老六"));
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("stu2"));
        oos.writeObject(stus);
    }
}

12.4 反序列化集合

package com.dljd.io;

import com.dljd.bean.Student;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;

public class ObjectInputStreamTest02 {
    public static void main(String[] args) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("stu2"));
        List<Student> stus = (List<Student>)ois.readObject();
        for (Student student : stus) {
            System.out.println(student);
        }
        ois.close();
    }
}

12.5 transient关键字

//transient关键字表示游离的,不参与序列化
private transient String name;//name不会被序列化

12.6 序列化版本号

序列化版本号有什么用?

假设过了很久,Student 这个类源代码改动了。源代码改动之后,需要重新编译,编译之后生成了全新的字节码文件。并且class文件再次运行的时候,java 虚拟机生成的序列化版本号也会发生相应的改变,如果两次版本号不一样,再次序列化就会出错

举个例子:
第一次,你将 Student 对象 stu1 序列化为 文件 “s1”,此时的 Student 类有一个序列化版本号 number1,你可以对 “s1” 进行反序列化。

后来,因为业务发展,你修改了 Student 类的源码,此时的 Student 类有一个序列化版本号 number2,你再对文件 “s1” 进行反序列化就会出错,因为 Student 类的版本号不一样了,使得虚拟机认为修改后的 Student 类不再是 Student 类了 。

java语言中是采用什么机制来区分类的?
第一:首先通过类名进行比对,如果类名不一样,肯定不是同一个类。
第二:如果类名一样,靠序列化版本号进行区分。

小三编写了一个类: com. bjpowernode .java.bean.Student implements Serializable
小四编写了一个类: com.bjpowernode.java.bean.Student implements Serializable

不同的人编写了同一个类,但这两个类确实不是同一个类。这个时候序列化版本就起上作用了。
对于java虚拟机来说,java虚拟机是可以区分开这两个类的,因为这两个类都实现了serializable接口,都有默认的序列化版本号,他们的序列化版本号不一样。所以区分开了。(这是自动生成序列化版本号的好处)

这种自动生成的序列化版本号缺点是:一旦代码确定之后,不能进行后续的修改,因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类。(这样就不好了!)

最终结论∶
凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类。

public class Student implements Serializable {
    private int no;
    //transient关键字表示游离的,不参与序列化
    private transient String name;
    private int age;
    //建议将序列化版本号手动的写出来。不建议自动生成
    private static final long serialVersionUID = 8683452581122892189L;
    
    ...
}

13. Io+ Properties联合使用

IO流:文件的读和写。
Properties:是一个 Map 集合,key 和 value 都是 String 类型

准备一个 userinfo.properties 文件

# 在.properties文件中,#号表示注释,=两边最好不要有空格
username=admin 
password=123
# "="可以换成":",不建议使用":"
age:13
package com.dljd.io;

import java.io.FileReader;
import java.util.Properties;

/**
 * 非常好的一个设计理念:
 *      以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取。将来只需要修改这个文件的内容,
 *      java代码不需要改动,不需要重新编译,服务器也不需要重启。就可以拿到动态的信息。
 *
 * 类似于以上机制的这种文件被称为配置文件。并且当配置文件中的内容格式是:
 *      key1=value
 *      key2=value
 * 的时候,我们把这种配置文件叫做属性配置文件。
 * java规范中有要求:属性配置文件建议以.properties结尾,但这不是必须的。
 * 这种以.properties结尾的文件在java中被称为:属性配置文件。
 * 其中Properties是专门存放属性配置文件内容的一个类。
 */
public class IoPropertiesTest01 {
    public static void main(String[] args) throws Exception {
        //将userinfo文件中的数据加载到Properties对象当中。
        FileReader reader = new FileReader("userinfo.properties");
        Properties pro = new Properties();
        //调用Properties对象的Load方法将文件中的数据加载到Nap集合中。
        pro.load(reader);//文件中的数据顺着管道加载到Map集合中,其中等号左边做key,右边做value

        String username = pro.getProperty("username");//传入key,返回value
        System.out.println(username);

        String pwd = pro.getProperty("password");
        System.out.println(pwd);

        String age = pro.getProperty("age");
        System.out.println(age);
    }
}

你可能感兴趣的:(Java笔记,java,开发语言,后端)