Java IO 知识体系
IO 知识背景
-
在程序与外部设备(文件、网络、管道)之间数据的传输需要以 “流” 作为媒介进行传输
-
特性:
- 先进先出:最先写入输出流的数据最先被输入流读取到;
- 顺序存取:不能随机访问中间的数据;
- 只读或者只写:同一个流不能具备两个功能;
-
传输方式划分:
-
字节流:字节流可以处理一切文件,一般用来处理图像、视频、音频、PPT、Word 等类型的文件;
- InputStream 类:
- int read():读取数据
- int read(byte b[], int off, int len):从第 off 位置开始读,读取 len 长度的字节,然后放入数组 b 中
- long skip(long n):跳过指定个数的字节
- int available():返回可读的字节数
- void close():关闭流,释放资源
- OutputStream 类:
- void write(int b): 写入一个字节,虽然参数是一个 int 类型,但只有低 8 位才会写入,高 24 位会舍弃
- void write(byte b[], int off, int len): 将数组 b 中的从 off 位置开始,长度为 len 的字节写入
- void flush(): 强制刷新,将缓冲区的数据写入
- void close():关闭流
- InputStream 类:
-
字符流:字符流只能处理纯文本文件(自动处理字符编码)——————>字符流为字节流的子类;
- Reader 类;
- int read():读取单个字符
- int read(char cbuf[], int off, int len):从第 off 位置开始读,读取 len 长度的字符,然后放入数组 b 中
- long skip(long n):跳过指定个数的字符
- int ready():是否可以读了
- void close():关闭流,释放资源
- Writer 类:
- void write(int c): 写入一个字符
- void write( char cbuf[], int off, int len): 将数组 cbuf 中的从 off 位置开始,长度为 len 的字符写入
- void flush(): 强制刷新,将缓冲区的数据写入
- void close():关闭流
- Reader 类;
PS: >为什么只有字节流和字符流,其他的流,如音频流呢?
答:字节流和字符流为处理数据提供了基本框架,而特定类型的流(如音频流、视频流等)通常是在这个框架之上,通过其他库或接口来实现的;
为什么我字节流可以对所有的数据类型进行调用对应的库或接口,还需要这个字符流呢?
答:尽管字节流可以处理所有类型的数据,但字符流对于文本数据的处理更加高效和方便。根据具体的应用场景选择合适的流类型,可以提高代码的可读性和性能。- 处理文本数据:字符流专门用于处理字符(文本)数据,能够正确处理不同的字符编码(如 UTF-8、GBK 等)。
- 简化编码:字符流自动处理字符的编码和解码,减少了手动转换的复杂性。
- 可读性:字符流在处理文本时,通常更易于理解和使用,尤其是在读取和写入文本文件
字节流和字符流缓存区别:
答:字节流本身没有缓冲区,缓冲字节流相对于字节流,效率提升非常高。而字符流本身就带有缓冲区,缓冲字符流相对于字符流效率提升就不是那么大了 -
-
-
操作对象划分(所有的程序,在执行的时候,都是在内存上进行的,一旦关机,中的数据就没了,那如果想要持久化,就需要把内存中的数据输出到外部):
-
Input:将外部的数据读入内存,比如说把文件从硬盘读取到内存,从网络数据到内存等等
-
Output:将内存中的数据写入到外部,比如说把数据从内存写入到文件,据从内存输出到网络等等。

