zoukankan      html  css  js  c++  java
  • Java笔记08

    基本介绍

    IO

    • Input/Output, 以内存为中心的输入输出
    • input: 外部数据读入内存, 并且以java能够识别的形式表示
    • output: 内存数据输入到外部, 避免易失性, 必须把处理后的数据以某种方式输出
    • 代码在内存中运行, 只有把数据读到内存中才能操作

    InputStream/OutputStream

    • IO流, 以byte(字节)为最小单位. 故也是字节流
    • InputStream: 输入字节流
    • OutputStream: 输出字节流

    Reader/Writer

    • 按照char来读写: 字符流
    • 字符流传输的最小单位char
    • ReaderWriter本质上是一个能自动编码的InputStreamOutputStream
    • 如果数据是文本, 使用Reader更方便一些

    同步和异步

    • 同步: 代码简单, CPU执行效率低
    • 异步: 代码复杂, CPU执行料率高
    • 同步IO: 输入/输出流的IO模型

    File对象

    • File来操作文件和目录

    文件和目录

    • File对象既可以操作文件, 也可以表示目录. 特别注意: 构建一个File对象, 即使传入的文件和目录不存在, 代码也不会出错.. 只有调用某些方法才会出错.
    • isFile()判断是否为一个文件
    • isDirectory()判断是否为一个目录
    • boolean canRead(): 是否可读
    • boolean canWrite(): 是否可写
    • boolean canExecute(): 是否可执行
    • long length(): 文件字节大小

    创建和删除文件

    如果当前File表示文件

    • createNewFile()创建文件
    • delete()删除文件
    • createTempFile()创建临时文件
    • deleteOnExit(): JVM退出时自动删除文件

    遍历文件和目录

    如果当前File表示目录

    • 使用list()listFiles()列出目录下的文件和子目录名
    • listFiles()提供一系列重载方法, 可以过滤不想要的文件和目录
    • listFiles(), 可以接受一个Filter对象, 筛选名称
    • boolean mkdir()创建当前File表示文件所在的目录
    • boolean mkdirs(): 不存在的父目录也创建出来
    • boolean delete: 删除目录

    Path

    • 对目录进复杂的拼接, 遍历等操作
      Path p1 = Paths.get(".", "project", "study");
      System.out.println(p1);
      Path p2 = p1.toAbsolutePath(); // 转换为绝对路径
      System.out.println(p2);
      Path p3 = p2.normalize(); // 转换为规范化路径
      System.out.println(p3);
      File f = p3.toFile(); // 转换为File对象
      System.out.println(f);
      for (Path p : Paths.get("..").toAbsolutePath()) { // 直接遍历Path
        System.out.println(p);
      }
    

    InputStream

    • java最基本的输入流
    • 抽象类, 所有输入类的超类
    • read(): 读取输入流的下一个字节, 返回字节表示的int值.
    • 读到末尾, 返回-1表示不能再读取了
    • close()关闭, 释放底层资源, 避免资源浪费.
    • I/O代码, 需要处理IOException
    • 使用try(resource)自动加入 `finally { resource.close() }
    • resource是否实现AutoCloseable接口

    缓冲

    • 一次性支持多个字符到缓冲区, 是一种高效操作.
    • 缓冲重载方法:
      • int read(byte[] b): 读取若干字节到byte[]数组, 返回读取的字节数.
      • int read(byte[] b, int off, int len): 指定byte[]数组的偏移量和最大填充数.
    • 先定义一个byte[]数组作为缓存区, read()尽可能多的读取字节到缓冲区, 并返回读取了多少个字节. 没有更多数据, 返回-1

    阻塞

    • read()方法时阻塞的, 必须等到返回后, 才能继续执行

    InputStream实现类

    • FileInputStream可以从文件中获取输入流.
    • ByteArrayInputStream可以在内存中模拟一个InputStream, 多作为测试使用
    • 面向对象原则: 接受InputStream抽象类型, 而不是具体的FileInputStream

    OutputStream

    • 抽象类, 所有输出流的超类
    • void write(int b), 写入一个字节到输入流, 只写入int最低8位表示字节部分(相当于b&0xff)
    • close()关闭输出流, 释放资源
    • flush()强制将缓冲区的内容真正输出到目的地
    • 一般情况下:
      • 缓存区写满了, OutputStream自动调用
      • close()关闭前, 自动调用
    • 手动调用情况:
      • 不能等到缓冲区装满的情况
    • InputStream也有缓冲区, 例如读取第一个字节, 操作系统一次性读取若干个字节到缓冲区
    • OutputStream中的write方法也是阻塞的
    • ByteArrayOutputStream可以在内存中模拟一个OutputStream

    Filter模式

    • InputStream根据来源可以分为:

      • FileInputStream, 从文件中读取, 是最终数据源
      • ServletInputStream: 从HTTP请求读取数据, 是最终数据源
      • Socket.getInputStream(): 从TCP链接读取数据, 是最终数据源
    • 如果给FileInputStream添加很多功能, 至少需要3个子类

    • 三种功能的组合又需要更多子类

    • 为了解决依赖继承导致的子类数量失控问题

    • 首先分为: 直接提供数据的基础InputStream, 和提供额外功能的InputStream

    • 1 确定数据来源: InputStream file = new FileInputStream("test.txt")

    • 2 提供特定的功能, 例如缓存功能: InputStream buffered = new BufferedInputStream(file)

    • 3 再包装一层解压: InputStream gzip = new GZIPInputStream(buffered)

    ┌─────────────────────────┐
    │GZIPInputStream │
    │┌───────────────────────┐│
    ││BufferedFileInputStream││
    ││┌─────────────────────┐││
    │││ FileInputStream │││
    ││└─────────────────────┘││
    │└───────────────────────┘│
    └─────────────────────────┘

    • 通过一个基础组件叠加各种功能组件的模式: Filter模式(装饰者模式: Decorator)
    • 通过少量的类, 实现很多特定的功能
    • OutputStream也以这种模式来提供各种功能

    编写FilterInputStream

    • 实现自己的FilterInputStream, 以叠加到任何一个InputStream
    • 我们只需要持有最外层的InputStream, 当最外层的InputStream关闭时, 内层的close自动调用

    操作Zip

    • ZipInputStream是一种FilterInputStream, 可以直接读取zip内容
    • jar包就是zip包, 只是增加了一些特定的文件
    • JarInputStreamZipInputStream派生, 读取MANIFEST.MF

    读取zip包

    • 创建ZipInputStream, 传入一个FileInputStream作为数据源
    • 循环调用getNextEntry()直到返回null, 表示zip流结束
    • 一个Entry表示一个文件或者目录, 如果是文件, 就用read()方法不断读取, 直到返回-1
        try (ZipInputStream zip = new ZipInputStream(new FileInputStream("...."))) {
          ZipEntry entry = null;
          while((entry = zip.getNextEntry()) != null) {
            String name = entry.getName();
            if (!entry.isDirectory()) {
              int n;
              while((n = zip.read()) != -1) {
                // ...
              }
            }
          }
        }
    

    写入zip包

    • ZipOutputStream是一种FilterOutputStream, 可以直接些人内容到zip
    • 首先创建一个ZipOutputStream, 通常是包装一个FileOutputStream
    • 写入文件前, 先调用putNextEntry(), 然后用write()写入byte[]数据
    • 写入完毕后调用closeEntry结束这个文件的打包
        try (ZipOutputStream zip = new ZipOutputStream(new FileOutputStream("..."))) {
          File[] files = ...;
          for (File file : files) {
            zip.putNextEntry(new ZipEntry(file.getName()));
            zip.write(getFileDataAsBytes(file));
            zip.close();
          }
        }
    
    • 代码中没有考虑目录结构, 使用new ZipEntry(name)传入name相对路径

    读取classpath资源

    • java存放.class的目录或者jar包也可以包含其他类型的文件
      • .properties: 配置文件
      • .jpg: 图片文件
      • .txt, .csv: 文本文件
    • classpath读取文件, 可以避免不同环境下文件路径不一致问题
    • classpath中的资源文件, 路径总是以/开头, 先获取当前的Class对象
    • 调用getResourceAsStream()可以直接从classpath读取任意的资源文件
    • 如果资源不存在, 返回null

    序列化

    • 序列化: 是指把一个Java对象变成二进制内容, 本质上是一个byte[]数组
    • 序列化原因: 可以把byte[]保存在文件中, 或者通过网络传输到远程
    • 反序列化: 把二进制内容byte[]变化Java对象
    • java对象能序列化, 需要实现接口: java.io.Serializable接口
    • Serializable空接口, 标记接口.

    "序列化"

    • 使用ObjectOutputStream, 把java对象写入一个字节流
    • ObjectOutputStream既可以写入基本类型, 如int, boolean, 也可以写入String, 还可以写入实现了Serializable接口的Object
    • 写入Object时, 需要大量的类型信息, 所有写入的内容很多

    反序列化

    • ObjectInputStream负责从一个字节流读取Java对象
    • 能够读取基本类型和String类型, readObject()可以直接返回一个Object对象. 然后就可以强制转换了
    • readObject()可能抛出的异常:
      • ClassNotFoundException: 没有找到对应的Class
      • InvalidClassException: Class不匹配
    • 序列化允许定义一个特殊的seriaVersionUID的静态变量, 用于标识Java类的序列化版本.
    • 反序列化时, 由JVM直接构造出Java对象, 不调用构造方法, 构造方法内部的代码, 在反序列化时不可能运行

    安全

    • 精心构造的byte[]数组被反序列化后可以执行特定的Java代码, 导致安全漏洞
    • 更换的序列化犯法是通过JSON这样的数据接口, 只输出基本类型,包括(String)内容, 不存储任何代码相关信息.

    Reader

    • Reader是另一个输入流接口

    • InputStream是字节流

    • Reader是字符流

    • InputStream:

      • 字节流, 以byte为单位
      • 读取字节(-1, 0-255): int read()
      • 读到字节数组: int read(byte[] b)
    • Reader

      • 字符流, 以char为单位
      • 读取字符(-1, 0-65535): int read()
      • 读到字符数组: int read(char[] c)
    • 所有字符流的超类, 返回int(0 ~ 65535), 读到末尾返回-1

    FileReader

    • 同样可以使用try进行关闭
    • 可以一次性读取若干个字符并填充到char[]数组的方法: public int read(char[] c) throws IOException
    • 设置缓存区, 尽可能填充缓冲区

    CharArrayReader

    • 可以模拟一个Reader.

    StringReader

    • 直接把String作为数据源, 其他和CharArrayReader一样

    InputStreamReader

    • 普通的Reader基于InputStream构造
    • Reader需要从InputStream中读入字节流(byte). 根据编码设置, 再转换char.
    • InputStreamReader可以转成然后InputStreamReader. 需要指定编码
        try (Reader reader = new InputStreamReader(new FileInputStream("src/readme.txt", "UTF-8"))) {
          // todo..
        }
    

    Writer

    • Writer: char转成byte输出

    • 对比:

    • OutputStream

      • 字节流, 以byte为单位
      • 写入字节(0~255): void write(int b)
      • 写入字节数组: void write(byte[] b)
      • 没有写入String方法
    • Writer

      • 字符流, 以char为单位
      • 写入字符(0~65535): void write(int c)
      • 写入字符数组: void write(char[] c)
      • 写入String: void write(String s)
    • 所有字符输出流的超类

    FileWriter

    • 向文件中写入字符流的Writer.

    CharArrayWriter

    • 在内存中创建一个Writer, 作为一个缓冲区, 可以写入char, 然后得到写入的char[]数组
    • 主要是用来测试模拟

    StringWriter

    • 基于内存的Writer, 和CharArrayWriter类似
    • 内部维护了一个StringBuffer接口, 并对外提供了Writer接口

    OutputStreamWriter

    • 将任意的OutputStream转换为Writer

    PrintStream和PrintWriter

    PrintStream

    • PrintStream是一种FilterOutStream
    • 输出的都是byte数据
    • OutputStream的接口上, 提供一些写入各种数据的方法
      • print(int)
      • print(boolean)
      • print(String)
      • print(Object)
      • 以及对应的一组println()方法, 自动加上换行符
    • System.out是系统默认提供的PrintStream
    • System.err系统默认提供的标准错误输出
    • 不会抛出IOException

    PrintWriter

    • 扩展了Writer接口
    • print()/println()最终输出的是char数据
  • 相关阅读:
    [Android-NDK编译] ndk 编译 c++ 兼容性问题汇总整理
    [云计算] 001.云计算简介
    eatwhatApp开发实战(十一)
    eatwhatApp开发实战(十)
    [Unity2d系列教程] 002.引用外部DLL
    Istio Sidecar
    Kubernetes Dashborad 搭建
    Istio 1.6架构及性能
    kubeadm 搭建kubernetes集群环境
    docker 安装
  • 原文地址:https://www.cnblogs.com/zhangrunhao/p/12759224.html
Copyright © 2011-2022 走看看