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数据
  • 相关阅读:
    Ext JS学习第三天 我们所熟悉的javascript(二)
    Ext JS学习第二天 我们所熟悉的javascript(一)
    Ext JS学习第十七天 事件机制event(二)
    Ext JS学习第十六天 事件机制event(一)
    Ext JS学习第十五天 Ext基础之 Ext.DomQuery
    Ext JS学习第十四天 Ext基础之 Ext.DomHelper
    Ext JS学习第十三天 Ext基础之 Ext.Element
    Ext JS学习第十天 Ext基础之 扩展原生的javascript对象(二)
    针对错误 “服务器提交了协议冲突. Section=ResponseHeader Detail=CR 后面必须是 LF” 的原因分析
    C# 使用HttpWebRequest通过PHP接口 上传文件
  • 原文地址:https://www.cnblogs.com/zhangrunhao/p/12759224.html
Copyright © 2011-2022 走看看