IO流完整笔记,个人学习的手敲笔记,看完你就能学会Java IO流

最近在准备面试,所以把之前的笔记都看了一遍,这是在学习IO流时做的笔记,觉得还是挺详细的,和大家分享一下。准备面试时间比较赶,我平时的笔记是在腾讯文档上中的,直接拷贝过来了,可能排版不太兼容,大家见谅,有时间我再好好整理排版。

目录

IO流图示

什么是IO?

IO流的分类

Java中的IO流

IO流四大家族

四大家族:

close()关闭流:

flush()刷新:

java.io下的16个流

文件的路径

read()方法:读

read()

read(byte数组)

String()方法:转换为字符串

String(byte数组)

string(byte数组,起始位置,读取的个数)

FileInputStream(字节输入流)读取txt文件

int available()方法:返回流当中剩余的没有读到的字节数量

long skip(long n):跳过n个字节不读

write()方法:写

getBytes()方法:转换为byte数组

FileOutputStream(字节输出流)写出到txt

拷贝1:FileInputStream、FileOutputStream

FileReader:字符输入流

FileWriter:字符输出流

拷贝2:FileReader、FileWriter

BufferedReader:带有缓冲区的字符输入流

readLine()方法:读一行

InputStreamReader:转换流,字节流转换为字符流

BufferedWriter:带有缓冲区的字符输出流

DataOutputStream:数据专属的字节输出流

DataInputStream:数据字节输入流

PrintStream:标准输出流

日志工具:使用PrintStream标准输出流

File类

File("目录")构造方法:

exists()方法:

createNewFile()方法:

mkdir()方法:

mkdirs()方法:

getParent()和getParentFile()方法:

getName()方法:

isDirectory()方法:

isFile()方法:

lastModified()方法:

length()方法:

listFiles()方法:

目录的拷贝

序列化和反序列化

ObjectOutputStream类:序列化

writeObject()方法:序列化对象

ObjectInputStream类:反序列化

readObject()方法:反序列化

transient关键字

序列化版本号

IO和Properties联合使用


 

  1. IO流图示

  1. 什么是IO?

    1. I:Input

    2. O:Output

    3. 通过IO可以完成硬盘文件的读和写

  2. IO流的分类

    1. 按流的方向分类(以内存为参照物):

      1. 输入流:进入到内存,叫作“输入”(Input),或者叫作“读”

      2. 输出流:从内存中出来,叫作“输出”(Output),或者叫作“写”

    2. 按照读取数据方式不同分类:

      1. 字节流:按照字节的方式读取数据,一次读取1个字节(byte),等同于一次读取8个二进制位。字节流是万能的,什么类型的文件都可以读取,包括:文本文件、图片、音频、视频等

      2. 字符流:按照字符的方式读取数据,一次读取一个字符。这种流是为了方便读取普通文本文件而存在的,字符流不能读取图片、声频、视频,只能读取纯文本文件(word是富文本,不能读取)

  3. Java中的IO流

    1. Java已经把IO流都写好了,Java中所有的流都在“java.io.*”下

    2. 我们只要研究如何new流对象,调用流对象的哪个方法进行读和写

  4. IO流四大家族

    1. 四大家族:

      1. java.io.InputStream 字节输入流

      2. java.io.OutputStream 字节输出流

      3. java.io.Reader 字符输入流

      4. java.io.Writer 字符输出流

    2. 技巧:在Java中,只要“类名”以“Stream”结尾的都是字节流;以“Reader/Writer”结尾的都是字符流

    3. close()关闭流:

      1. 所有流都实现了“java.io.Closeable”接口,都可以用close()方法关闭。

      2. 流是一个内存和硬盘之间的管道,会占用很多资源,流使用完一定要关闭

    4. flush()刷新:

      1. 所有输出流都实现了“java.io.Flushable”接口,都可以用flush()刷新。输出流在最终输出之后,一定要调用flush()刷新一下。

      2. 刷新就是把管道中剩余为输出的数据强行全部输出(清空管道)。刷新的作用就是清空管道

      3. 如果不刷新,有可能会造成数据丢失

  5. java.io下的16个流

    1. 重点掌握FileInputerStream、FileOutputStream,其他原理类似;ObjectInputStream、ObjectOutputStream也比较常用

    2. 16个流

      1. 文本专属:

        1. java.io.FileInputerStream 文件的字节输入流(重点)

        2. java.io.FileOutputStream 文件的字节输出流(重点)

        3. java.io.FileReader 文件的字符输入流

        4. java.io.FileWriter 文件的字符输出流

      2. 转换流(将字节流转换为字符流)

        1. java.io.InputStreamReader 转换输入流

        2. java.io.OutputStreamWriter 转换输出流

      3. 缓冲流专属

        1. java.io.BufferReader 带缓冲的字符输入流

        2. java.io.BufferWriter 带缓冲的字符输出流

        3. java.io.BufferInputStream 带缓冲的字节输入流

        4. java.io.BufferOutputStream 带缓冲的字节输出流

      4. 数据流专属

        1. java.io.DataInputStream 数据输入流

        2. java.io.DataOutputStream 数据输出流

      5. 标准输出流

        1. java.io.PrintWriter 字符输出流

        2. java.io.PrintStream 标准输出流(重点)

      6. 对象专属流

        1. java.io.ObjectInputStream 对象输入流(重点)

        2. java.io.ObjectOutputStream 对象输出流(重点)

  6. 文件的路径

    1. IDEA中,默认的当前路径是工程文件Project的根目录

  7. read()方法:读

    1. read()

      1. 无参数时,每次只会读取一个字节,效率较低

      2. 调用一次读取一个字节,每次调用都会自动向下读取

      3. read方法的返回值是读取到的“字节”的阿斯克码值;如果没有读取到字节就会返回-1

      4. 使用示范:

