【学习笔记】Java 输入输出

本文详细介绍了Java的输入输出流,包括流的分类、常用流的使用,如节点流、处理流,以及标准输入输出流。重点讲解了RandomAccessFile、Scanner、对象序列化、NIO块操作的相关概念和方法,提供了Java I/O操作的全面理解。

Java 输入与输出

流(IO)

“流”(Stream)是Java对输入输出源的一种抽象,表示了一种逐一读取或写出的处理方式。

  • 输入流、输出流:通常以程序运行所在内存来区分
  • 字节流、字符流:所操作的数据单元不同
  • 节点流、处理流:直接关联IO设备的流称为节点流,而在节点流的基础上封装各种各样的处理方式后的流称为处理流,代码中表现为以节点流为构造器参数。处理流使得程序可以使用相同的代码来处理不同的数据源。
分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
抽象基类Filter~Filter~Filter~Filter~
访问文件File~File~File~File~
访问数组ByteArray~ByteArray~CharArray~CharArray~
访问管道Piped~Piped~Piped~Piped~
访问字符串String~String~
缓冲流Buffered~Buffered~Buffered~Buffered~
转换流InputStream~OutputStream~
对象流Object~Object~
打印流PrintStreamPrintWriter
推回输入流PushBack~
特殊流Data~Data~
基类类的通用方法
  • InputStream/Reader

    • int read()

    • int read(byte[]/char[] buf)

    • int read(byte[]/char[] buf, int off, int len)

      InputStream is = ...
      length = ...
      byte[] buf = new byte[length];
      for (int hasRead = 0; (hasRead = is.read(buf)) > 0; ) {
          <处理 buf 的 从 0 开始 hasRead 长度的数据> 
      }
      
    • void mark(int readlimit)

    • void reset()

    • long skip(long n)

  • OutputStream/Writer

    • void write(int c)
    • void write(byte[]/char[] buf)
    • void write(byte[]/char[] buf, int off, int len)
    • void write(String str) 限于 Writer
    • void write(String str, int off, int len) 限于 Writer
常用的节点流
  • 访问字符串:String~

  • 访问字符字节数组:ByteArray~/CharArray~

  • 访问文件对象:File~

    File 对象

    • 构造函数
      • File(String pathname)
      • File(String parent, String child)
      • File(File parent, String child)
      • File(URI uri)
        根据路径字符串创建File对象,可以包含...表示当前和上级,也可以传入相对路径,参考是系统属性user.dir(jvm工作路径)。不要以空字符""来创建对象
    • 常用函数
      • String getName()

      • String getPath()

      • String getAbsolutePath() or File getAbsoluteFile()

      • String getCanonicalPath() or File getCanonicalFile()

        Canonical 具有唯一性,是绝对路径,且消除了路径中的...

      • String getParent() or File getParentFile()

      • boolean exists()

      • boolean canWrite()

      • boolean canRead()

      • boolean isFile()

      • boolean isDirectory()

      • boolean isAbsolute()

      • long length()

      • long lastModified()

      • boolean createNewFile()

      • boolean mkdir()

      • boolean delete()

      • String[] list() or String[] list(FilenameFilter filter)

        FilenameFilter 接口包含一个boolean accept(File dir, String name)

      • File[] listFiles() or File[] listFiles(FileFilter filter)

        FileFilter接口包含一个boolean accept(File file)

      • void deleteOnExit()

  • 转换流:InputStreamReader OutputStreamWriter

    • 以字节流为构造参数,默认使用平台编码,也可以指定编码集
    • 自身可以作为参数去构造其他处理流
常用的处理流

处理流要继承 Filer~ 基类

  • 缓冲流:Buffered~ 提供缓冲功能
    • 输出基类中的 flush() 方法不做任何处理,缓冲流重写了该方法,用于刷新缓冲区
    • BufferedReader 实例的 readLine() 用于读取一个文本行
    • BufferedWriter 实例的 newLine() 用于输出一个行分隔符
  • 打印流:PrintStream / PrintWriter 提供与打印相关的方法,比如向屏幕打印等
  • 数据流:Data~ 提供了解析二进制数据的方法,比如可以直接获取读或写一个 int 等
标准输入输出流
  • System.in:默认为键盘输入
  • System.err / System.out:默认为屏幕输出,即控制台输出

System 的静态方法可以重定向:

  • static void setErr(PrintStream err)
  • static void setIn(InputStream in)
  • sattic void setOut(PrintStream out)

子进程(Process 对象)的输入输出:

  • InputStream getErrorStream() 获取子进程的错误输出流,对主进程是输入
  • InputStream getInputStream() 获取子进程的输出流,对主进程是输入
  • OutputStream getOutputStream() 获取子进程的输入流,对主进程是输出

RandomAccessFile

随机访问文件内容。与流相比,不是逐一读写,而是将文件内容放入内存,任意定位指针的访问方式。该类实现了DataInput、DataOutput 接口,所以可以像数据流那样,实现底层二进制数据和Java类型的交互。

  • 添加了定位指针的方法:

    • long getFilePointer()

    • void seek(long pos)

  • 创建该对象是可以指定文件访问模式,"r"只读"rw"读写

  • 写数据的时候是以覆盖的形式写入的。

Scanner

基于正则表达式的字符扫描解析器,可以以字符串、文件、输入流等源作为构造参数创建(二进制源需要指定编码),功能上和 DataInputStream 非常相似,不同的是 DataInputStream 解析的是二进制数据,且不需要扫描,比如要读一个 int 就直接解析当前的4个字节的数据;而 Scanner 每次扫描一个字符,碰到分割符进行解析,比如 "... 1234 34 ..." 将分割出 1234 和 34 进行解析。

  • boolean hasNextXxx()
  • Xxx nextXxx()

