java中的IO流

1、IO流概述
2、文件专属
---2.1关于FileInputStream
---2.2关于FileOutputStream
---2.3文件的拷贝
---2.4FileReader
---2.5FileWriter
3、缓冲区流专属
---3.1BufferedReader
---3.2BufferedWriter
4、转换流
5、数据流专属
---5.1DataOutputStream
---5.2DataInputStream
6、标准输出流
---6.1PrintStream
---6.2日志工具
7、File类
---7.1概述
---7.2File类的常用方法
8、对象专属流
---8.1概述
---8.2ObjectOutputStream
---8.3ObjectInputStream
---8.4序列化多个对象
---8.5transient关键字
---8.6序列化版本号有什么用?
---8.7IDEA自动生成序列化版本号
9、IO和Properties的联合使用
---9.1概述
---9.2配置文件
---9.3代码示例

IO流概述

  • 什么是IO?
    I:Input(硬盘到内存);O:Output(内存到硬盘)
    通过IO可以完成硬盘文件的读和写。
  • IO流的分类
    1、输入流、输出流
    按照流的方向进行分类:以内存作为参照物,往内存中去,叫做输入(Input),或者叫做读(Read)。从内存中出来,叫做输出(Output),或者叫做写(Write)。
    2、字节流
    按照读取数据方式不同进行分类:有的流是按照字节的方式读取数据,一次读取1个字节(byte),等同于一次读取8个二进制位。这种流是万能的,什么类型的文件都可以读取。包括:文本文件、图片、声音文件、视频文件等。
    3、字符流
    有的流是按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的。这种流不能读取图片、声音、视频等文件,只能读取纯文本文件,连word文件都无法读取。
    假设文件file.txt,采用字符流的话是这样读的:
    a中国bc张三fe
    第一次读:a字符("a"字符在windows系统中占用1个字节。)
    第二次读:中字符("中"字符在windows系统中占用2个字节。)
  • java中IO流的四大版块
    ①、java.io.InputStream; 字节输入流
    ②、java.io.OutputStream; 字节输出流
    ③、java.io.Reader; 字符输入流
    ④、java.io.Writer; 字符输出流
    注:类名以Stream结尾的都是字节流。以Reader/Writer结尾的都是字符流。以上四大板块都是抽象类。
    1、所有的流都实现了:java.io.Closeable接口,都是可关闭的,都有close ()方法。流相当于内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费(占用)很多资源。
    2、所有的输出流都实现了:java.io.Flushable接口,都是可刷新的,都有flush()方法。输出流在最终输出之后,一定要用flush()刷新。这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道),刷新的作用就是清空管道。
    注意:如果没有flush()可能会导致丢失数据。
  • java.io包下需要掌握的16个流:
    文件专属
    java.io.FileInputStream(重点)
    java.io.FileOutputStream(重点)
    java.io.FileReader
    java.io.FileWriter
    转换流:(将字节流转换成字符流)
    java.io.InputStreamReader
    java.io.OutputStreanWriter
    缓冲流专属
    java.io.BufferedReader
    java.io.BufferedWriter
    java.io.BufferedInputStream
    java.io.BufferedOutputStream
    数据流专属
    iava.io.DataInputStream
    java.io.DataOutputStream
    标准输出流
    java.io.PrintWriter
    java.io.PrintStream(重点)
    对象专属流
    java.io.ObjectInputStream(重点)
    java.io.ObjectOutputStream(重点)

文件专属