fis = new FileInputStream("E:\\IOliu_ceshi\\temp.txt");
int readData = fis.read();
  1. read(byte数组)

    1. 参数是byte数组,每次会读取byte数组.length个字节,效率较高

    2. 返回值是本次读取到的字节数量;如果没有读取到字节会返回-1

    3. 读取到的字节的阿斯克码值会保存在byte数组中,从位置0开始向后覆盖;如果最后读取到的字节数小与于数组长度,则新字节无法把所有的旧字节覆盖,应该舍弃最后多出的字节

    4. 使用示范:

fis = new FileInputStream("E:\\IOliu_ceshi\\temp.txt");
byte[] bytes = new byte[4];// 数组长度是4,则每次最多读取4个字节
int readData = fis.read(bytes);
  1. String()方法:转换为字符串

    1. String(byte数组)

      1. 参数是byte数组时,会把整个byte数组转换为字符串

      2. 返回值是字符串

      3. 使用示范:

fis = new FileInputStream("E:\\IOliu_ceshi\\temp.txt");
byte[] bytes = new byte[4];
int readData = 0;
while ((readData = fis.read(bytes)) != -1){
    // String(byte数组)方法可以把byte数组全部转换为字符串。不合理,应该是读取了多少字符就转多少字符
    System.out.println(new String(bytes));
}
  1. string(byte数组,起始位置,读取的个数)

    1. 参数是如上时,会把byte数组的部分内容转换为字符串,以“起始位置”和“读取的个数”来确定需转换的部分

    2. 使用示范:

fis = new FileInputStream("E:\\IOliu_ceshi\\temp.txt");
byte[] bytes = new byte[4];
int readData = 0;
while ((readData = fis.read(bytes)) != -1){
    // String(byte数组)方法可以把byte数组全部转换为字符串。不合理,应该是读取了多少字符就转多少字符
    System.out.println(new String(bytes, 0, readData));
}
  1. FileInputStream(字节输入流)读取txt文件

    1. FileInputStream是字节输入流

    2. 读取txt文件中的内容并输出到控制台

package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
// java.io.FileInputStream:
public class FileInputStreamTest04 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            // 创建文件字节输入流对象
            fis = new FileInputStream("E:\\IOliu_ceshi\\temp.txt");
            
            byte[] bytes = new byte[4];
            int readCount = 0;
            // read(bytes)每次读取bytes.length个字符,读取到的字符保存在数组中
            while ((readCount = fis.read(bytes)) != -1){
                // string(byte数组,起始位置,读取的个数):把byte数组中的字节转换为字符串
                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();
                }
            }
        }
    }
}
  1. int available()方法:返回流当中剩余的没有读到的字节数量

    1. 作用:返回流当中剩余的没有读到的字节数量

    2. 通过available方法获取总字节数,创建一个相应长度的byte数组,一次读取全部字节。因为byte数组长度不能太大,所以不能用来读取大文件(不多于574560字节)

    3. 代码展示:

