15 IO流

IO流

IO流原理及流的分类

概念

  1. I/O技术用于处理数据传输,如读/写文件、网络通讯等。
  2. Java程序中,对于数据的输入/输出操作以"流(stream)"的方式进行。

流的分类

image-20211209232822852

输入流和输出流
概念

Java 程序通过流来完成输入/输出,所有的输入/输出以流的形式处理。

输入就是将数据从各种输入设备(包括文件、键盘等)中读取到内存中,输出则正好相反,是将数据写入到各种输出设备(比如文件、显示器、磁盘等)。例如键盘就是一个标准的输入设备,而显示器就是一个标准的输出设备,但是文件既可以作为输入设备,又可以作为输出设备。

字节流和字符流
概念

字节流:java程序在输入和输出的时候是以字节来进行输入和输出的。(音视频文件、图片等用字节流比较好)

字符流:java程序在输入和输出的时候是以字符来进行输入和输出的。不同的字符编码,读取的格式也是不一样的,比如Unicode编码,汉字和字母都是用两个字节来存储的,但是utf-8编码,汉字用三个字节来存储,而字母用一个字节来存储。(word文件、txt文件等用字符流比较好)

节点流和处理流(包装流)

节点流:节点流可以从一个特定的数据源(文件、数组、管道等)读写数据。如FileReader、FileWriter等。

处理流(包装流):是用来包装节点流的,可以为程序提供更为强大的功能。如BufferedFileReader、BufferedFileWriter等。处理流用了修饰器设计模式。

IO流体系图

image-20211209232927500

节点流和处理流

概念

节点流可以从一个特定的数据源(文件、数组、管道等)读写数据。

处理流(包装流):是用来包装节点流的,可以为程序提供更为强大的功能。处理流用了修饰器设计模式

节点流和处理流(图)

image-20211209235357530

文件流

常用的文件操作(File)

创建文件
  • new File(String filePath) // 根据路径构建一个File对象
  • new File(File parent,String child) // 根据父目录文件+子路径构建
  • new File(String parent,String child)//根据父目录 + 子路径构建
 @Test
    public  void create02() {
        File parentFile = new File("e:/");
        String fileName = "news2.txt";
        //这里的file对象,在java程序中,只是一个对象
        //只有执行了createNewFile 方法,才会真正的,在磁盘创建该文件
        File file = new File(parentFile, fileName);

        try {
            file.createNewFile();
            System.out.println("创建成功~");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
获取文件的一些相关信息(文件的方法)

image-20211209232113365

    @Test
    public void info() throws Exception{
        String pathname = "/Users/zhaojiawei/Desktop/test/new1.txt";
        File file = new File(pathname);
        System.out.println("文件名:" + file.getName());//文件名:new1.txt
        System.out.println("文件路径:" + file.getAbsolutePath());//文件路径:/Users/zhaojiawei/Desktop/test/new1.txt
        System.out.println("父目录:" + file.getParent());//父目录:/Users/zhaojiawei/Desktop/test
        System.out.println("文件大小(字节):" + file.length());//文件大小(字节):0
        System.out.println("是否存在:" + file.exists());//是否存在:false
        System.out.println("是不是一个文件:" + file.isFile());//是不是一个文件:false
        System.out.println("是不是一个目录:" + file.isDirectory());//是不是一个目录:false
    }
目录的操作和文件删除

mkdir创建一级目录、mkdirs创建多级目录、delete删除空目录或文件,这三个方法的返回值都是boolean类型,成功则返回true,失败返回false。

FileInputStream(字节输入流)

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();
            }
        }

    }
}

FileOutputStream(字节输出流)

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

    }

    /**
     * 演示使用FileOutputStream 将数据写到文件中,
     * 如果该文件不存在,则创建该文件
     */
    @Test
    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);
            //写入字符串
            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();
            }
        }
    }
}

FileReader(字符输入流)

  • new FileReader(File/String)
  • read:每次读取单个字符,返回该字符,如果到文件末尾返回-1
  • read(char[]):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1

FileWriter(字符输出流)image-20211209234529725

BufferedReader、BufferedWriter

字符处理文件输入流BufferedReader和字符处理文件输出流BufferedWriter演示文件拷贝,这两个流是处理流

