Java语言基础--IO流


常用文件操作

  • File:文件操作类,用于对文件进行操作,在java中目录也是文件

    • 类继承

  • 方法

    • createNewFile:根据已有的类信息,创建文件(可以配合构造器指定文件路径创建文件)
    • getName:获取文件名称
    • getAbsolutePath:获取文件绝对路径
    • getParent: 获取父级目录
    • length:文件大小,单位字节
    • exists:是否存在
    • isFlile:是否是一个文件
    • isDirectory:是否是一个目录
    • delete:删除,返回值是布尔值(成功或失败)
    • mkdirs:创建多级目录,返回布尔值
    • mkdir:创建一级目录,返回布尔值

IO流原理与分类

原理

  • 用于处理数据传输:读写文件,网络通讯...
  • java中使用流方式来处理数据传输,所有相关的类、接口集合在java.io包下
  • 输入input流(将其他位置的数据输入到内存中),输出output流(将内存中的数据输出到其他位置)

分类

  • 按单位分:字节流(8 bit),字符流(按字符)

  • 按流向分:输入流,输出流

  • 按角色分:节点流,处理流/包装流

  • 四大抽象基类:

  • java的IO流涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的
  • 由这四个类派生的子类名称都是以其父类名作为子类的后缀名

体系

IO流常用对象

InputStream

FileInputStream

  • 使用