fis = new FileInputStream("E:\\IOliu_ceshi\\temp.txt");
System.out.println("字节总数量:" + fis.available());
// 读取一个字节
int readByte = fis.read();
System.out.println("剩下的字节数量=" + fis.available());

// 通过available方法获取总字节数,创建一个相应长度的byte数组,一次读取全部字节
byte[] bytes = new byte[fis.available()];
int readCount = fis.read(bytes);// 不用循环,一次读取全部
System.out.println(new String(bytes));
  1. long skip(long n):跳过n个字节不读

    1. 作用:跳过n个字节不读

    2. 代码展示

fis = new FileInputStream("E:\\IOliu_ceshi\\temp.txt");
System.out.println("字节总数量:" + fis.available());
// 使用skip方法跳过n个字节
fis.skip(3);
System.out.println("跳过3个字节后剩余的字节数=" + fis.available());
  1. write()方法:写

    1. 作用:往文件里面写入数据

    2. write(byte[])构造方法:

      1. 把byte数组里的全部数据写出

b. write(byte[],起始位置, 数量)构造方法:

  1. 把byte数组的部分数据写出

  1. getBytes()方法:转换为byte数组

    1. 作用:可以把字符串转换为byte数组

    2. 代码示范:

String s = "\n今晚月色真美";
// 将字符串转换为byte数组
byte[] bytes = s.getBytes();
  1. FileOutputStream(字节输出流)写出到txt

    1. FileOutputStream是字节输出流

    2. 创建字节输出流对象时,文件不存在时会自动新建

    3. FileOutputStream(文件目录)构造方法:

      1. 这个构造方法会把原来文件的内容清空(从文件开头写入),谨慎使用

b. FileOutputStream(文件目录,true/false)构造方法:

  1. 这个构造方法可以通过第二个参数设置从开头还是末尾进行追加

  2. true是从末尾追加,false从开头写出(相当于和没有第二个参数的构造方法一样)

  1. 代码展示:将byte数组数据写出到txt文件

fos = new FileOutputStream("E:\\IOliu_ceshi\\temp.txt", true);
// 准备要写出的数据:byte数组
byte[] bytes = {97, 98, 99, 102, 105, 122};
// 写出数据。write(byte数组)方法把byte数组的全部数据写出
fos.write(bytes);
// write(byte数组,起始位置,个数):写出byte数组的部分数据
fos.write(bytes, 2, 1);
// 写完之后,一定要刷新
fos.flush();
  1. 代码展示:将字符串数据写出到txt文件

    1. 将字符串转换为byte数组在写出

fos = new FileOutputStream("E:\\IOliu_ceshi\\temp.txt", true);
// 准备要写入的数据:字符串
String s = "\n今晚月色真美";
// 将字符串转换为byte数组
byte[] bytes = s.getBytes();
// 写出数据
fos.write(bytes);
// 写完之后,一定要刷新
fos.flush();
  1. 拷贝1:FileInputStream、FileOutputStream

    1. 流程:

      1. 文件原地址 ----读----> 内存 ----写---->新地址

      2. 一边读一边写

    2. 代码展示

package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
 * 使用FileInputStream和FileOutputStream完成文件的拷贝
 * 一边读一边写。使用的是字节流,所以可以拷贝任意类型的文件
 */
