秋招突击——语法基础——Java的IO、多线程

IO

IO

  • 这里主要分为两种,分别时字节流和字符流
字节流
  • InputStream和OutputStream时以byte为最小单位,是字节流。
字符流
  • Reader和Writer表示字符流,字符流传输的最小数据单位是char。本质上是一个自动编解码的InputStream和OutputStream。
同步和异步
  • 同步IO是指,读写IO时代码等待数据返回后,才继续执行后续代码,有点事代码编写简单,缺点是CPU执行效率低
  • 异步IO是指,读写IO仅发出请求,然后立刻执行后续代码,优点是CPU执行效率高,缺点是代码编写复杂。
  • 上面我们讨论的InputStream、OutputStream、Reader和Writer都是同步IO的抽象类,对应的具体实现类,以文件为例,有FileInputStream、FileOutputStream、FileReader和FileWriter

File对象

  • Java中使用File对象来操作文件和目录,构造一个File对象,需要传入文件路径
    在这里插入图片描述
  • 3种形式表示的路径
    • getPath():返回构造方法传入的路径
    • getCanonicalPath():它和绝对路径类似,但是返回的是规范路径。
    • getAbsolutePath():返回绝对路径

文件和目录

  • File对象既可以表示文件,可以表示目录。

    • 即使传入的路径不存在,也不会报错,创建File并不会有任何磁盘操作。只有调用相关函数才会对磁盘操作。
  • isFile:判断file对象是否是一个已存在的文件。

  • isDirectory:判断file对象是否是一个已经存在目录。

  • boolean canRead():是否可读;

  • boolean canWrite():是否可写;

  • boolean canExecute():是否可执行;

  • long length():文件字节大小。

创建和删除文件
  • 创建文件和删除文件
File file = new File"Path";
file.createNewFile()  // 创建新文件
file.delete()  //删除文件
  • 创建临时文件和删除临时文件,JVM退出后自动删除
File f = File.createTempFile("tmp-", ".txt"); // 提供临时文件的前缀和后缀
f.deleteOnExit(); // JVM退出时自动删除
遍历文件和目录
  • 当File对象表示一个目录时,
    • list列出目录下的文件
    • listFiles列出目录下的子目录名
      • listFiles()提供了一系列重载方法,可以过滤不想要的文件和目录

在这里插入图片描述

创建文件和目录
  • boolean mkdir():创建当前File对象表示的目录;
  • boolean mkdirs():创建当前File对象表示的目录,并在必要时将不存在的父目录也创建出来
  • boolean delete():删除当前File对象表示的目录,当前目录必须为空才能删除成功
Path
  • 和File相类似,操作更简单
  • 这里就不细究了,用File就行了。

InputStream

  • 并不是一个接口,而是一个抽象类,是所有输入流的超类
    • 抽象类最重要的方法,int read()
    • public abstract int read() throw IOException;
    • 所以定义inputStream需要捕获异常
FileInputStream文件输入流
  • FileInputStream是InputSream的子类,具体的使用如下
    在这里插入图片描述
  • 这里要注意,对于InputStream的实现类来说,必须要抛出IO异常,然后这个是字节流,默认是读取字节的,如果要细致观察,需要对齐进行字符解码。
  • 文件资源是由操作系统管理,进程占用之后,要及时进行释放,防止资源浪费。
  • 最后一定要确保关闭资源,所以要把close写在finally中

自动关闭资源的两种写法

  • 使用try-finally,确保最后始终能够关闭端口
  • 使用try(resource){},这个继承了AutoCloseable接口,超过了这个会自动关闭。
    在这里插入图片描述
缓冲
  • 并非是一个字节一个字节读取,往往是一次读入多个字节,到缓冲区,然后再从缓冲区读取数据更加高效。
  • 所以,不要单纯使用read函数,要创建对应的缓冲区,常见方法有以下两种
    • int read(byte[] b):读取若干字节到b中,返回的是读取的字节的数量
    • int read(byte[] b, int off, int len):指定b的偏移量和最大填充数,返回同上

在这里插入图片描述

阻塞
  • read方法是阻塞的,必须等到对应的read返回之后,才能后续的语句

在这里插入图片描述

InputStream的实现类
  • 使用ByteArrayInputStream在内存中模拟一个字节流输入,这个仅仅在测试中有用,看看代码就行了,实际应用很少。
public class Main {
   
    public static void main(String[] args) throws IOException {
   
        byte[] data = {
    72, 101, 108, 108, 111, 33 };
        try (InputStream input = new ByteArrayInputStream(data)) {
   
            String s = readAsString(input);
            System.out.println(s);
        }
    }

