Java 核心知识总结 I/O 流
File 类
File 类
- File 类定义在
java.io
包下,一个 File 对象代表硬盘或网络中可能存在的一个文件或者文件目录,与平台无关。 - File 能新建、删除、重命名文件和文件目录以及查看和修改文件和文件目录的信息,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。File 对象可以作为参数传递给流的构造器。
- 想要在 Java 程序中表示一个真实存在的文件或目录,那么必须有一个 File 对象,但是 Java 程序中的一个 File 对象,可能没有一个真实存在的文件或目录。
I/O 流
I/O 流的原理
Java 程序中,对于数据的输入/输出操作以流(stream)的方式进行,可以看做是一种数据的流动。
I/O 是 Input/Output 的缩写, I/O 技术用于处理设备之间的数据传输,如读/写文件、网络通讯等。
- 输入:读取外部(磁盘、光盘等存储设备)数据到程序(内存)中。
- 输出:将程序(内存)数据输出到外部(磁盘、光盘等存储设备)。
流的分类
java.io
包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
- 按数据的流向不同分为:输入流和输出流。
- 输入流 :把数据从其他设备上读取到内存中的流,以
InputStream
、Reader
结尾。 - 输出流 :把数据从内存中写出到其他设备上的流,以
OutputStream
、Writer
结尾。
- 输入流 :把数据从其他设备上读取到内存中的流,以
- 按操作数据单位的不同分为:字节流(8bit)和字符流(16bit)。
- 字节流 :以字节为单位,读写数据的流,以
InputStream
、OutputStream
结尾。 - 字符流 :以字符为单位,读写数据的流,专门用于处理文本文件,以
Reader
、Writer
结尾。
- 字节流 :以字节为单位,读写数据的流,以
- 根据 I/O 流的角色不同分为:节点流和处理流。
- 节点流:直接从数据源或目的地读写数据。
- 处理流:不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
常用的流
Java 的 I/O 流共涉及 40 多个类,都是从 InputStream、OutputStream、Reader、Writer 4 个抽象基类派生的,由这 4 个类派生出来的子类名称都是以其父类名作为子类名后缀。
I/O 流为什么要分为字节流和字符流?
不管是文件读写还是网络发送接收,信息的最小存储单元都是字节,那为什么 I/O 流操作要分为字节流操作和字符流操作呢?
主要有两点原因:
- 字符流是由 Java 虚拟机将字节转换得到的,这个过程比较耗时;
- 如果我们不知道编码类型,使用字节流的过程中很容易出现乱码问题。
节点流 - FileReader/FileWriter
Reader 和 Writer
java.io.Reader
抽象类是表示用于读取字符流的所有类的父类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。
public int read()
: 从输入流读取一个字符。 虽然读取了一个字符,但是会自动提升为 int 类型。返回该字符的 Unicode 编码值。如果已经到达流末尾了,则返回 -1。public int read(char[] cbuf)
: 从输入流中读取一些字符,并将它们存储到字符数组 cbuf 中 。每次最多读取 cbuf.length 个字符。返回实际读取的字符个数。如果已经到达流末尾,没有数据可读,则返回 -1。public int read(char[] cbuf,int off,int len)
:从输入流中读取一些字符,并将它们存储到字符数组 cbuf 中,从 cbuf[off] 开始的位置存储。每次最多读取 len 个字符。返回实际读取的字符个数。如果已经到达流末尾,没有数据可读,则返回 -1。public void close()
:关闭此流并释放与此流相关联的任何系统资源。当完成流的操作时,必须调用 close() 方法,释放系统资源,否则会造成内存泄漏。
java.io.Writer
抽象类是表示用于写出字符流的所有类的父类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。
public void write(int c)
:写出单个字符。public void write(char[] cbuf)
:写出字符数组。public void write(char[] cbuf, int off, int len)
:写出字符数组的某一部分。off:数组的开始索引;len:写出的字符个数。public void write(String str)
:写出字符串。public void write(String str, int off, int len)
:写出字符串的某一部分。off:字符串的开始索引;len:写出的字符个数。public void flush()
:刷新该流的缓冲。public void close()
:关闭此流。
FileReader 和 FileWriter
java.io.FileReader
类用于读取字符文件,构造时使用系统默认的字符编码和默认字节缓冲区。
FileReader(File file)
: 创建一个新的 FileReader ,给定要读取的File对象。FileReader(String fileName)
: 创建一个新的 FileReader ,给定要读取的文件的名称。
FileReader fr = null;
try {
//1. 创建File类的对象,对应着物理磁盘上的某个文件
File file = new File("hello.txt");
//2. 创建FileReader流对象,将File类的对象作为参数传递到FileReader的构造器中
fr = new FileReader(file);
//3. 通过相关流的方法,读取文件中的数据
char[] cbuf = new char[5];
//read(char[] cbuf) : 每次将文件中的数据读入到cbuf数组中,
//并返回读入到数组中的字符的个数。
int len; //记录每次读入的字符的个数
while ((len = fr.read(cbuf)) != -1) {
//处理char[]数组即可
String str = new String(cbuf, 0, len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭相关的流资源,避免出现内存泄漏
try {
if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
java.io.FileWriter
类用于写出字符到文件,构造时使用系统默认的字符编码和默认字节缓冲区。
FileWriter(File file)
: 创建一个新的 FileWriter,给定要读取的File对象。FileWriter(String fileName)
: 创建一个新的 FileWriter,给定要读取的文件的名称。FileWriter(File file,boolean append)
: 创建一个新的 FileWriter,指明是否在现有文件末尾追加内容。
注意:
- 因为出现流资源的调用,为了避免内存泄漏,需要使用 try-catch-finally 处理异常。
- 对于输入流来说,File类的对象必须在物理磁盘上存在,否则执行就会报 FileNotFoundException。如果传入的是一个目录,则会报 IOException 异常。
介绍一下 flush
因为内置缓冲区的原因,如果 FileWriter 不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要 flush()
方法。
flush()
:刷新缓冲区,流对象可以继续使用。close()
:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用。
注意:即便是 flush()
方法写出了数据,操作的最后还是要调用 close()
方法,释放系统资源。
// 为了可读性,这里不使用 try-catch-finally,而是使用 throw
// 使用文件名称创建流对象
FileWriter fw = new FileWriter("fw.txt");
// 写出数据,通过flush
fw.write('刷'); // 写出第1个字符
fw.flush();
fw.write('新'); // 继续写出第2个字符,写出成功
fw.flush();
// 写出数据,通过close
fw.write('关'); // 写出第1个字符
fw.close();
fw.write('闭'); // 报错,java.io.IOException: Stream closed
fw.close();
节点流 - FileInputStream/FileOutputStream
InputStream 和 OutputStream
java.io.InputStream
抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
public int read()
: 从输入流读取一个字节。返回读取的字节值。虽然读取了一个字节,但是会自动提升为int类型。如果已经到达流末尾,没有数据可读,则返回-1。public int read(byte[] b)
: 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。每次最多读取b.length个字节。返回实际读取的字节个数。如果已经到达流末尾,没有数据可读,则返回-1。public int read(byte[] b,int off,int len)
:从输入流中读取一些字节数,并将它们存储到字节数组 b中,从b[off]开始存储,每次最多读取len个字节 。返回实际读取的字节个数。如果已经到达流末尾,没有数据可读,则返回-1。public void close()
:关闭此输入流并释放与此流相关联的任何系统资源。当完成流的操作时,必须调用此方法,释放系统资源。
java.io.OutputStream
抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。
public void write(int b)
:将指定的字节输出流。虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。public void write(byte[] b)
:将 b.length字节从指定的字节数组写入此输出流。public void write(byte[] b, int off, int len)
:从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。public void flush()
:刷新此输出流并强制任何缓冲的输出字节被写出。public void close()
:关闭此输出流并释放与此流相关联的任何系统资源。当完成流的操作时,必须调用此方法,释放系统资源。
FileInputStream 和 FileOutputStream
java.io.FileInputStream
类是文件输入流,从文件中读取字节。
FileInputStream(File file)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。FileInputStream(String name)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
// 使用文件名称创建流对象.
FileInputStream fis = new FileInputStream("read.txt"); // 文件内容为 abcde
// 定义变量,作为有效个数
int len;
// 定义字节数组,作为装字节数据的容器
byte[] b = new byte[2];
// 循环读取
while (( len= fis.read(b))!=-1) {
// 每次读取后,把数组的有效字节部分,变成字符串打印
System.out.println(new String(b, 0, len));// len 每次读取的有效字节个数
// 如果使用 `System.out.println(new String(b));`,最后一次读取只读取一个字节 e,
// 而数组中上次读取的数据没有被完全替换,因此输出结果为 ed
}
// 关闭资源
fis.close();
java.io.FileOutputStream
类是文件输出流,用于将数据写出到文件。
public FileOutputStream(File file)
:创建文件输出流,写出由指定的 File对象表示的文件。public FileOutputStream(String name)
: 创建文件输出流,指定的名称为写出文件。public FileOutputStream(File file, boolean append)
:创建文件输出流,指明是否在现有文件末尾追加内容。
缓冲流
介绍一下缓冲流
- 为了提高数据读写的速度,Java API 提供了带缓冲功能的流类:缓冲流。
- 缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:
- 字节缓冲流:
BufferedInputStream
,BufferedOutputStream
- 字符缓冲流:
BufferedReader
,BufferedWriter
- 字节缓冲流:
- 缓冲流的基本原理:在创建流对象时,内部会创建一个缓冲区数组(缺省使用
8192个字节(8Kb)
的缓冲区),通过缓冲区读写,减少系统 IO 次数,从而提高读写的效率。
构造器:
public BufferedInputStream(InputStream in)
:创建一个新的字节型的缓冲输入流。public BufferedOutputStream(OutputStream out)
: 创建一个新的字节型的缓冲输出流。public BufferedReader(Reader in)
:创建一个 新的字符型的缓冲输入流。public BufferedWriter(Writer out)
: 创建一个新的字符型的缓冲输出流。
特有方法:
- BufferedReader:
public String readLine()
: 读一行文字。 - BufferedWriter:
public void newLine()
: 写一行行分隔符, 由系统属性定义符号。
说明:
- 涉及到嵌套的多个流时,如果都显式关闭的话,需要先关闭外层的流,再关闭内层的流。
- 其实在开发中,只需要关闭最外层的流即可,因为在关闭外层流时,内层的流也会被关闭。
转换流
介绍一下转换流
如果使用 FileReader
读取项目中的文本文件与解析文件的编码不一致就会产生乱码情况;使用一个字节流对包含中文的文本数据数据读入,将数据显示在控制台上时可能会出现乱码。这时可以使用转换流。转换流是字节与字符间的桥梁。
InputStreamReader 和 OutputStreamWriter
InputStreamReader
- 转换流
java.io.InputStreamReader
,是 Reader 的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。 - 构造器
InputStreamReader(InputStream in)
: 创建一个使用默认字符集的字符流。InputStreamReader(InputStream in, String charsetName)
: 创建一个指定字符集的字符流。
OutputStreamWriter
- 转换流
java.io.OutputStreamWriter
,是 Writer 的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。 - 构造器
OutputStreamWriter(OutputStream in)
: 创建一个使用默认字符集的字符流。OutputStreamWriter(OutputStream in,String charsetName)
: 创建一个指定字符集的字符流。
数据流、对象流
数据流和对象流的作用
Java 提供了数据流和对象流来将内存中定义的变量(包括基本数据类型或引用数据类型)保存在文件中。
介绍一下数据流
通过 DataOutputStream、DataInputStream 对象构建数据流。
- DataOutputStream:允许应用程序将基本数据类型、String类型的变量写入输出流中
- DataInputStream:允许应用程序以与机器无关的方式从底层输入流中读取基本数据类型、String类型的变量。
方法:
// read... 是 DataInputStream 中的方法
// 改为 write... 就是 DataOutputStream 中的方法
byte readByte()
short readShort()
int readInt()
long readLong()
float readFloat()
double readDouble()
char readChar()
boolean readBoolean()
String readUTF()
void readFully(byte[] b)
数据流的弊端:只支持 Java 基本数据类型和字符串的读写,而对象流支持 Java 基本数据类型和对象的读写。
介绍一下对象流
通过 ObjectOutputStream、ObjectInputStream 对象构建数据流。
- ObjectOutputStream:将 Java 基本数据类型和对象写入字节输出流中。通过在流中使用文件可以实现 Java 各种基本数据类型的数据以及对象的持久存储。
- ObjectInputStream:ObjectInputStream 对以前使用 ObjectOutputStream 写出的基本数据类型的数据和对象进行读入操作,保存在内存中。
构造器:
public ObjectOutputStream(OutputStream out)
: 创建一个指定的ObjectOutputStream。
public ObjectInputStream(InputStream in)
: 创建一个指定的ObjectInputStream。
ObjectOutputStream 中的方法:
public void writeBoolean(boolean val)
:写出一个 boolean 值。public void writeByte(int val)
:写出一个 8 位字节public void writeShort(int val)
:写出一个 16 位的 short 值public void writeChar(int val)
:写出一个16 位的 char 值public void writeInt(int val)
:写出一个 32 位的 int 值public void writeLong(long val)
:写出一个 64 位的 long 值public void writeFloat(float val)
:写出一个 32 位的 float 值。public void writeDouble(double val)
:写出一个 64 位的 double 值public void writeUTF(String str)
:将表示长度信息的两个字节写入输出流,后跟字符串 s 中每个字符的 UTF-8 修改版表示形式。根据字符的值,将字符串 s 中每个字符转换成一个字节、两个字节或三个字节的字节组。注意,将 String 作为基本数据写入流中与将它作为 Object 写入流中明显不同。 如果 s 为 null,则抛出 NullPointerException。public void writeObject(Object obj)
:写出一个obj对象public void close()
:关闭此输出流并释放与此流相关联的任何系统资源
ObjectInputStream中的方法:
public boolean readBoolean()
:读取一个 boolean 值public byte readByte()
:读取一个 8 位的字节public short readShort()
:读取一个 16 位的 short 值public char readChar()
:读取一个 16 位的 char 值public int readInt()
:读取一个 32 位的 int 值public long readLong()
:读取一个 64 位的 long 值public float readFloat()
:读取一个 32 位的 float 值public double readDouble()
:读取一个 64 位的 double 值public String readUTF()
:读取 UTF-8 修改版格式的 Stringpublic void readObject(Object obj)
:读入一个obj对象public void close()
:关闭此输入流并释放与此流相关联的任何系统资源
序列化
什么是序列化和反序列化?
对象序列化机制允许把内存中的 Java 对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的 Java 对象。
- 序列化: 将对象转换成二进制字节流的过程,该字节序列包含该对象的类型和对象中存储的属性等信息。用 ObjectOutputStream 类保存基本类型数据或对象的机制,方法为
public final void writeObject (Object obj)
。 - 反序列化:将在序列化过程中所生成的二进制字节流转换成者对象的过程。用 ObjectInputStream 类读取基本类型数据或对象的机制。方法为
public final Object readObject ()
。
序列化是 RMI(Remote Method Invoke,远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础。
下面是序列化和反序列化常见应用场景:
- 对象在进行网络传输(比如远程方法调用 RPC 的时候)之前需要先被序列化,接收到序列化的对象之后需要再进行反序列化;
- 将对象存储到文件之前需要进行序列化,将对象从文件中读取出来需要进行反序列化;
- 将对象存储到数据库(如 Redis)之前需要用到序列化,将对象从缓存数据库中读取出来需要反序列化;
- 将对象存储到内存之前需要进行序列化,从内存中读取出来之后需要进行反序列化。
序列化的主要目的是通过网络传输对象或将对象存储到文件系统、数据库、内存中。
如何实现序列化
如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实 java.io.Serializable
接口。Serializable
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出 NotSerializableException
。
- 如果对象的某个属性也是引用数据类型,那么如果该属性也要序列化的话,也要实现
Serializable
接口。 - 如果不需要可序列化某属性,则该属性必须注明是瞬态的,使用
transient
关键字修饰。 - 静态变量的值不会序列化。因为静态变量的值不属于某个对象。(
serialVersionUID
除外)
// 序列化
// 可以通过 list 序列化多个对象
ArrayList<Employee> list = new ArrayList<>();
list.add(new Employee("张三", "宏福苑", 23));
list.add(new Employee("李四", "白庙", 24));
list.add(new Employee("王五", "平西府", 25));
// 创建序列化流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("employees.dat"));
// 写出对象
oos.writeObject(list);
// 释放资源
oos.close();
// 反序列化
// 创建反序列化流
FileInputStream fis = new FileInputStream("employees.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
// 读取一个对象
ArrayList<Employee> list = (ArrayList<Employee>) ois.readObject();
// 释放资源
ois.close();
fis.close();
System.out.println(list);
反序列化过程中常见的问题有哪些?
- JVM 反序列化对象必须要能够找到该类的 class 文件,如果找不到就抛出
ClassNotFoundException
异常。 - JVM 反序列化对象时能找到 class 文件,但是 class 文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个
InvalidClassException
异常。发生这个异常的原因可能如下:- 该类的序列版本号与从流中读取的类描述符的版本号不匹配
- 该类包含未知数据类型
解决办法:
Serializable
接口给需要序列化的类,提供了一个序列版本号serialVersionUID
。凡是实现Serializable
接口的类都应该有一个表示序列化版本标识符的静态变量。
serialVersionUID 有什么作用?
serialVersionUID
用来表明类的不同版本间的兼容性。简单来说,Java 的序列化机制是通过在运行时判断类的serialVersionUID
来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的serialVersionUID
与本地相应实体类的serialVersionUID
进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException
)。- 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,
serialVersionUID
可能发生变化。因此,建议显式声明。 - 如果声明了
serialVersionUID
,即使在序列化完成之后修改了类导致类重新编译,则原来的数据也能正常反序列化,只是新增的字段值是默认值而已。
序列化协议对应于 TCP/IP 四层模型的哪一层?
网络通信的双方必须要采用和遵守相同的协议。TCP/IP 四层模型包括应用层、传输层、网络层、网络接口层。
如上图所示,OSI 七层协议模型中,表示层(即 TCP/IP 四层模型中的应用层)。做的事情主要就是对应用层的用户数据进行处理转换为二进制流。反过来的话,就是将二进制流转换成应用层的用户数据,这就对应的是序列化和反序列化。
常见序列化协议有哪些?
JDK 自带的序列化方式一般不会用 ,因为序列化效率低并且存在安全问题。比较常用的序列化协议有 Hessian、Kryo、Protobuf、ProtoStuff,这些都是基于二进制的序列化协议。
像 JSON 和 XML 这种属于文本类序列化方式。虽然可读性比较好,但是性能较差,一般不会选择。
JDK 自带的序列化方式
如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现java.io.Serializable
接口。Serializable
是一个标记接口
,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
。
- 如果对象的某个属性也是引用数据类型,那么如果该属性也要序列化的话,也要实现
Serializable
接口 - 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient
关键字修饰。 - 静态变量的值不会序列化。因为静态变量的值不属于某个对象。
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@ToString
public class RpcRequest implements Serializable {
private static final long serialVersionUID = 1905122041950251207L;
private String requestId;
private String interfaceName;
private String methodName;
private Object[] parameters;
private Class<?>[] paramTypes;
private RpcMessageTypeEnum rpcMessageTypeEnum;
}
如果不想对一些字段序列化怎么办?
对于不想进行序列化的变量,使用 transient
关键字修饰,它阻止实例中那些用此关键字修饰的变量序列化;当对象被反序列化时,被 transient
修饰的变量值不会被持久化和恢复。
关于 transient
还有几点注意:
transient
只能修饰变量,不能修饰类和方法。transient
修饰的变量,在反序列化后变量值将会被置成类型的默认值。static
变量因为不属于任何对象(Object),所以无论有没有transient
关键字修饰,均不会被序列化。
为什么不推荐使用 JDK 自带的序列化?
我们很少或者说几乎不会直接使用 JDK 自带的序列化方式,主要原因有下面这些原因:
- 不支持跨语言调用 : 如果调用的是其他语言开发的服务的时候就不支持了。
- 性能差 :相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。
- 存在安全问题 :序列化和反序列化本身并不存在问题。但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。
其它流
介绍一下标准输入流、输出流
System.in
和System.out
分别代表了系统标准的输入和输出设备- 默认输入设备是:键盘,输出设备是:显示器
System.in
的类型是InputStream
System.out
的类型是PrintStream
,其是OutputStream
的子类FilterOutputStream
的子类- 重定向:通过
System
类的setIn
,setOut
方法对默认设备进行改变public static void setIn(InputStream in)
public static void setOut(PrintStream out)
介绍一下打印流
- 实现将基本数据类型的数据格式转化为字符串输出。
- 打印流:
PrintStream
和PrintWriter
- 提供了一系列重载的 print() 和 println()方法,用于多种数据类型的输出
- PrintStream 和 PrintWriter 的输出不会抛出 IOException 异常
- PrintStream 和 PrintWriter 有自动 flush 功能
- PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
- System.out 返回的是 PrintStream 的实例
构造器:
PrintStream(File file)
:创建具有指定文件且不带自动行刷新的新打印流。PrintStream(File file, String csn)
:创建具有指定文件名称和字符集且不带自动行刷新的新打印流。PrintStream(OutputStream out)
:创建新的打印流。PrintStream(OutputStream out, boolean autoFlush)
:创建新的打印流。 autoFlush如果为 true,则每当写入 byte 数组、调用其中一个 println 方法或写入换行符或字节 ('\n') 时都会刷新输出缓冲区。PrintStream(OutputStream out, boolean autoFlush, String encoding)
:创建新的打印流。PrintStream(String fileName)
:创建具有指定文件名称且不带自动行刷新的新打印流。PrintStream(String fileName, String csn)
:创建具有指定文件名称和字符集且不带自动行刷新的新打印流。
介绍一下 Scanner 类
构造方法:
- Scanner(File source) :构造一个新的 Scanner,它生成的值是从指定文件扫描的。
- Scanner(File source, String charsetName) :构造一个新的 Scanner,它生成的值是从指定文件扫描的。
- Scanner(InputStream source) :构造一个新的 Scanner,它生成的值是从指定的输入流扫描的。
- Scanner(InputStream source, String charsetName) :构造一个新的 Scanner,它生成的值是从指定的输入流扫描的。
常用方法:
- boolean hasNextXxx(): 如果通过使用nextXxx()方法,此扫描器输入信息中的下一个标记可以解释为默认基数中的一个 Xxx 值,则返回 true。
- Xxx nextXxx(): 将输入信息的下一个标记扫描为一个Xxx
转载自:https://juejin.cn/post/7384303275376672779