public class Copy01 {
    public static void main(String[] args) {
        FileInputStream fis = null;// 字节输入流对象
        FileOutputStream fos = null;// 字节输出流对象

        try {
            // 字节输入流对象
            fis = new FileInputStream("C:\\Users\\young\\Desktop\\Mojito-周杰伦 .mp3");
            // 字节输出流对象
            fos = new FileOutputStream("E:\\IOliu_ceshi\\Mojito-周杰伦 .mp3");

            System.out.println("大小:" + fis.available());
            // 一边读一边写
            byte[] bytes = new byte[1024 * 1024];// 一次拷贝1M
            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 (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  1. FileReader:字符输入流

    1. FileReader是字符输入流,只能读取普通文本。读取文本内容时,比较方便快捷

    2. 代码展示:

public static void main(String[] args) {
    FileReader reader = null;
    try {
        // 创建文件字符输入流
        reader = new FileReader("E:\\IOliu_ceshi\\temp.txt");
        // 开始读
        char[] chars = new char[4];// 一次读4个
        int readCount = 0;
        while ((readCount = reader.read(chars)) != -1) {
            System.out.print(new String(chars, 0, readCount));
        }

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. FileWriter:字符输出流

    1. 文件字符输出流,只能输出普通文本

    2. 使用的是char[]数组

    3. 可以使用write(String字符串)直接写出字符串

    4. 代码展示:

public static void main(String[] args) {
    FileWriter out = null;
    try {
        // 创建文件输出流对象
        out = new FileWriter("E:\\IOliu_ceshi\\temp.txt");// 覆盖写入
        // out = new FileWriter("E:\\IOliu_ceshi\\temp.txt", true);// 追加写入
        // 创建需要写出的数据:char数组
        char[] chars = {'今', '晚', '月', '色', '真', '美', '!'};
        out.write(chars);// 写出char数组全部数据
        out.write(chars, 4, 2);// 写出char数组部分数据

        out.write("\n风也温柔!");// 直接写入字符串

        // 刷新
        out.flush();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. 拷贝2:FileReader、FileWriter

    1. 使用FileReader和FileWriter完成文件的拷贝

    2. 一边读一边写

    3. 使用的是字符流,只能拷贝普通文本文件

    4. 代码展示:

package com.bjpowernode.java.io;
import java.io.*;
public class Copy02 {
    public static void main(String[] args) {
        FileReader in = null;// 字节输入流对象
        FileWriter out = null;// 字节输出流对象

        try {
            // 字符输入流对象
            in = new FileReader("E:\\IOliu_ceshi\\temp.txt");
            // 字符输出流对象
            out = new FileWriter("E:\\IOliu_ceshi\\temp2.txt");

            // 边读边写
            char[] chars = new char[1024 * 1024];// 1M
            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 {
            // 关闭两个流时分开try,避免前面的流出异常影响后面的流关闭
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  1. BufferedReader:带有缓冲区的字符输入流

    1. BufferedReader是带有缓冲区的字符输入流,使用的时候不需要自定义char数组或byte数组,自带缓存

    2. 当一个流的构造方法中需要一个流的时候,这个被传进去的流叫做“节点流”,外部负责包装的流叫做“包装流”,或者叫“处理流”

    3. 关闭流时只需要关闭最外层流(最外层包装流),节点流会自动关闭

    4. readLine()方法:读一行

      1. readLine()方法每调用一次读取一行(不包括换行符),读不到时返回null

    5. 代码展示:

package com.bjpowernode.java.io;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
public class BUfferedReaderTest01 {
    public static void main(String[] args) throws Exception {
        FileReader reader = new FileReader("E:\\IOliu_ceshi\\temp.txt");
        // 当前这个程序的节点流是FileReader,包装流/处理流是BufferedReader
        BufferedReader br = new BufferedReader(reader);

        // 读一行。readLine()方法每调用一次读取一行(不包括换行符),读不到时返回null
        String firstLine = br.readLine();
        System.out.println(firstLine);

        String s = null;
        while ((s = br.readLine()) != null) {
            System.out.println(s);
        }

        // 关闭流。只需要关闭最外层流(最外层包装流),节点流会自动关闭
        br.close();
    }
}
  1. InputStreamReader:转换流,字节流转换为字符流

    1. InputStreamReader:转换流,字节流转换为字符流

    2. 代码展示:

package com.bjpowernode.java.io;
import java.io.*;
public class BUfferedReaderTest02 {
    public static void main(String[] args) throws Exception {
//        // 1、字节流对象
//        FileInputStream in = new FileInputStream("E:\\IOliu_ceshi\\temp.txt");
//
//        // 2、InputStreamReader方法将字节流转换为字符流。in是节点流,reader是包装流
//        InputStreamReader reader = new InputStreamReader(in);
//
//        // 3、这个构造方法只能传一个字符流,不能传字节流(所以通过InputStreamReader方法把字节流转换为了字符流)
//            // reader是节点流,br是包装流
//        BufferedReader br = new BufferedReader(reader);

        // 合并123步
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("E:\\IOliu_ceshi\\temp.txt")));

        String line = null;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }

        // 关闭最外层
        br.close();
    }
}
  1. BufferedWriter:带有缓冲区的字符输出流

    1. BufferedWriter是带有缓冲区的字符输出流

    2. 代码展示:

package com.bjpowernode.java.io;
import java.io.*;
public class BufferedWriterTest {
    public static void main(String[] args) throws Exception {
        // 创建带有缓冲区的字符输出流
        // BufferedWriter out = new BufferedWriter(new FileWriter("E:\\IOliu_ceshi\\temp.txt"));
        // BufferedWriter out = new BufferedWriter(new FileWriter("E:\\IOliu_ceshi\\temp.txt", true));
        // 使用OutputStreamWriter转换流:字节流转换为字符流
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("E:\\IOliu_ceshi\\temp.txt", true)));

        out.write("今晚月色真美");
        out.write("\n");
        out.write("风也温柔\n");

        out.flush();
        out.close();
    }
}
  1. DataOutputStream:数据专属的字节输出流

    1. DataOutputStream是数据专属的字节输出流

    2. 这个流可以将数据连同数据的类型一并写入文件

    3. 注意:所写入的文件不是普通文本文件,无法使用记事本正常打开,会乱码

    4. 代码展示:

package com.bjpowernode.java.io;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class DataOutputStreamTest {
    public static void main(String[] args) throws Exception {
        // 创建数据专属的字节输出流
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("E:\\IOliu_ceshi\\temp3.txt"));
        // 准备要写的数据
        byte b = 1;
        short s = 2;
        int i = 3;
        long l = 4L;
        float f = 3.0F;
        double d = 3.14;
        boolean sex = true;
        char c = 'a';
        // 写
        dos.writeByte(b);
        dos.writeShort(s);
        dos.writeInt(i);
        dos.writeLong(l);
        dos.writeFloat(f);
        dos.writeDouble(d);
        dos.writeBoolean(sex);
        dos.writeChar(c);
        
        dos.flush();
        dos.close();
    }
}
  1. DataInputStream:数据字节输入流

    1. DataOutputStream写的文件只能使用DataInputStream去读

    2. 读的时候需要提前知道写入的顺序,读的顺序需要和写的顺序一致,才可以正常取出数据

    3. 代码展示(读DataOutputStream写入的文件,也就是上面的那段代码):

package com.bjpowernode.java.io;
import java.io.DataInputStream;
import java.io.FileInputStream;
public class DataInputStreamTest01 {
    public static void main(String[] args) throws Exception {
        DataInputStream dis = new DataInputStream(new FileInputStream("E:\\IOliu_ceshi\\temp3.txt"));
        // 开始读,顺序要符合文件数据的顺序
        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 sex = 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(sex);
        System.out.println(c);

        dis.close();
    }
}
  1. PrintStream:标准输出流

    1. java.io.PrintStream是标准的子节输出流,默认输出到控制台,标准输出流不需要手动关闭

    2. 可以改变标准输出流的输出方向

// 1、标准流指向log.txt文件(定义流的方向)
PrintStream printStream = new PrintStream(new FileOutputStream("E:\\IOliu_ceshi\\log.txt"));
// 2、修改输出方向,将输出方向指向log文件(使用流的方向)
System.setOut(printStream);
  1. 代码展示:

package com.bjpowernode.java.io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamTest {
    public static void main(String[] args) throws Exception {
        // 联合起来写
        System.out.println("hello world!");

        // 分开写
        PrintStream ps = System.out;
        ps.println("hello world!");

        // 改变标准输出流的输出方向(默认方向是控制台)
        // 1、标准流指向log.txt文件(定义流的方向)
        PrintStream printStream = new PrintStream(new FileOutputStream("E:\\IOliu_ceshi\\log.txt"));
        // 2、修改输出方向,将输出方向指向log文件(使用流的方向)
        System.setOut(printStream);
        // 3、输出到log.txt文件
        System.out.println("hello kitty");
    }
}
  1. 日志工具:使用PrintStream标准输出流

    1. 日志工具代码:

package com.bjpowernode.java.io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

// 日志工具
public class Logger {
    /**
     * 记录日志的方法
     * @param msg 日志的内容
     */
    public static void log(String msg) {
        PrintStream printStream = null;
        try {
            // 1、标准流指向log.txt文件(定义流的方向)
            printStream = new PrintStream(new FileOutputStream("E:\\IOliu_ceshi\\log.txt", true));
            // 2、修改输出方向,将输出方向指向log文件(使用流的方向)
            System.setOut(printStream);

            // 获取当前时间
            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();
        }
    }
}
  1. 日志工具的测试:

package com.bjpowernode.java.io;

// 测试日志工具类
public class LogTest {
    public static void main(String[] args) {
        Logger.log("调用了System类的gc()方法,建议启动垃圾回收");
        Logger.log("调用了UserService的doSome()方法");
        Logger.log("用户尝试进行登录,验证失败");
    }
}
  1. File类

    1. File类不属于四大家族,不能完成文件的读和写

    2. File对象代表什么?

      1. 文件和目录路径名的抽象表达式

      2. 一个File对象有可能是目录,也有可能是文件

    3. 提示:掌握下面列出来的方法就可以了

    4. File("目录")构造方法:

      1. 创建File对象

    5. exists()方法:

      1. 判断某文件是否存在

    6. createNewFile()方法:

      1. 以文件的形式创建

    7. mkdir()方法:

      1. 以目录的形式创建

    8. mkdirs()方法:

      1. 以多级目录的形式创建

    9. getParent()和getParentFile()方法:

      1. 都是获取文件的上层目录,如果没有指定的父目录,则返回空

      2. getParent() 的返回值是String型的,getParentFile()的返回值是File型的

    10. 以上方法的代码展示:

package com.bjpowernode.java.io;
import java.io.File;
import java.io.IOException;
public class FileTest01 {
    public static void main(String[] args) throws IOException {
        // 创建一个File对象
        File f1 = new File("E:\\IOliu_ceshi\\File\\file1");
        File f2 = new File("E:\\IOliu_ceshi\\File\\file2");
        File f3 = new File("E:\\IOliu_ceshi\\File\\file3\\a\\b\\c");

        // 判断f1是否存在
        System.out.println(f1.exists());

        // 如果f1不存在则以文件的形式创建
        if (!f1.exists()) {
            f1.createNewFile();
            System.out.println("file1文件创建成功");
        } else {
            System.out.println("文件:file1存在");
        }

        // 如果f2不存在则以目录的形式创建
        if (!f2.exists()) {
            f2.mkdir();
            System.out.println("file2目录创建成功");
        } else {
            System.out.println("目录:file2存在");
        }

        // 如果f3多级目录不存在则以目录的形式创建
        if (!f3.exists()) {
            f3.mkdirs();
            System.out.println("file3多级目录创建成功");
        } else {
            System.out.println("多级目录:file3存在");
        }

        // 获取文件的路径
        // getParent方法获取上层目录
        String parentPath = f3.getParent();
        System.out.println("f3的路径:" + parentPath);//E:\IOliu_ceshi\File\file3\a\b
        File parentFile = f3.getParentFile();
        System.out.println("获取f3的绝对路径:" + parentFile.getAbsolutePath());
    }
}
  1. getName()方法:

    1. 获取文件名

  2. isDirectory()方法:

    1. 判断是否是目录

  3. isFile()方法:

    1. 判断是否是文件

  4. lastModified()方法:

    1. 获取文件最后一次修改时间,从1970年至今的毫秒数

  5. length()方法:

    1. 获取文件的大小,单位是字节

  6. listFiles()方法:

    1. File[] listFiles()方法:获取当前目录下所有的子文件,调用对象需要是目录

  7. 以上方法的代码展示

package com.bjpowernode.java.io;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileTest02 {
    public static void main(String[] args) {
        File f1 = new File("E:\\IOliu_ceshi\\temp.txt");
        File f = new File("E:\\IOliu_ceshi");

        // 获取文件名
        System.out.println("文件名:" + f1.getName());

        // 判断是否是目录
        System.out.println(f1.isDirectory());// false

        // 判断是否是文件
        System.out.println(f1.isFile());// true

        // 获取文件最后一次修改时间,从1970年至今的毫秒数
        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);

        // 获取文件的大小,单位是字节
        System.out.println("文件大小:" + f1.length());

        // File[] listFiles()方法:获取当前目录下所有的子文件,调用对象需要是目录
        File[] files = f.listFiles();
        for (File file : files) {
            System.out.println(file.getAbsolutePath());
        }
    }
}
  1. 目录的拷贝

    1. 直接看代码吧……

    2. 代码展示:

package com.bjpowernode.java.io;
import java.io.*;
/**
 * 递归拷贝
 * 难点:递归、文件路径的拼接
 * 路径拼接思路:copy目标目录 + copy源文件名称
 */
public class CopyAll {
    public static void main(String[] args) {
        // 拷贝源(复制的文件)(从File的上级目录IOliu_ceshi开始,会把IOliu_ceshi拷贝到目标目录下)
        File srcFile = new File("E:\\IOliu_ceshi\\File");

        // 拷贝目标(粘贴的地方)("E:\\IOliu_ceshi\\File-拷贝"\\IOliu_ceshi\\……)
        File destFile = new File("E:\\IOliu_ceshi\\File-拷贝");

        // 调用拷贝的方法
        copyDir(srcFile, destFile);
    }

    /**
     * 拷贝
     * 是文件则拷贝,是目录则创建
     * @param srcFile  拷贝源(复制的文件)
     * @param destFile  拷贝目标(粘贴的地方)
     */
    private static void copyDir(File srcFile, File destFile) {
        if (srcFile.isFile()) {
            //如果是文件则拷贝,一边读一边写
            FileInputStream in = null;
            FileOutputStream out = null;
            try {
                // 读文件
                in = new FileInputStream(srcFile);
                // 写文件
                String path = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath() : destFile.getAbsolutePath()  + "\\") + srcFile.getAbsolutePath().substring(3);
                out = new FileOutputStream(path);
                // 一边读一边写
                byte[] bytes = new byte[1024 * 1024];
                int readCount = 0;
                while ((readCount = in.read(bytes)) != -1){
                    out.write(bytes, 0, readCount);
                }
            } 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();
                    }
                }
            }
            return;// 如果是文件则递归到头了
        }

        // 获取拷贝源下面的子目录
        File[] files = srcFile.listFiles();

        //
        for (File file : files) {
            //
            if(file.isDirectory()){
                String srcDir = file.getAbsolutePath();
                String destDir = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath() : destFile.getAbsolutePath()  + "\\") + srcDir.substring(3);
                File newFile = new File(destDir);

                if (!newFile.exists()){
                    newFile.mkdirs();
                }
            }
            // 递归调用
            copyDir(file, destFile);
        }
    }
}
  1. 序列化和反序列化

    1. 图解

  1. ObjectOutputStream类:序列化

  2. 实现Serializable接口

    1. 如果bena需要序列化,那么需要实现Serializable接口

    2. 通过源代码可以发现这个接口没有需要实现的方法,相当于是一个标识(标识供JVM参考)

    3. 不实现Serializable接口时,进行对象序列化时报NotSerializableException(对象不支持序列化)

    4. JVM虚拟机看到实现Serializable接口后,会自动生产序列化版本号(不用手写,JVM会默认提供)

  3. writeObject()方法:序列化对象

  4. 序列化单个对象代码展示:

    1. 创建bena

