IO流知识点笔记

定义:

按照流向的不同, 分为输入流输出流, 我们应该站在程序的角度

按照数据单位的不同, 分为**字节流(8bit)**和 字符流(16bit)

按照流的角色不同: 节点流处理流

流的体系

重点标出了☺

节点流

字符流

对于文本文件(.txt .java .c .cpp), 用字符流

注意 : 如果文本文件只复制的话, 用字节流也可以, 读取的话可能会出现乱码

具体使用步骤
  1. 实例化File类的对象, 指明文件
  2. 提供具体的流
  3. 数据的读入
  4. 流的关闭 后果:内存泄漏

输出有问题, 原因 : 某个长度的数组, 对于每一轮都不会是空的, 而是用之后的数据覆盖上一轮, 所以会出现错误

/*
    1 下面的hello.text文件内容读取到程序中, 并输出到控制台
    2 异常的处理, 为了保证资源一定被关闭, 需要try-catch-finally处理, 如果抛出那么最后面可能不会执行
    3 需要确报读入的文件一定要存在, 否者会报FileNotFoundException
     */
    @Test
    public void FileReaderTest() {
        FileReader fr = null;
        try {
            // 1. 实例化File类的对象, 指明文件
            File file = new File("hello.text"); // 相较于当前的Module
//        System.out.println(file.getAbsolutePath());
            // 2. 提供具体的流
            fr = new FileReader(file);
            // 3. 数据的读入
            // read()返回一个字符串. 如果达到文件末尾, 返回-1
            int read = fr.read();
            while (read != -1) {
                System.out.print((char) read);
                read = fr.read(); // 相当于遍历下一个, 指正下移(类似next())
            }
//        方式二:
//        int date;
//        while ((date = fr.read()) != -1) {
//            System.out.println((char) read);
//
//        }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 4. 流的关闭 后果:内存泄漏
//            try {
//                if (fr != null)
//                    fr.close();
//            } catch (IOException e) {
//                throw new RuntimeException(e);
//            }

            // 方式二 和方式一没有区别
             if (fr != null) {
                 try {
                     fr.close();
                 } catch (IOException e) {
                     throw new RuntimeException(e);
                 }
             }
        }
    }

改良版本 : 使用read()的重载构方法

