Java初学笔记(六):I/O接口

12 篇文章 0 订阅

IO

主要位于java.io.*

File对象

  1. File f = new File("C:\\Windows\\notepad.exe")

    • File对象可以传入绝对路径和相对路径
  2. File输出路径:

    • getPath(): 返回构造函数传入的路径
    • getAbsolutePath(): 返回绝对路径, 可能有../.路径,同File.toString()
    • getCanonicalPath(): 返回简洁的规范路径, 简化了../.路径
  3. File对象既可以表示文件, 也可以表示目录:

    • isFile(): 判断是否为已存在的文件
    • isDirectory(): 判断是否为已存在的目录
  4. File对象获取一个文件/目录后:

    • boolean canRead(): 是否可读
    • boolean canWrite(): 是否可写
    • boolean canExecute(): 是否可执行, 对目录而言表示能否列出其子目录和文件
    • long length(): 文件字节大小
  5. 创建/删除文件:

    • boolean createNewFile()

    • boolean delete()

    • 创建临时文件:

      File f = File.createTempFile("tmp-", ".txt");   // 临时文件前缀和后缀
      f.deleteOnExit();   // 指定JVM退出时自动删除
      ...
      
  6. 遍历文件和目录:

    • list(): 返回String[]相对路径
    • listFiles(): 返回File[]
    • listFiles(new FileNameFilter() {public boolean accept(File dir, String name) {return name.endsWith(".exe");}}): 使用文件过滤器筛选
  7. 创建/删除目录:

    • boolean mkdir(): 创建当前File对象表示的目录
    • boolean mkdirs(): 创建当前File对象表示的目录, 并在必要时将不存在的父目录也创建出来
    • boolean delete(): 删除当前File对象表示的目录, 当前目录必须为空才能删除成功
Path对象

java.nio.file.Path
java.nio.file.Paths

Path p1 = Paths.get(String... path);  // 构造Path类型, 可以传入相对路径/绝对路径, 支持路径拼接
Path p2 = p1.toAbsolutePath();  // 转换为绝对路径
Path p3 = p2.normalize();       // 转换为规范路径
File f = p3.toFlie();           // 转换为File对象

// Path对象支持for each遍历, 获取每一层目录的相对路径Path对象

InputStream

  1. InputStream是Java标准库提供的最基本的字节输入流, 位于java.io.InputStream, 是一个抽象类

  2. public abstract int read() throws IOException;是最重要的一个方法

    • 每次读取输入流的下一个字节, 返回字节表示的int整数(范围0-255), 读到末尾返回-1表示不能再读了
  3. 所有IO调度都由操作系统完成, 所以在IO输入输出完成后要及时归还资源

    • InputStreamOutputStream都是提供close()方法来关闭流, 进而释放底层资源
  4. 与IO操作相关的代码都必须正确的处理可能的IOException

  5. 可以使用try{}finally{}确保IO流能够正常的关闭

    • Java编译器优化可以使用try(getIO){}的方式简化:
    try (InputStream input = new FileInputStream("src/readme.txt")) {
        int n;
        while ((n = input.read()) != -1) {
            System.out.println(n);
        }
    }
    
    • 实际上,编译器是通过检查try()中的对象是否实现了java.lang.AutoCloseable接口, 来决定是否补写finally
  6. 缓冲

    • int read(byte[] b): 读取若干字节并填充到byte[]数组, 返回读取的字节数
    • int read(byte[] b, int off, int len): 指定byte[]数组的偏移量和最大填充数
    • 如果返回-1表示已经没有更多数据
  7. IO读写时是阻塞的, 必须到达执行完成才能继续执行下面的代码

  8. 常见的FileInputStream可以从文件获取输入流, ByteArrayInputStream可以在内存中模拟一个InputStream输入流

OuputStream

  1. InputStream同理, OutputStream是Java标准库中最基本的输出流
  2. void write(int b), 写入低8位(0-255)
  3. flush(), 立刻把缓冲区数据写入
  4. write(byte[] b), 一次性写入多个字节
  5. try(){}, 处理IOException并及时关闭资源
  6. 阻塞操作
  7. FileOutputStream, ByteArrayOutputStream

Filter模式