package com.bjpowernode.java.bean;
import java.io.Serializable;
// 如果bena需要序列化,那么需要实现Serializable接口
public class Student implements Serializable {
    private int no;
    private String name;
    ……
}
  1. 序列化

package com.bjpowernode.java.io;
import com.bjpowernode.java.bean.Student;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamTest01 {
    public static void main(String[] args) throws Exception {
        // 创建Student对象
        Student s = new Student(1111, "zhangsan");
        // 创建序列化对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\IOliu_ceshi\\xuliehua.txt"));
        //序列化对象
        oos.writeObject(s);

        // 刷新
        oos.flush();
        // 关闭
        oos.close();
    }
}
  1. ObjectInputStream类:反序列化

  2. readObject()方法:反序列化

  3. 反序列化代码展示

package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.ObjectInputStream;
public class ObjectInputStreamTest01 {
    public static void main(String[] args) throws Exception {
        // 创建反序列化对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\IOliu_ceshi\\xuliehua.txt"));
        // 开始反序列化,读
        Object obj = ois.readObject();
        // 反序列化回来是一个学生对象,会自动调用学生对象的toString方法
        System.out.println(obj);

        // 关闭流
        ois.close();
    }
}
  1. 序列化和反序列化多个对象

    1. 将对象封装到集合中,序列化和反序列化集合对象

    2. 代码展示:List集合

      1. 创建bena

