第十六天 IO流

//本章只讲解了部分常用的IO流,并不全面
定义

I :Input
O :Output
通过IO可以完成硬盘文件的读写操作

分类
  • 按照流的方向进行分析

    • 往内存中去,叫做“输入”,or 读
    • 从内存中出,叫做“输出”,or 写
  • 按照读取数据方式不同

    • 按照字节的方式读取,一次读取一个字节
      • 这种流是万能的,什么类型的文件都可以读取
    • 按照字符方式读取,一次读取一个字符
      • 这种流仅仅是为了读取普通文本文件,即能使用记事本打开的文件(不能读取word文档)
Java.IO流的四大家族
  1. Java.IO.InputStream —— 字节输入流
  2. Java.IO.OutputStream —— 字节输出流
  3. Java.IO.Reader —— 字符输入流
  4. Java.IO.Writer —— 字符输出流
  • Java中只要“类名”以 Stream 结尾的都是字节流
  • Java中只要“类名”以“Reader、Writer”结尾的都是字符流
  • 四大家族的首领都是抽象类(abstract class)即不能直接实例化对象的类
流的close()、flush()方法
Java.IO.Closeable接口
  • 所有的流都实现了该接口,所以所有的流都有close()方法
  • 方法因为流就是一个管道,连接内存和硬盘之间的管道,用完之后一定要关闭,不然会占用很多的资源
Java.IO.Flushable接口
  • 所有的输出流都实现了该接口,都有flash()方法
  • 输出流在最终输出之后,一定要用flush()方法刷新一下,从而将管道中剩余的没有输出的数据全部输出出去(即清空管道)
  • 因为如果没有flush()的话,可能会导致数据丢失
需要掌握的16个流
文件专属流
种类
  1. Java.IO.FileInputStream
  2. Java.IO.FileOutputStream
  3. Java.IO.FileReader
  4. Java.IO.FileWriter
FileInputStream
  • 读取步骤

    1. 创建文件输入对象
    2. 读取数据(注意异常处理)
  • 读取方法

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

