Java零基础入门笔记22-Java输入输出流

知识补充:1.文本文件和二进制文件在存储时的区别
2.【字符编码系列】常用的几种字符编码(GBK,UTF-8,UTF-16)

1、概述

  • 输入输出流的应用场景: 比如我们在自己电脑上复制粘贴文件;再比如更改qq或微信的头像(我们将本地数据上传到网络服务器)等,这些问题的解决都需要输入输出流。
  • 流:就是指一连串流动的字符,以先进先出的方式发送信息的通道
  • 下图展示了输入输出流:
    这里写图片描述
  • Java的输入输出流分为字节流字符流两类。
  • 输出设备:像屏幕、打印机等;输入设备:键盘、扫描仪等。
  • 文件既可以作为输入设备,也可以作为输出设备,当作为输入设备时,叫读取文件,而作为输出设备时,叫写入文件。

2、File类的使用

  • File即文件,文件可认为是相关记录或放在一起的数据的集合
  • 在Java中,使用java.io.File类对文件进行操作。
  • Windows中的目录分隔符为"\",Linux中的目录分隔符为"/"

2.1、File类的常用方法

  • 1.首先在自己电脑的某个分区(这里为d盘)上,新建一个名为File的文件夹,在File文件夹里分别新建名为ioset两个文件夹,然后在io文件夹建一个名为score.txt的空白的文本文件。
    这里写图片描述
  • 2.首先来说一下创建文件对象的几种方式(具体解释可以参考官方API)。
// 方式1
File f1 = new File("d:\\File\\io\\score.txt");
// 方式2
File f1 = new File("d:\\File", "io\\score.txt");
// 方式3
File f1 = new File("d:\\File");
File f2=new File(f1,"io\\score.txt");

这里写图片描述

  • 2.在eclipse中新建一个IOProj的Java项目,新建一个com.cxs.file的包,在其中新建一个FIleDemo的类,勾选主方法(自行尝试,结果略)。