import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStream_ {
    public static void main(String[] args) {
    }

    /**
     * 演示读取文件...
     * 单个字节的读取,效率比较低
     * -> 使用 read(byte[] b)
     */
    @Test
    public void readFile01() {
        String filePath = "e:\\hello.txt";
        int readData = 0;
        FileInputStream fileInputStream = null;
        try {
            //创建 FileInputStream 对象,用于读取 文件
            fileInputStream = new FileInputStream(filePath);
            //从该输入流读取一个字节的数据。 如果没有输入可用,此方法将阻止。
            //如果返回-1 , 表示读取完毕
            while ((readData = fileInputStream.read()) != -1) {
                System.out.print((char)readData);//转成char显示
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭文件流,释放资源.
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 使用 read(byte[] b) 读取文件,提高效率
     */
    @Test
    public void readFile02() {
        String filePath = "e:\\hello.txt";
        //字节数组
        byte[] buf = new byte[8]; //一次读取8个字节.
        int readLen = 0;
        FileInputStream fileInputStream = null;
        try {
            //创建 FileInputStream 对象,用于读取 文件
            fileInputStream = new FileInputStream(filePath);
            //从该输入流读取最多b.length字节的数据到字节数组。 此方法将阻塞,直到某些输入可用。
            //如果返回-1 , 表示读取完毕
            //如果读取正常, 返回实际读取的字节数
            while ((readLen = fileInputStream.read(buf)) != -1) {
                System.out.print(new String(buf, 0, readLen));//显示
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭文件流,释放资源.
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

OutputStream

FileOutputStream

  • 类图

  • 使用

    • 输出样例
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public void writeFile() {
    
            //创建 FileOutputStream对象
            String filePath = "e:\\a.txt";
            FileOutputStream fileOutputStream = null;
            try {
                //得到 FileOutputStream对象
                //1. new FileOutputStream(filePath) 创建方式,当写入内容是,会覆盖原来的内容
                //2. new FileOutputStream(filePath, true) 创建方式,当写入内容是,是追加到文件后面
                fileOutputStream = new FileOutputStream(filePath, true);
                //写入一个字节
                //fileOutputStream.write('H');//
                //写入字符串
                String str = "hsp,world!";
                //str.getBytes() 可以把 字符串-> 字节数组
                //fileOutputStream.write(str.getBytes());
                /*
                write(byte[] b, int off, int len) 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流
                 */
                fileOutputStream.write(str.getBytes(), 0, 3);
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    • 复制文件
    import java.io.*;
    
    /**
     * @author 韩顺平
     * @version 1.0
     */
    public class FileCopy {
        public static void main(String[] args) {
            //完成 文件拷贝,将 e:\\Koala.jpg 拷贝 c:\\
            //思路分析
            //1. 创建文件的输入流 , 将文件读入到程序
            //2. 创建文件的输出流, 将读取到的文件数据,写入到指定的文件.
            String srcFilePath = "e:\\Koala.jpg";
            String destFilePath = "e:\\Koala3.jpg";
            FileInputStream fileInputStream = null;
            FileOutputStream fileOutputStream = null;
    
            try {
    
                fileInputStream = new FileInputStream(srcFilePath);
                fileOutputStream = new FileOutputStream(destFilePath);
                //定义一个字节数组,提高读取效果
                byte[] buf = new byte[1024];
                int readLen = 0;
                while ((readLen = fileInputStream.read(buf)) != -1) {
                    //读取到后,就写入到文件 通过 fileOutputStream
                    //即,是一边读,一边写
                    fileOutputStream.write(buf, 0, readLen);//一定要使用这个方法
                }
                System.out.println("拷贝ok~");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    //关闭输入流和输出流,释放资源
                    if (fileInputStream != null) {
                        fileInputStream.close();
                    }
                    if (fileOutputStream != null) {
                        fileOutputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

Reader

FileReader

  • 类图

  • 使用
/**
     * 单个字符读取文件
     */
    @Test
    public void readFile01() {
        String filePath = "e:\\story.txt";
        FileReader fileReader = null;
        int data = 0;
        //1. 创建FileReader对象
        try {
            fileReader = new FileReader(filePath);
            //循环读取 使用read, 单个字符读取
            while ((data = fileReader.read()) != -1) {
                System.out.print((char) data);
            }

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

    /**
     * 字符数组读取文件
     */
    @Test
    public void readFile02() {
        System.out.println("~~~readFile02 ~~~");
        String filePath = "e:\\story.txt";
        FileReader fileReader = null;

        int readLen = 0;
        char[] buf = new char[8];
        //1. 创建FileReader对象
        try {
            fileReader = new FileReader(filePath);
            //循环读取 使用read(buf), 返回的是实际读取到的字符数
            //如果返回-1, 说明到文件结束
            while ((readLen = fileReader.read(buf)) != -1) {
                System.out.print(new String(buf, 0, readLen));
            }

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

Writer

FileWriter

  • 使用(大同小异,一定注意关闭流,否则不保存数据,老韩亲身经历)

    底层使用的还是FileOutputStream(字节流)

节点流与处理流

节点流:

底层流、低级流,直接与数据相接

处理流(包装流):

包装节点流,将抽象和实体分离,实现更强大的功能,优点:性能、便捷

常见的处理流:

  • 字符处理流

    • BufferedReader
    • BufferedWrirer
    • 示例
    public class BufferedCopy_ {
    
        public static void main(String[] args) {
            //老韩说明
            //1. BufferedReader 和 BufferedWriter 是安装字符操作
            //2. 不要去操作 二进制文件[声音,视频,doc, pdf ], 可能造成文件损坏
            //BufferedInputStream
            //BufferedOutputStream
            String srcFilePath = "e:\\a.java";
            String destFilePath = "e:\\a2.java";
            BufferedReader br = null;
            BufferedWriter bw = null;
            String line;
            try {
                br = new BufferedReader(new FileReader(srcFilePath));
                bw = new BufferedWriter(new FileWriter(destFilePath));
    
                //说明: readLine 读取一行内容,但是没有换行
                while ((line = br.readLine()) != null) {
                    //每读取一行,就写入
                    bw.write(line);
                    //插入一个换行
                    bw.newLine();
                }
                System.out.println("拷贝完毕...");
                
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //关闭流
                try {
                    if(br != null) {
                        br.close();
                    }
                    if(bw != null) {
                        bw.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
  • 字节处理流

    • BufferedInputStream
    • BufferedOutputStream
    • 示例
    public class BufferedCopy02 {
        public static void main(String[] args) {
    
    //        String srcFilePath = "e:\\Koala.jpg";
    //        String destFilePath = "e:\\hsp.jpg";
    //        String srcFilePath = "e:\\0245_韩顺平零基础学Java_引出this.avi";
    //        String destFilePath = "e:\\hsp.avi";
            String srcFilePath = "e:\\a.java";
            String destFilePath = "e:\\a3.java";
    
            //创建BufferedOutputStream对象BufferedInputStream对象
            BufferedInputStream bis = null;
            BufferedOutputStream bos = null;
    
            try {
                //因为 FileInputStream  是 InputStream 子类
                bis = new BufferedInputStream(new FileInputStream(srcFilePath));
                bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
    
                //循环的读取文件,并写入到 destFilePath
                byte[] buff = new byte[1024];
                int readLen = 0;
                //当返回 -1 时,就表示文件读取完毕
                while ((readLen = bis.read(buff)) != -1) {
                    bos.write(buff, 0, readLen);
                }
    
                System.out.println("文件拷贝完毕~~~");
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
    
                //关闭流 , 关闭外层的处理流即可,底层会去关闭节点流
                try {
                    if(bis != null) {
                        bis.close();
                    }
                    if(bos != null) {
                        bos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
            }
    
        }
    }
    
  • 对象处理流

    • ObjectInputStream
    • ObjectOutputStream
    • 序列化
      • 序列化就是在保存数据时,保存数据的值和数据类型
      • 反序列化就是在恢复数据时,恢复数据的值和数据类型
      • 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一
        • Serializable //标记接口,无任何方法,推荐使用
        • Externalizable //带两个方法
    • 细节
      • 读写顺序要一致
      • 要求实现序列化或反序列化对象,需要实现Serializable
      • 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性(private static final long seriaVersionUID = 1L)
      • 序列化对象时,默认将里面所有的属性都进行序列化,但除了static或transient修饰的成员
      • 序列化对象时,要求里面属性的类型也需要实现序列化接口
      • 序列化具备可继承性,父类实现了,子类默认继承
    • 示例
    import java.io.Serializable;
    
    //如果需要序列化某个类的对象,实现 Serializable
    public class Dog implements Serializable {
        private String name;
        private int age;
        //序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
        private static String nation;
        private transient String color;
        //序列化对象时,要求里面属性的类型也需要实现序列化接口
        private Master master = new Master();
    
        //serialVersionUID 序列化的版本号,可以提高兼容性
        private static final long serialVersionUID = 1L;
    
        public Dog(String name, int age, String nation, String color) {
            this.name = name;
            this.age = age;
            this.color = color;
            this.nation = nation;
        }
    
        @Override
        public String toString() {
            return "Dog{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", color='" + color + '\'' +
                    '}' + nation + " " +master;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    import java.io.FileOutputStream;
    import java.io.ObjectOutputStream;
    
    
    /**
     * 演示ObjectOutputStream的使用, 完成数据的序列化
     */
    public class ObjectOutStream_ {
        public static void main(String[] args) throws Exception {
            //序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
            String filePath = "e:\\data.dat";
    
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
    
            //序列化数据到 e:\data.dat
            oos.writeInt(100);// int -> Integer (实现了 Serializable)
            oos.writeBoolean(true);// boolean -> Boolean (实现了 Serializable)
            oos.writeChar('a');// char -> Character (实现了 Serializable)
            oos.writeDouble(9.5);// double -> Double (实现了 Serializable)
            oos.writeUTF("韩顺平教育");//String
            //保存一个dog对象
            oos.writeObject(new Dog("旺财", 10, "日本", "白色"));
            oos.close();
            System.out.println("数据保存完毕(序列化形式)");
    
        }
    }
    
    import java.io.*;
    
    /**
     * 演示ObjectInputStream的使用, 完成数据的反序列化
     */
    public class ObjectInputStream_ {
        public static void main(String[] args) throws IOException, ClassNotFoundException {
    
            //指定反序列化的文件
            String filePath = "e:\\data.dat";
    
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
    
            //读取
            //老师解读
            //1. 读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致
            //2. 否则会出现异常
    
            System.out.println(ois.readInt());
            System.out.println(ois.readBoolean());
    
            System.out.println(ois.readChar());
            System.out.println(ois.readDouble());
            System.out.println(ois.readUTF());
    
    
            //dog 的编译类型是 Object , dog 的运行类型是 Dog
            Object dog = ois.readObject();
            System.out.println("运行类型=" + dog.getClass());
            System.out.println("dog信息=" + dog);//底层 Object -> Dog
    
            //这里是特别重要的细节:
    
            //1. 如果我们希望调用Dog的方法, 需要向下转型
            //2. 需要我们将Dog类的定义,放在到可以引用的位置
            Dog dog2 = (Dog)dog;
            System.out.println(dog2.getName()); //旺财..
    
            //关闭流, 关闭外层流即可,底层会关闭 FileInputStream 流
            ois.close();
    
    
        }
    }
    
  • 标准输入输出流

    • System.in 标准输入(编译类型InputStream,运行类型BufferedInputStream,默认设备:键盘)
    • System.out 标准输出(编译类型PrintStream,运行类型PrintStream,默认设备:显示器)
  • 转换流

    • InputStreamReader:将InputStream(字节流)包装为Reader(字符流),处理纯文本数据时,使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转为字符流,且可以指定编码格式
    • OutputStreamWriter:将OutputStream(字节流)包装为Writer(字符流),处理纯文本数据时,使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转为字符流,且可以指定编码格式
  • 打印流

    • PrintStream(打印字节流)

    • PrintWriter(打印字符流)

    • 示例

    public class PrintStream_ {
        public static void main(String[] args) throws IOException {
    
            PrintStream out = System.out;
            //在默认情况下,PrintStream 输出数据的位置是 标准输出,即显示器
            out.print("john, hello");
            //因为print底层使用的是write , 所以我们可以直接调用write进行打印/输出
            out.write("你好".getBytes());
            out.close();
    
            //我们可以去修改打印流输出的位置/设备
            //1. 输出修改成到 "e:\\f1.txt"
            //2. "hello" 就会输出到 e:\f1.txt
            System.setOut(new PrintStream("e:\\f1.txt"));
            System.out.println("hello");
    
        }
    }
    
  • 配置类

    • Properties
    • 示例
    public class Properties01 {
        public static void main(String[] args) throws IOException {
    
            //读取mysql.properties 文件,并得到ip, user 和 pwd
            BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties"));
            String line = "";
            while ((line = br.readLine()) != null) { //循环读取
                String[] split = line.split("=");
                //如果我们要求指定的ip值
                if("ip".equals(split[0])) {
                    System.out.println(split[0] + "值是: " + split[1]);
                }
            }
    
            br.close();
        }
    }
    
    public class Properties02 {
        public static void main(String[] args) throws IOException {
            //使用Properties 类来读取mysql.properties 文件
    
            //1. 创建Properties 对象
            Properties properties = new Properties();
            //2. 加载指定配置文件
            properties.load(new FileReader("src\\mysql.properties"));
            //3. 把k-v显示控制台
            properties.list(System.out);
            //4. 根据key 获取对应的值
            String user = properties.getProperty("user");
            String pwd = properties.getProperty("pwd");
            System.out.println("用户名=" + user);
            System.out.println("密码是=" + pwd);
        }
    }
    
    
    public class Properties03 {
        public static void main(String[] args) throws IOException {
            //使用Properties 类来创建 配置文件, 修改配置文件内容
            Properties properties = new Properties();
            //创建
            //1.如果该文件没有key 就是创建
            //2.如果该文件有key ,就是修改
            properties.setProperty("charset", "utf8");
            properties.setProperty("user", "汤姆");//注意保存时,是中文的 unicode码值
            properties.setProperty("pwd", "888888");
            //将k-v 存储文件中即可
            properties.store(new FileOutputStream("src\\mysql2.properties"), null);
            System.out.println("保存配置文件成功~");
    
        }
    }