字节流和字符流

目录

 

IO概述

1.1 什么是IO

1.2 IO的分类

1.3 IO的流向说明图解

1.4 顶级父类们

字节流

2.1 一切皆为字节

2.2 字节输出流【OutputStream】

2.3 FileOutputStream类

构造方法

写出字节数据 ​

数据追加续写

写出换行

2.4 字节输入流【InputStream】

2.5 FileInputStream类

构造方法

读取字节数据

2.6 字节流练习:图片复制

字符流

3.1 字符输入流【Reader】

3.2 FileReader类

构造方法

读取字符数据

3.3 字符输出流【Writer】

3.4 FileWriter类 

构造方法

基本写出数据

关闭和刷新

写出其他数据

IO异常的处理

JDK7前处理

JDK7的处理(扩展知识点了解内容)

JDK9的改进(扩展知识点了解内容)

属性集

5.1 概述

5.2 Properties类

构造方法

基本的存储方法

与流相关的方法


IO概述

1.1 什么是IO

数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为 输入input 输出

output ,即流向内存是输入流,流出内存的输出流。

1.2 IO的分类

根据数据的流向分为:输入流输出流

输入流 :把数据从 其他设备 上读取到 内存 中的流。

输出流 :把数据从 内存 中写出到 其他设备 上的流。

格局数据的类型分为:字节流字符流

字节流 :以字节为单位,读写数据的流。

字符流 :以字符为单位,读写数据的流。

1.3 IO的流向说明图解

1.4 顶级父类们

字节流

2.1 一切皆为字节

一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一

样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底

层传输的始终为二进制数据。

2.2 字节输出流【OutputStream

java.io.OutputStream 抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字

节输出流的基本共性功能方法。

  • public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
  • public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
  • public void write(byte[] b) :将 b.length字节从指定的字节数组写入此输出流。
  • public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 offff开始输 出到此输出流。
  • public abstract void write(int b) :将指定的字节输出流。

小贴士:

close方法,当完成流的操作时,必须调用此方法,释放系统资源。

2.3 FileOutputStream

OutputStream 有很多子类,我们从最简单的一个子类开始。

java.io.FileOutputStream 类是文件输出流,用于将数据写出到文件。

构造方法

  • public FileOutputStream(File file) :创建文件输出流以写入由指定的 File对象表示的文件。
  • public FileOutputStream(String name) : 创建文件输出流以指定的名称写入文件。

当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文

件,会清空这个文件的数据。

写出字节数据

1. 写出字节write(int b) 方法,每次可以写出一个字节数据,代码使用演示:

    public static void main(String[] args) throws IOException {
        // 1.创建一个FileOutStream对象
        // FileOutputStream fos = new FileOutputStream("a.txt");
        FileOutputStream fos = new FileOutputStream(new File("a.txt"));
        // 2.调用write(int a)写入方法
        fos.write(97);
        // 3.释放资源
        fos.close();
    }

2. 写出字节数组write(byte[] b) ,每次可以写出数组中的数据,代码使用演示:

    public static void main(String[] args) throws IOException {
        // 1.创建一个FileOutStream对象
        FileOutputStream fos = new FileOutputStream(new File("a.txt"));
        // 将字符串转换成字节数组
        byte[] b = "你好啊".getBytes();
        // 2.调用write(byte[] b)写入方法
        fos.write(b);
        // 3.释放资源
        fos.close();
    }

3. 写出指定长度字节数组write(byte[] b, int off, int len) ,每次写出从offff索引开始,len个字节,代码 使用演示:

    public static void main(String[] args) throws IOException {
        // 1.创建一个FileOutStream对象
        FileOutputStream fos = new FileOutputStream(new File("a.txt"));
        // 将字符串转换成字节数组
        byte[] b = "abcde".getBytes();
        // 2.调用write(byte[] b)写入方法
        // 从b开始2位
        fos.write(b,1,2);
        // 3.释放资源
        fos.close();
    }

数据追加续写

经过以上的演示,每次程序运行,创建输出流对象,都会清空目标文件中的数据。如何保留目标文件中数据,还能