read()也有类似String的public int read(char cbuf[], int off, int len)构造器, 我们一般用的是他的特殊情况len = cbuf.length();

 @Test
    public void test2()  {
        FileReader fr = null;
        try {
            // 1. File类的实例化
            File file = new File("hello.text");
            // 2. StringBuffer的实例化
            fr = new FileReader(file);
            // 3. 读入操作
            char[] cbuff = new char[5];

            // 正确写法一
//            int len;
//            while ((len = fr.read(cbuff)) != -1) {
//                for (int i = 0; i < len; i++) {
//                    System.out.print(cbuff[i]);
//                }
//            }
            // 正确写法二
//            int len;
//            while ((len = fr.read(cbuff)) != -1) {
//                String s = new String(cbuff, 0, len);
//                System.out.print(s);
//            }

            // 错误写法
//            int len;
//            while ((len = fr.read(cbuff)) != -1) {
//                String s = new String(cbuff);
//                System.out.print(s);
//            }

            // 错误写法二
            int len;
            while ((len = fr.read(cbuff)) != -1) {
                for (int i = 0; i < cbuff.length; i++) {
                    System.out.print(cbuff[i]);
                }
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (fr != null) {
                // 4.关闭流
                try {
                    fr.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

把内存中写出的数据到硬盘说明:

  1. 输出操作, File对应的文件可以不存在, 并不会报异常

  2. 如果不存在, 总结创建个 如果存在, 根据构造器选择不同的方式:

    1. new FileWriter(“haha.txt”) / new FileWriter(“haha.text”, false) 直接覆盖原文件
    2. new FileWriter("haha.txt, true) 不覆盖原文件, 而是在原文件中添加内容
把内存中写出的数据到硬盘

说明:
 1. 输出操作, File对应的文件可以不存在, 并不会报异常
 2. 如果不存在, 总结创建个
    如果存在, 根据构造器的不同选择不同的方式:
             new FileWriter("haha.txt") / new FileWriter("haha.text" , false) 直接覆盖原文件
             new FileWriter("haha.txt, true) 不覆盖原文件, 而是在原文件中添加内容

实例:

@Test
    public void testFileWriter()  {
        FileWriter fw = null;
        try {
            // 1. 提供File类的对象, 指明写出到的文件
            File file = new File("haha.txt");
            // 2. 提供FileWriter的实例化对象, 用于数据的写出
            fw = new FileWriter(file, true);
            // 3. 写入操作
            fw.write("I have a dream!");
            fw.write(" you need have a dream!");
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (fw != null) {// 可能会有空指针异常
                try {
                    fw.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

读入写出测试:

两个关闭操作, 可以并列的写.

因为: 两个处理异常时候都会执行, 因为try-catch-finally会把异常处理

   @Test
    public void test() {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            // 1. File类
            File srcFile = new File("haha.txt");
            File destFile = new File("copy.txt");
            // 2. 创建对象
            fr = new FileReader(srcFile);
            fw = new FileWriter(destFile);
            // 3. 调用方法
            char[] cbuf = new char[5];
            int len;
            while ((len = fr.read(cbuf)) != -1) {
                fw.write(cbuf, 0, len);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally { // 两个处理异常时候都会执行, 因为try-catch-finally会把异常处理
//            if (fw != null) {
//                try {
//                    fw.close();
//                } catch (IOException e) {
//                    throw new RuntimeException(e);
//                }
//            }
//            if (fr != null) {
//                try {
//                    fr.close();
//                } catch (IOException e) {
//                    throw new RuntimeException(e);
//                }
//            }

            if (fw != null) {
                try {
                    if (fw != null)  fw.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                } finally {
                    try {
                        if (fr != null)  fr.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

字节流

对于非文本文件(.jpj .doc .ppt .mp4), 用字节流

具体使用步骤
  1. 实例化File类的对象, 指明文件
  2. 提供具体的流
  3. 数据的读入
  4. 流的关闭 后果:内存泄漏
	// 封装成方法
    public void copyFile(String srcFile, String desFile) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            File file = new File(srcFile);
            File file1 = new File(desFile);

            fis = new FileInputStream(file);
            fos = new FileOutputStream(file1);
            byte[] bytes = new byte[1024];
            int len;
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (fis != null) fis.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            try {
                if (fos != null) fos.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     *  复制花费的时间为:1184  对应 byte[] bytes = new byte[1024];
     *
     *  复制花费的时间为:185390  对应 byte[] bytes = new byte[5];
     */
    @Test
    public void teat2() {
        long start = System.currentTimeMillis();

        String Start = "D:\\下载\\Video\\鱼皮.mp4";
        String End = "D:\\SOFT\\destbook\\yupi.mp4";
        copyFile(Start, End);

        long end = System.currentTimeMillis();
        System.out.println("复制花费的时间为:" +( end - start));

    }

处理流

特征

是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。 作用在其他流上.

缓冲流
   			//  创建流
            // 1 造节点流
            FileInputStream fis = new FileInputStream(srcFile);
            FileOutputStream fos = new FileOutputStream(destFile);
            // 2 造缓存流
            BufferedInputStream bis = new BufferedInputStream(fis);
            BufferedOutputStream bos = new BufferedOutputStream(fos);

注意: 关闭流的顺序是由内到外, 但是关闭缓冲流默认关闭节点流

练习

练习三

利用流读取数据, 利用**HashMap<>()**存储数据, 然后利用writer()写入数据 HashMap的 key 是存储的存储数据的类型 利用 **value **存储此类型出现的次数

    public void test() {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            File file = new File("dbcp.txt");
            File desFile = new File("WordCount.txt");

            fr = new FileReader(file);
            fw = new FileWriter(desFile);

            HashMap<Character, Integer> map = new HashMap<>();

            int len;
            while ((len = fr.read()) != -1) {
                char achar = (char) len;
                if (map.containsKey(achar)) {
                    map.put(achar, map.get(achar) + 1);
                } else {
                    map.put(achar, 1);
                }
            }
            Set<Map.Entry<Character, Integer>> entries = map.entrySet();
            for (Map.Entry<Character, Integer> set : entries) {
                switch (set.getKey()) {
                    case ' ':
                        fw.write("空格 = " + set.getValue() + "\n");
                        break;
                    case '\t' :
                        fw.write("制表符 = " + set.getValue() + "\n");
                        break;
                    case '\n':
                        fw.write("换行 = " + set.getValue() + "\n");
                        break;
                    case '\r':
                        fw.write("回车 = " + set.getValue() + "\n");
                        break;
                    default:
                        fw.write(set.getKey() + " = " + set.getValue() + "\n");
                        break;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (fr != null) fr.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            try {
                if (fw != null) fw.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

转换流

  1. 转化流: 属于什么, 看后缀即可.

​ InputStreamReader属于字符流 : 将一个字节输入流转化为字符的输入流

​ OutputStream**Writer **属于字符流 : 将一个字符的输出流转化为字节的输出流

  1. 作用: 提供字节流和字符流的转化

  2. 解码 : 字节 , 字节数组 —> 字符, 字符数组

    编码: 字符, 字符数组 —> 字节, 字节数组

例子:

		   // 1. 创建文件和流
		    File file1 = new File("WordCount.txt");
            File file2 = new File("WordCount_gbk.txt");

            FileInputStream fis = new FileInputStream(file1);
            FileOutputStream fos = new FileOutputStream(file2);

            isr = new InputStreamReader(fis, "utf-8");
            osw = new OutputStreamWriter(fos, "gbk");

            // 2. 操作数据
            char[] cbuf = new char[5];
            int len;
            while ((len = isr.read(cbuf)) != -1) {
                osw.write(cbuf, 0, len);
            }

字符编码

ANSI编码,通常指的是平台的默认编码,

例如英文操作系统中是ISO-8859-1,中文系统是GB

其他流(了解)
  1. 标准的输入输出流:
    System.in:标准的输入流,默认从键盘输入
    System.out:标准的输出流,默认从控制台输出

​ 修改默认的输入和输出行为:
​ System类的setIn(InputStream is) / setOut(PrintStream ps)方式重新指定输入和输出的流。

  1. 打印流:
    PrintStream 和PrintWriter
    说明:
    提供了一系列重载的print()和println()方法,用于多种数据类型的输出
    System.out返回的是PrintStream的实例

  2. 数据流:
    DataInputStream 和 DataOutputStream
    作用:
    用于读取或写出基本数据类型的变量或字符串

    注意 : 一定要按照写入的数据顺序进行读取

示例代码

/ *
练习:将内存中的字符串、基本数据类型的变量写出到文件中。

注意:处理异常的话,仍然应该使用try-catch-finally.
 */
 @Test
public void test3() throws IOException {
    //1.
    DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
    //2.
    dos.writeUTF("刘建辰");
    dos.flush();//刷新操作,将内存中的数据写入文件
    dos.writeInt(23);
    dos.flush();
    dos.writeBoolean(true);
    dos.flush();
    //3.
    dos.close();

}
/*
将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中。

注意点:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致!

 */
 @Test
public void test4() throws IOException {
    //1.
    DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
    //2.
    String name = dis.readUTF();
    int age = dis.readInt();
    boolean isMale = dis.readBoolean();
    System.out.println("name = " + name);
    System.out.println("age = " + age);
    System.out.println("isMale = " + isMale);

    //3.
    dis.close();
}

对象流

序列化 : 将内存中Java的对象保存到磁盘或通过网络传输 使用ObjectOutputStream实现

反序列化 : 将磁盘中的文件还原为java内存中的对象 使用ObjectInputStream实现

要求 :

  1. 需要实现 java.io.Serializable

  2. 自定义一个序列号,如果不定义那么数据修改系统生成的就会报错

  3. 属性必须是可以序列化的, 基本数据类型都是可序列化的

例子:

反序列化是, 输出必须按照写入的顺序

   /**
     * 序列化 : 将内存中java的对象保存到磁盘或通过网络传输
     * 使用ObjectOutputStream实现
     */
    @Test
    public void ObjectOutputStreamTest() {
        ObjectOutputStream oos = null;

        try {
            oos = new ObjectOutputStream(new FileOutputStream("Object.dat"));

            oos.writeObject(new String("我爱学习Java"));

            oos.flush(); // 刷新操作
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     * 反序列化 : 将磁盘中的文件还原为java内存中的对象
     * ObjectInputStream
     */
    @Test
    public void ObjectInputStreamTest() {
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("Object.dat"));

            Object o = ois.readObject();
            String str = (String) o;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if(ois != null) ois.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

序列化 (重点)

以后可以用 JSON 字符串, 代替对象流

特点 : 允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象

随机存取文件流

构造器的参数

注意点:

  1. RandomAccessFileTest 直接继承与java.long.object,

    实现了DataInput和DataOutput可以是输入流也可以是输出流(但是需要造两个对象)

  2. 如果文件不存在, 那么直接创建

    如果文件存在, 那么会覆盖文件, 特点: 默认情况, 覆盖文件的内容从头开始覆盖

如何实现插入效果 ?

   /**
     * 实现插入的效果
     */
    @Test
    public void test3() {

        RandomAccessFile eaf = null;
        RandomAccessFile raf = null;
        try {
            eaf = new RandomAccessFile("hello.txt", "rw");

            eaf.seek(3); // 把指针指到序号为3的位置(从3开始)
            StringBuffer sb = new StringBuffer((int) new File("hello.txt").length()); // 存储从序号3以后的数据
            byte[] bytes = new byte[20]; // 存储到bytes数组之中
            int len;
            while ((len = eaf.read(bytes)) != -1) {
                sb.append(new String(bytes, 0, len));
            }
            eaf.seek(3);// 上面操作结束之后, 指针在最后面, 需要把指正放在前面
            eaf.write("xyz".getBytes());
            eaf.write(sb.toString().getBytes());

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if(eaf != null) eaf.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            try {
                if (raf != null) raf.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

NIO.2中Path、 Paths、Files类的使用

对应的方法

导入第三方jar包

你可能感兴趣的:(java)