package com.bjpowernode.java.bean;

import java.io.Serializable;

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

}
  1. 序列化

package com.bjpowernode.java.io;
import com.bjpowernode.java.bean.User;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
// 将对象放到序列化集合中,可一次序列化多个对象,
// 参与序列化的ArrayList集合和集合中的元素User需要实现java.io.Serializable接口
public class ObjectOutputStreamTest02 {
    public static void main(String[] args) throws Exception {
        // 将需要序列化的多个数据存入集合
        List<User> userList = new ArrayList<>();
        userList.add(new User(1, "张三"));
        userList.add(new User(2, "李四"));
        userList.add(new User(3, "王五"));
        userList.add(new User(4, "赵六"));
        // 创建序列化对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\IOliu_ceshi\\xuliehua2.txt"));
        // 序列化一个集合,集合中放了很多对象,相当于一次序列化多个对象
        oos.writeObject(userList);

        oos.flush();
        oos.close();
    }
}
  1. 反序列化

package com.bjpowernode.java.io;
import com.bjpowernode.java.bean.User;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;
// 反序列化集合,相当于一次反序列化多个对象
public class ObjectInputStreamTest02 {
    public static void main(String[] args) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\IOliu_ceshi\\xuliehua2.txt"));
        //Object obj = ois.readObject();
        // System.out.println(obj instanceof List);
        List<User> userList = (List<User>) ois.readObject();
        for (User user : userList){
            System.out.println(user);
        }
        ois.close();
    }
}
  1. transient关键字

    1. ransient关键字:表示游离。如果bena的某个属性不参与序列化,可以通过该关键字来设置

    2. 代码展示