对象序列化

程序中,Java类对象是和运行环境绑定在一起的,运行环境包含了类的信息和运行基础等,可以操纵类对象。对象的序列化实现了对象自身数据的存储,脱离了运行环境而独立存在。

  • 远程方法调用
  • 停机配置还原
序列化的一般流程
  • 实现 Serializable 标记接口
  • ObjectOutputStream 实例的 writeObject(Object obj) 完成序列化
  • ObjectInputStream 实例的 readObject() 并强制类型转换,完成反序列化
深入理解序列化原理
  • 本质上是对对象属性的记录,并没有保存程序运行所必须的类信息,所以反序列化时需要提供对应的类文件

  • 对象的引用属性对象也必须是可序列化的类型

  • 可以一次性序列化多个对象,反序列化时按照顺序读取

  • 为了避免重复序列化一个对象,已序列化的对象都有一个序列化编号来标示内存中的对象。这样,反序列化后,原先指向同一个对象的两个引用,现在还是指向同一个对象。

    陷阱:序列化过程中如果对象在序列化之后发生变化,这种变化将不可能被保存,即使是再次序列化

  • transient 修饰的实例变量不会被序列化,反序列化时的值为对象默认值

  • private static final long serialVersionUID 用于控制类文件的版本

自定义序列化

需要添加额外的特殊方法来完成自定义过程:

  • private void writeObject(ObjectOutputStream out) throws IOException

    自定义序列化的过程,可以对属性进行加密,或者决定那些属性被序列化

  • private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException

    自定义反序列化的过程,根据上面的方法按顺序进行解密等处理

  • private void readObjectNoData() throws ObjectStreamException

    当数据异常,调用该方法初始化对象

  • private/protected Object writeReplace() throws ObjectStreamException

    序列化过程中,完全替换当前对象,程序将序列化该方法返回值

  • private/protected Object readResolve() throws ObjectStreamException

    反序列化过程中,完全替换当前对象,程序将返回该方法的返回值(主要用于枚举类的反序列化)

实现 Externalizable 接口,不同之处在与该接口是非标记的接口,需要实现两个方法和提供无参数的构造器:

  • void writeObject(ObjectOutput out) throws IOException
  • void readExternal(ObjectInput in) throws IOException, ClassNotFoundException

块(NIO)

“块”也是一种抽象,代表了一次性处理一块数据的处理方式,所以比“流”的处理效率更高。底层采用内存映射的方式处理输入输出。

程序
Buffer
Channel

这种处理方式是双向的

Buffer

代表内存中一段缓冲区,也就是那个“块”,程序和 Channel 都是通过访问缓冲区来实现输入输出。

在这里插入图片描述

Buffer 分为两个状态,IN 状态和 OUT 状态,对应图中的箭头方向。访问 Buffer 一般遵循以下四个步骤:

  1. 写入数据到 Buffer
  2. 调用 void flip() 进入 OUT 状态
  3. 从 Buffer 中读取数据
  4. 调用 void clear() 方法或者 void compact() 方法进入 IN 状态

基本数据类型都有相对应的 Buffer 实现类,常用的 Buffer 实现类是 ByteBuffer。需要调用实现类的静态方法 static XxxBuffer allocate(int capacity)得到一个 Buffer 对象。每一个实现类都添加了 get() 和 put() 方法类实现程序对缓冲区的写入和读取:

  • Xxx get()
  • Xxx get(int a)(绝对)
  • XxxBuffer get(Xxx[] dst)
  • XxxBuffer get(Xxx[] dst, int off, int length)
  • XxxBuffer put(Xxx xxx)
  • XxxBuffer put(int a, Xxx xxx)(绝对)
  • XxxBuffer put(Xxx[] dst)
  • XxxBuffer put(Xxx[] dst, int off, int length)

其他常用的方法有:

  • int capacity()
  • boolean hasRemaining()
  • int limit()
  • Buffer limit(int newlt)
  • Buffer mark()
  • int position()
  • Buffer position(int newps)
  • int remaining()
  • Buffer reset()
  • Buffer rewind()
  • static ByteBuffer allocateDirect()创建一个直接 Buffer ,仅限 ByteBuffer
Channel

代表数据源,常用的实现类有:

  • FileChannel:从文件读取数据的
  • DatagramChannel:读写UDP网络协议数据
  • SocketChannel:读写TCP网络协议数据
  • ServerSocketChannel:可以监听TCP连接

所有的 Channel 需要通过 IO 节点流的 getChannel() 来创建,例如 FileInputStream、FileOutputStream、RandomAccessFile 的 getChannel() 返回 FileChannel 。每一个实现类都添加了 read() 和 write() 方法来实现 Channel 对缓冲区的读取和写入:

  • int read(ByteBuffer bbf)
  • int read(ByteBuffer bbf, long position)
  • int write(ByteBuffer bbf)
  • int write(ByteBuffer bbf, long position)

其他常用的方法:

  • MappedByteBuffer map(FileChannel.MapMode mode, long posision, long size)
  • void position(int pos)
Charset

编码集对象,使用流程:

Charset cs = Charset.forName("xxx");
CharSetEncoder ec = cs.newEncoder();
CharSetDecoder dc = cs.newDecoder();
ByteBuffer bbf = ec.encode(cbf);
CharBuffer cbf = dc.decode(bbf);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值