44.IO流概述、字节流


一、IO流

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

Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。

  • 输入 也叫作 读取数据;
  • 输出 也叫作 写出数据

根据数据的流向分为

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

根据数据的类型分为

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

写入数据的原理(内存-->硬盘):java程序-->JVM(java虚拟机)-->OS(操作系统)-->OS调用写数据的方法-->把数据写入到文件中

顶级父类抽象类

输入流 输出流
字节流 字节输入流(InputStream) 字节输出流(OutputStream)
字符流 字符输入流(Reader) 字符输出流(Writer)

二、字节输出流【OutputStream】顶级父类

无论使用什么样的流,底层都是二进制数据。
StringObj.getBytes():得到字节字符串对应的字节数组

java.io.OutputStream抽象类 字节输出流超类定义了基本共性的方法(忽略public)

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

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

FileOutputStream 字节输出流

  1. 属于OutputStream的子类
  2. java.io.FileOutputStream类是文件输出流,将数据写出到文件。

构造方法

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

创建一个流对象,必须传入一个文件路径,该路径下,如果没有这个文件,会创建该文件。
如果这个文件已存在,会清空这个文件的数据

// 使用File对象创建流对象
File file = new File(".\\day_09\\test.txt");
FileOutputStream fos = new FileOutputStream(file);

// 使用文件名创建流对象
FileOutputStream fos2 = new FileOutputStream(".\\day_09\\test2");

1.写出字节数据write(int b) : 每次写出一个字节数据

FileOutputStream fos2 = new FileOutputStream(".\\day_09\\test2.txt");
fos2.write(100);
fos2.close(); // 必须关闭资源

int类型在内存中为4个字节,但是上面的只需要一个字节就能存储,所以只会保留一个字节(字节流没编码那么麻烦)。
使用完必须释放资源

3.写出字节数组 write(byte[] b)

FileOutputStream fos2 = new FileOutputStream(".\\day_09\\test2.txt");
fos2.write("我是一个兵,来自老百姓".getBytes());
fos2.close(); // 必须关闭资源

写多个字节,文本方式打开的时候,查询的规则

  • 第一个字节是正数(0-127),查询Ascii表
  • 第一个字节是负数,(winGBK)查询第一个与第二个组合找中文。

4.写出指定长度字节数组write(byte[] b, int off, int len)

FileOutputStream fos2 = new FileOutputStream(".\\day_09\\test2.txt");
fos2.write("天王盖地虎,宝塔镇河妖".getBytes(), 18, 15);
// utf-8编码   宝塔镇河妖
fos2.close(); // 必须关闭资源

5.数据追加续写

保留数据并进行追加操作,构造方法需要传入一个是否追加的参数

  • public FileOutputStream(File file, boolean append): 文件对象形式,追加操作
  • public FileOutputStream(String name, boolean append): 字符串路径形式, 追加操作。

如果要追加数据就传入true,清空就传入false(默认是false)

FileOutputStream fos2 = new FileOutputStream(".\\day_09\\test2.txt", true);
fos2.write("天王盖地虎,宝塔镇河妖".getBytes(), 18, 15);
fos2.close();

6.写出换行

FileOutputStream fos2 = new FileOutputStream(".\\day_09\\test2.txt", true);
fos2.write("\r\n".getBytes());
fos2.write("windows下换行了".getBytes());
fos2.close();
  • 回车符 \r 和换行符 \n :
    • 回车符:回到一行的开头(return)。
    • 换行符:下一行(newline)。
  • 系统中的换行:
    • Windows系统里,每行结尾是 回车+换行 ,即 \r\n
    • Unix系统里,每行结尾只有 换行 ,即 \n
    • Mac系统里,每行结尾是 回车 ,即 \r 。从 Mac OS X开始与Linux统一。

三、字节输人流【InputStream】顶级父类

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

先介绍字节输出流的超类InputStream字节输出流
java.io.InputStream抽象类 总超类 ,字节信息->读到内存,共性方法如下(省略public)

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

必须使用close方法释放资源

FileInputStream 字节输出流

java.io.FileInputStream 类 读取字节

1.构造方法(省略public)

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

创建一个流对象,必须传入一个文件路径,如果该路径下没有改文件(文件不存在),会抛出FileNotFondException

// 使用File对象创建流对象
File file = new File(".\\day_09\\a.txt");
new FileOutputStream(file).write("还是写一点东西吧".getBytes());
FileInputStream fos = new FileInputStream(file);

// 使用文件名称创建流对象
FileInputStream fos2 = new FileInputStream(".\\day_09\\a.txt");

2.读取字节数据int read()方法,每次读取一个字节的数据提升为int类型,读到文件末尾,返回-1

// 使用文件名称创建流对象
FileInputStream fos2 = new FileInputStream(".\\day_09\\a.txt");
int b;
while ((b = fos2.read()) != -1)
    System.out.println((char)b);

fos2.close();
  1. 虽然读取了一个字节,但是会自动提升为int类型。
  2. 流操作完毕后,调用close方法,必须释放系统资源。

3.使用字节数组读取开发中建议的方式】:int read(byte[] b) 每次读取b的长度个字节到数组中,返回读取代的有效字节个数,读取到末尾时,返回-1。
1. 方法的参数byte[]的作用:
- 起到缓冲作用,存储每次读取到的多个字节
- 数组的长度一把定义为1024(1kb)或者1024的整数倍
2. 方法的返回值int是每次读取的有效字节个数
3. String类的构造方法
- String(byte[] bytes) :把字节数组转换为字符串
- String(byte[] bytes, int offset, int length) 把字节数组的一部分转换为字符串 offset:数组的开始索引 length:转换的字节个数

FileInputStream fos2 = new FileInputStream(".\\day_09\\a.txt");
int len;
byte[] b = new byte[7];
while ((len = fos2.read(b)) != -1) {
    System.out.println(new String(b).substring(0, len));
    // 也可以使用 new String(b, 0, len);
}

fos2.close();

使用数组读取,每次读取多少个字节,减少了系间的IO操作次数,从而提高了读写效率,建议开发中使用

复制图片案例,带有多线程的进度条

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Random;

// Courier进行数据传递
class Courier {
    long size;
    long receive;
    public Courier(long size){
        this.size = size;
        receive = 0;
    }
}

public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {

        int len; // 记录返回的字节大小。
        byte[] temp = new byte[1000]; // 记录字节数据。
        Random random = new Random();

        // 使用文件名称创建流对象
        File file = new File(".\\day_09\\feifei.jpg");
        FileInputStream in = new FileInputStream(file);
        FileOutputStream out = new FileOutputStream(".\\day_09\\newfeifei.jpg", true);

        // new 一个数据传递对象。
        Courier courier = new Courier(file.length());

        Runnable r = () -> {
            Courier cour = courier;
            int n = 25; // 进度条的长度
            while (true) {
                long computd = courier.receive;
                long exists = computd * n / courier.size;
                long i = 0;
                System.out.print("[");
                for(;i <= exists; i++) System.out.print(">");
                for(;i < n; i++) System.out.print("-");

                System.out.printf("]%5.2f%%",(computd * 1.0 / courier.size) * 100.0);
                if(courier.receive == courier.size) {
                    System.out.println("\n传输完毕");
                    break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.print("\r");
            }
            
        };


        // 开启线程
        new Thread(r,"进度条").start();

        // 写入数据
        while ((len = in.read(temp)) != -1) {
            out.write(temp, 0, len);
            courier.receive += len;
            Thread.sleep(random.nextInt(100));
        }

    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容