注:在使用BufferedWriter时不会立即写入文件,只有在调用flush方法或close方法时,才会写入到文件,在网络传输中也是这样,不会立即放入到网络传输的管道中去,只有在调用flush方法或close方法是,才会放进网络传输的管道中去,对方才能读取到。

public class FileCopy {
    public static void main(String[] args) {
        String srcFilePath = "/Users/zhaojiawei/Desktop/b.txt";
        String dstFilePath = "/Users/zhaojiawei/Desktop/a.txt";
        FileCopy.fileCopy(srcFilePath,dstFilePath);
    }

    /**
     * 演示将srcFilePath文件拷贝到dstFilePath
     * @param srcFilePath 源文件
     * @param dstFilePath 目标文件
     */
    public static void fileCopy(String srcFilePath,String dstFilePath){
        BufferedReader bufferedReader = null;
        BufferedWriter bufferedWriter = null;
        try {
            //创建一个字符处理输入流
            bufferedReader = new BufferedReader(new FileReader(srcFilePath));
            //创建一个字符处理输出流
            bufferedWriter = new BufferedWriter(new FileWriter(dstFilePath));
            /*
             * 将字符处理输入流按行读取,然后将读取到的内容放入到字符处理输出流中
             */
            String content;
            //当readLine读取不到内容的时候,会返回null,注:readLine读取不到换行
            while((content = bufferedReader.readLine()) != null){
                bufferedWriter.write(content);
                //插入一个和系统相关的换行
                bufferedWriter.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            try {
                if(bufferedReader != null) {
                    bufferedReader.close();
                }
                if(bufferedWriter != null){
                    bufferedWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

BufferedInputStream、BufferedOutputStream

字节处理文件输入流BufferedInputStream和字节处理文件输出流BufferedOutputStream 演示文件拷贝,这两个流是处理流,

注:在使用BufferedOutputStream时不会立即写入文件,只有在调用flush方法或close方法时,才会写入到文件,在网络传输中也是这样,不会立即放入到网络传输的管道中去,只有在调用flush方法或close方法是,才会放进网络传输的管道中去,对方才能读取到。

public class FileCopy2 {
    public static void main(String[] args) {
        String srcFilePath = "/Users/zhaojiawei/Desktop/b.txt";
        String dstFilePath = "/Users/zhaojiawei/Desktop/a.txt";
        FileCopy.fileCopy(srcFilePath,dstFilePath);
    }

    /**
     * 演示将srcFilePath文件拷贝到dstFilePath,一般来说,音视频、图片文件用字节流读取好,而文档用字符流读取好
     * @param srcFilePath 源文件
     * @param dstFilePath 目标文件
     */
    public static void fileCopy(String srcFilePath,String dstFilePath){
        BufferedInputStream bufferedInputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        try {
            //创建一个字节处理文件输入流
            bufferedInputStream = new BufferedInputStream(new FileInputStream(srcFilePath));
            //创建一个字节处理文件输出流
            bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(dstFilePath));
            /*
             * 将字节处理文件输入流读取到bytes数组中去,然后将读取到的内容放入到字节处理文件输出流中
             */
            byte[] bytes = new byte[1024];
            int len;
            //read(byte[] b)可以读取到换行符
            while((len = bufferedInputStream.read(bytes)) != -1){
                bufferedOutputStream.write(bytes,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            try {
                if(bufferedInputStream != null) {
                    bufferedInputStream.close();
                }
                if(bufferedOutputStream != null){
                    bufferedOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

对象流(序列化、反序列化) (处理流)

ObjectInputStream(反序列化)和ObjectOutputStream(序列化),这两个流是处理流,是用来包装节点流的。主要将对象存储进文件中,或在网络中进行传输

序列化和反序列化

  1. 序列化就是在保存数据时,保存数据的值和数据类型。
  2. 反序列化就是在恢复数据时,恢复数据的值和数据类型。
  3. 要让某个类支持序列化,这该类必须实现下面两个接口:
    1. Serializable //这是一个标记接口,没有方法
    2. Externalizable //该接口有方法需要实现,因此我们一般实现上面的Serializable接口

注意事项

  1. 读写顺序要一致

  2. 序列化或反序列化对象时,需要实现Serializable

  3. 序列化的类中建议添加SerialVersionUID.为了提高版本的兼容性

    1. //序列化的版本号,如果该类改变,则你应该显示的改变其版本号,反序列化的时候会检查该类的版本号是否一致,如果不一致,则反序列化失败
      private static final long serialVersionUID = 1L;
      
    2. serialVersionUID详解

  4. 序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员。

  5. 序列化对象时,要求里面属性的类型也需要实现序列化接口。

  6. 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化。

对象流序列化
public class ObjectOutputStream01 {
    public static void main(String[] args) throws Exception{
        String filePath = "/Users/zhaojiawei/Desktop/c.txt";
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));
        objectOutputStream.writeInt(100);
        objectOutputStream.writeBoolean(true);
        objectOutputStream.writeChar('a');
        objectOutputStream.writeDouble(9.5);
        objectOutputStream.writeUTF("hello world");
        objectOutputStream.writeObject(new Dog("旺财",10));
        objectOutputStream.close();
        System.out.println("序列化完毕");
    }

对象流反序列化
public class ObjectInputStream01 {
    public static void main(String[] args) throws Exception{
        String filePath = "/Users/zhaojiawei/Desktop/c.txt";
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
        //读取的顺序和你保存数据一致
        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());
        Object o = ois.readObject();
        System.out.println((Dog)o);
        ois.close();
    }
}

标准输入和输出流

image-20211210204517745

打印流

PrintStream
public class PrintStream_ {
    public static void main(String[] args) throws IOException {

        PrintStream out = System.out;
        //在默认情况下,PrintStream 输出数据的位置是 标准输出,即显示器
        /*
             public void print(String s) {
                if (s == null) {
                    s = "null";
                }
                write(s);
            }

         */
        out.print("john, hello");
        //因为print底层使用的是write , 所以我们可以直接调用write进行打印/输出
        out.write("韩顺平,你好".getBytes());
        out.close();

        //我们可以去修改打印流输出的位置/设备
        //1. 输出修改成到 "e:\\f1.txt"
        //2. "hello, 韩顺平教育~" 就会输出到 e:\f1.txt
        //3. public static void setOut(PrintStream out) {
        //        checkIO();
        //        setOut0(out); // native 方法,修改了out
        //   }
        System.setOut(new PrintStream("e:\\f1.txt"));
        System.out.println("hello, 韩顺平教育~");
    }
}

PrintWriter
public class PrintWriter_ {
    public static void main(String[] args) throws IOException {

        //PrintWriter printWriter = new PrintWriter(System.out);
        PrintWriter printWriter = new PrintWriter(new FileWriter("e:\\f2.txt"));
        printWriter.print("hi, 北京你好~~~~");
        printWriter.close();//flush + 关闭流, 才会将数据写入到文件..

    }
}

转换流(InputStreamReader和OutputStreamWriter) 转换流是节点流

作用:即将字节流转换为字符流。转换流可以在输入和输出的时候指定编码格式,例如InputStreamReader(InputStream in,String charset);

简介

image-20211210205452530

InputStreamReader

 public static void main(String[] args) throws Exception{
        String filePath = "/Users/zhaojiawei/Desktop/b.txt";
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String line;
        while((line = bufferedReader.readLine()) != null){
            System.out.println(line);
        }
        bufferedReader.close();
    }

OutputStreamWriter


    public static void main(String[] args) throws Exception{
        String filePath = "/Users/zhaojiawei/Desktop/a.txt";
        java.io.OutputStreamWriter outputStreamWriter = new java.io.OutputStreamWriter(new FileOutputStream(filePath), StandardCharsets.UTF_8);
        BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
        bufferedWriter.write("你好啊,测试测试");
        bufferedWriter.close();
    }

Properties类

简介

image-20211210210756985

常见方法

image-20211210210816712

使用Properties 类来读取mysql.properties 文件

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);
    }
}

使用Properties 类来创建 配置文件, 修改配置文件内容

public class Properties03 {
    public static void main(String[] args) throws IOException {
        //使用Properties 类来创建 配置文件, 修改配置文件内容

        Properties properties = new Properties();
        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("保存配置文件成功~");
    }
}

总结

通常来说,音视频文件、图片用字节输入流和输出流读取和写入比较好,而word文档、txt等文件用字符输入流和输出流读取比较好

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值