关于FileInputStream
  • 概述
    1、文件字节输入流,是万能的。任何类型的文件都可以采用这个流来读。
    2、字节的方式完成输入的操作和读的操作(硬盘到内存)
    3、关于路径:
    IDEA的默认的当前路径是Project的根。
  • 代码示例(txt文件内容是zhang):
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Demo{
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("D:\\111.txt");
            /*int read():
            从该输入流读取一个字节的数据。*/
            int readDate = fis.read();
            System.out.println(readDate);
            /*当读到文件的末尾,再读的时候
            就读取不到任何数据了,就会返回-1。*/
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //在finally语句块当中确保流一定关闭。
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

输出(ASCII码的形式):122(字母z)

  • 用循环读取数据:
    1、while循环
while (true){
  int readDate = fis.read();
  if (readDate == -1){
      break;
  }
  System.out.println(readDate);
}

输出:
在这里插入图片描述
2、改良while循环

int readDate = 0;
while ((readDate = fis.read()) != -1){
    System.out.println(readDate);
}

3、以下代码会出现错误:

while (fis.read() != -1){
    System.out.println(fis.read());
}
//fis.read()会使指针下移,会漏掉一个字节

输出:
在这里插入图片描述
4、缺点:
一次读取一个字节(byte),这样内存和硬盘交互太频繁,时间/资源耗费在交互上面了。

  • 往byte数组中读
    int read(byte[] b)
    1、从该输入流读取最多 b.length个字节的数据为字节数组。(返回值是读取到的字节数量,不是字节本身。)
    减少硬盘和内存的交互,提高程序的执行效率。
    2、代码示例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Demo{
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("D:\\111.txt");
            byte[] bytes = new byte[4];
            System.out.println(fis.read(bytes));
            //读取到zhan,输出4
            System.out.println(fis.read(bytes));
            //读取到g,输出:1
            System.out.println(fis.read(bytes));
            //没有读到,输出:-1
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

3、将字节数组全部转换为字符串:

byte[] bytes = new byte[4];
System.out.println(fis.read(bytes));
System.out.println(new String(bytes));

System.out.println(fis.read(bytes));
System.out.println(new String(bytes));

System.out.println(fis.read(bytes));
System.out.println(new String(bytes));

输出:
在这里插入图片描述
第二次读取到的“g”会把第一次读取到的“zhan”里面的“z”覆盖掉。
4、改良
不应该全部都转换,应该是读取了多少个字节,转换多少个。
使用String(char[] value, int offset, int count)
分配一个新的 String ,其中包含字符数组参数的子阵列中的字符。(offset代表起始位置,count代表长度)
例:

byte[] bytes = new byte[4];
int readCount = fis.read(bytes);
System.out.println(readCount);
System.out.println(new String(bytes, 0, readCount));
readCount = fis.read(bytes);
System.out.println(readCount);
System.out.println(new String(bytes, 0, readCount));

输出:
在这里插入图片描述

  • FileInputStream读取数据最终版本
    代码示例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Demo{
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("D:\\111.txt");
            byte[] bytes = new byte[4];
            /*while (true){
                int readCount = fis.read(bytes);
                if (readCount==-1){
                    break;
                }
                System.out.print(new String(bytes,0,readCount));
            }*/
            //再改进
            int readCount = 0;
            while ((readCount = fis.read(bytes)) != -1){
                System.out.print(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();
                }
            }
        }
    }
}

输出:
在这里插入图片描述

  • FileInputStream的其它常用方法
    1、int available()
    返回流中剩余(没有被读到)字节数的估计值,从而不会被下一次调用此输入流的方法阻塞。
    代码示例:
//读一个字节(有5个字节)
int readByte = fis.read();
System.out.println("剩下的没有读的字节数:" + fis.available());
//输出:4
/*对于小文件使用这种方式读取,可以不用循环*/
byte[] bytes = new byte[fis.available()];
int readCount = fis.read(bytes);
System.out.println(new String(bytes));
/*这种方式不太适合太大的文件,因为byte[]数组不能太大*/

2、long skip(long n)
跳过并从输入流中丢弃 n字节的数据。
代码示例:

fis = new FileInputStream("D:\\111.txt");
byte[] bytes = new byte[fis.available()];
/*跳过两个字节不读取*/
fis.skip(2);
int readCount = fis.read(bytes);
System.out.println(new String(bytes));
/*输出:ang*/
关于FileOutputStream
  • 概述
    文件字节输出流,负责写。
    从内存到硬盘。
  • 代码示例:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo{
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            /*这种方式谨慎使用,这种方式会
            先将原文件清空,然后重新写入。*/
            fos = new FileOutputStream("D://111.txt");
            //开始写
            byte[] bytes = {97,98,99,100};
            //将byte数组全部写出(abcd)
            fos.write(bytes);
            //将byte数组一部分写出(ab)
            fos.write(bytes,0,2);
            //写完之后一定要刷新
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行后:
在这里插入图片描述

  • 追加的方式在文件末尾写入
FileOutputStream(String name, boolean append)

以追加的方式在文件末尾写入,不会清空原文件内容。
代码示例:

fos = new FileOutputStream("D://111.txt",true);
byte[] bytes = {97,98,99,100};
fos.write(bytes);
fos.write(bytes,0,2);
fos.flush();

运行后:
在这里插入图片描述

  • 写入字符串
    代码示例:
fos = new FileOutputStream("D://111.txt",true);
//写入字符串
String s = "我是修电脑的";
//转换成byte类型
byte[] bytes = s.getBytes();
//写入
fos.write(bytes);
fos.flush();

运行后:
在这里插入图片描述

文件的拷贝

代码示例:

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

public class Demo{
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //创建一个输入流对象
            fis = new FileInputStream("D://111.txt");
            //创建一个输出流对象
            fos = new FileOutputStream("D://222.txt");
            //核心:边读边写
            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 {
            //分开try,一起可能会导致另一个流的关闭
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                }catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行后:
在这里插入图片描述

FileReader
  • 概述
    文件宇符输入流,只能读取普通文本。填取文本内容时,比较方便,快捷。
  • 代码示例:
FileReader fr = null;
try {
    fr = new FileReader("D://111.txt");
    char[] chars = new char[4];
    int readCount = 0;
    while ((readCount = fr.read(chars)) != -1){
        System.out.println(new String(chars,0,readCount));
    }

输出:
在这里插入图片描述

FileWriter
  • 概述
    文件字符输出流(写)。只能输出普通文本。
  • 代码示例
import java.io.FileWriter;
import java.io.IOException;

public class Demo {
    public static void main(String[] args) {
        FileWriter out = null;
        try {
            out = new FileWriter("D://222.txt");
            //开始写
            char[] chars = {'修','电','脑'};
            out.write(chars);
            out.write(chars,1,2);
            out.write("\n");
            out.write("我是真不会");
            //刷新
            out.flush();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行后:
在这里插入图片描述

  • 文件复制
    使用FileReader和FileWriter进行拷贝的话,只能拷贝“普通文本"文件。
    代码示例:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Demo {
    public static void main(String[] args) {
        FileWriter out = null;
        FileReader in = null;
        try {
            //读
            in = new FileReader("D://111.txt");
            //写
            out = new FileWriter("D://222.txt");
            //边读边写
            char[] chars = new char[1024 * 512];
            int readCount = 0;
            while ((readCount = in.read(chars)) != -1){
                out.write(chars,0,readCount);
            }
            //刷新
            out.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

运行后:
在这里插入图片描述

缓冲区流专属

BufferedReader
  • 概述
    是一个带有缓冲区的字符输入流。使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组,其自带缓冲。
  • 代码示例:
import java.io.BufferedReader;
import java.io.FileReader;

public class Demo {
    public static void main(String[] args) throws Exception {
        FileReader fr = new FileReader("D://111.txt");
        /*1、当一个流的构造方法中需要一个流的时候,
        这个被传进来的流叫做:节点流。
        2、外部负责包装的这个流,叫做:包装流,
        还有一个名字叫做:处理流。
        3、就当前这个程序来说:FileReader就是一个节点流。
        BufferedReader就是包装流/处理流。*/
        BufferedReader br = new BufferedReader(fr);
        //读一行,但不会读出换行符
        /*String firstLine = br.readLine();
        System.out.println(firstLine);
        String secondLine = br.readLine();
        System.out.println(secondLine);*/
        String s = null;
        while ((s = br.readLine()) != null){
            System.out.println(s);
        }
        /*
        * 关闭流
        * 对于包装流来说,只需要关闭最外层流就行,
        * 里面的节点流会自动关闭。(详见源代码)
        * br.close();
        * */
    }
}

输出:
在这里插入图片描述

BufferedWriter
  • 概述:
    带有缓冲的字符输出流
  • 代码示例:
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.OutputStreamWriter;

public class Demo {
    public static void main(String[] args) throws Exception {
        BufferedWriter out1 = new BufferedWriter(new FileWriter("D://222.txt"));
        //转换流的方式
        //BufferedWriter out2 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("D://222.txt",true)));
        out1.write("这是BufferedWriter");
        out1.write("\n");
        out1.write("这是BW第二行");
        out1.flush();
        out1.close();
    }
}

运行后:
在这里插入图片描述

转换流

  • 将字节流转换成字符流
    java.io.InputStreamReader
    java.io.OutputStreanWriter
  • 代码示例:
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;

public class Demo {
    public static void main(String[] args) throws Exception {
        //字节流
        FileInputStream fis = new FileInputStream("D://111.txt");
        //通过转换流转换
        //这里的fis是节点流,reader是包装流
        InputStreamReader reader = new InputStreamReader(fis);
        //这个构造方法只能传一个字符流,不能传字节流。
        //这里的reader是节点流,br是包装流
        BufferedReader br = new BufferedReader(reader);
        //最强套娃
        //BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("D://111.txt")));
        String line = null;
        while ((line = br.readLine()) != null){
            System.out.println(line);
        }
        //关闭最外层
        br.close();
    }
}

输出:
在这里插入图片描述

数据流专属

DataOutputStream
  • 概述
    1、DataOutputStream(OutputStream out)
    创建一个新的数据输出流,以将数据写入指定的底层输出流。
    2、这是一个数据专属的流。
    3、这个流可以将数据连同数据的类型一并写入文件。
    4、注意:这个文件不是普通文本文档。(这个文件用记事本打不开)
  • 代码示例:
import java.io.DataOutputStream;
import java.io.FileOutputStream;

public class Demo {
    public static void main(String[] args) throws Exception{
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("data"));
        //写数据
        byte b = 11;
        short s = 22;
        int i = 33;
        long l = 44L;
        float f = 55F;
        double d = 66.66;
        boolean bl = true;
        char c = 'a';
        //写,把数据以及数据的类型一并写入到文件当中。
        dos.writeByte(b);
        dos.writeInt(i);
        dos.writeShort(s);
        dos.writeLong(l);
        dos.writeFloat(f);
        dos.writeDouble(d);
        dos.writeBoolean(bl);
        dos.writeChar(c);
        //刷新和关闭
        dos.flush();
        dos.close();
    }
}

运行后(用记事本方式打开为乱码):
在这里插入图片描述

DataInputStream
  • 概述
    1、DataInputStream(InputStream in)
    创建使用指定的底层InputStream的DataInputStream。
    2、数据字节输入流。
    3、DataOutputStream写的文件,只能使用DataInputStream去读。并且读的时候你需要提前知道写入的顺序。
    4、读的顺序需要和写的顺序一致,才可以正常取出数据。
  • 代码示例
import java.io.DataInputStream;
import java.io.FileInputStream;

public class Demo {
    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);
        //关闭
        dis.close();
    }
}

输出:
在这里插入图片描述

标准输出流

PrintStream
  • 概述
    标准的字节输出流,默认输出到控制台。
  • 代码示例
import java.io.PrintStream;

public class Demo {
    public static void main(String[] args){
        //合起来
        System.out.println("hello world!");
        //分开写
        PrintStream ps = System.out;
        ps.println("hello kitty!");
        //标准输出流不需要手动close()关闭。
    }
}

输出:
在这里插入图片描述

  • 改变标准输出流的输出方向
    static void setOut(PrintStream out)
    重新分配“标准”输出流。
import java.io.FileOutputStream;
import java.io.PrintStream;

public class Demo {
    public static void main(String[] args) throws Exception{
        //标准输出流不再指向控制台,指向"data"文件。
        PrintStream printStream = new PrintStream(new FileOutputStream("data"));
        //修改输出方向,将输出方向修改到"data"文件。
        System.setOut(printStream);
        //再输出
        System.out.println("改变标准输出流的输出方向");
        System.out.println("输出到了data文件里");
    }
}

运行后:
在这里插入图片描述

日志工具
import java.text.SimpleDateFormat;
import java.util.Date;

/**
日志工具
*/
public class Demo {
    public static void log(String msg){
        PrintStream out = null;
        try {
            //指向一个日志文件
            out = new PrintStream(new FileOutputStream("log",true));
            //改变输出方向
            System.setOut(out);
            //当前时间
            Date nowTime = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
            String strTime = sdf.format(nowTime);
            System.out.println(strTime + ":" + msg);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}
public class DemoTest{
    public static void main(String[] args) {
        Demo.log("调用了a方法");
        Demo.log("调用了b方法");
        Demo.log("用户登录失败");
    }
}

log文件:
在这里插入图片描述

File类

  • 概述
    1、File类和IO流的四大版块没有关系,所以File类不能完成文件的读和写。
    2、File对象代表文件和目录路径名的抽象表示形式。
    3、一个File对象有可能对应的是目录,也可能是文件。
  • File类的常用方法
    1、boolean exists()
    测试此抽象路径名表示的文件或目录是否存在。
    代码示例:
import java.io.File;

public class Demo {
    public static void main(String[] args) {
        /*判断File是否存在*/
        File f1 = new File("D://Demo//file");
        System.out.println(f1.exists());
        //输出:false
    }
}

2、boolean createNewFile()
当且仅当具有该名称的文件尚不存在时,创建一个由该抽象路径名命名的新的空文件。
代码示例:

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

public class Demo {
    public static void main(String[] args) throws IOException {
        File f1 = new File("D://Demo//file");
        /*如果D://file不存在,则以文件的方式创建出来*/
        if (!f1.exists()){
            f1.createNewFile();
        }
    }
}

运行后:
在这里插入图片描述
3、boolean mkdir()
创建由此抽象路径名命名的目录。
代码示例:

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

public class Demo {
    public static void main(String[] args) throws IOException {
        File f1 = new File("D://Demo//file");
        /*如果D://file不存在,则以目录的方式创建出来*/
        if (!f1.exists()){
            f1.mkdir();
        }
    }
}

运行后:
在这里插入图片描述
4、boolean mkdir()
创建由此抽象路径名命名的目录。
代码示例:

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

public class Demo {
    public static void main(String[] args) throws IOException {
        /*创建多重目录*/
        File f2 = new File("D://a//b//c");
        if (!f2.exists()){
            f2.mkdirs();
        }
    }
}

运行后:
在这里插入图片描述
5、String getParent()
返回此抽象路径名的父 null的路径名字符串,如果此路径名未命名为父目录,则返回null。
File getParentFile()
返回此抽象路径名的父,或抽象路径名 null如果此路径名没有指定父目录。
String getAbsolutePath()
返回此抽象路径名的绝对路径名字符串。
代码示例:

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

public class Demo {
    public static void main(String[] args) throws IOException {
        /*获取文件的父路径*/
        File f3 = new File("D://a//b//c");
        String parentPath = f3.getParent();
        System.out.println(parentPath);
        //方法二
        File parentFile = f3.getParentFile();
        System.out.println("获取绝对路径:" + parentFile.getAbsolutePath());
    }
}

输出:
在这里插入图片描述
6、

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

public class Demo {
    public static void main(String[] args) throws Exception {
        File f1 = new File("D:\\Demo");
        /*获取文件名*/
        System.out.println("文件名:" + f1.getName());
        //输出:文件名:Demo

        /*判断是否是一个目录*/
        System.out.println(f1.isDirectory());
        //输出:true

        /*判断是否是一个文件*/
        System.out.println(f1.isFile());
        //输出:false

        /*获取文件最后一次修改时间*/
        Long haomiao = f1.lastModified();
        //将总毫秒数转换为日期
        Date time = new Date(haomiao);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        String strTime = sdf.format(time);
        System.out.println(strTime);
        //输出:2020-05-23 22:00:13 379

        /*获取文件大小*/
        System.out.println(f1.length());
        //输出:0(字节)(因为是空文件夹)
    }
}

输出:
在这里插入图片描述
7、File[] listFiles()
返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件。(获取当前目录下所有的子文件)
代码示例:

import java.io.File;

public class Demo {
    public static void main(String[] args) {
        File f = new File("D:\\Demo");
        File[] files = f.listFiles();
        for (File file:files
             ) {
            System.out.println(file.getAbsolutePath());
            System.out.println("文件名是:" + file.getName());
        }
    }
}

输出:
在这里插入图片描述

对象专属流

  • 概述
    1、序列化(Serialize): 将在内存中的java对象存储到硬盘文件中,将java对象的状态保存下来的过程。(拆分对象)
    反序列化(DeSerialize):将硬盘上的数据重新恢复到内存当中,恢复成java对象。(组装对象)
    2、ObjectOutputStream:序列化
    ObjectInputStream:反序列化
    3、参与序列化和反序列化的对象,必须实现Serializable接口。通过源代码发现,Serializable接口只是一个标志接口,这个接口当中什么代码都没有。
public interface Serializable {
}

4、它起到标识(标志)的作用,java虚拟机看到这个类实到了这个接口,可能会对这个类进行特殊待遇。
5、Java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号。这里没有手动写出来,java虚拟机会默认提供这个序列化版本号。

  • ObjectOutputStream
    代码示例:
import java.io.Serializable;

public class Demo implements Serializable {
    private int no;
    private String name;

    public Demo() {
    }

    public Demo(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 "Demo{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class DemoTest{
    public static void main(String[] args) throws Exception {
        Demo demo = new Demo(11,"zhang");
        //序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\Demo\\111.txt"));
        //序列化对象
        oos.writeObject(demo);
        //刷新
        oos.flush();
        //关闭
        oos.close();
    }
}

运行后:
在这里插入图片描述

  • ObjectInputStream
    代码示例:
import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class Demoois {
    public static void main(String[] args) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\Demo\\111.txt"));
        //反序列化(读)
        Object obj = ois.readObject();
        System.out.println(obj.toString());
        ois.close();
    }
}

输出:
在这里插入图片描述

  • 序列化多个对象
    采用集合的形式
    1、序列化
    代码示例:
import java.io.Serializable;

public class Demo implements Serializable {
    private int no;
    private String name;

    public Demo() {
    }

    public Demo(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 "Demo{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

public class DemoTest{
    public static void main(String[] args) throws Exception {
        List<Demo> demos = new ArrayList<>();
        demos.add(new Demo(3,"zhangsan"));
        demos.add(new Demo(4,"lisi"));
        demos.add(new Demo(5,"wangwu"));
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("demos"));
        //序列化一个集合,集合中包括许多对象
        oos.writeObject(demos);
        oos.flush();
        oos.close();
    }
}

运行后:
在这里插入图片描述
2、反序列化
代码示例:

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

public class Demoois {
    public static void main(String[] args) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("demos"));
        List<Demo> demoList = (List<Demo>) ois.readObject();
        for (Demo demo : demoList) {
            System.out.println(demo.toString());
        }
        ois.close();
    }
}

输出:
在这里插入图片描述

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

序列化后:
在这里插入图片描述
反序列化后:
在这里插入图片描述

  • 序列化版本号有什么用?
    1、在Demo里面添加以下代码:private int age;再运行Demoois会出现以下错误:
java.io.InvalidClassException: Demo;
local class incompatible:
stream classdesc serialVersionUID = 2324874497685740715,
local class serialVersionUID = -2371890423749661961

2、原因
源代码改动之后,需要重新编译,编译之后生成了全新的字节码文件。并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会发生相应的改变。所有相同类名的类可以通过序列化版本号来区分。(java虚拟机识别一个类的时候先通过类名,如果类名一致,再通过序列化版本号。)
3、缺点
一旦代码确定之后,不能进行后续的修改,因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类。
4、改善缺点
凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类。
5、手动写出序列化版本号
例(ArrayList的手动序列化版本号):

private static final long serialVersionUID = 8683452581122892189L;

加在上述代码中后:
在这里插入图片描述
即使修改了Demo里面的代码,(修改后不序列化)反序列化也不会出现问题了。

  • IDEA自动生成序列化版本号
    1、
    在这里插入图片描述
    2、光标停留出:Alt+回车
    在这里插入图片描述
    3、这样具有唯一性
    在这里插入图片描述

IO和Properties的联合使用

  • 概述
    1、以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取
    2、将来只需要修改这个文件的内容,java代码不需要改动,不需要重新编译,服务器也不需要重启,就可以拿到动态的信息。
  • 配置文件
    在这里插入图片描述
    1、类似于以上机制的这种文件被称为配置文件。当配置文件中的内容格式是:key1=value key2=value的时候,我们把这种配置文件叫做属性配置文件。
    2、java规范中有要求:属性配置文件建议以.properties结尾,但这不是必须的。这种以.properties结尾的文件在java中被称为:属性配置文件。其中properties是专门存放属性配置文件内容的一个类。
    3、在配置文件中井号是注释,属性配置文件的key重复的话, value会自动覆盖。中间最好不要有空格。
  • 代码示例
import java.io.FileInputStream;
import java.util.Properties;

public class Demo{
    public static void main(String[] args) throws Exception{
        //创建输入流对象
        FileInputStream fis = new FileInputStream("data");
        //新建map集合
        Properties pro = new Properties();
        /*
         调用properties对象的load方法将文件中的数据加载到map集合中。
         文件中的数据顺着管道加载到Map集合中,
         其中等号左边做key,右边做value。
         */
        pro.load(fis);
        //通过key来获取value
        String username = pro.getProperty("username");
        System.out.println(username);
        String password = pro.getProperty("password");
        System.out.println(password);
    }
}

输出:
在这里插入图片描述

posted @ 2020-05-22 23:08  YU_UY  阅读(365)  评论(0)    收藏  举报