如果想要给特定的Stream添加一下额外功能, 如缓冲/加解密/解压缩/数字签名等, 通过继承来实现会无法控制继承规模
Java为了解决继承子类数量失控问题, 提供了具有额外功能的Stream类, 可以对指定类包装装饰
基本的InputStream和来源:

  • FileInputStream
  • ServletInputStream: HTTP
  • Socket.getInputStream(): TCP

  • 包装类:
  • BufferedInputStream
  • DigestInputStream
  • CipherInputStream
  • GZIPInputStream

  • 称之为Filter/Decorator
    构建时传入InputStream, 返回一个具有新功能的InputStream
FilterInputStream

继承FilterInputStream来自定义一个:

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();  // 调用绑定的InputStream的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);   // 调用绑定的InputStream的read(...)方法, 自动关联到read()计数
        this.count += n;
        return n;
    }
    // read(byte[] b)方法会调用read(byte[] b, int off, int len), 不必重写 
}

ZIP

  1. ZipInputStream也是一种FliterInputStream, JarInputStreamZipInputStream派生, 可以读取jar包的内容

  2. ZipInputStream通常以FileInputStream作为数据来源:

     try (ZipInputStream zip = new ZipInputStream(new FileInputStream(...))) {
         ZipEntry entry = null;
         while ((entry = zip.getNextEntry()) != null) {  // 获取包内实体(压缩文件或目录), 为null表示结束
             String name = entry.getName();
             if (!entry.isDirectory()) {     // 如果是文件, 可以直接读取内容
                 int n;
                 while ((n = zip.read()) != -1);
             }
         }
     }
    
  3. ZipOutputStream可以向zip包内写入数据:

     try (ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(...))) {
         File[] files = ...
         for (File file : files) {
             zip.putNextEntry(new ZipEntry(file.getName()));
             zip.write(getFileDataAsBytes(file));
             zip.closeEntry();
         }
     }
    

classpath

Java程序读取配置时, 利用classpath相对路径更方便:
getClass().getResourceAsStream("/settings.properties")
利用Propertiesload默认配置, 再load外部用户配置

序列化

  1. Java对象序列化需要实现java.io.Serializable接口
  2. Serializable接口没有定义任何方法, 是一个空接口(标记接口), 实现了标记接口的类仅仅相当于添加了一个标记, 没有增加任何额外的方法
  3. Java对象序列化需要ObjectOutputStream
  4. 反序列化出对象时不会调用构造函数
  5. 由于Java序列化机制存在安全隐患, 一个特定的byte[]反序列化后可能会执行不安全代码
  6. 更好的序列化是利用通用的JSON数据结构来序列化

Reader

  1. 字符流, 以字符char(0-65535)为单位读取
  2. int read() / int read(char[] c)
  3. FileReader通过字符流读取文件
    • 读取时的编码默认与系统有关, 可能会出现乱码
    • 在创建时可以指定编码: var fr = new FileReader("xx.txt", StandardCharsets.UTF_8)
    • try(){}正确关闭资源
  4. CharArrayReader: 把一个char[]变成Reader
  5. StringReader: 以String作为数据源, 与CharArrayReader类似
  6. InputStreamReader: 把InputStream转换成Reader
    • 除了特殊的CharArrayReaderStringReader, 普通的Reader实际上是基于InputStream构造的
    • 构造InputStreamReader时, 我们需要传入InputStream, 还需要指定编码, 就可以得到一个Reader对象

Writer

  1. Reader是带编码转换器的InputStream, 它把byte转换为char, 而Writer就是带编码转换器的OutputStream, 它把char转换为byte并输出
  2. void write(int c)
  3. void write(char[] c)
  4. void write(String s)
  5. FileWriter
  6. CharArrayWriter
  7. StringWriter
  8. OutputStreamWriter

PrintStream / PrintWriter

  1. PrintStream是一种FilterOutputStream:

    • print(int): 写入int
    • print(boolean): 写入boolean
    • print(String): 写入String
    • print(Object): 写入Object, 相当于print(Object.toString())
    • 以及对应的println(), 自动添加换行符
  2. System.out是系统默认提供的PrintStream, 表示标准输出

  3. System.err是系统默认提供的标准错误输出

  4. PrintStream不会抛出IOException, 不必捕获异常

  5. PrintStream输出的是字节流(byte), 而PrintWriter扩展了Writer, 输出的是字符流(char)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值