继续添加新数据呢?

  • public FileOutputStream(File file, boolean append) : 创建文件输出流以写入由指定的 File对象表示的 文件。
  • public FileOutputStream(String name, boolean append) : 创建文件输出流以指定的名称写入文件。

这两个构造方法,参数中都需要传入一个boolean类型的值, true 表示追加数据, false 表示清空原有数据。

这样创建的输出流对象,就可以指定是否追加续写了,代码使用演示:

    public static void main(String[] args) throws IOException {
        // 1.创建一个FileOutStream对象,true追加数据
        FileOutputStream fos = new FileOutputStream(new File("a.txt"),true);
        // 将字符串转换成字节数组
        byte[] b = "哈哈哈哈".getBytes();
        // 2.调用write(byte[] b)写入方法
        fos.write(b);
        // 3.释放资源
        fos.close();
    }

写出换行

Windows系统里,换行符号是 \r\n 。把

以指定是否追加续写了,代码使用演示:

    public static void main(String[] args) throws IOException {
        // 1.创建一个FileOutStream对象,true追加数据
        FileOutputStream fos = new FileOutputStream(new File("a.txt"),true);
        // 定义字节字节数组
        byte[] words = {97,98,100,102};
        // 2.调用write() 写入换行
        for (int i = 0; i < words.length; i++) {
            fos.write(words[i]);
            fos.write("\r\n".getBytes());
        }
        // 3.释放资源
        fos.close();
    }

 

回车符 \r 和换行符 \n

回车符:回到一行的开头(return)。

换行符:下一行(newline)。

系统中的换行:

Windows系统里,每行结尾是 回车+换行 ,即 \r\n

Unix系统里,每行结尾只有 换行 ,即 \n

Mac系统里,每行结尾是 回车 ,即 \r 。从 Mac OS X开始与Linux统一。

2.4 字节输入流【InputStream

java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入 流的基本共性功能方法。

  • public void close() :关闭此输入流并释放与此流相关联的任何系统资源。
  • public abstract int read() : 从输入流读取数据的下一个字节。
  • public int read(byte[] b) : 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。

读取数据的原理(硬盘-->内存)
        java程序-->JVM-->OS-->OS读取数据的方法-->读取文件

2.5 FileInputStream

java.io.FileInputStream 类是文件输入流,从文件中读取字节,继承InputStream

构造方法

  • FileInputStream(File file) : 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 fifile命名。
  • FileInputStream(String name) : 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。

当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出 FileNotFoundException

读取字节数据

1. 读取字节read 方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回 -1 ,代码使 用演示:

    public static void main(String[] args) throws IOException {
        // 1. 创建FileInputStream 对象,  read //abc
        FileInputStream fis = new FileInputStream("read.txt");

        // 2. int read()读取文件中的一个字节并返回,读取到文件的末尾返回-1
        int res = fis.read();
        System.out.println(res); //97
        int res1 = fis.read();
        System.out.println(res1); //98
        int res2 = fis.read();
        System.out.println(res2); //99
        int res3 = fis.read();
        System.out.println(res3); //-1
        
        // 3. 关闭资源
        fis.close();
    }

循环改进读取方式,代码使用演示:

    public static void main(String[] args) throws IOException {
        // 1. 创建FileInputStream 对象,  read //abc
        FileInputStream fis = new FileInputStream("read.txt");

        // 2. int read()读取文件中的一个字节并返回,读取到文件的末尾返回-1
        // 循坏读取去

        int len = 0; //记录读取到的字节
        while ((len = fis.read())!=-1){
            System.out.print(len); // 979899
        }
        // 3. 关闭资源
        fis.close();
    }

2. 使用字节数组读取read(byte[] b) 每次读取b的长度个字节到缓冲区数组 b 中,返回读取到的有效字节个数,读取到末尾时,返回 -1 ,代码使用演示:

    public static void main(String[] args) throws IOException {
        // 1. 创建FileInputStream 对象,  read //abcde
        FileInputStream fis = new FileInputStream("read.txt");
        //使用FileInputStream对象中的方法read读取文件
        //int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
        byte[] b = new byte[2];  // 缓冲区
//        int len = fis.read(b);
//        System.out.println(len);  // 2
//        System.out.println(Arrays.toString(b)); // [97, 98]
        // 循坏读取
        int len = 0;
        while ((len= fis.read(b))!=-1){
            //String(byte[] bytes, int offset, int length) 把字节数组的一部分转换为字符串 offset:数组的开始索引 length:转换的字节个数
            System.out.print(new String(b,0,len)); // abcde
        }
        // 3. 关闭资源
        fis.close();
    }