    public static String readAsString(InputStream input) throws IOException {
   
        int n;
        StringBuilder sb = new StringBuilder();
        while ((n = input.read()) != -1) {
   
            sb.append((char) n);
        }
        return sb.toString();
    }
}

OutputStream

  • 这个也是一个抽象类,是所有输出流的超类,该方法有三个特殊的方法
    • 一个write方法,这个方法会抛出一个IOException,所以使用这个方法需要去捕获这个异常。write方法是写一个字节到输出流
    • close():方法关闭输出流,释放资源
    • flush():将缓冲区的内容输出到真正的磁盘中
      • 对于IO设备而言,一次写入一个字节和1000个字节的时间是一样的,为了提高效率,所以等缓冲区满了,在一次性全部写入内存中。
      • 通常情况下,不需要调用这个方法,缓存满了或者close之前,都会执行这个方法
FileOutputStream
  • FileOuputStream是OutputStream的一个具体的实现类,使用write来实现写入字节到缓存中
  • 同FileInputStream,都需要在最后关闭对应的对象,所以这里有两种方式,具体实现方式如下。

在这里插入图片描述

阻塞
  • 和文件输入的情况相同,output也是阻塞的,只有当前输出执行完毕,才能执行后续的语句
ByteArrayOuputStream
  • 使用这个可以模拟OutputStream,看一下这个代码就行,这个不重要,用的少
public class Main {
   
    public static void main(String[] args) throws IOException {
   
        byte[] data;
        try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
   
            output.write("Hello ".getBytes("UTF-8"));
            output.write("world!".getBytes("UTF-8"));
            data = output.toByteArray();
        }
        System.out.println(new String(data, "UTF-8"));
    }
}

Filter模式

  • Filter模式是分别为InputStream和OutputStream增加额外的功能
    • 分别将InputStream和FilterInputStream进行组合,可以实现不同的功能,但是仍旧是通过InputStream进行调用
    • 将OutputStream和FilterOutputStream进行组合,可以实现不同的功能,但是仍旧是通过OutputStream进行调用。
  • 本质上是根据不同的功能,选择不同的FilterInputStream或者FilterOuptutStream,对结果进行包装,但是引用始终是通过InputStream进行引用。

在这里插入图片描述

  • 左侧是提供数据的基础的inputStream和OutputStream,右侧是提供额外附加功能的inputStream和OutputStream。根据所需要的功能,调用右侧对左侧的结果进行包装,即可实现不同的功能。

在这里插入图片描述
在这里插入图片描述

使用方式
  • 首先确定数据来源,选择对应的基础类型的数据操作流,然后根据功能对齐进行包装。
InputStream file = new FileInputStream("test.gz");
InputStream buffered = new BufferedInputStream(file);  // 实现缓冲区功能的BufferedInput Stream进行包装
InputStream gzip = new GZIPinputStream("buffered");// 实现读取压缩文件的GZIPInputStream进行包装。
对输入的字节进行计数
  • 下述例子是实现一个计算输入字节数的FilterInputStream,虽然本来的InputStream就能实现这种功能,这里只是叫你一下怎么实现这个FilterInputStream,学习一下。
    • 需要重写read函数,使用的话就是正常的文件输入输出流的用法。
class CountInputStream extends FilterInputStream {
   
    private int count = 0;

    CountInputStream(InputStream in) {
   
        super(in);
    }

    public int getBytesRead() {
   
        return this.count;
    }

    public int read() throws IOException {
   
        int n = in.read();
        if (n != -1) {
   
            this.count ++;
        }
        return n;
    }

    public int read(byte[] b, int off, int len) throws IOException {
   
        int n = in.read(b, off, len);
        if (n != -1) {
   
            this.count += n;
        }
        return n;
    }
}

操作ZIP

  • ZipInputStream是一种FilterInputStream,能够直接读取Zip内容。
读取Zip包
// 创建一个基础类型的文件操作流,FileInputStream,然后使用ZipInputStream这个FilterInputStream对结果对齐进行包装。
try (ZipInputStream zip = new ZipInputStream(new FileInputStream(...))) {
   
    ZipEntry entry = null;
    // 通过getNextEntry获取对应的zip的entry,可能是一个文件也可能是一个目录,然后在进行逐个操作
    while ((entry = zip.getNextEntry()) != null) {
   
        String name = entry.getName();
        if (!entry.isDirectory()) {
   
            int n;
            // 如果不是目录,是文件,就调用read进行不断读取
            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值