public class Test02{
    public static void main(String[] args){
        FileInputStream fis = null;
        
        try{
            //创建文件读取对象
            fis = new FileInputStream("F:\\Hello_world.txt");
            
            //遍历读取文件
            /*while (true){
                int readData = fis.read();
                if(readData == -1){
                    break;
                }
            }*/
            
            //优化while循环
            int readData = 0;
            while((readData = fis.read()) != -1){           //因为如果文件读完后,最后返回-1
                System.out.println(readData);
            }
            
        } catch (IOException e){
            e.printStackTrace();
        } finally {
            if (fis != null){
                try {
                    fis.close();
                } catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

上述方法有着明显的缺陷:一次只能读取一个byte,导致时间、资源都耗费在内存和硬盘交互上了,所以下述方法将会一次读取多个字节

  • 从byte数组中读取
    • 背景:一个一个字节读取耗费时间和资源
    • 作用:一次型读取多个字节,减少硬盘和内存的交互,提高程序的执行效率
    • 步骤:
      1. 创建文件输入对象
      2. 创建自定义长度的byte数组(该数组长度就是一次性读取字节的长度)
      3. 使用read(byte b[ ])方法读取文件(该方法返回的是读取的字节数量,而不是字节的内容)
      4. 将读取到的文件转换成具体内容
import java.io.FileInputStream;
import java.io.IOException;

public class Test03{
    public static void main(String[] args){
        FileInputStream fis = null;
        
        try{
            //1、创建文件输入对象
            fis = new FIleInputStream("F:\\Hello_world.txt");
            
            //2、创建自定义长度的byte数组
            byte[ ] b = new byte[4];
            
            /*
            //3、使用read(byte b[])方法读取文件
            int readCount = fis.read(b);
            
            //打印readCount的内容
            System.out.println(readCount);
            
            //4、将读取到的文件转换成具体内容
            System.out.println(new String(b, 0, readCount));
            */
            
            //循环读取文件内容
            int readCount = 0;
            while((readCount = fis.read(b)) != -1){
                System.out.println(new String(b, 0, readCount));
            }
            
        } catch(IOException e){
            e.printStackTrace();        //注:printStackTrace是一个方法,所以需要加括号
        } finally{
            if (fis != null){
                try{
                    fis.close();
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

  • FileInputStream中的其他方法
    • int available()
      • 返回流中剩余的没有读到的字节数量
      • 作用:不需要使用循环,读取一次就可以了(大文件不行,因为byte[]不能太大)
    • long skip(long n)
      • 跳过几个字节不读
import java.io.FileInputStream;
import java.io.IOException;

public class Test04{
    public static void main(String[] args){
        FileInputStream fis = null;
        
        try {
            fis = new FileInputStream("F:\\Hello_world.txt");
            //总字节数:
            System.out.println(fis.available());
            
            byte b[ ] = new byte[fis.available()];
            int readCount = fis.read(b);
            System.out.println(new String(b));      //为什么可以直接带入数组b,见下
            
            
            /*
            fis.skip(3);    //跳过几个字节不读
            System.out.println(fis.read());
            */
            
        } catch (IOException e){
            e.printStackTrace();
        } finally {
            if (fis != null){
                try {
                    fis.close();
                } catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}


Test04的 new String 为什么能直接带入数组b


//String相对应的源码
public String(byte bytes[]) {  
    this(bytes, 0, bytes.length);
}

//read相对应的源码
public int read(byte b[]) throws IOException {  
    return readBytes(b, 0, b.length);
}

//所以可以看出,Test04中fis.read(b) 执行的时候,返回的是已经读取了的文件
FileOutputStream
  • 文件字节输出流,负责写,即从内存到硬盘的过程
  • 写入步骤:
    1. 创建文件(注意覆盖写入和追加写入的区别)
    2. 使用write()方法写入
package Day023IO流;

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

public class Test05{
    public static void main(String[ ] args){
        FileOutputStream fos = null;
        try{
            //fos = new FIleOutputStream("myfile");         //覆盖写入,第一个参数是写入文件地址

            fos = new FileOutputStream("myfile",true);      //追加写入

            //创建需要写入的内容
            byte[ ] b = {97, 98, 99, 100, 101, 111};

            //将数组b全部写入
            fos.write(b);

            //将数组b部分写入 —— 第二个参数:起始位置,第三个参数:长度
            fos.write(b, 0, 2);

            //写入其他中文
            String s = "今天星期一";
            //将字符串转换成byte数组
            byte[ ] bs = s.getBytes();
            //写入
            fos.write(bs);

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

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







Test05中写入中文字符串的时候,为什么需要将字符串转换成byte数组?

//write方法的源代码
public void write(byte b[]) throws IOException { 
    writeBytes(b, 0, b.length, append);
}

//从而可以看出,使用write()方法的时候,参数必须是一个byte数组,所以需要转换
文件复制
  • 目的:使用FileInputStream 和 FileOutputStream 完成文件的拷贝
  • 拷贝核心:一边读一边写
  • 优点:任何形式的文件都可以进行拷贝
  • 步骤:
    1. 创建一个输入流对象
    2. 创建一个输出流对象
    3. 创建数组
    4. 一边读一边写
    5. 刷新流
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

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

        try{
            //1、创建一个输入流对象
            fis = new FileInputStream("F:\\杂七杂八\\汤圆\\汤圆视频02.mp4");

            //2、创建一个输出流对象
            fos = new FileOutputStream("F:\\学习\\JAVA\\知识点\\PDF文件\\汤圆视频02.mp4");

            //3、创建数组
            byte[ ] b = new byte[1024 * 1024];  //一次拷贝1MB

            //4、一边读一边写
            int readCount = 0;
            while((readCount = fis.read(b)) != -1){
                fos.write(b, 0, readCount);
                /*
                * 第一个参数是:传入的数组
                * 第二个参数是:数据中的开始偏移量
                * 第三个参数是:读取的内容
                *
                * */
            }

            //5、刷新流
            fos.flush();

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

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


FileReader
  • 定义:文件字符输入流,只能读取普通文本(比较方便、快捷)
  • 步骤:
    1. 创建文件字符输入流
    2. 准备一个char数组
    3. 使用read()方法读取char数组长度的内容
  • 注:FileInputStream和FIleReader的read()方法的区别:
    • FIleInputStream的是,从输入流读取一个字节的数据(不是read(byte b[ ])
    • FIleReader的是,将字符读入一个数组(所以可以使用foreach方法)
import java.io.FileReader;
import java.io.IOException;

public class Test07{
    public static void main(String[ ] args){
        FileReader reader = null;
        
        try{
            //1、创建文件字符输入流
            redaer = new FileReader("myfile");
            
            //2、准备一个char数组
            char[ ] chars = new char[4];
            
            //3、按照字符的方式读取文本内容
            reader.read(chars);
            
            /*
            //使用foreach遍历读取的内容(只能读取数组长度的内容)
            for(char c : chars){
                System.out.println(c);
            }
            */
            
            //使用while循环遍历读取的内容(可以读取文件全部内容)
            int readCount = 0;
            while((readCount = reader.read(chars)) != -1){
                System.out.println(new String(chars,0,readCount));
            }
        
        } catch(IOException e){
            e.printStackTrace();
        } finally{
            if (redaer != null){
                try{
                    reader.close();
                } catch(IOException e){
                    e.printStackTrace():
                }
            } 
        }
    }
}

FileWriter
  • 文件字符输出流,写(只能输出普通文本)
  • 步骤
    1. 创建FileWriter对象(注意写入方式——覆盖or追加)
    2. 使用char数组的方式写入(可以选择长度写入)
    3. 直接写入
    4. 刷新
import java.io.FileWriter;
import java.io.IOException;

public class Test08{
    public static void main(String[ ] args){
        FileWriter writer = null;
        
        try{
            //1、创建FileWriter对象
            writer = new FileWriter("myfile");              //覆盖写入
            writer = new FileWriter("myfile",true);       //追加写入
            
            //2、使用char数组的方式写入数据
            char[ ]  chars = {'罗','晓','辉','是','小','猪'};
            writer.write(chars);
            writer.write(chars, 5, 2);
            
            //换行
            writer.write("\n");

            
            //3、直接写入
            writer.write("罗晓辉是猪猪");
            
            //4、刷新
            writer.flush();
            
        } catch (IOException e){
            e.printStackTrace();
        } finally {
            if (writer != null){
                try{
                    writer.close();
                }
               catch(IOException e){
                e.printStackTrace();
               }
            }
        }
    
    }
}

缓冲流 and 转换流
  • 种类

    • Java.IO.BufferedReader
    • Java.IO.BufferedWriter
    • Java.IO.BufferedInputStream
    • Java.IO.BufferedOutputStream
  • 带有缓冲区的字符输入输出流

    • 使用的时候,不需要自定义char或byte数组,因为其自带缓冲
    • 当一个流的构造方法中需要一个流的时候,被传进来的流叫做节点流
    • 外部负责包装的这个流,叫做包装流,或者处理流
  1. BufferedReader的使用
    • 步骤:
      1. 创建FileReader流(仅仅是在举例中使用的该类,传入的是实现Reader抽象类的即可)
      2. 创建BufferedReader流,并将FileReader流作为参数传入
      3. 遍历读数据
      4. 关闭流
    • 注:
      • 使用了直接抛出异常,和try…catch两种方法
      • 下面会同时写两者一次,然后后面代码为了简便,均为直接抛出
      • readLine()方法读取的是文档中的一行,且不带换行符

使用try…catch方式

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

public class Test09{
    public static void main(String[ ] args){
        FileReader reader = null;
        
        try{
            //1、创建FileReader流
            reader = new FileReader("myfile");
            
            //2、创建BufferedReader流,并将FileReader流作为参数传入
            BufferedReader br = new BufferedReader(reader);     //详见解释一
            
            //3、遍历读数据
            String s = null;
            while((s = br.readLine()) != null){
                System.out.println(s);
            }
        } catch (IOException e){
            e.printStackTrace();
        } finally {
            if (reader != null){
                try {
                    //4、关闭流
                    reader.close();                 //详见解释三
                } catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    
    }
}


使用直接抛出异常的方式

import java.io.BufferedReader;
import java.io.FileReader;

public class Test010{
    public static void main(String[ ] args) throws Exception{
    //1、创建FileReader流
    FileReader reader = new FileReader("myfile");
    
    //2、创建BufferedReader流,并将FileReader流作为参数传入
    BufferedReader br = new BufferedReader(reader);
    
    //3、遍历读数据
    String s = null;
    while((s = br.readLine()) != null){
        System.out.println(s);
    }
    
    //4、关闭流
    br.close();          //详见解释二
    
    }
}

解释一:为什么 BufferedReader 在实例化对象的时候,需要传入参数 Reader

//BufferedReader 构造方法的源代码
public BufferedReader(Reader in) {
    this(in, defaultCharBufferSize);
}

public BufferedReader(Reader in, int sz){
    super(in);
    if (sz <= 0){
        throw new IllegalArgumentException("Buffer size <= 0");
        this.in = in;
        cb = new char[sz];
        nextChar = nChars = 0;
    }
}

//在实例化 BufferedReader 对象的时候,根据源代码可以知道,必须传入一个 Reader类型 的参数

public abstract class Reader implements Readable, Closeable {......}

//因为Reader类有 abstract关键字,所以Reader是一个抽象类,是不能直接实例化对象的,因为FileReader是Reader是子类,所以我们选择使用FileReader作为参数传入

解释二:为什么关闭的包装流而不是节点流

//BufferedReader 重写后close()方法的源代码
public void close() throws Exception {
    synchronized (lock) {
        if (in == null)
            return;
        try {
            in.close();
        } finally {
            in = null;
            cb = null;
        }
    }
}    

//源码中的 变量in 就是传入的参数Reader,所以可以看出,当包装流调用close()方法的时候,实际上执行的是传入的节点流的关闭

解释三:为什么try…catch方式关闭流的时候关闭的是节点流,而直接抛出异常的方式关闭的是包装流


//暂时不知道

字节流转换成缓冲流
  • 种类
    • Java.IO.InputStreamReader
    • Java.IO.OutputStreamWriter
  • 步骤
    1. 创建字节流对象
    2. 通过转换流转换(InputStreamReader 将字节流转换成字符流)
    3. 创建缓冲流对象,并将转换后的字符流传入进去
    4. 读取数据
    5. 关闭流
    • 步骤1~3可以合并成一个步骤(把缓冲流、转换流、字节输入流合并)
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;

public class Test011{
    public static void main(String[ ] args) throws Exception{
        //1、创建字节流对象
        FileInputStream fis = new FileInputStream("myfile");
        
        //2、通过转换流转换
        InputStreamReader reader = new InputStreamReader(fis);
        
        //3、创建缓冲流对象,并将转换后的字符流传入进去
        BufferedReader br = new BufferedReader(reader);
        
        //4、读取数据
        String s = null;
        while((s = br.readLine()) != null){
            System.out.println(s);
        }
        
        //5、关闭流
        br.close();

        System.out.println("=====================分割线======================");

        //将步骤1~3合并为一步:
        BufferedReader br2 = new BufferedReader(new InputStreamReader(new FileInputStream("myfile")));
        
        //读数据
        String s2 = null;
        while ((s2 = br2.readLine()) != null){
            System.out.println(s2);
        }
        
        //关闭流
        br2.close();
    }
}
BufferedWriter
  • 定义:带有缓冲的字符输出流
  • 步骤:(使用字节流转换为字符流方式写入数据)
    1. 创建字节流对象
    2. 通过转换流转换(OutputStreamReader)
    3. 创建缓冲流对象,并将转换后的字符流作为参数带入
    4. 写入数据
    5. 刷新流
    6. 关闭流
  • 注:
    • 步骤1~3可以合并(将缓冲流、转换流、字节输出流合并)
    • 输出流一定要刷新
  • 步骤:(直接使用BufferedWriter实例化对象写入数据)
    1. 实例化FileWriter对象
    2. 实例化BufferedWriter对象,并将FileWriter对象作为参数传入
    3. 写入数据
    4. 刷新流
    5. 关闭流
  • 注:
    • 步骤1~2可以合并(缓冲流、字符数出流合并)

(使用字节流转换为字符流方式写入数据)

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

public class Test012{
    public static void main(String[ ] args) throws Exception{
        //步骤1~3合并(将缓冲流、转换流、字节输出流合并)
        BufferedWriter br = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("myfile2",true)));       //注意这里是覆盖写入还是追加写入
        
        //写入数据
        br.write("Hello World");
        br.write("\n");     //写入一个换行符
        br.write("Hello Java");
        
        //刷新
        br.flush();
        
        //关闭流
        br.close();
    }
}

(直接使用BufferedWriter实例化对象写入数据)

import java.io.BufferedWriter;
import java.io.FileWriter;

public class Test013{
    public static void main(String[ ] args) throws Exception{
        //同时实例化BufferedWriter对象 和 FileWriter对象
        BufferedWriter br = new BufferedWriter(new FileWriter("myfile2", true));
        
        //写入数据
        br.write("Hello ZZ");
        
        //刷新流
        br.flush();
        
        //关闭流
        br.close();
    }   
}

数据流
  • 种类

    • Java.IO.DataInputStream
    • Java.IO.DataOutputStream
  • 注:

    • DataOutputStream写入的文件,只能使用 DataIntputStream 去读,并且读的时候,需要提前知道写入的顺序,读写的顺序要一致,才能正常取出数据
DataOutputStream
  • 定义:将数据连同数据类型一并写入文件

  • 步骤:

    1. 创建数据专属的字节数出流
    2. 写入数据
    3. 刷新流
    4. 关闭流
  • 注:

    • 写入数据的时候不同的数据类型要用其特定的write…()方法
import java.io.DataOutputStream;
import java.io.FileOutputStream;

public class Test014{
    public static void main(String[ ] args) throws Exception{
        //创建数据专属的字节输出流
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("data"));         //详见解释四
        
        byte b = 100;
        short s = 200;
        int i = 300;
        long l = 400L;
        float f = 3.0F;
        double d = 3.14;
        boolean sex = false;
        char c = 'c';
        
        //写入数据
        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();
    }
}

解释四:为什么实例化 DataOutputStream对象 的时候,需要传入一个FileOutputStream

    //DataOutputStream的源代码:
    public DataOutputStream(OutputStream out) {
        super(out);
    }
    //可以看出,DataOutputStream在实例化对象的时候,需要传入一个OutputStream类型的对象作为参数
    
    
    //OutputStream的源代码
    public abstract class OutputStream implements Closeable, Flushable{....}
    //因为关键字abstract,所以OutputStream是一个抽象类,不能直接实例化对象,而FIleOutputStream是其子类,所以可以实例化

DataInputStream
  • 步骤:

    1. 实例化DataInputStream对象
    2. 根据写入顺序开始读取数据
    3. 关闭流
  • 注:

    • 一定要知道写入顺序
import java.io.DataInputStream;
import java.io.FileInputStream;

public class Test015 {
    public static void main(String[] args) throws Exception{
        //1、实例化DataInputStream对象
        DataInputStream dis = new DataInputStream(new FileInputStream("data"));

        //2、开始读
        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);

        //3、关闭流
        dis.close();
    }
}
标准输出流
  • 种类
    • Java.IO.PrintWriter
    • Java.IO.PrintStream
  • 作用:
    • 改变输出方向
  • 注:
    • 标准输出流不需要手动close()关闭
PrintWriter
  • 步骤(改变输出流方向):
    1. 实例化PrintStream对象1(传入的参数,是实例化OutputStream子类的对象2,对象2的参数是需要改变后的目标文件地址)
    2. 修改输出方向,将输出方向修改到实例化出来的对象上
    3. 输出
import java.io.FileOutputStream;
import java.io.PrintStream;

public class Test016{
    public static void main(String[ ] args) throws Exception{
        //输出到控制台联合起来写:
        System.out.println("斯巴达");
        
        //分开写:
        PrintStream ps = System.out;
        ps.println("斯巴达");
        
        //改变标准输出流的方法,不再指向控制台,而是指向“log”文件
        //1、实例化PrintStream对象
        PrintStream printStream = new PrintStream(new FileOutputStream("log"));         //详见解释六
        
        //2、修改输出方向,将输出方向修改到实例化出来的对象上
        System.setOut(printStream);
        
        //3、输出
        System.out.println("Oh my god");
        
    }

}
  • 实例 —— 日志工具
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Logger{
    public static void log(String msg){
        try {
            //指向一个日志文件
            PrintStream out = new PrintStream(new FileOutputStream("log.txt", true));
            
            //改变输出方向
            System.setOut(out);
            
            //获取当前时间
            Date nowTime = new Date();
            
            //时间格式化
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
            
            //用String数据类型获取时间
            String strTime = sdf.format(nowTime);
            
            //将记录放到log日志文件中
            System.out.println(strTime + ":" + msg);
        } catch (FileNotFoundException e){
            e.printStackTrace();
        }
    }
}

测试日志工具

public class logTest{
    public static void main(String[ ] args) {
        Logger.log("测试一");
        Logger.log("测试二");
        Logger.log("测试三");
    }
}

/*
2020-12-15 17:14:57 545 :测试一
2020-12-15 17:14:57 567 :测试二
2020-12-15 17:14:57 568 :测试三
*/
Java.IO.File类
  • 定义:文件和目录路径名的抽象表示格式
  • 注:
    • File类和四大家族没有关系,所以File类不能完成文件的读和写
    • File对象代表什么? —— 文件|和目录路径名的抽象表示格式
  • File类需要掌握的方法:
修饰符和类型方法描述
booleanexists()判断File对象是否存在
booleancreateNewFile()以文件的形式创建
booleanmkdir()以目录的形式新建(目录就是文件夹)
booleanmkdirs()以多重目录的形式新建
StringgetParent()获取文件的父路径
FilegetParentFile()获取父路径,但是以file的方式返回
StringgetAbsolutePath()获取绝对路径
StringgetName()获取文件名
booleanisDirectory()判断是否是一个目录
booleanisFile()判断是否是一个文件
longlastModified()获取文件最后一次修改时间
longlength()获取文件大小
File[ ]listFiles()获取当前目录下所有子文件
    • lastModified()方法返回的是从1970到现在的毫秒数,所以需要转换成日期,再进行日期格式化

测试:

package Day023IO流;

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

public class Test017 {
    public static void main(String[] args) throws Exception {
        //新建File对象
        File file = new File("myfile3");

        //boolean exists() 判断File对象是否存在
        System.out.println(file.exists());

        System.out.println("=========分割线1=========");

        //boolean createNewFile() 以文件的形式创建
        if (!file.exists()){
            file.createNewFile();
            System.out.println("文件创建成功");
        } else {
            System.out.println("文件已经存在");
        }

        System.out.println("=========分割线2=========");

        //boolean mkdir() 以目录的形式新建(目录就是文件夹)
        if (!file.exists()){
            file.mkdir();
            System.out.println("文件夹创建成功");
        } else {
            System.out.println("文件夹已经存在");
        }

        System.out.println("=========分割线3=========");

        // 新建File1对象
        File file1 = new File("F://学习//myfile");

        //boolean mkdirs() 以多重目录的形式新建
        if (!file1.exists()){
            file1.mkdirs();
            System.out.println("多重目录形式新建成功");
        } else {
            System.out.println("文件已经存在");
        }

        System.out.println("=========分割线4=========");

        //String getParent() 获取文件的父路径
        String parentPath = file1.getParent();
        System.out.println(parentPath);

        System.out.println("=========分割线5=========");

        //File getParentFile() 获取父路径的父路径,但是以file的方式返回
        File parentPath1 = file1.getParentFile();
        System.out.println("获取绝对路径:" + parentPath1.getParentFile());

        System.out.println("=========分割线6=========");

        //String getAbsolutePath() 获取绝对路径
        String absolutePath = file1.getAbsolutePath();
        System.out.println(absolutePath);

        System.out.println("=========分割线7=========");

        //String getName() 获取文件名
        String name = file1.getName();
        System.out.println(name);

        System.out.println("=========分割线8=========");

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

        System.out.println("=========分割线9=========");

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

        System.out.println("=========分割线10=========");

        //long lastModified() 获取文件最后一次修改时间
        long lm = file1.lastModified();
        //将毫秒装换成日期
        Date time = new Date(lm);
        //日期格式化
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        String strTime = sdf.format(time);
        System.out.println(strTime);

        System.out.println("=========分割线11=========");

        //long length() 获取文件大小
        long lenght = file1.length();
        System.out.println(lenght);

        System.out.println("=========分割线12=========");


        File file2 = new File("F://学习");
        //File[ ] listFiles() 获取当前目录下所有子文件
        File[] files = file2.listFiles();
        for (File f : files){
            System.out.println(f);
        }


    }
}

对象流
  • 种类

    • Java.IO.ObjectInputStream
    • Java.IO.ObjectOutputStream
    • 参与序列化和反序列化的对象,必须实现Serializable接口
    • 通过源码发现,Serializable只是一个标志接口,该接口中什么都没有,仅仅起到了一个标识、标志的作用,JVM看见了类实现了这个接口,可能会对这个类进行特殊待遇
    • 特殊待遇:Serializable这个接口是给JVM参考的,JVM看到这个接口后,会为该类自动生成一个序列化版本号
  • 序列化和反序列化

    • 序列化(Serialize):java对象存储到文件中,将java对象的状态保存下来的过程 ———— 使用ObjectOutputStream
    • 反序列化(DeSerialize):将硬盘上的数据重新恢复成java对象 ———— 使用ObjectInputStream
序列化单个对象
  • 步骤
    1. 创建java对象
    2. 序列化
    3. 序列化对象
    4. 刷新
    5. 关闭
序列化单个对象

(新建了一个Student类,来序列化该类的对象)

import java.io.Serializable;

//新建Student类
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 + '\'' +
                '}';
    }
}

实例化具体过程

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

public class Test018 {
    public static void main(String[] args) throws Exception{
        //1、创建Java对象
        Student student = new Student(01,"张三");

        //2、实例化序列化对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Student"));       //详见解释七

        //3、序列化对象
        oos.writeObject(student);

        //4、刷新
        oos.flush();

        //5、关闭
        oos.close();
    }
}

解释七:为什么传入的是一个FileOutputStream对象

//ObjectOutputStream构造方法的源代码:
public ObjectOutputStream(OutputStream out) throws IOException {....}
//可以看出:参数位置需要传入一个 OutputStream类型 的对象


//OutputStream 的源代码
public abstract class OutputStream implements Closeable, Flushable {....}
//可以看出:OutputStream类 是一个抽象类,不能实例化对象,所以上述代码中实例化了OutputStream的子类
反序列化单个对象
  • 步骤
    1. 新建反序列化对象
    2. 反序列化(读)
    3. 关闭流

具体流程:

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

public class Test019 {
    public static void main(String[] args) throws Exception{
        //创建反序列化对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Student"));

        //开始序列化(读)
        Object obj = ois.readObject();

        //输出反序列化的内容
        System.out.println(obj);    //因为反序列化出来的是一个Student对象,所以会调用Student类的toString方法

        //关闭流
        ois.close();
    }
}
序列化多个对象
  • 步骤
    1. 创建集合
    2. 向集合添加数据
    3. 创建实例化对象
    4. 实例化集合
    5. 刷新流
    6. 关闭流

(新建了一个User类,来序列化多个该类的对象)

package 对象流.bean;

import java.io.Serializable;

public class User implements Serializable {
    private int no;

    private String name;

    public User() {
    }

    public User(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 "User{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}

实例化多个对象具体操作

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

public class Test020 {
    public static void main(String[] args) throws Exception{
        //1、创建List集合
        List<User> userList = new ArrayList<>();

        //2、向集合添加数据
        userList.add(new User(01,"张三"));
        userList.add(new User(02,"李四"));
        userList.add(new User(03,"王五"));

        //3、创建实例化对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("User"));

        //4、序列化List集合
        oos.writeObject(userList);

        //5、刷新流
        oos.flush();

        //6、关闭流
        oos.close();
    }
}
反序列化多个对象
  • 步骤:
    1. 创建反实例化对象
    2. 新建List集合接收反实例化出来的内容
    3. 遍历输出内容
  • 注:
    • 第二步接收的时候,要转换读取数据类型,因为使用LIst接收,读取到的是Object型
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;

public class Test021 {
    public static void main(String[] args) throws Exception{
        //1、创建反实例化对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("User"));

        //2、新建List集合接收反实例化出来的内容
        List<User> userList = (List<User>)ois.readObject();

        //3、遍历输出内容
        for (User user : userList){
            System.out.println(user);
        }
    }
}
transient关键字
  • 定义: transient关键字表示游离的,不参与序列化
    private transient String name;  //name不参与序列化操作
/*
User{no=1, name='null'}
User{no=2, name='null'}
User{no=3, name='null'}
*/
序列化版本号
  • 背景:自定义类的源代码发生改动后,就会重写编译,然后会形成全新的字节码文件。当字节码文件再次运行的时候,JVM生成的序列化版本号也会发生相应的变化
  • 建议:序列化版本号手动写出来,不建议自动生成
  • 注:
    • JVM识别一个类的时候,先通过类名,再通过序列化版本号

改变前面的Student类:

import java.io.Serializable;

public class Student implements Serializable {
//    JVM看见Serializable接口后,会自动生成一个序列化版本号
//    这里没有手动写出来,JVM会默认提供这个序列化版本号
    private int no;

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

    //private static final long serialVersionUID = 1L;    
    //JVM识别一个类的时候,先通过类名,再通过序列化版本号

    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 + '\'' +
                '}';
    }
}
IDEA生成序列化版本号
  • 步骤
    1. Setting —— Inspection —— Serializable class without ‘serialVersionUID’
    2. 勾选,然后确定
    3. 在实现了Serializable接口的类名上,点击alt + enter,选择"Add ‘serialVersionUID’ field "
IO和Properties联合使用
  • 定义:

    • IO流:文件的读和写
    • Properties:是一个Map集合,key和value都是String类型
  • 一个设计理念:

    • 经常改变的数据,可以单独写到一个文件中,使用程序动态读取
      将来只需要修改这个文件的内容,Java代码不需要改动,不需要重新编译,服务器不需要重启,就可以拿到动态信息
  • 注:

    • 类似于以上机制的这种文件被称为配置文件,并且当配置文件中的内容格式是:key=value
      的时候,将这种配置文件叫做属性配置文件
    • Java规范中有要求:属性配置文件建议以 .properties结尾,但是不是必须的
    • 这种以.properties结尾的文件在Java中被称为:属性配置文件,其中properties对象是专门存放属性配置文件的一个类
  • 步骤:

    1. 新建一个输入流对象
    2. 新建一个Properties集合
    3. 调用Properties对象的load方法将文件中的数据加载到Map集合中
    4. 通过key获取value

(新建一个属性配置文件)

username=admin
#############在配置文件中井号是注释##################
#属性配置文件的key重复的化,value会自动覆盖
#password=1234
password=123
#最好不要有空格
data = 100

#也可以使用冒号:  但是不建议使用
username2:admin2

具体实现:

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

public class Test022 {
    public static void main(String[] args) throws Exception{
        //1、新建一个输入流对象
        FileReader reader = new FileReader("999 review//Day023IO流//user.properties");

        //2、新建一个Properties集合
        Properties pro = new Properties();

        //3、调用Properties对象的load方法将文件中的数据加载到Map集合中
        pro.load(reader);

        //4、通过key获取value
        String name = pro.getProperty("username");
        System.out.println(name);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值