2.6 字节流练习:图片复制

package com.long123.copy;

import java.io.*;

public class FileCopy {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            // 1. 创建文件输入流和文件输出流
            fis = new FileInputStream("D://a.txt");
            fos = new FileOutputStream(new File("H://a.txt"));

            // 2. 读取D://a.txt写入到H://a.txt中
            // 定义缓冲区
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = fis.read(bytes))!=-1){
               // 写入到硬盘
                fos.write(bytes,0,len);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if (fis!=null){
                  fis.close();
                }
                if(fos!=null){
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

 

字符流

当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为

一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文

件。

3.1 字符输入流【Reader

java.io.Reader 抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。

  • public void close() :关闭此流并释放与此流相关联的任何系统资源。
  • public int read() : 从输入流读取一个字符。
  • public int read(char[] cbuf) : 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。

3.2 FileReader

java.io.FileReader 类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

小贴士:

1. 字符编码:字节与字符的对应规则。Windows系统的中文编码默认是GBK编码表。

ideaUTF-8

2. 字节缓冲区:一个字节数组,用来临时存储字节数据。

构造方法

  • FileReader(File file) : 创建一个新的 FileReader ,给定要读取的File对象。
  • FileReader(String fileName) : 创建一个新的 FileReader ,给定要读取的文件的名称。

当你创建一个流对象时,必须传入一个文件路径。类似于FileInputStream

读取字符数据

1. 读取字符read 方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回 -1 ,循环读

取,代码使用演示:

    public static void main(String[] args) throws IOException {
        // 1. 创建字符输入流
        FileReader fr = new FileReader("b.txt");

        // 2. 使用read() 方法每次读取一个字符
        int res = 0;
        while ((res = fr.read()) != -1){
            System.out.println((char)res);
        }

        // 3. 关闭流
        fr.close();
    }

小贴士:虽然读取了一个字符,但是会自动提升为int类型。

 

2. 使用字符数组读取read(char[] cbuf) ,每次读取b的长度个字符到数组中,返回读取到的有效字符个数, 读取到末尾时,返回 -1 ,代码使用演示:

     public static void main(String[] args) throws IOException {
        // 1. 创建字符输入流
        FileReader fr = new FileReader("b.txt");

        // 创建缓冲区
        char[] buff = new char[2];
        // 2. 使用read(char[] buf) 读取buf大小的字符,存入buf缓冲区
       int len = 0;
        while ((len= fr.read(buff))!=-1){
            System.out.println(buff);
       }
        // 3. 关闭流
        fr.close();
    }

 

3.3 字符输出流【Writer

java.io.Writer 抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节

输出流的基本共性功能方法。

  • void write(int c) 写入单个字符。
  • void write(char[] cbuf) 写入字符数组。
  • abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,offff数组的开始索引,len 写的字符个数。
  • void write(String str) 写入字符串。
  • void write(String str, int off, int len) 写入字符串的某一部分,offff字符串的开始索引,len写的字符个数。
  • void flush() 刷新该流的缓冲。
  • void close() 关闭此流,但要先刷新它。

3.4 FileWriter类 

java.io.FileWriter 类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

构造方法

  • FileWriter(File file) : 创建一个新的 FileWriter,给定要读取的File对象。
  • FileWriter(String fileName) : 创建一个新的 FileWriter,给定要读取的文件的名称。

当你创建一个流对象时,必须传入一个文件路径,类似于FileOutputStream

基本写出数据

写出字符write(int b) 方法,每次可以写出一个字符数据,代码使用演示:

使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)

    public static void main(String[] args) throws IOException {
        // 1.  创建输出流对象
        FileWriter fw = new FileWriter("b.txt");

        // 2. 写入数据,使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
        fw.write(97);
        fw.write('b');
        fw.write('c');

        /*
        【注意】关闭资源时,与FileOutputStream不同。
         如果不关闭,数据只是保存到缓冲区,并未保存到文件。
          */
        fw.close();
    }

小贴士:

1. 虽然参数为int类型四个字节,但是只会保留一个字符的信息写出。

2. 未调用close方法,数据只是保存到了缓冲区,并未写出到文件中。

 

关闭和刷新

因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据

的。如果我们既想写出数据,又想继续使用流,就需要 flush 方法了。

  • flush :刷新缓冲区,流对象可以继续使用。
  • close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。

小贴士:即便是flflush方法写出了数据,操作的最后还是要调用close方法,释放系统资源。

 

写出其他数据

1. 写出字符数组 write(char[] cbuf) write(char[] cbuf, int off, int len) ,每次可以写出字符数组中的数据,用法类似FileOutputStream,代码使用演示:

    public static void main(String[] args) throws IOException {
        // 1.  创建输出流对象
        FileWriter fw = new FileWriter("b.txt");

        // 将字符串转成字符数组
        char[] chars = "我喜欢你".toCharArray();
        System.out.println(chars);
        // 2. 写入数据
        fw.write(chars);
        fw.write(chars,2,2);
        fw.flush();

        // 3. 关闭资FileWriterDemo源
        fw.close();
    }

2. 写出字符串write(String str) write(String str, int off, int len) ,每次可以写出字符串中的数据,更为方便,代码使用演示:

    public static void main(String[] args) throws IOException {
        // 1.  创建输出流对象
        FileWriter fw = new FileWriter("b.txt");

        String s = new String("我喜欢你");
        // 2. 写入数据
        fw.write(s);
        fw.write(s,2,2);
        fw.flush();

        // 3. 关闭资FileWriterDemo源
        fw.close();
    }

3. 续写和换行:操作类似于FileOutputStream

    public static void main(String[] args) throws IOException {
        // 1.  创建输出流对象,续写 true
        FileWriter fw = new FileWriter("b.txt",true);

        String s = new String("我喜欢你");
        // 2. 写入数据
        for (int i = 0; i <10 ; i++) {
            fw.write(s+"\r\n");
            fw.flush();
        }

        // 3. 关闭资FileWriterDemo源
        fw.close();
    }

 

小贴士:字符流,只能操作文本文件,不能操作图片,视频等非文本文件。

当我们单纯读或者写文本文件时 使用字符流 其他情况使用字节流

IO异常的处理

JDK7前处理

之前的入门练习,我们一直把异常抛出,而实际开发中并不能这样处理,建议使用 try...catch...finally 代码

块,处理异常部分,代码使用演示:

  public static void main(String[] args) {
        /*
    在jdk1.7之前使用try catch finally 处理流中的异常
    格式:
        try{
            可能会产出异常的代码
        }catch(异常类变量 变量名){
            异常的处理逻辑
        }finally{
            一定会指定的代码
            资源释放
        }
    */
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            // 1. 创建文件输入流和文件输出流
            fis = new FileInputStream("D://a.txt");
            fos = new FileOutputStream(new File("H://a.txt"));

            // 2. 读取D://a.txt写入到H://a.txt中
            // 定义缓冲区
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = fis.read(bytes))!=-1){
                // 写入到硬盘
                fos.write(bytes,0,len);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if (fis!=null){
                    fis.close();
                }
                if(fos!=null){
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

JDK7的处理(扩展知识点了解内容)

还可以使用JDK7优化后的 try-with-resource 语句,该语句确保了每个资源在语句结束时关闭。所谓的资源

resource)是指在程序完成后,必须关闭的对象。

格式:

try (创建流对象语句,如果多个,使用';'隔开) { 
    // 读写数据 
} catch (IOException e) {
     e.printStackTrace(); 
}
    public static void main(String[] args) {
        try (// 1. 创建文件输入流和文件输出流
             FileInputStream fis = new FileInputStream("D://a.txt");
             FileOutputStream fos = new FileOutputStream("H://a.txt")
        )  {
            // 2. 读取D://a.txt写入到H://a.txt中
            // 定义缓冲区
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = fis.read(bytes))!=-1){
                // 写入到硬盘
                fos.write(bytes,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

JDK9的改进(扩展知识点了解内容)

JDK9try-with-resource 的改进,对于引入对象的方式,支持的更加简洁。被引入的对象,同样可以自动关闭,

无需手动close,我们来了解一下格式。

 A a = new A();
 B b = new B();
 try(a;b){
    可能会产出异常的代码
 }catch(异常类变量 变量名){
    异常的处理逻辑
 }
   public static void main(String[] args) throws Exception {
        // 1. 创建文件输入流和文件输出流
        FileInputStream fis = new FileInputStream("D://a.txt");
        FileOutputStream fos = new FileOutputStream("H://a.txt");
        try (fis;fos)  {
            // 2. 读取D://a.txt写入到H://a.txt中
            // 定义缓冲区
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = fis.read(bytes))!=-1){
                // 写入到硬盘
                fos.write(bytes,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

属性集

5.1 概述

java.util.Properties 继承于 Hashtable ,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其

对应值都是一个字符串。该类也被许多Java类使用,比如获取系统属性时, System.getProperties 方法就是返回

一个 Properties 对象。

5.2 Properties

构造方法

  • public Properties() :创建一个空的属性列表。

基本的存储方法

  • public Object setProperty(String key, String value) : 保存一对属性。
  • public String getProperty(String key) :使用此属性列表中指定的键搜索属性值。
  • public Set<String> stringPropertyNames() :所有键的名称的集合。
    public static void main(String[] args) {
        //1.创建Properties集合对象
        Properties prop = new Properties();

        // 2. 使用setProperty往集合中添加数据
        prop.setProperty("赵丽颖","168");
        prop.setProperty("迪丽热巴","165");
        prop.setProperty("古力娜扎","160");

        // 3. 遍历所有的键值对
        Set<String> keys = prop.stringPropertyNames();
        for(String key:keys){
            String value = prop.getProperty(key);
            System.out.println(key+"="+value);
        }

        /*
         赵丽颖=168
        古力娜扎=160
        迪丽热巴=165
         */
    }

与流相关的方法

  • public void load(Reader reader):从字符输入流中读取键值对。
  • public void load(InputStream inStream) : 从字节输入流中读取键值对。 
  • public void load(InputStream inStream) : 从字节输入流中读取键值对。
    •  InputStream inStream:字节输入流,不能读取含有中文的键值对
    •  Reader reader:字符输入流,能读取含有中文的键值对
  • public void store(OutputStream out, String comments): 把集合中的临时数据,持久化写入到硬盘中存储
  • public void store(Writer write, String comments): 把集合中的临时数据,持久化写入到硬盘中存储
    • ​​​​​​​​​​​​​​outputStream out:字节输出流,不能写入中文
    • Writer writer:字符输出流,可以写中文
    • String comments:注释,用来解释说明保存的文件是做什么用的
                          不能使用中文,会产生乱码,默认是Unicode编码
                          一般使用""空字符串
    public static void main(String[] args) throws IOException {
        //1.创建Properties集合对象
        Properties prop = new Properties();

        // 2. 使用setProperty往集合中添加数据
        prop.setProperty("赵丽颖","168");
        prop.setProperty("迪丽热巴","165");
        prop.setProperty("古力娜扎","160");

        // 3.创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
        FileWriter writer = new FileWriter("b.properties");

        // 4. 使用store 写入硬盘
        prop.store(writer,"save");

        // 5. 关闭资源
        writer.close();
    }
   public static void main(String[] args) throws IOException {
        //1.创建Properties集合对象
        Properties prop = new Properties();

        // 2. 加载数据文件
        prop.load(new FileReader("b.properties"));
//        prop.load(new FileInputStream("b.properties"));  // 不能读有中文的


        // 3. 遍历文件中的键值对
        Set<String> keys = prop.stringPropertyNames();
        for(String key : keys){
            String value = prop.getProperty(key);
            System.out.println(key+"="+value);
        }
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值