-
由上面的不同操作对象可以设置不同类型的流进行变换,如:
流类型 描述 文件流 (FileInputStream) 输入输出控制读写文件数据。 内存流 (ByteArrayInputStream) 在内存中读写数据,适用于压缩、加密、序列化等操作。优点是无需创建临时文件,提高效率;缺点是存储数据量有限,可能导致内存溢出。 管道 (PipedOutputStream) 实现不同线程之间的数据传输,适用于线程间通信和数据传递。局限性是只能在同一 JVM 中的线程之间使用。 数据类型 (DataInputStream) 提供基本的读写操作。 缓冲 (Buffer) 在内存中设置缓冲区,优化读写性能;只有当缓冲区存储足够多数据或调用 flush()方法时,才与内存或硬盘交互。打印流 (PrintStream / PrintWriter) PrintStream输出字节数据,而PrintWriter输出字符数据,二者的使用方式几乎相同。对象序列化/反序列化 ObjectOutputStream将 Java 对象转成字节数组,便于保存或传输;ObjectInputStream将字节数组转为 Java 对象。转换 InputStreamReader是从字节流到字符流的桥连接,它使用指定的字符集读取字节并将它们解码为字符.OutputStreamWriter将一个字符流的输出对象变为字节流的输出对象,是字符流通向字节流的桥梁
-
一、文件流
Java IO (Input/Output) 中的文件流是用于读写文件的核心组件。它们允许程序与文件系统进行交互,实现数据的持久化存储和读取。
文件类、输入/输出流与 Java 文件操作的关系
1. 输入流和输出流与 File 类的关系
-
File类是文件操作的入口:File类本身不是用来直接读写文件内容的。它的主要作用是代表文件或目录的路径,并提供文件和目录的元数据操作,例如:- 判断文件是否存在 (
exists()) - 判断是文件还是目录 (
isFile(),isDirectory()) - 创建、删除文件或目录 (
createNewFile(),delete(),mkdir(),mkdirs()) - 获取文件路径、名称、大小、最后修改时间等信息 (
getPath(),getName(),length(),lastModified())
- 判断文件是否存在 (
-
输入/输出流依赖
File对象: Java 中的输入流 (InputStream, Reader) 和 输出流 (OutputStream, Writer) 才是真正用于读写文件内容的。 但是,文件流的创建通常需要File对象作为参数,或者需要文件路径字符串,而文件路径字符串最终也会被用来创建File对象。- 例如:
FileInputStream和FileOutputStream的构造方法可以接受File对象或文件路径字符串。FileReader和FileWriter的构造方法同样可以接受File对象或文件路径字符串。RandomAccessFile的构造方法也接受File对象或文件路径字符串。
- 例如:
-
流操作作用于
File代表的文件: 当您创建一个文件输入流或输出流,并将其与File对象关联起来后,后续的read()和write()等流操作,实际上是在操作File对象所代表的那个文件 的内容。
总结: File 类是文件操作的基础和入口,它提供了文件和目录的抽象路径表示以及元数据操作。而输入/输出流才是真正负责数据传输的组件,它们需要 File 对象来指定操作的目标文件。 File 类负责 “定位” 文件,而流负责 “操作” 文件内容。
2. File 类是 Java 底层的文件操作类吗?
-
File类是 Java IO 的基石: 可以认为File类是 Java IO 文件操作体系的基石。 虽然File类本身不直接进行底层的文件 I/O 操作,但它为所有文件相关的操作提供了统一的抽象表示。 -
底层依赖操作系统: Java 的文件 IO 操作最终都依赖于底层操作系统的文件系统 API。 无论是
File类的方法,还是文件输入/输出流的操作,最终都会通过 JNI (Java Native Interface) 调用操作系统的本地方法,来完成实际的文件读写、创建、删除等操作。 -
File类提供抽象层:File类在 Java 层面提供了一个抽象层,使得 Java 程序可以以平台无关的方式操作文件系统。 Java 开发者无需直接关注底层操作系统文件系统 API 的细节,只需使用File类和各种流 API 即可完成文件操作。
3. 工具类 (FileUtils, FileUtil) 以 File 对象展开文件操作吗?
-
工具类基于
File类: 是的,像 Apache CommonsFileUtils和 HutoolFileUtil这样的文件工具类,它们都是构建在File类和 Java IO 流 API 之上的。 它们的核心功能仍然是使用File对象来代表文件,并使用各种输入/输出流来读写文件内容。 -
工具类封装和简化操作: 工具类的主要目的是封装和简化常见的、重复性的文件操作,并提供更便捷、更高级的功能。 它们通常会:
- 封装流操作: 工具类内部会使用
FileInputStream,FileOutputStream,FileReader,FileWriter等流来完成实际的文件读写操作,但会将流的创建、关闭、异常处理等细节隐藏起来,提供更简洁的方法给用户调用。 - 提供组合操作: 工具类会提供一些组合操作,例如文件复制、目录复制、递归删除目录、读取整个文件内容到字符串等,这些操作通常需要手动组合
File类和流 API 才能完成,工具类将其封装成一个方法调用,提高了开发效率。 - 增强功能: 工具类可能会提供一些额外的功能,例如文件类型判断、MIME 类型获取、文件大小格式化等,这些功能在
File类和标准流 API 中可能没有直接提供。
- 封装流操作: 工具类内部会使用
-
工具类方法通常接受
File或路径: 工具类的方法通常会接受File对象或文件路径字符串作为参数,这进一步印证了File类在文件操作中的核心地位。 即使传入的是路径字符串,工具类内部通常也会先创建File对象,再进行后续操作。
总结:
Java IO 文件操作组件详解 (底层到高层)
本表格总结了 Java IO 中用于文件操作的关键组件,从底层到高层进行了对比,包括 File 类、输入/输出流、RandomAccessFile 以及工具类 (FileUtils / FileUtil)。
| 特性 | File 类 (java.io.File) | 输入/输出流 (IO Streams) | RandomAccessFile (java.io.RandomAccessFile) | 工具类 (FileUtils / FileUtil) (第三方库) |
|---|---|---|---|---|
| 类型 | 基础功能类 (文件/目录抽象表示) | 核心功能组件 (数据传输管道) | 核心功能类 (随机访问文件) | 工具类库 (静态方法集合) |
| 目的 | 文件和目录的路径表示和元数据操作 | 实现程序与文件之间的数据输入和输出 (读写文件内容) | 提供对文件内容的随机读写能力 | 简化和增强常见文件操作,提供便捷方法 |
| 操作层面 | 文件系统抽象层面 (路径、属性) | 底层数据传输层面 (字节/字符序列) | 中层文件内容操作层面 (字节级别,随机访问) | 高层文件操作层面 (封装流操作,提供高级功能) |
| 访问模式 | 路径操作,不涉及内容访问 | 顺序访问 (为主),也支持缓冲等优化 | 随机访问 | 顺序访问 (底层基于流) |
| 数据单位 | 路径名字符串,文件/目录对象 | 字节 (字节流),字符 (字符流),对象 (对象流) | 字节 (字节流) | 字节,字符,字符串,行,字节数组等 (根据具体方法而定) |
| 输入/输出流类型 | 不适用 (非流) | 字节流 (InputStream, OutputStream),字符流 (Reader, Writer),缓冲流,对象流等 | 字节流 | 不适用 (工具类封装了流操作) |
| 易用性 | 基础,简单,用于元数据和结构操作 | 相对底层,需要手动管理流的创建、关闭、异常处理等 | 相对复杂,需要理解文件指针和字节操作 | 高度易用,提供简洁的静态方法,封装了底层细节,易于上手和使用 |
| 适用场景 | 文件和目录的创建、删除、重命名、属性获取 | 所有需要读写文件内容的场景 (文本文件,二进制文件,各种数据格式) | 需要随机访问文件内容的特定场景 (数据库文件,断点续传等) | 通用文件操作场景,日常文件任务,提高开发效率,代码简洁性 |
与 File 对象的关系 | File 对象本身就是操作对象 | 输入/输出流通常以 File 对象或路径作为操作目标,作用于 File 代表的文件内容 | RandomAccessFile 以 File 对象或路径作为操作目标,在其代表的文件上进行随机读写 | 工具类方法通常接受 File 对象或路径作为输入参数,操作指定的文件或目录 |
表格总结:
File类: 位于文件操作的最底层,是所有文件相关操作的基础。它主要关注文件和目录的抽象路径和元数据,但不直接处理文件内容的数据流。- 输入/输出流: 构建在
File类之上,是 Java IO 的核心,负责实际的数据传输。它们提供了多种类型的流 (字节流、字符流等) 来处理不同类型的数据,并支持顺序访问和缓冲等机制。 RandomAccessFile: 与输入/输出流同级,但专注于随机访问文件内容。它提供了一种更灵活的文件访问模式,适用于特定场景。- 工具类 (
FileUtils/FileUtil): 位于文件操作的最高层,是基于File类和输入/输出流构建的工具库。它们封装了底层的复杂性,提供了更简洁、更易用的 API,用于处理各种常见的文件操作任务,提高开发效率。
层层递进,协同工作:
Java IO 的文件操作体系是分层设计的,从 File 类的抽象表示,到输入/输出流的数据传输,再到 RandomAccessFile 的随机访问,以及工具类的便捷封装,它们共同构建了一个完整、灵活的文件操作框架。 在实际开发中,您可以根据具体需求选择合适的组件:
- 基础操作 (文件/目录管理):
File类 - 顺序读写文件内容: 输入/输出流 (
FileInputStream,FileOutputStream,FileReader,FileWriter等) - 随机读写文件内容:
RandomAccessFile - 便捷、高效的通用文件操作: 工具类 (
FileUtils,FileUtil)
二、字节流
-
一切文件(文本、视频、图片)的数据都是以二进制的形式存储的,传输时也是。所以,字节流可以传输任意类型的文件数据。
-
字节流 (Byte Streams) 是 Java IO (Input/Output) 体系的最基础和核心组成部分。它们是所有其他类型的 IO 流的基础,理解字节流对于深入掌握 Java IO 至关重要。
1. 什么是字节流? (What are Byte Streams?)
- 定义: 字节流是处理以 字节 (byte) 为单位的数据的流。在 Java 中,一个字节是 8 位 (bits) 的数据。
- 目的: 字节流的主要目的是读取和写入原始的二进制数据。 计算机中所有的数据最终都以二进制形式 (字节序列) 存储和传输,因此字节流是处理各种数据的基础。
- 抽象类: Java 提供了两个抽象类作为字节流的基类:
InputStream(输入字节流): 用于从数据源 (例如文件、网络连接、内存) 读取字节数据 到程序中。OutputStream(输出字节流): 用于将字节数据从程序 写入 到数据目的地 (例如文件、网络连接、内存)。
- 常用实现类 (文件操作): 在文件 IO 中,最常用的字节流实现类是:
FileInputStream: 用于从文件读取字节数据。FileOutputStream: 用于向文件写入字节数据。
2. 为何是 IO 的基石? (Why are they the Cornerstone?)
字节流之所以被称为 Java IO 的基石,是因为:
- 通用性: 字节是计算机数据存储和传输的最基本单位。 任何类型的数据,包括文本、图像、音频、视频、程序代码等,在底层都以字节序列的形式存在。 因此,字节流可以处理所有类型的数据。
- 其他流的基础: Java IO 中其他类型的流 (例如字符流、缓冲流、对象流) 都是基于字节流构建的。 它们在字节流的基础上添加了额外的功能或抽象层次,例如:
- 字符流 (
Reader,Writer): 在字节流的基础上,提供了字符编码处理能力,更方便地处理文本数据。 但字符流的底层仍然是字节流。 - 缓冲流 (
BufferedInputStream,BufferedOutputStream): 在字节流的基础上,添加了缓冲区,提高了 IO 效率。 但缓冲流仍然是对字节数据的缓冲和操作。 - 对象流 (
ObjectInputStream,ObjectOutputStream): 在字节流的基础上,提供了对象序列化和反序列化能力,用于对象的持久化和网络传输。 但对象流最终也是将对象转换为字节序列进行传输。
- 字符流 (
- 最底层的操作: 字节流提供了最直接、最底层的 IO 操作方式。 理解字节流的工作原理,有助于深入理解 Java IO 的本质。
3. 字节流的关键概念 (Key Concepts)
- 字节 (Byte): 8 位二进制数据,是计算机存储和处理数据的基本单位。 字节流操作的是一个个独立的字节。
- 数据方向 (Input/Output):
- Input (输入): 数据从外部来源 (数据源) 流入程序。
InputStream及其子类用于输入操作。 - Output (输出): 数据从程序流出到外部目的地 (数据目的地)。
OutputStream及其子类用于输出操作。
- Input (输入): 数据从外部来源 (数据源) 流入程序。
- 抽象类与实现类 (Abstract Classes and Implementations):
InputStream和OutputStream是抽象类,定义了字节输入流和输出流的通用接口和方法。FileInputStream,FileOutputStream等是实现类,提供了针对特定数据源/目的地的具体实现。 例如,FileInputStream提供了从文件读取字节数据的具体实现。
- 基本操作方法 (Basic Operations):
read()(InputStream): 从输入流中读取一个字节的数据。 返回值为int类型 (0-255 代表读取的字节值,-1 代表已到达流的末尾)。write()(OutputStream): 向输出流中写入一个字节的数据。 参数为int类型 (实际写入的是参数的低 8 位,即一个字节)。close()(InputStream & OutputStream): 关闭流,释放与流关联的系统资源 (例如文件句柄、网络连接等)。 务必在流使用完毕后关闭流,避免资源泄漏。
- 二进制数据处理 (Binary Data Handling): 字节流非常适合处理二进制数据,例如:
- 图像文件: JPEG, PNG, GIF 等图像文件都是二进制格式。
- 音频/视频文件: MP3, MP4, AVI 等音频/视频文件也是二进制格式。
- 程序代码 (class 文件, 可执行文件): 编译后的 Java class 文件、可执行文件等都是二进制格式。
- 任何非文本数据: 例如,压缩文件 (ZIP, GZIP),加密数据等。
三、字符流
01、字符输入流(Reader)
-
java.io.Reader 是字符输入流的超类(父类),它定义了字符输入流的一些共性方法:
- 1、close():关闭此流并释放与此流相关的系统资源。
- 2、read():从输入流读取一个字符。(每次可以读取一个字符,返回读取的字符(转为 int 类型),当读取到文件末尾时,返回-1)
- 3、read(char[] cbuf):从输入流中读取一些字符,并将它们存储到字符数组 cbuf 中;
02、字符输出流(Writer)
-
java.io.Writer 是字符输出流类的超类(父类),可以将指定的字符信息写入到目的地,来看它定义的一些共性方法:
- 1、write(int c) 写入单个字符。
- 2、write(char[] cbuf) 写入字符数组。
- 3、write(char[] cbuf, int off, int len) 写入字符数组的一部分,off 为开始索引,len 为字符个数。
- 4、write(String str) 写入字符串。
- 5、write(String str, int off, int len) 写入字符串的某一部分,off 指定要写入的子串在 str 中的起始位置,len 指定要写入的子串的长度。
- 6、flush() 刷新该流的缓冲。
- 7、close() 关闭此流,但要先刷新它。
-
java.io.FileWriter 类是 Writer 的子类,用来将字符写入到文件
03、流的注意事项
-
flush 和 close 的用法:
- flush :刷新缓冲区,流对象可以继续使用。(进行流的操作时,数据先被读到内存中,然后再把数据写到文件中)
- close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了
-
IO 异常处理规范:
- try…catch…finally
- try-with-resources (会在 try 块执行完毕后自动关闭流的对象,不需要手动关闭流)
四、缓冲流
-
传统的 Java IO 是阻塞模式的,它的工作状态就是“读/写,等待,读/写,等待。。。。。。”
-
字节缓冲流解决的就是这个问题:一次多读点多写点,减少读写的频率,用空间换时间。
- 减少系统调用次数:在使用字节缓冲流时,数据不是立即写入磁盘或输出流,而是先写入缓冲区,当缓冲区满时再一次性写入磁盘或输出流。这样可以减少系统调用的次数,从而提高 I/O 操作的效率。
- 减少磁盘读写次数:在使用字节缓冲流时,当需要读取数据时,缓冲流会先从缓冲区中读取数据,如果缓冲区中没有足够的数据,则会一次性从磁盘或输入流中读取一定量的数据。同样地,当需要写入数据时,缓冲流会先将数据写入缓冲区,如果缓冲区满了,则会一次性将缓冲区中的数据写入磁盘或输出流。这样可以减少磁盘读写的次数,从而提高 I/O 操作的效率。
- 提高数据传输效率:在使用字节缓冲流时,由于数据是以块的形式进行传输,因此可以减少数据传输的次数,从而提高数据传输的效率。
五、转换流
InputStreamReader 和 OutputStreamWriter 是将字节流转换为字符流或者将字符流转换为字节流。通常用于解决字节流和字符流之间的转换问题,可以将字节流以指定的字符集编码方式转换为字符流,或者将字符流以指定的字符集编码方式转换为字节流。
InputStreamReader 类的常用方法包括:
- read():从输入流中读取一个字符的数据。
- read(char[] cbuf, int off, int len):从输入流中读取 len 个字符的数据到指定的字符数组 cbuf 中,从 off 位置开始存放。
- ready():返回此流是否已准备好读取。
- close():关闭输入流。
OutputStreamWriter 类的常用方法包括:
- write(int c):向输出流中写入一个字符的数据。
- write(char[] cbuf, int off, int len):向输出流中写入指定字符数组 cbuf 中的 len 个字符,从 off 位置开始。
- flush():将缓冲区的数据写入输出流中。
- close():关闭输出流。
在使用转换流时,需要指定正确的字符集编码方式,否则可能会导致数据读取或写入出现乱码。
六、序列流
-
序列化的思想是“冻结”对象状态,然后写到磁盘或者在网络中传输;反序列化的思想是“解冻”对象状态,重新获得可用的 Java 对象。
-
一个对象要想序列化,必须满足两个条件:
- 该类必须实现 java.io.Serializable 接口,否则会抛出 NotSerializableException 。
- 该类的所有字段都必须是可序列化的。如果一个字段不需要序列化,则需要使用 transient 或者 static 关键字进行修饰。
- static 关键字表示该成员变量是属于类的,不属于对象的,因此不需要序列化和反序列化。
Serializable 接口
- 定义:Serializable 是一个标记接口(marker interface),意味着它没有任何方法。实现这个接口的类可以被序列化。
- 内部含义:
- JVM 在序列化对象时,会自动处理对象的所有字段,包括私有字段。
- 如果对象的某个字段不希望被序列化,可以使用 transient 关键字修饰该字段。
- 在序列化过程中,JVM 会生成一个版本号(serialVersionUID),用于确保反序列化时版本兼容。如果类的结构发生变化,可能会导致 InvalidClassException。
Externalizable 接口
- 定义:Externalizable 接口是 Serializable 的一个扩展,允许开发者控制序列化的过程。
- 内部含义:
- 需要实现 writeExternal(ObjectOutput out) 和 readExternal(ObjectInput in) 方法,以自定义序列化和反序列化逻辑。
- 由于需要手动处理字段的读写,Externalizable 提供了更高的灵活性和控制力。
- 不需要 serialVersionUID,但开发者需要确保序列化和反序列化时的字段一致性
transient
transient 关键字在 Java 中用于指示某个字段不应被序列化。具体来说,transient 修饰的字段在对象序列化时将被忽略,也就是说,当对象被转换为字节流时,这些字段的值不会被写入字节流;在反序列化时,这些字段会被赋予默认值(如 null、0、false 等)。
- 使用的场景:
- 敏感数据:
- 当对象包含敏感信息(如密码、信用卡信息等)时,使用 transient 可以防止这些信息被序列化,从而提高安全性。
- 临时字段:
- 如果某个字段是临时计算的结果,不需要在对象的持久化状态中保存(比如缓存值),可以使用 transient。
- 不必要的字段:
- 对于一些在反序列化时不需要恢复的字段,比如某些统计信息或状态标志,也可以使用 transient。
- 避免序列化开销:
- 某些字段可能会占用大量内存或序列化开销,如果这些字段在反序列化后不需要使用,可以将其标记为 transient 来减少序列化的负担。
- 敏感数据:
Kryo 序列化的优势
在实际开发中,开发者通常更倾向于使用 Kryo 序列化而非 JDK 自带的序列化,原因如下:
| 问题 | 描述 |
|---|---|
| 可移植性差 | JDK 的序列化机制是 Java 特有的,无法跨语言进行序列化和反序列化。这限制了与其他语言或平台的交互。 |
| 性能差 | JDK 的序列化生成的字节流体积较大,增加了数据传输和存储的成本。Kryo 通过更高效的算法和更小的字节序列来提高性能。 |
| 安全问题 | JDK 序列化存在安全风险,攻击者可以构造恶意数据来实现远程代码执行,从而对系统造成严重的安全威胁。这种安全漏洞在 Java 反序列化中尤为突出。 |
七、打印流
-
类别:
- PrintStream 是 OutputStream 的子类(System.out 返回的正是打印流 PrintStream);
- PrintWriter 是 Writer 的子类;
-
PrintStream 类的常用方法包括:
- print():输出一个对象的字符串表示形式。
- println():输出一个对象的字符串表示形式,并在末尾添加一个换行符。
- printf():使用指定的格式字符串和参数输出格式化的字符串。
- 内部结构: public PrintStream printf(String format,
Object... args); - %s:输出一个字符串。
- %d 或 %i:输出一个十进制整数。
- %x 或 %X:输出一个十六进制整数,%x 输出小写字母,%X 输出大写字母。
- %f 或 %F:输出一个浮点数。
- %e 或 %E:输出一个科学计数法表示的浮点数,%e 输出小写字母 e,%E 输出大写字母 E。
- %g 或 %G:输出一个浮点数,自动选择 %f 或 %e/%E 格式输出。
- %c:输出一个字符。
- %b:输出一个布尔值。
- %h:输出一个哈希码(16 进制)。
- %n:换行符。
- 内部结构: public PrintStream printf(String format,
855

被折叠的 条评论
为什么被折叠?



