• Java中的IO流操作


    IO流

    一、IO产生的原因

    1、在操作系统中,一切数据都是以文件的形式存储。

    2、需要将文件长久存储在外部设备。

    3、程序运行时,所有的数据都需要在内存中,而内存的大小有限,因此常常需要在内存和外设之间交换数据,即IO。

    4、在Java语言中,主要通过输入流和输出流完成I/O功能,实现和外设之间的数据交互。


    二、IO流

    1、IO流用来处理JVM和外部设备之间的数据传输,Java通过流(Stream)的方式,完成数据的传输过程。

    2、IO流的分类:

    (1)按数据流动的方向:

    a、输入流:对应数据的读入,将外设数据读入JVM内存

    b、输出流:对于数据的写出,将数据写入到外设

    (2)、按流中的内容():

    a、字节流:流中的数据都是以字节为单位的二进制数据。字节流可以操作一切类型的数据,只是有时操作字符数据不太方便,所有字符数据专门交给字符流。

    b、字符流:流中的数据都是以字符为单位的二进制数据。文本编辑器可以打开,并且人可以看懂的数据。

    3、IO流的基类:

    (1)、字节流:

    a、输入流:InputStream (抽象类)

    b、输出流:OutputStream (抽象类)

    (2)、字符流:

    a、输入流:Reader(抽象类)

    b、输出流:Writer(抽象类)

    (3)、由这四个类派生出来的子类名字都是以其父类名作为子类名的后缀。如:InputStream的子类FileInputStream;Reader的子类FileReader。

    4、字节流(重要):

    (1)输出流(OutputStream):

    a、实例化:OutputStream是抽象类,无法直接实例化,只能间接实例化,因为通常操作的都是文件数据,所以使用其操作文件的具体子类FileOutputStream实例化

    FileOutputStream的构造方法:

    (1)、FileOutputStream(File file)
    (2)、FileOutputStream(String name)
    

    b、使用,即向外设写入数据:

    // write()的三个重载方法
    public void write(int b)
    public void write(byte[] b)
    public void write(byte[] b, int off, int len)
    

    以下分别实现三个重载方法:

    // void write(int b) 
    // 将指定的字节写入此输出流。
    // write 的常规协定是:向输出流写入一个字节。要写入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。
    
    package com.io.review;
    
    import java.io.*;
    
    /**
     * @author: Lucas
     * @version: 1.0
     * @date: 2020/6/26 20:25
     *  void write(int b)方法
     */
    public class WriteDemo1 {
    
        public static void main(String[] args) throws IOException {
            // 创建File对象
            File file = new File("E:\TestDir\a.txt");
    
            // 1、利用OutputStream的子类FileOutputStream创建输出流对象
            OutputStream out = new FileOutputStream(file);
    
            // 2、向目标目录写入数据(写入字符串"Hello,World")
            byte[] bytes = "Hello,World".getBytes();
            for (int i = 0; i < bytes.length; i++) {
                // 逐个读入字符
                out.write(bytes[i]);
            }
            // 3、关闭输出流对象,并释放资源
            out.close();
    
        }
    }
    
    
    // void write(byte[] b)
    // 将 b.length 个字节从指定的 byte 数组写入此输出流。
    
    package com.io.review;
    
    import java.io.*;
    
    /**
     * @author: Lucas
     * @version: 1.0
     * @date: 2020/6/26 20:48
     *      实现 void write(byte[] b)
     */
    public class WriteDemo2 {
    
        public static void main(String[] args) throws IOException {
            // 创建File对象
            File file = new File("E:\TestDir\b.txt");
    
            // 1、创建输出流对象
            OutputStream out = new FileOutputStream(file);
    
            // 2、将数据写入目标目录(向b.txt中写入
            //"zhangsan
            // lisi
            // wangwu")
            byte[] bytes = "zhangsan
    lisi
    wangwu
    ".getBytes();
            out.write(bytes);
    
            // 关闭输出流对象,并释放资源
            out.close();
        }
    }
    
    // void write(byte[] b, int off, int len)
    // 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
    // 参数:
    // b - 数据。
    // off - 数据中的初始偏移量。
    // len - 要写入的字节数。
    
    package com.io.review;
    
    import java.io.*;
    
    /**
     * @author: Lucas
     * @version: 1.0
     * @date: 2020/6/26 20:57
     *
     *  void write(byte[] b, int off, int len)
     */
    public class WriteDemo3 {
    
        public static void main(String[] args) throws IOException {
            // 创建File对象
            File file = new File("E:\TestDir\c.txt");
    
            // 1、创建输出流对象
            OutputStream out = new FileOutputStream(file);
    
            // 2、向目标目录写出数据(向b.txt写出“”)
            byte[] bytes = ("我是个俗气至顶的人,
    " +
                    "见山是山,
    " +
                    "见海是海,
    " +
                    "见花便是花。
    " +
                    "唯独见了你,
    " +
                    "云海开始翻涌,
    " +
                    "江潮开始...
    ").getBytes();
            out.write(bytes, 0, bytes.length);
    
            // 3、关闭输出流,并释放资源
            out.close();
        }
    }
    
    

    c、字节流写数据常见的问题:

    创建字节输出流到底做了哪些事情?

    1)、FileOutputStream会先在操作系统中找到目标文件:若目标文件不存在,FileOutputStream类会创建这个文件;若文件存在,则不再创建,清空文件内容,准备从文件最开始的地方写入

    2)、在内存中,创建FileOutputStream对象

    3)、在FileOutputStream和目录之间建立数据传送通道

    数据写成功后,为什么要close()?

    关闭次输出流并释放与此流有关的所有系统资源

    如何实现速记的换行?(利用换行符)

    windows:' ' 或 ’ ‘

    linux、mac OS:’ ‘

    如何实现数据的追加写入?

    1)FileOutputStream(File file, boolean append)

    2)创建一个指定File对象表示的文件中写入数据的文件输出流。如果第二个参数为true,则将字节写入文件末尾处(而不是写入文件开始处)

    // 实现数据的追加写入
    // FileOutputStream(File file, boolean append)
    // 创建一个向具有指定 File 的文件中写入数据的输出文件流。
    // 如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处
    
    package com.cskaoyan.review;
    
    import java.io.*;
    
    /**
     * @author: Lucas
     * @version: 1.0
     * @date: 2020/6/26 23:27
            实现数据的追加写入:
                FileOutputStream(String name, boolean append)
                创建一个向具有指定 name 的文件中写入数据的输出文件流。
                如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处
    
                FileOutputStream(File file, boolean append)
                创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
                如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处
     */
    
    public class AppendDemo {
    
        public static void main(String[] args) throws IOException {
    
            // 创建File对象
            File file = new File("E:\TestDir\d.txt");
    
            // 1、创建输出流对象
            OutputStream out = new FileOutputStream(file, true);
    
            // 2、向目标目录文件追加写入数据
            byte[] bytes = ("我们聊天的时候,有的人说“再见”,
    " +
                    "后面是一个波浪号,是轻盈的,
    " +
                    "这种是就算不约定几月几号也...
    ").getBytes();
    
            out.write(bytes);
    
            // 追加写入
            byte[] bytes1 = ("我们年轻的时候,总是把创作的冲动误以为是创作的才华;
    " +
                    "总是把对孤独的恐惧误以为是对爱情的向往。").getBytes();
            out.write(bytes1);
    
            // 3、关闭输出流,并释放资源
            out.close();
        }
    }
    
    
    
    

    (2)、输出流(InputStream):

    a、InputStream是抽象类,无法直接实例化,只能间接实例化,因为通常操作的都是文件数据,所以使用其操作文件的具体子类FileInputStream实例化。

    b、FileInputStream的构造方法:

    (1)、FileInputStream(File file)
    (2)、FileInputStream(String name)
     FileInputStream的构造方法
       // 创建一个读取指定文件(目标文件)数据的,文件字节输入流对象(目标文件以File对象的形式表示)
          FileInputStream(File file)
       // 创建一个读取指定文件(目标文件)数据的,文件字节输入流对象(目标文件以File对象的形式表示)
           FileInputStream(String name)
          
    

    c、使用,即从外设读入数据:

    // read()的两个重载方法
    public int read() 
    public int read(byte[] b) 
    

    以下分别实现read()的两个重载方法:

    // int read()
    package com.cskaoyan.review;
    
    import java.io.*;
    
    /**
     * @author: Lucas
     * @version: 1.0
     * @date: 2020/6/26 23:53
     *  int read()
     *      从输入流中读取数据的下一个 字节。
     *      返回值:
     *            1.下一个数据字节;
     *            2.如果到达流的末尾(没有数据可读),则返回 -1。
     */
    public class ReadDemo1 {
    
        public static void main(String[] args) throws IOException {
            // 创建File对象
            File file = new File("E:\testDir\c.txt");
    
            // 1、创建输入流对象
            InputStream in = new FileInputStream(file);
    
            // 2、int read() 逐个字符读入
            int readByte;
            while ((readByte = in.read()) != -1) {
                char c = (char) readByte;
                System.out.print(c);
            }
    
            // 3、关闭输入流对象,并释放资源
            in.close();
        }
    }
    
    
    
    // int read(byte[] b) 
    package com.cskaoyan.review;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * @author: Lucas
     * @version: 1.0
     * @date: 2020/6/27 0:10
     * int read(byte[] b)
     *     从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
     *     返回值:
     *          1. 读入缓冲区的总字节数;
     *          2. 如果因为已经到达流末尾而不再有数据可用,则返回 -1。
     */
    public class ReadDemo2 {
    
        public static void main(String[] args) throws IOException {
            // 1. 创建流对象
            InputStream input = new FileInputStream("E:\TestDir\a.txt");
    
            //2. 从目标文件读取数据
            // int read(byte[] b)
            // 实际开发中,通常给1024的整数倍 2^10
            byte[] buffer = new byte[1024];
            // len 就告诉我们实际向字节数组中填充了多少个字节数据(实际读取到的字节个数)
            int len = input.read(buffer);
    
            String s = new String(buffer,0, len);
            System.out.println(s);
    
            // 3. 关闭流并释放资源
            input.close();
        }
    }
    
    
    d、一次读入读入或写出一个字节效率高,还是一次读入或写出一个字节数组效率高
    一个字节数组效率高?
    

    ==》一个字节数组效率高,原因如下:

    每一次,读入或写出,即每次和外设的数据交互都需要依赖操作系统内核实现,这意味着每次读入或写出,都需要付出额外的通信代价。一次读入或写出一个字节数组的数据,平均到每个字节,付出的额外代价少很多

    练习:把d:a.txt内容复制到e:.txt中,把e:**.jpg内容复制到当前项目目录下的mn.jpg

    package com.cskaoyan.review;
    
    import java.io.*;
    
    /**
     * @author: Lucas
     * @version: 1.0
     * @date: 2020/6/27 0:19
            把d:\a.txt内容复制到e:\b.txt中
            把e:\**.jpg内容复制到当前项目目录下的mn.jpg
     */
    
    
    public class Exercise {
    
        public static void main(String[] args){
            // 复制文本文件
            //copyFile("e:\a.txt", "d:\b.txt");
    
            // 复制图片文件
            //copyFile("e:\copy\upload.jpg", "copy.jpg");
    
    
            // 演示一次复制一个字节数据
            String srcFilePath = "d:\a.txt";
            String destFilePath = "e:\b.txt";
            copyByByte(srcFilePath, destFilePath);
    
        }
    
        private static void copyByByte(String srcFilePath, String destFilePath) {
            InputStream input = null;
            OutputStream output = null;
            try {
                // 创建输入流对象,用来读取源文件内容
                input = new FileInputStream(srcFilePath);
                // 创建输出流对象, 用来向目标文件写入源文件内容
                output = new FileOutputStream(destFilePath);
    
                //读取源文件数据, 以单个字节为单位复制
                int readByte;
    
                while ((readByte = input.read()) != -1) {
                    // 向目标文件写一个字节
                    output.write(readByte);
                }
    
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 关闭流并释放资源
                closeQuietly(input);
    
                //关闭输出流
                closeQuietly(output);
    
            }
        }
    
    
        private static void copyFile(String srcFilePath, String destFilePath) {
            InputStream input = null;
            OutputStream output = null;
            try {
                // 创建输入流对象,用来读取源文件内容
                input = new FileInputStream(srcFilePath);
                // 创建输出流对象, 用来向目标文件写入源文件内容
                output = new FileOutputStream(destFilePath);
    
                //读取源文件数据
                byte[] buffer = new byte[2048];
                int len;
                while ((len = input.read(buffer)) != -1) {
                    // 将读取到的源文件内容,写入到输出流中
                    output.write(buffer, 0, len);
                }
    
                //close
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 关闭流并释放资源
                closeQuietly(input);
    
                //关闭输出流
                closeQuietly(output);
    
            }
        }
    
    
        private static void closeQuietly(Closeable closeable) {
            try {
                if (closeable != null) {
                    // 多态
                    closeable.close();
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    }
    
    
    
    

    (3)、缓冲流

    a、产生原因:

    1)、字节流一次读写一个数组的速度明显比读一个字节的速度快很多

    2)、这是加入了数组这样的缓冲区效果

    3)、Java本身在设计的时候,也考虑到了这样的情况,所以提供了字节缓冲区流

    b、字节缓冲输出流(BufferedOutputStream)

    ​ 字节缓冲输入流(BufferedInputStream)

  • 相关阅读:
    Map 合并
    如何对hashmap按value值排序
    svn使用
    java中key-value数据有重复KEY如何存储
    linux 定时
    java 执行shell命令
    Java相对路径读取文件
    MySql之on duplicate key update详解
    前端学习资源整合
    Number浮点数运算详解
  • 原文地址:https://www.cnblogs.com/lcpp/p/13196958.html
Copyright © 2020-2023  润新知