39讲 字节流、IO流异常处理

IO流简述

IO流是一种顺序读写数据的模式,它的特点是单向流动。数据类似自来水一样在水管中流动,所以我们把它称为IO流。

I 是指输入InputO 是指输出Output

  • Input指从外部读入数据到内存,例如,把文件从磁盘读取到内存,从网络读取数据到内存等。
  • Output指把数据从内存输出到外部,例如,把数据从内存写入到文件,把数据从内存输出到网络等。

在读取或写入IO流的过程中,可能会发生错误。例如,文件不存在导致无法读取,没有写权限导致写入失败等。这些底层错误由Java虚拟机自动封装成IOException异常并抛出。

因此,所有与IO操作相关的代码都必须正确处理IOException

IO流体系

在这里插入图片描述

按流的方向分类

输入流:读,数据从数据源读入程序。
输出流:写,数据从程序写出到目的地。

按处理的数据单元分类

字节流:以字节(1 Byte = 8 bit) 为单位进行读取或者写出数据。
字符流:以字符为单位进行读取或者写出数据。

使用场景

场景
拷贝任意类型文件字节流
读、写纯文本文件字符流

字节流

1 字节流简述

该流是以byte(字节)为最小单位,因此也称为字节流

在Java中,InputStream 代表输入字节流,OuputStream 代表输出字节流,这是最基本的两种IO流。

继承体系

在这里插入图片描述

2 字节输出流

2.1 OutputStream

OutputStream 是Java标准库提供的最基本的输出流。

InputStream类似,OutputStream也是抽象类,它是所有输出流的超类。

导包: java.io.OutputStream

在计算机中,类似文件、网络端口这些资源,都是由操作系统统一管理的。应用程序在运行的过程中,如果打开了一个文件进行读写,完成后要及时地关闭,以便让操作系统把资源释放掉,否则,应用程序占用的资源会越来越多,不但白白占用内存,还会影响其他应用程序的运行。

InputStreamOutputStream都是通过close()方法来关闭流。关闭流就会释放对应的底层资源。

2.2 FileOutputStream

FileOutputStreamOutputStream 的一个实现类。它可以将若干个字节写入文件流:

所有与IO操作相关的代码都必须正确处理IOException

2.2.1 构造方法
方法说明
public FileOutputStream(File file)根据File对象创建输出流对象。
public FileOutputStream(File file, boolean append)根据File对象创建输出流对象,续写。
public FileOutputStream(String pathname)根据路径创建输出流对象。
public FileOutputStream(String pathname, boolean append)根据路径创建输出流对象,续写。

注意:

  • 关联的路径如果不存在,构造方法会创建一个
  • 关联的路径的父级路径如果不存在,会报错
  • 如果使用一个参数的构造方法,每次写入底层都会先清空文件
  • 如果想续写,可以传递第二个参数为true
2.2.2 常用方法
方法说明
void write(int b)写入一个字节的数据
void write(byte[] b)写入整个字节数组的数据
void write(byte[] b, int off, int len)写入字节数组中从off开始共len个数据
void close()关闭流
2.2.3 写
import java.io.FileOutputStream;
import java.io.IOException;

public class OutputDemo1 {
    public static void main(String[] args) throws IOException {	//抛出IOException
        //1.创建对象
        FileOutputStream fos = new FileOutputStream("src\\do26IO\\a.txt");

        //2.写入数据
        fos.write(97);	// a
        fos.write(99);	// c
        fos.write(100);	// d

        //3.关闭文件
        fos.close();
    }
}

每次写入一个字节非常麻烦,更常见的方法是一次性写入若干字节。这时,可以用OutputStream提供的重载方法void write(byte[])来实现:

public static void main(String[] args) throws IOException {
    OutputStream fos = new FileOutputStream("src\\do26IO\\a.txt");
    
    String str = "HelloWorld";
    byte[] bytes = str.getBytes();
    fos.write(bytes);   // HelloWorld
    
    fos.close();
}
2.2.4 换行

换行的思想就是在需要换行的地方写入一个换行符。

在不同的操作系统中,换行符是不一样的。如Windows是 \r\n ,Linux是 \n , Mac是 \r

在Windows系统中,Java对换行进行了优化,无论是输入 \r\n 还是单独输入 \r\n 都可以实现换行,Java底层会自动补全。

import java.io.FileOutputStream;
import java.io.IOException;
public class OutputDemo2 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("src\\do26IO\\a.txt");

        fos.write("HelloWorld".getBytes()); // HelloWorld
        fos.write("\r\n".getBytes());       // 换行
        fos.write("Java".getBytes());       // Java

        fos.close();
    }
}

3 字节输入流

3.1 InputStream

InputStream 是Java标准库提供的最基本的输入流。

InputStream 并不是一个接口,而是一个抽象类,它是所有输入流的超类。

导包: java.io.InputStream

3.2 FileInputStream

3.2.1 构造方法
方法说明
public FileInputStream(File file)根据File对象创建输入流对象。
public FileInputStream(String name)根据路径创建输入流对象。

注意:

  • 关联的路径如果不存在,会报错
3.2.2 常用方法
方法说明
int read()读出一个字节的数据。返回值表示读取到的数据,如果没有数据返回-1
int read(byte b[])读出一个字节数组的数据,大小与创建的数组长度有关。返回值表示读取了多少字节数据,如果没有数据返回-1
void close()关闭流