public class User implements Serializable {
    private int no;
    // transient关键字:表示游离,该属性不参与序列化
    private transient String name;
}
  1. 序列化版本号

    1. Java中区分类的机制:

      1. 通过类名区分,如果类名不一样,则肯定不是同一个类

      2. 如果类名一样,则通过序列化版本号进行区分

    2. 序列化版本号缺陷

      1. 这种自动生成序列化版本号的缺点是:一旦代码确定之后,不能进行后续的修改。因为一旦修改,必然会重新编译,此时会生成全新的序列化版本号,JVM就会认为这是一个全新的类

    3. 最终结论(解决方案):

      1. 凡是一个类实现了Serializable接口,建议给该类一个固定的序列化版本号。即使之后修改了代码,版本号不变,JVM会认为是同一个类

    4. 手动设置序列化版本号

      1. 手动设置序列化版本号,不采用自动生成

      2. 建议使用全球唯一的版本号,可用UUID

      3. 代码展示(这里为了简便,使用了简单的版本号)

public class Student implements Serializable {
    // 设置序列化版本号
    private static final long serialVersionUID = 1L;

    private int no;
    private String name;
}
  1. IO和Properties联合使用

    1. IO流:文件的读写

    2. Properties(属性配置文件):是一个Map集合,key和value都是String类型,如果key重复则会自动覆盖

    3. 经常改变的数据可以写到Properties文件中,使用程序动态读取。将来只需要修改Properties文件中的内容即可,不需要改动Java代码,不需要重新编译,不需要重新启动服务器

    4. 类似的文件称之为配置文件,文件名一般采用“.Properties”结尾

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值