public class FileDemo {
    public static void main(String[] args) {
        // 创建File对象
        File f1 = new File("d:\\File\\io\\score.txt");
        // 判断是文件还是目录
        System.out.println("是否是目录:" + f1.isDirectory());
        System.out.println("是否是文件:" + f1.isFile());
        // 创建目录
        File f2 = new File("d:\\File\\set", "hashset");
        if (!f2.exists()) {
            f2.mkdir();// 若是创建多级目录,则需要调用mkdirs()方法
        }
        // 创建文件,运行前先将新建好的文件score.txt删除
        if (!f1.exists()) {
            try {
                f1.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2.2、绝对路径与相对路径

  • 绝对路径:从盘符开始的路径
  • 相对路径:从当前路径开始的路径
  • 我们可以在cmd命令窗口做以下尝试(每个人的文件及文件夹位置有所不同,仅作示例)。
    这里写图片描述

  • 1.删除上面的代码,重新编写以下代码。
public class FileDemo {
    public static void main(String[] args) {
        File f = new File("chaixingsi.txt");
        try {
            f.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 2.运行代码,打开项目位置,发现指定文件已被创建。若不指定绝对路径,则默认会在当前项目位置创建文件。
    这里写图片描述
  • 3.继续补充代码,如下图所示,结果略。
    这里写图片描述

3、字节流

3.1、字节流概述

  • 字节流分为字节输入流(InputStream)和字节输出流(OutputStream)。
  • 字节输入流类的结构如下
    这里写图片描述
  • 字节输出流类的结构如下
    这里写图片描述
  • 这里我们不会讲解所有的输入输出流,重点会讲解一下文件输入流文件输出流缓冲输入流缓冲输出流,然后在讲解到对象序列化时会讲解对象输入流对象输出流

3.2、FileInputStream

  • 文件输入流:从文件系统中的某个文件中获得输入字节。
  • 用途:用于读取诸如图像数据(二进制数据)之类的原始字节流(具体见官方API文档,若是从文档中读取字符串进行显示或写入到其他地方,则一般会采用字符流去完成)
  • FileInputStream类常用的方法有:
方法名 描述
public int read() 从输入流中读取一个数据字节
public int read(byte[] b) 从输入流中将最多b.length个字节的数据读入一个byte数组中
public int read(byte[] b,int off,int len) 从输入流中将最多len个字节的数据读入一个byte数组中,off代表偏移量
public int close() 关闭此文件输入流并释放与此流有关的所有系统资源

注释:当read()方法的返回值为-1时,表示已经读到文件的末尾。


  • 1.新建一个FileInputDemo类,勾选主方法,代码如下。(创建好类之后在当前项目(即IOProj所在位置处)添加一个自定义文本文件,里面用英文写一句话,名字和内容随意)
public class FileInputDemo {
    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("chaixingsi.txt");
            int n = fis.read();
            while (n != -1) {
                System.out.print((char) n);
                n = fis.read();
            }
            fis.close();// 关闭输入流并释放与此输入流有关的系统资源
        } catch (FileNotFoundException e) {// 此处FileNotFoundException为IOException的子类
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 2.运行程序,结果如下。
    这里写图片描述
  • 3.我们重新简化一下上面的代码,这样是不是简洁多了呢\(^o^)/
FileInputStream fis = new FileInputStream("chaixingsi.txt");
int n = 0;
while ((n = fis.read()) != -1) {
    System.out.print((char) n);
}
fis.close();
  • 4.新建一个FileInputDemo2的类,勾选主方法。
public class FileInputDemo2 {
    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("chaixingsi.txt");
            byte[] b = new byte[100];
            fis.read(b, 1, 5);// 偏移1,从第二个字符位置开始存放数据,并且读取长度为5
            System.out.println(new String(b));
            fis.close();
        } catch (FileNotFoundException e) {// 此处FileNotFoundException为IOException的子类
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 5.运行程序结果如下。
    这里写图片描述

3.3、FileOutputStream

  • 文件输出流是用来将数据写入到文件当中。
  • 与文件输入流构造方法不同的是,文件输出流还有个带布尔值参数的构造方法,如下图,若在写数据时该文件已存在了,当布尔值为true时,会在文件的末尾追加数据;当布尔值为false时,会将原有的数据覆盖掉
    这里写图片描述
  • FileOutputStream类常用的方法有:
方法名 描述
public int write(int b) 将指定字节写入此文件输出流
public int write(byte[] b) 将b.length个字节从指定byte数组写入此文件输出流中
public int write(byte[] b,int off,int len) 将指定byte数组中从偏移量off开始的len个字节写入此文件输出流中
public int close() 关闭此文件输出流并释放与此流有关的所有系统资源

  • 1.新建一个FileOutputDemo类,我们将在上面文件的基础上在后面附加内容(hi)代码如下:
public class FileOutputDemo {
    public static void main(String[] args) {
        FileOutputStream fos;
        try {
            fos = new FileOutputStream("chaixingsi.txt", true);//布尔值为true时则在原有文件后追加内容
            fos.write('h');
            fos.write('i');
            fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 2.打开文件查看内容。
    这里写图片描述
下面我们来演示一张图片复制粘贴的操作,而复制和粘贴的过程便是读取和写入的过程。
  • 1.新建一个FileOutputDemo2类,代码如下。
public class FileOutputDemo2 {
    public static void main(String[] args) {
        FileInputStream fis;
        FileOutputStream fos;
        try {
            fis = new FileInputStream("chaixingsi.jpg");
            fos = new FileOutputStream("chaixingsicopy.jpg");
            byte[] b = new byte[1024];
            int n = 0;
            while ((n = fis.read(b)) != -1) {
                fos.write(b, 0, n);
            }
            fis.close();
            fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 2.运行代码结如下,成功拷贝了一张图片。
    这里写图片描述

3.4、缓冲流

  • 缓冲流分为:缓冲输入流(BufferedInputStream)和缓冲输出流(BufferedOutputStream)。
  • 之前我们都是直接从硬盘上读取数据的,但这样的话读取速度是比较慢的,远没有直接从内存当中读取数据的速度快。而通过缓冲流便可以提高数据的读写速度。
  • 以缓冲输入流为例,我们首先通过FileInputStream将数据读出来,然后加一个通道即BufferedInputStream,然后通过BufferedInputStream读到程序(比如说字节数组)当中;而写文件呢,首先写入到BufferedOutputStream当中,然后再通过FileOutputStream写入到指定文件中。
  • 这里需要强调一下BufferedOutputStream的一个方法:flush()方法,即清空缓冲区。
    • 首先我们说一下缓冲区的工作原理,缓冲区是有一定大小的,当存放到缓冲区中的数据大小刚好是缓冲区大小的时候,此时缓冲区会自动执行写操作,比如将输入写入到FileOutputStream当中(然后通过文件输出流写入到文件);
    • 但是若存储到缓冲区的数据没有满时,这个时候并不会触发write方法去自动写入到文件输出流当中(即不会将数据写入到文件中),而此时调用flush()方法,则会清空缓冲区(强制把数据输出)。
    • 更多的解释请见这篇博客:关于java中输出流flush()方法

  • 新建一个类BufferedDemo,操作的数据还是之前的chaxingsi.txt文件(运行及结果略)。
public class BufferedDemo {
    public static void main(String[] args) {
        try {
            FileOutputStream fos = new FileOutputStream("chaixingsi.txt");
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            FileInputStream fis = new FileInputStream("chaixingsi.txt");
            BufferedInputStream bis = new BufferedInputStream(fis);

            bos.write(50);
            bos.write('a');
            bos.flush();
            System.out.println(bis.read());
            System.out.println((char) bis.read());

            bos.close();
            bis.close();
            fos.close();
            fis.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

4、字符流

4.1、字符流概述

  • 字符流也分为字符输入流和字符输出流。
  • 字符输入流的类结构图如下
    这里写图片描述
  • 字符输出流的类结构图如下
    这里写图片描述

    • 学了这么多,我们先来总结一下:
      • 处理字节流的抽象类:InputStream(字节输入流)、OutputStream(字节输出流)
        • InputStream:是字节输入流的所有类的超类,一般我们使用它的子类,如FileInputStream等。
        • OutputStream:是字节输出流的所有类的超类,一般我们使用它的子类,如FileOutputStream等。
      • 处理字符流的抽象类:InputStreamReader、OutputStreamWriter
        • InputStreamReader:是字节流通向字符流的桥梁,它将字节流转换为字符流。
        • OutputStreamWriter:是字符流通向字节流的桥梁,它将字符流转换为字节流。
      • BufferedReader与BufferedWriter
        • BufferedReader由Reader类扩展而来,提供通用的缓冲方式文本读取,readLine读取一个文本行,从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。
        • BufferedWriter由Writer 类扩展而来,提供通用的缓冲方式文本写入,newLine使用平台自己的行分隔符,将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
  • 字节输入输出流要求读写的都是二进制格式的数据;

  • 而字符输入输出流适用于这样一个场景:比如我们再qq上给朋友发消息,所发的消息为一串字符,我们将消息发送到网络传输时,实际上会将文字内容转换为二进制的字节形式进行传输,到达目的后,另一个人会读取网络传输过来的数据再转换为可读的一串字符。

4.2、字节字符转换流

  • 字节字符转换流即上面总结的InputStreamReader和OutputStreamWriter。
  • 下面会使用文件输入输出流和字节字符转换流来完成文件的拷贝(准备一个任意内容(随便写)的文本文件)。
    这里写图片描述
  • 之所以采用文件输入输出流来进行读写,是为了模拟网络环境下采用的是字节流传输,然后转换为字符的场景。
  • 1.新建一个类ReaderWriterDemo,勾选主方法,直接在主方法中编写代码。
public class ReaderWriterDemo {
    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("chaixingsi.txt");
            InputStreamReader isr = new InputStreamReader(fis, "utf-8");// notepad++默认编码格式为utf-8,避免出现乱码
            FileOutputStream fos = new FileOutputStream("chaixingsi1.txt");
            OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");//同上
            int n = 0;
            char[] cbuf = new char[10];
            while ((n = isr.read(cbuf)) != -1) {
                osw.write(cbuf, 0, n);
                osw.flush();// flush()方法可以写到循环外面
            }
            fis.close();
            fos.close();
            isr.close();
            osw.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 2.运行代码,发现项目中多了一个文本文件chaixingsi1.txt

4.3、其他字符流

  • 我们在上面代码的基础上进行修改,加入字符的缓冲输入流(BufferedReader)和缓冲输出流(BufferedWriter),以提高读写速度。
  • 修改上面的代码如下:
public class ReaderWriterDemo {
    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("chaixingsi.txt");
            InputStreamReader isr = new InputStreamReader(fis, "utf-8");
            BufferedReader br = new BufferedReader(isr);
            FileOutputStream fos = new FileOutputStream("chaixingsi1.txt");
            OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");
            BufferedWriter bw = new BufferedWriter(osw);
            int n = 0;
            char[] cbuf = new char[10];
            while ((n = br.read(cbuf)) != -1) {
                bw.write(cbuf, 0, n);
                bw.flush();// flush()方法可以写到循环外面
            }
            fis.close();
            fos.close();
            isr.close();
            osw.close();
            br.close();
            bw.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5、对象序列化与反序列化

  • 对象的读写会涉及到两个类:对象输入流(ObjectInputStream)和对象输出流(ObjectOutputStream)。
  • 序列化:把Java对象转换为字节序列的过程。
  • 反序列化:把字节序列恢复为Java对象的过程。
  • 下面演示对象序列化包含这几个步骤:
    • 创建一个类,继承Serializable接口;
    • 创建对象;
    • 将对象写入文件(由于不涉及网络,此处将写入文件);
    • 从文件读取对象信息。
  • 1.新建一个包名为com.cxs.serial,新建一个Goods类并实现Serializable接口。
public class Goods implements Serializable {
    private String goodsId;
    private String goodsName;
    private double price;

    public Goods(String goodsId, String goodsName, double price) {
        this.goodsId = goodsId;
        this.goodsName = goodsName;
        this.price = price;
    }

    public String getGoodsId() {
        return goodsId;
    }

    public void setGoodsId(String goodsId) {
        this.goodsId = goodsId;
    }

    public String getGoodsName() {
        return goodsName;
    }

    public void setGoodsName(String goodsName) {
        this.goodsName = goodsName;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Goods [goodsId=" + goodsId + ", goodsName=" + goodsName + ", price=" + price + "]";
    }
}
  • 2.新建一个测试类GoodsTest。
public class GoodsTest {
    public static void main(String[] args) {
        Goods g1 = new Goods("g001", "电脑", 4999);

        try {
            FileOutputStream fos = new FileOutputStream("chaixingsi.txt");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            FileInputStream fis = new FileInputStream("chaixingsi.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            // 将Goods对象信息写入文件
            oos.writeObject(g1);
            oos.writeBoolean(true);//当然,这里还可以写入其他类型
            oos.flush();
            // 从文件读对象信息
            try {
                Goods g2 = (Goods) ois.readObject();
                System.out.println(g2);
                System.out.println(ois.readBoolean());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            fos.close();
            oos.close();
            fis.close();
            ois.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 3.运行代码结果如下。
    这里写图片描述

猜你喜欢

转载自blog.csdn.net/chaixingsi/article/details/82500337