注意:

  • read方法每调用一次,记录读取位置的指针就会后移一次。
  • read方法读不到数据会返回-1
  • 读的数组大小最好是1024的整数倍
3.2.3 读
import java.io.FileInputStream;
import java.io.IOException;

public class InputDemo1 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("src\\do26IO\\a.txt");//已有数据 HelloWorld

        //定义第三方变量,记录每次读取到的数据
        int b;
        while ((b = fis.read()) != -1) {
            System.out.print((char) b);	// HelloWorld
        }
        
        fis.close();
    }
}

4 应用

4.1 拷贝文件

拷贝的思想是边读边写。

public static void copy(File source, File obj) throws IOException {
        FileInputStream fis = new FileInputStream(source);
        FileOutputStream fos = new FileOutputStream(obj);

        //记录本次读取到了多少字节数据
        int len;
        //一次读5mb数据
        byte[] bytes = new byte[1024 * 1024 * 5];
        while ((len = fis.read(bytes)) != -1) {
            //写入字节数组中从0开始共len个数据
            fos.write(bytes, 0, len);
        }

        fos.close();
        fis.close();
    }

4.2 拷贝文件夹

/**
     * 用于文件夹拷贝,(包含子文件拷贝)
     * @param source 源文件
     * @param obj    目的地
     * @return 是否拷贝成功
     * @throws IOException
     */
    public static boolean copy(File source, File obj) throws IOException {
        //如果不存在目的文件夹就创建,存在就略过,不需要判断
        obj.mkdirs();
        File[] files = source.listFiles();
        //判断目的文件夹是否带有权限
        if (files == null) {
            return false;
        }
        for (File file : files) {
            //如果是文件夹,直接拷贝
            if (file.isFile()) {
                FileInputStream fis = new FileInputStream(file);
                FileOutputStream fos = new FileOutputStream(new File(obj, file.getName()));
                byte[] bytes = new byte[1024 * 1024 * 5];
                int len;
                while ((len = fis.read(bytes)) != -1) {
                    fos.write(bytes, 0, len);
                }
                fos.close();
                fis.close();
            } else {
                copy(file, new File(obj, file.getName()));
            }
        }
        return true;
    }

4.3 加密解密

原理:异或运算的自反性。 A XOR B XOR B = A

异或是一种基于二进制的位运算,用符号XOR或者 ^ 表示,其运算法则是对运算符两侧数的每一个二进制位,同值取0,异值取1。

/**
     * 该方法用于加密和解密
     * @param source 要加密、解密的文件
     * @param obj    加密、解密后的文件
     * @throws IOException
     */
    public static void encryption(File source, File obj) throws IOException {
        FileInputStream fis = new FileInputStream(source);
        FileOutputStream fos = new FileOutputStream(obj);

        int b;
        while ((b = fis.read()) != -1) {
            fos.write(b ^ 10);
        }
        fos.close();
        fis.close();
    }

IO流捕获异常处理

上述代码中的InputStreamOutputStream没有考虑到在发生异常的情况下如何正确地关闭资源。写入和读出过程会经常发生IO错误,例如,磁盘已满,无权限写入等。

我们需要用try(resource)来保证InputStreamOutputStream在无论是否发生IO错误的时候都能够正确地关闭。

捕获异常处理过于繁琐,了解即可。一般都是选择使用抛出处理。

5.1 基本操作

语法:

try {
    可能出现异常的代码;
} catch (异常类名 对象名) {
    遇到异常后的处理代码;
} finally {
    释放资源代码;
}

finally里的代码一定会被执行,除非虚拟机停止

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class IODemo1 {
    public static void main(String[] args) {
        //1.创建对象
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //2.拷贝
            fis = new FileInputStream("D:\\test\\aaa.txt");
            fos = new FileOutputStream("D:\\test\\copy.txt");
            int len;
            byte[] bytes = new byte[1024 * 1024 * 5];
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //3.释放资源
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

5.2 JDK7优化

语法:

try (创建流对象1; 创建流对象2; ...) {
    可能出现异常的代码;
} catch (异常类名 对象名) {
    遇到异常后的处理代码;
}

凡是实现接口 AutoCloseable 的类,在特定情况下都可以自动释放资源。

只有实现接口 AutoCloseable 的类,才能在try后的括号里创建对象。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class IODemo2 {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("D:\\test\\aaa.txt");
             FileOutputStream fos = new FileOutputStream("D:\\test\\copy.txt")) {
            int len;
            byte[] bytes = new byte[1024 * 1024 * 5];
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5.3 JDK9优化

将所有的创建流的代码写在try后的括号里,代码阅读性不高。JDK9可以将创建对象写在try外面

语法:

创建流对象1;
创建流对象2;
...
try (流对象1; 流对象2; ...) {
    可能出现异常的代码;
} catch (异常类名 对象名) {
    遇到异常后的处理代码;
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class IODemo3 {
    public static void main(String[] args) throws FileNotFoundException {
        FileInputStream fis = new FileInputStream("D:\\test\\aaa.txt");
        FileOutputStream fos = new FileOutputStream("D:\\test\\copy.txt");
        try (fis; fos) {
            int len;
            byte[] bytes = new byte[1024 * 1024 * 5];
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值