1.I/O流是什么
Java的I/O流是实现编程语言的输入/输出的基础能力,操作的对象有外部设备、存储设备、网络连接等等,是所有服务器端的编程语言都应该具备的基础能力。
I = Input(输入),输入是相对程序而言,既程序从外部设备、存储设备或网络连接中读取数据;
O = Output(输出),输出也是相对程序而言,既程序写入数据到外部设备、存储设备或网络连接;
“流”(stream)是一个抽象、动态的概念,是一连串连续动态的数据集合,是一连串的1和0。
在Java编程语言中,运行程序存放在JVM中,而Java 虚拟机运行在内存上,所以程序是装载到内存里,所以常见的几种IO情况有:
2.File 类
为什么在学习IO流之前需要File类呢?
在操作系统中无非就两样东西:文件和目录(文件夹)
在Java中就是通过File 类来操作文件和目录,File类可以新建、删除、重命名文件和目录,但不能访问文件内容本身。如果要访问文件内容本身,则需要通过IO流来完成。
所以在学习IO流操作磁盘文件数据之前,先要学会如何操作文件和目录。
2.1.操作文件和目录
目录或文件:检测-》新建-》访问-》重命名-》删除。
目录操作
File file = new File("D:/test");
// 1.检测目录是否存在
boolean exists = file.exists();
System.out.println("目录是否存在:"+exists);
// 2.不存在就新增目录
if (!exists){
boolean mkdir = file.mkdir();
System.out.println("add: "+mkdir);
}
// 3.目录访问
String absolutePath = file.getAbsolutePath();
System.out.println("目录在哪:"+absolutePath);
// 4.目录存在就修改:等价于删除后,再新建
boolean exists1 = file.exists();
File file1 = new File("D:/test1");
if (exists1){
boolean renameTo = file.renameTo(file1);
System.out.println("update: "+renameTo);
}
// 5.目录删除
boolean exists2 = file1.exists();
if (exists2){
boolean delete = file1.delete();
System.out.println("delete: "+delete);
}
文件操作
错误示范:新建文件。未确定目录是否存在就创建文件,报错:java.io.IOException: 系统找不到指定的路径。
File file = new File("D:/test/test.txt");
if (!file.exists()){
boolean newFile = file.createNewFile();
System.out.println(newFile);
}
正确示范:新建文件。
File file = new File("D:/test/test.txt");
// 判断是否需要新建目录,代码可以直接写成一行,方便理解就不那么写
if (!file.isDirectory()){
// 获得文件的目录,对于多级目录可以使用mkdirs()方法创建目录
File parentFile = file.getParentFile();
boolean mkdir = parentFile.mkdir();
System.out.println("目录不存在,先新建目录:"+mkdir);
}
if (!file.exists()){
boolean newFile = file.createNewFile();
System.out.println(newFile);
}
访问文件、修改文件名和删除文件
// 访问文件
String path = file.getPath();
String name = file.getName();
System.out.println("文件路径和名称:"+path+",文件名:"+name);
// 修改文件名:获得原文件目录,再修改文件名。File.separator由系统指定路径分隔(linux 和window分隔符不一样)
String newName ="newTest"+name.substring(name.lastIndexOf(".")); // 新名+后缀
File file1 = new File(file.getParentFile() +File.separator+ newName);
boolean renameTo = file.renameTo(file1);
System.out.println("update: "+renameTo);
// 删除文件
boolean delete = file1.delete();
System.out.println("delete:"+file1.exists());
关于File 类的更多操作就不一一展开了,难度不大可自行学习:
3.I/O流分类
按数据流的方向不同分类:输入流,输出流按处理数据单位不同分类:字节流,字符流
-
(1)字节流:数据流中最小的数据单元是字节,基类为InputStream和OutputStream;
-
(2)字符流:数据流中最小的数据单元是字符,基类为Reader和Writer。
不同字符编码的同一个字符占用的字节数不同。
ASCII:1个字符1个字节(8个二进制位)
GBK :1个中文字符2个字节(16个二进制位)
UTF-8:1个中文字符3个字节(24个二进制位)
字符流命名规则:XxxxReader 或 XxxxWriter
字节流命名规则:XxxxInputStream、XxxxOutputStream 或XxxxStream
按流的角色不同分类:节点流,处理流
- (1)节点流是可以从一个特定的数据源(如内存、磁盘、网络等)中读取或写入数据的流。
- (2)处理流是对一个已存在的流的连接和封装,通过调用封装后的流的功能来实现数据的读写。
3.字节和字符
字符流和字节流的历史渊源:先有字节流,再有字符流。字节流符合机器的操作,但字符流更符合人类的正常使用习惯。字符流的操作最终还是转换为字节流来完成。
感受下int数据变成字符的案例:int>16进制字符串>字节数组>字符串
/**
* 整型转换成汉字:15120522 ==utf-8==> 渊
* 一个utf-8的汉字,占用3个字节
*/
String string = Integer.toHexString(15120522); //"E6B88A"
byte[] bytes = new byte[string.length() / 2];
for(int i = 0; i < bytes.length; i ++){
byte high = Byte.parseByte(string.substring(i * 2, i * 2 + 1), 16);
byte low = Byte.parseByte(string.substring(i * 2 + 1, i * 2 + 2), 16);
bytes[i] = (byte) (high << 4 | low);
}
String result = new String(bytes, "utf-8");
System.out.println("15120522 ==utf-8==>"+result);
执行结果:
15120522 ==utf-8==>渊
一个utf-8的汉字,占用3个字节。整数(15120522)转化成16进制 (E6B88A),每两位16进制(既是8位二进制)代表一个字节,这3个字节(E6B88A)在utf-8编码下表示汉字“渊”。
I/O流具体使用
按流的角色分类(节点流>>>处理流)来讲解Java的IO流。
4.文件节点流
对磁盘文件的内容进行读写,Java程序和磁盘文件的沟通桥梁。
字节流:FileOutputStream 和 FileInputStream
字符流:FileReader 和 FileWriter
4.1.字节流--文件
FileOutputStream 和 FileInputStream
File file = new File("D:/test.txt");
// 写
FileOutputStream fout = new FileOutputStream(file);
fout.write(65);//ASCII编码:65=A
fout.write("ZZZ".getBytes());//
fout.write("渊".getBytes());
fout.close();// 关流
// 读
FileInputStream fin = new FileInputStream(file);
byte[] bytes2 = new byte[10]; // 一次读10个字节
fin.read(bytes2); //将数据读取到bytes2
fin.close();// 关流
System.out.println(new String(bytes2));
FileOutputStream
构造方法和描述 |
---|
FileOutputStream(File file) 创建文件输出流以写入由指定的 File 对象表示的文件。 |
FileOutputStream(File file, boolean append) 创建文件输出流以写入由指定的 File 对象表示的文件。 |
FileOutputStream(FileDescriptor fdObj) 创建文件输出流以写入指定的文件描述符,表示与文件系统中实际文件的现有连接。 |
FileOutputStream(String name) 创建文件输出流以指定的名称写入文件。 |
FileOutputStream(String name, boolean append) 创建文件输出流以指定的名称写入文件。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
close() 关闭此文件输出流并释放与此流相关联的任何系统资源。 |
protected void |
finalize() 清理与文件的连接,并确保当没有更多的引用此流时,将调用此文件输出流的 close 方法。 |
FileChannel |
getChannel() 返回与此文件输出流相关联的唯一的FileChannel 对象。 |
FileDescriptor |
getFD() 返回与此流相关联的文件描述符。 |
void |
write(byte[] b) 将 b.length 个字节从指定的字节数组写入此文件输出流。 |
void |
write(byte[] b, int off, int len) 将 len 字节从位于偏移量 off 的指定字节数组写入此文件输出流。 |
void |
write(int b) 将指定的字节写入此文件输出流。 |
FileInputStream
构造方法和描述 |
---|
FileInputStream(File file) 通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File 对象 file 命名。 |
FileInputStream(FileDescriptor fdObj) 创建 FileInputStream 通过使用文件描述符 fdObj ,其表示在文件系统中的现有连接到一个实际的文件。 |
FileInputStream(String name) 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name 命名。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
int |
read() 从该输入流读取一个字节的数据。 |
int |
read(byte[] b) 从该输入流读取最多 b.length 个字节的数据为字节数组。 |
int |
read(byte[] b, int off, int len) 从该输入流读取最多 len 字节的数据为字节数组。 |
long |
skip(long n) 跳过并从输入流中丢弃 n 字节的数据。 |
int |
available() 返回从此输入流中可以读取(或跳过)的剩余字节数的估计值,而不会被下一次调用此输入流的方法阻塞。 |
void |
close() 关闭此文件输入流并释放与流相关联的任何系统资源。 |
protected void |
finalize() 确保当这个文件输入流的 close 方法没有更多的引用时被调用。 |
FileChannel |
getChannel() 返回与此文件输入流相关联的唯一的FileChannel 对象。 |
FileDescriptor |
getFD() 返回表示与此 FileInputStream 正在使用的文件系统中实际文件的连接的 FileDescriptor 对象。 |
4.2.字符流--文件
FileReader 和 FileWriter
对于文本的读写,很明显字符流更适合人类的语言的读写操作。
File file = new File("D:/test.txt");
// 写
FileWriter fileWriter = new FileWriter(file);
fileWriter.write("渊渟岳");
fileWriter.close();
FileReader fileReader = new FileReader(file);
// 如果读取的文件内容很少,甚至知道长度的时候
//char[] c = new char[10];
//fileReader.read(c);
//System.out.println(String.valueOf(c));
// 如果读取的文件内容很多可以循环读取
int len = -1; //用于接收每次读取数据的长度
char[] chars = new char[2];
while ((len=fileReader.read(chars))!=-1){
System.out.println(new String(chars,0,len));
}
fileWriter.close();
FileWriter
构造方法和描述 |
---|
FileWriter(File file) 给一个File对象构造一个FileWriter对象。 |
FileWriter(File file, boolean append) 给一个File对象构造一个FileWriter对象。 |
FileWriter(FileDescriptor fd) 构造与文件描述符关联的FileWriter对象。 |
FileWriter(String fileName) 构造一个给定文件名的FileWriter对象。 |
FileWriter(String fileName, boolean append) 构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
write(int c) 写一个字符 |
void |
write(char[] cbuf) 写入一个字符数组。 |
void |
write(char[] cbuf, int off, int len) 写入字符数组的一部分。 |
void |
write(String str) 写一个字符串 |
void |
write(String str) 写一个字符串 |
void |
close() 关闭流,先刷新。 |
void |
flush() 刷新流。 |
String |
getEncoding() 返回此流使用的字符编码的名称。 |
Writer |
append(char c) 将指定的字符附加到此作者。 |
Writer |
append(CharSequence csq) 将指定的字符序列附加到此作者。 |
Writer |
append(CharSequence csq, int start, int end) 将指定字符序列的子序列附加到此作者。 |
FileReader
构造方法和描述 |
---|
FileReader(File file) 创建一个新的 FileReader ,给出 File 读取。 |
FileReader(FileDescriptor fd) 创建一个新的 FileReader ,给定 FileDescriptor 读取。 |
FileReader(String fileName) 创建一个新的 FileReader ,给定要读取的文件的名称。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
close() 关闭流并释放与之相关联的任何系统资源。 |
String |
getEncoding() 返回此流使用的字符编码的名称。 |
int |
read() 读一个字符 |
int |
read(char[] cbuf) 将字符读入数组。 |
int |
read(char[] cbuf, int offset, int length) 将字符读入数组的一部分。 |
int |
read(CharBuffer target) 尝试将字符读入指定的字符缓冲区。 |
boolean |
ready() 告诉这个流是否准备好被读取。 |
void |
reset() 重置流。 |
long |
skip(long n) 跳过字符 |
void |
mark(int readAheadLimit) 标记流中的当前位置。 |
boolean |
markSupported() 告诉这个流是否支持mark()操作。 |
5.数组节点流
内存操作流的一种,对内存中的数组进行读写(对应的不是文件,而是内存中的数组)。Java程序通过数组节点流,将数据以byte(字节)或char(字符)数组的形式缓存到内存中。
字节流:ByteArrayOutputStream 和 ByteArrayInputStream
字符流:CharArrayWriter 和 CharArrayReader
5.1.字节流--数组
ByteArrayOutputStream 和 ByteArrayInputStream
// 默认byte数组大小为32
ByteArrayOutputStream baout = new ByteArrayOutputStream();
baout.write("渊渟岳".getBytes());
baout.close();// 关闭无效果
// 获取数据类型一
byte[] bytes = baout.toByteArray();// 获取字节数组数据
System.out.println(new String(bytes));
// 获取数据类型二
String string = baout.toString();//使用平台的默认字符集将缓冲区内容转换为字符串解码字节
System.out.println(string);
// 读取byte数组数据
byte[] bytes1 = new byte[50];
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
byteArrayInputStream.read(bytes1);//读取数据到新的字节数组
System.out.println(bytes1.length+"|"+new String(bytes1));
ByteArrayOutputStream
在创建ByteArrayOutputStream类实例时,内存中会创建一个byte数组类型的缓冲区,缓冲区会随着数据的不断写入而自动增长。
构造方法和描述 |
---|
ByteArrayOutputStream() 创建一个新的字节数组输出流。 |
ByteArrayOutputStream(int size) 创建一个新的字节数组输出流,具有指定大小的缓冲区容量(以字节为单位)。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
close() 关闭 ByteArrayOutputStream 没有任何效果。 |
void |
reset() 将此字节数组输出流的 count 字段重置为零,以便丢弃输出流中当前累积的所有输出。 |
int |
size() 返回缓冲区的当前大小。 |
byte[] |
toByteArray() 创建一个新分配的字节数组。 |
String |
toString() 使用平台的默认字符集将缓冲区内容转换为字符串解码字节。 |
String |
toString(int hibyte) 已弃用此方法无法将字节正确转换为字符。从JDK 1.1的,要做到这一点的优选方法是通过toString(String enc) 方法,它需要一个编码名称参数,或toString() 方法,它使用平台的默认字符编码。 |
String |
toString(String charsetName) 通过使用命名的charset 解码字节,将缓冲区的内容转换为字符串。 |
void |
write(byte[] b, int off, int len) 从指定的字节数组写入 len 字节,从偏移量为 off 开始,输出到这个字节数组输出流。 |
void |
write(int b) 将指定的字节写入此字节数组输出流。 |
void |
writeTo(OutputStream out) 将此字节数组输出流的完整内容写入指定的输出流参数,就像使用 out.write(buf, 0, count) 调用输出流的写入方法 out.write(buf, 0, count) 。 |
ByteArrayInputStream
构造方法和描述 |
---|
ByteArrayInputStream(byte[] buf) 创建一个 ByteArrayInputStream ,使其使用 buf 作为其缓冲区数组。 |
ByteArrayInputStream(byte[] buf, int offset, int length) 创建 ByteArrayInputStream 使用 buf 作为其缓冲器阵列。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
int |
available() 返回可从此输入流读取(或跳过)的剩余字节数。 |
void |
close() 关闭 ByteArrayInputStream 没有任何效果。 |
void |
mark(int readAheadLimit) 设置流中当前标记的位置。 |
boolean |
markSupported() 测试 InputStream 是否支持标记/复位。 |
int |
read() 从该输入流读取下一个数据字节。 |
int |
read(byte[] b, int off, int len) 将 len 字节的数据读入此输入流中的字节数组。 |
void |
reset() 将缓冲区重置为标记位置。 |
long |
skip(long n) 从此输入流跳过 n 个字节的输入。 |
5.2.字符流--数组
CharArrayWriter 和 CharArrayReader
// 默认char数组大小为32
CharArrayWriter charArrayWriter = new CharArrayWriter();
charArrayWriter.write("渊渟岳");
charArrayWriter.close();// 关闭无效果
// 获取数据类型一
char[] chars = charArrayWriter.toCharArray();// 获取字符数组数据
System.out.println(String.valueOf(chars));
// 获取数据类型二
String string = charArrayWriter.toString();
System.out.println(string);
// 读取char数组数据
char[] chars1 = new char[50];
CharArrayReader charArrayReader = new CharArrayReader(chars);
charArrayReader.read(chars1);//读取数据到新的字符数组
System.out.println(chars1.length+"|"+new String(chars1));
CharArrayWriter
构造方法和描述 |
---|
CharArrayWriter() 创建一个新的CharArrayWriter。 |
CharArrayWriter(int initialSize) 用指定的初始大小创建一个新的CharArrayWriter。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
CharArrayWriter |
append(char c) 将指定的字符附加到此作者。 |
CharArrayWriter |
append(CharSequence csq) 将指定的字符序列附加到此作者。 |
CharArrayWriter |
append(CharSequence csq, int start, int end) 将指定字符序列的子序列附加到此作者。 |
void |
close() 关闭流。 |
void |
flush() 冲洗流。 |
void |
reset() 重置缓冲区,以便您可以再次使用它,而不会丢弃已经分配的缓冲区。 |
int |
size() 返回缓冲区的当前大小。 |
char[] |
toCharArray() 返回输入数据的副本。 |
String |
toString() 将输入数据转换为字符串。 |
void |
write(char[] c, int off, int len) 将字符写入缓冲区。 |
void |
write(int c) 将一个字符写入缓冲区。 |
void |
write(String str, int off, int len) 将一部分字符串写入缓冲区。 |
void |
writeTo(Writer out) 将缓冲区的内容写入另一个字符流。 |
CharArrayReader
构造方法和描述 |
---|
CharArrayReader(char[] buf) 从指定的字符数组中创建CharArrayReader。 |
CharArrayReader(char[] buf, int offset, int length) 从指定的字符数组中创建CharArrayReader。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
close() 关闭流并释放与之相关联的任何系统资源。 |
void |
mark(int readAheadLimit) 标记流中的当前位置。 |
boolean |
markSupported() 告诉这个流是否支持mark()操作。 |
int |
read() 读一个字符 |
int |
read(char[] b, int off, int len) 将字符读入数组的一部分。 |
boolean |
ready() 告诉这个流是否准备好被读取。 |
void |
reset() 将流重新设置为最近的标记,或将其重新设置为从未被标记的开始。 |
long |
skip(long n) 跳过字符 |
6.字符串节点流
内存操作流的一种,对内存中的字符串进行读写。Java程序通过字符串节点流,将数据以StringBuffer的形式缓存到内存中。
字节流:无
字符流:StringWriter 和 StringReader
6.1.字符流--字符串
StringWriter 和 StringReader
StringWriter stringWriter = new StringWriter();
stringWriter.write("渊渟岳");
stringWriter.close();
// 获取字符串缓冲区本身
StringBuffer buffer = stringWriter.getBuffer();
System.out.println(String.valueOf(buffer));
// 获取缓冲区的当前值的字符串
String string = stringWriter.toString();// 等价于:buffer.toString()
System.out.println(string);
//读取内存中的字符串数据
char[] chars = new char[50];
StringReader stringReader = new StringReader(String.valueOf(buffer));
stringReader.read(chars);
System.out.println(chars.length+"|"+String.valueOf(chars));
StringWriter
构造方法和描述 |
---|
StringWriter() 使用默认的初始字符串缓冲区大小创建一个新的字符串写入程序。 |
StringWriter(int initialSize) 使用指定的初始字符串缓冲区大小创建一个新的字符串写入器。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
StringWriter |
append(char c) 将指定的字符附加到此作者。 |
StringWriter |
append(CharSequence csq) 将指定的字符序列附加到此作者。 |
StringWriter |
append(CharSequence csq, int start, int end) 将指定字符序列的子序列附加到此作者。 |
void |
close() 关闭 StringWriter 没有任何效果。 |
void |
flush() 冲洗流。 |
StringBuffer |
getBuffer() 返回字符串缓冲区本身。 |
String |
toString() 以缓冲区的当前值作为字符串返回。 |
void |
write(char[] cbuf, int off, int len) 写一个字符数组的一部分。 |
void |
write(int c) 写一个字符 |
void |
write(String str) 写一个字符串 |
void |
write(String str, int off, int len) 写一个字符串的一部分 |
StringReader
构造方法和描述 |
---|
StringReader(String s) 创建一个新的字符串阅读器。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
close() 关闭流并释放与之相关联的任何系统资源。 |
void |
mark(int readAheadLimit) 标记流中的当前位置。 |
boolean |
markSupported() 告诉这个流是否支持mark()操作。 |
int |
read() 读一个字符 |
int |
read(char[] cbuf, int off, int len) 将字符读入数组的一部分。 |
boolean |
ready() 告诉这个流是否准备好被读取。 |
void |
reset() 将流重新设置为最近的标记,如果从未被标记,则将其重置到字符串的开头。 |
long |
skip(long ns) 跳过流中指定数量的字符。 |
7.管道节点流
主要用于线程之间的数据通讯。
字节流:PipedInputStream 和 PipedOutputStream
字符流:PipedReader 和 PipedWriter
7.1.字节流--管道
PipedInputStream 和 PipedOutputStream
管道输入流线程类
public class PipedInputThread implements Runnable{
private PipedInputStream pipedInputStream = new PipedInputStream();
public PipedInputStream getPipedInputStream() {
return pipedInputStream;
}
@Override
public void run() {
byte[] buf = new byte[1024];
try {
int len = pipedInputStream.read(buf);
System.out.println(new String(buf, 0, len));
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
pipedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
测试:主线程充当管道输出线程类
public static void main(String[] args) throws IOException {
PipedOutputStream pipedOutputStream = new PipedOutputStream();
// 管道输入流线程类
PipedInputThread pipedInputThread = new PipedInputThread();
// 管道连接:接水管
pipedOutputStream.connect(pipedInputThread.getPipedInputStream());
/**
* 管道的读写操作是互相阻塞的,当缓冲区为空时,读操作阻塞;当缓冲区满时,写操作阻塞
* 管道流的读写线程开启无先后顺序
*/
new Thread(pipedInputThread).start();
pipedOutputStream.write("渊渟岳".getBytes());
pipedOutputStream.close();
}
PipedOutputStream
管道输出流可以连接到管道输入流以创建通信管道。管道输出流是管道的发送端,既“写”。
构造方法和描述 |
---|
PipedOutputStream() 创建一个尚未连接到管道输入流的管道输出流。 |
PipedOutputStream(PipedInputStream snk) 创建连接到指定管道输入流的管道输出流。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
close() 关闭此管道输出流,并释放与此流相关联的任何系统资源。 |
void |
connect(PipedInputStream snk) 将此管道输出流连接到接收器。 |
void |
flush() 刷新此输出流并强制任何缓冲的输出字节被写出。 |
void |
write(byte[] b, int off, int len) 从指定的字节数组写入 len 个字节,从偏移量 off 开始输出到这个管道输出流。 |
void |
write(int b) 写入指定 byte 到管道输出流。 |
PipedInputStream
管道输入流应连接到管道输出流; 管道输入流读取管道输出流的字节数据,既“读”。
构造方法和描述 |
---|
PipedInputStream() 创建一个 PipedInputStream ,所以它还不是 connected。 |
PipedInputStream(int pipeSize) 创建一个 PipedInputStream ,使其尚未connected,并使用指定的管道大小作为管道的缓冲区。 |
PipedInputStream(PipedOutputStream src) 创建一个 PipedInputStream ,使其连接到管道输出流 src 。 |
PipedInputStream(PipedOutputStream src, int pipeSize) 创建一个 PipedInputStream ,使其连接到管道输出流 src ,并为管道缓冲区使用指定的管道大小。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
int |
available() 返回可以从该输入流读取而不阻塞的字节数。 |
void |
close() 关闭此管道输入流,并释放与流相关联的任何系统资源。 |
void |
connect(PipedOutputStream src) 使此管道输入流连接到管道输出流 src 。 |
int |
read() 从这个管道输入流读取数据的下一个字节。 |
int |
read(byte[] b, int off, int len) 从这个管道输入流读取最多 len 个字节的数据到字节数组。 |
protected void |
receive(int b) 接收一个字节的数据。 |
7.2.字符流--管道
PipedReader 和 PipedWriter
和字节流例子的写法类似
管道输入流线程类
public class PipedReaderThread implements Runnable{
private PipedReader pipedReader = new PipedReader();
public PipedReader getPipedReader() {
return pipedReader;
}
@Override
public void run() {
try {
char[] chars = new char[1024];
int len = pipedReader.read(chars);
System.out.println(new String(chars, 0, len));
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
pipedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
测试:主线程充当管道输出线程类
public static void main(String[] args) throws IOException{
PipedWriter pipedWriter = new PipedWriter();
// 管道输入流线程类
PipedReaderThread pipedReaderThread = new PipedReaderThread();
// 管道连接:接水管
pipedWriter.connect(pipedReaderThread.getPipedReader());
/**
* 管道的读写操作是互相阻塞的,当缓冲区为空时,读操作阻塞;当缓冲区满时,写操作阻塞
* 管道流的读写线程开启无先后顺序
*/
new Thread(pipedReaderThread).start();
pipedWriter.write("渊渟岳");
pipedWriter.close();
}
PipedWriter
构造方法和描述 |
---|
PipedWriter() 创建一个尚未连接到管道读取器的管道写入器。 |
PipedWriter(PipedReader snk) 创建连接到指定管道读取器的管道写入器。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
close() 关闭此管道输出流,并释放与此流相关联的任何系统资源。 |
void |
connect(PipedReader snk) 将此管道写入器连接到接收器。 |
void |
flush() 刷新此输出流并强制任何缓冲的输出字符被写出。 |
void |
write(char[] cbuf, int off, int len) 从指定的字符数组写入 len 字符,从偏移量 off 开始 off 到这个管道输出流。 |
void |
write(int c) 写入指定 char 到管道输出流。 |
PipedReader
构造方法和描述 |
---|
PipedReader() 创建一个 PipedReader ,所以它还不是 connected 。 |
PipedReader(int pipeSize) 创建一个 PipedReader ,使其尚未 connected 并使用指定的管道大小作为管道的缓冲区。 |
PipedReader(PipedWriter src) 创建一个 PipedReader ,使其连接到管道写入器 src 。 |
PipedReader(PipedWriter src, int pipeSize) 创建一个 PipedReader ,使其连接到管道写入器 src ,并使用指定的管道大小作为管道的缓冲区。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
close() 关闭此管道流,并释放与流相关联的任何系统资源。 |
void |
connect(PipedWriter src) 使这个管道读取器连接到管道写入器 src 。 |
int |
read() 从这个管道流读取下一个数据字符。 |
int |
read(char[] cbuf, int off, int len) 从这个管道数据流读取最多 len 字符数组。 |
boolean |
ready() 告诉这个流是否准备好阅读。 |
8.网络节点流
对网络通讯进行数据读写的IO流。
SocketInputStream 和 SocketOutputStream用于Socket编程使用的IO流。
这两个类都是非公共类,只能在同一个package中才可以创建对象。由AbstractPlainSocketImpl类创建了Socket的输入和输出流,源码如下:
protected synchronized InputStream getInputStream() throws IOException {
synchronized (fdLock) {
if (isClosedOrPending())
throw new IOException("Socket Closed");
if (shut_rd)
throw new IOException("Socket input is shutdown");
if (socketInputStream == null)
socketInputStream = new SocketInputStream(this);
}
return socketInputStream;
}
protected synchronized OutputStream getOutputStream() throws IOException {
synchronized (fdLock) {
if (isClosedOrPending())
throw new IOException("Socket Closed");
if (shut_wr)
throw new IOException("Socket output is shutdown");
if (socketOutputStream == null)
socketOutputStream = new SocketOutputStream(this);
}
return socketOutputStream;
}
下面开始学习“处理流”。处理流又称包装流,将已有的节点流包装起来动态地扩展(增强)其功能,这种设计模式称为装饰模式。
9.缓冲流
通过内存数组缓冲数据来提高读写速度,字节使用byte[]缓冲,字符使用char[]缓冲,默认数组长度都是8192,可通过构造方法自定义缓冲区大小。
字节流:BufferedInputStream 和 BufferedOutputStream
字符流:BufferedReader和 BufferedWriter
9.1.字节流--缓冲
BufferedInputStream 和 BufferedOutputStream
File file = new File("D:/test.txt");
// 缓冲默认大小:8192个byte
BufferedOutputStream bufOut = new BufferedOutputStream(new FileOutputStream(file));
bufOut.write("渊渟岳".getBytes());
bufOut.close();
// 缓冲默认大小:8192个byte
BufferedInputStream bufIn = new BufferedInputStream(new FileInputStream(file));
int len = -1; //用于保存每次读取数据的长度
byte[] bytes =new byte[1024]; //每次最大读取1024个字节
//读取数据保存到bytes数组,并判断读到的数据长度是否不等于-1,如果是 -1,表示已经读取到文件的末尾
while ((len=bufIn.read(bytes))!=-1){
//将每次读到的bytes数组,按读到的长度(数组长度)转换成字符串,然后输出
System.out.println(new String(bytes,0,len));
}
bufIn.close();
BufferedOutputStream
构造方法和描述 |
---|
BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。 |
BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
flush() 刷新缓冲输出流。 |
void |
write(byte[] b, int off, int len) 从指定的字节数组写入 len 个字节,从偏移 off 开始到缓冲的输出流。 |
void |
write(int b) 将指定的字节写入缓冲的输出流。 |
BufferedInputStream
构造方法和描述 |
---|
BufferedInputStream(InputStream in) 创建一个 BufferedInputStream 并保存其参数,输入流 in ,供以后使用。 |
BufferedInputStream(InputStream in, int size) 创建 BufferedInputStream 具有指定缓冲区大小,并保存其参数,输入流 in ,供以后使用。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
int |
available() 返回从该输入流中可以读取(或跳过)的字节数的估计值,而不会被下一次调用此输入流的方法阻塞。 |
void |
close() 关闭此输入流并释放与流相关联的任何系统资源。 |
void |
mark(int readlimit) 见的总承包 mark 的方法 InputStream 。 |
boolean |
markSupported() 测试这个输入流是否支持 mark 和 reset 方法。 |
int |
read() 见 read 法 InputStream 的一般合同。 |
int |
read(byte[] b, int off, int len) 从给定的偏移开始,将字节输入流中的字节读入指定的字节数组。 |
void |
reset() 见 reset 法 InputStream 的一般合同。 |
long |
skip(long n) 见 skip 法 InputStream 的一般合同。 |
9.2.字符流--缓冲
BufferedReader和 BufferedWriter
File file = new File("D:/test.txt");
// 缓冲默认大小:8192个char
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
bw.write("渊渟岳");
bw.close();
// 缓冲默认大小:8192个byte
BufferedReader br = new BufferedReader(new FileReader(file));
int len = -1; //用于保存每次读取数据的长度
char[] chars =new char[1024]; //每次最大读取1024个字符
//读取数据保存到chars数组,并判断读到的数据长度是否不等于-1,如果是 -1,表示已经读取到文件的末尾
while ((len=br.read(chars))!=-1){
//将每次读到的chars数组,按读到的长度(数组长度)转换成字符串,然后输出
System.out.println(new String(chars,0,len));
}
br.close();
BufferedReader
构造方法和描述 |
---|
BufferedWriter(Writer out) 创建使用默认大小的输出缓冲区的缓冲字符输出流。 |
BufferedWriter(Writer out, int sz) 创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
close() 关闭流,先刷新。 |
void |
flush() 刷新流。 |
void |
newLine() 写入行的分隔符,不一定是‘\n’,由系统属性决定。 |
void |
write(char[] cbuf, int off, int len) 写入字符数组的一部分。 |
void |
write(int c) 写一个字符 |
void |
write(String s, int off, int len) 写一个字符串的一部分。 |
BufferedWriter
相比其他IO流,BufferedWriter可以每次读取一行字符,并返回字符串对象:【String
readLine()
】,对于需要一行一行的读取数据的操作,使用这个方法无疑是最明智的。
构造方法和描述 |
---|
BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。 |
BufferedReader(Reader in, int sz) 创建使用指定大小的输入缓冲区的缓冲字符输入流。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
close() 关闭流并释放与之相关联的任何系统资源。 |
Stream |
lines() 返回一个 Stream ,其元素是从这个 BufferedReader 读取的行。 |
void |
mark(int readAheadLimit) 标记流中的当前位置。 |
boolean |
markSupported() 告诉这个流是否支持mark()操作。 |
int |
read() 读一个字符 |
int |
read(char[] cbuf, int off, int len) 将字符读入数组的一部分。 |
String |
readLine() 读一行文字。 |
boolean |
ready() 告诉这个流是否准备好被读取。 |
void |
reset() 将流重置为最近的标记。 |
long |
skip(long n) 跳过字符 |
10.转换流
顾名思义,对流进行转换,对什么流怎么转换呢?
将字节流转换为字符流,转换的对象是继承了OutputStream和InputStream的字节流。转换流是字符和字节的桥梁,字符转换流原理:字节流+编码表。
字节流转字符流:OutputStreamWriter 和InputStreamReader
字符流转字节流:无
File file = new File("D:/test.txt");
// 只要是字节流就行
Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
writer.write("UTF-8字符串");
writer.close();
Reader reader = new InputStreamReader(new FileInputStream(file), "UTF-8");
int len = -1;
char[] chars = new char[1024];
while ((len=reader.read(chars))!=-1){
System.out.println(new String(chars,0,len));
}
reader.close();
10.1字符流--转换
OutputStreamWriter
将字节输出流转换为字符输出流。
构造方法和描述 |
---|
OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter。 |
OutputStreamWriter(OutputStream out, Charset cs) 创建一个使用给定字符集的OutputStreamWriter。 |
OutputStreamWriter(OutputStream out, CharsetEncoder enc) 创建一个使用给定字符集编码器的OutputStreamWriter。 |
OutputStreamWriter(OutputStream out, String charsetName) 创建一个使用命名字符集的OutputStreamWriter。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
close() 关闭流,先刷新。 |
void |
flush() 刷新流。 |
String |
getEncoding() 返回此流使用的字符编码的名称。 |
void |
write(char[] cbuf, int off, int len) 写入字符数组的一部分。 |
void |
write(int c) 写一个字符 |
void |
write(String str, int off, int len) 写一个字符串的一部分。 |
InputStreamReader
将字节输入流转换为字符输入流。
构造方法和描述 |
---|
InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader。 |
InputStreamReader(InputStream in, Charset cs) 创建一个使用给定字符集的InputStreamReader。 |
InputStreamReader(InputStream in, CharsetDecoder dec) 创建一个使用给定字符集解码器的InputStreamReader。 |
InputStreamReader(InputStream in, String charsetName) 创建一个使用命名字符集的InputStreamReader。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
close() 关闭流并释放与之相关联的任何系统资源。 |
String |
getEncoding() 返回此流使用的字符编码的名称。 |
int |
read() 读一个字符 |
int |
read(char[] cbuf, int offset, int length) 将字符读入数组的一部分。 |
boolean |
ready() 告诉这个流是否准备好被读取。 |
11.打印流
打印流都是基于转换流和缓冲流来实现的方便打印功能的IO流。
字符流:PrinterWriter
字节流:PrintStream
11.1.字节流--打印
PrintStream 部分构造方法源码
private PrintStream(boolean autoFlush, OutputStream out) {
super(out);
this.autoFlush = autoFlush;
this.charOut = new OutputStreamWriter(this);
this.textOut = new BufferedWriter(charOut);
}
private PrintStream(boolean autoFlush, OutputStream out, Charset charset) {
super(out);
this.autoFlush = autoFlush;
this.charOut = new OutputStreamWriter(this, charset);
this.textOut = new BufferedWriter(charOut);
}
字节打印流PrintStream是经常用到的IO流,刚入门Java就已经开始用字节打印流了,只是不知道罢了就是这个【System.out.println()】,out 就是System类中的一个PrintStream类型的静态成员变量。System类中还有一个err 静态成员变量,使用err打印消息到控制台时,字体颜色是红色的。
例子
System.out.println("控制台输出一般信息--黑色");
System.err.println("控制台输出错误信息--红色");
// 打印数据到文件
File file = new File("D:/test.txt");
PrintStream printStream = new PrintStream(file);
printStream.println("每次打印一行就换行");
printStream.print("我打印从来不换行");
printStream.close();
构造方法
构造方法和描述 |
---|
PrintStream(File file) 使用指定的文件创建一个新的打印流,而不需要自动换行。 |
PrintStream(File file, String csn) 使用指定的文件和字符集创建新的打印流,而不需要自动换行。 |
PrintStream(OutputStream out) 创建一个新的打印流。 |
PrintStream(OutputStream out, boolean autoFlush) 创建一个新的打印流。 |
PrintStream(OutputStream out, boolean autoFlush, String encoding) 创建一个新的打印流。 |
PrintStream(String fileName) 使用指定的文件名创建新的打印流,无需自动换行。 |
PrintStream(String fileName, String csn) 创建一个新的打印流,不需要自动换行,具有指定的文件名和字符集。 |
所有方法
print() 和 println()
做了全类型的方法重载,支持所有类型参数的打印,表中就不一一列出。
返回值类型 | 方法和描述 |
---|---|
PrintStream |
append(char c) 将指定的字符附加到此输出流。 |
PrintStream |
append(CharSequence csq) 将指定的字符序列附加到此输出流。 |
PrintStream |
append(CharSequence csq, int start, int end) 将指定字符序列的子序列附加到此输出流。 |
boolean |
checkError() 刷新流并检查其错误状态。 |
protected void |
clearError() 清除此流的内部错误状态。 |
void |
close() 关闭流。 |
void |
flush() 刷新流。 |
PrintStream |
format(Locale l, String format, Object... args) 使用指定的格式字符串和参数将格式化的字符串写入此输出流。 |
PrintStream |
format(String format, Object... args) 使用指定的格式字符串和参数将格式化的字符串写入此输出流。 |
void |
print(Object obj) 打印一个对象。 |
void |
println() 通过写入行分隔符字符串来终止当前行。 |
void |
println(Object x) 打印一个对象,然后终止该行。 |
PrintStream |
printf(Locale l, String format, Object... args) 使用指定的格式字符串和参数将格式化的字符串写入此输出流的便利方法。 |
PrintStream |
printf(String format, Object... args) 使用指定的格式字符串和参数将格式化的字符串写入此输出流的便利方法。 |
protected void |
setError() 将流的错误状态设置为 true 。 |
void |
write(byte[] buf, int off, int len) 从指定的字节数组写入 len 个字节,从偏移 off 开始到此流。 |
void |
write(int b) 将指定的字节写入此流。 |
11.2.字符流--打印
PrinterWriter 部分构造方法源码
public PrintWriter(OutputStream out, boolean autoFlush) {
this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush);
// save print stream for error propagation
if (out instanceof java.io.PrintStream) {
psOut = (PrintStream) out;
}
}
public PrintWriter(File file) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))),
false);
}
例子
// 打印字符到文件
File file = new File("D:/test.txt");
PrintWriter pw = new PrintWriter(file);
pw.println("每次打印一行就换行");
pw.print("我打印从来不换行");
pw.close();
构造方法
构造方法和描述 |
---|
PrintWriter(File file) 使用指定的文件创建一个新的PrintWriter,而不需要自动的线路刷新。 |
PrintWriter(File file, String csn) 使用指定的文件和字符集创建一个新的PrintWriter,而不需要自动进行线条刷新。 |
PrintWriter(OutputStream out) 从现有的OutputStream创建一个新的PrintWriter,而不需要自动线路刷新。 |
PrintWriter(OutputStream out, boolean autoFlush) 从现有的OutputStream创建一个新的PrintWriter。 |
PrintWriter(String fileName) 使用指定的文件名创建一个新的PrintWriter,而不需要自动执行行刷新。 |
PrintWriter(String fileName, String csn) 使用指定的文件名和字符集创建一个新的PrintWriter,而不需要自动线路刷新。 |
PrintWriter(Writer out) 创建一个新的PrintWriter,没有自动线冲洗。 |
PrintWriter(Writer out, boolean autoFlush) 创建一个新的PrintWriter。 |
所有方法
print() 和 println()
做了全类型的方法重载,支持所有类型参数的打印,表中就不一一列出。
返回值类型 | 方法和描述 |
---|---|
PrintWriter |
append(char c) 将指定的字符附加到此作者。 |
PrintWriter |
append(CharSequence csq) 将指定的字符序列附加到此作者。 |
PrintWriter |
append(CharSequence csq, int start, int end) 将指定字符序列的子序列附加到此作者。 |
boolean |
checkError() 如果流未关闭,请刷新流并检查其错误状态。 |
protected void |
clearError() 清除此流的错误状态。 |
void |
close() 关闭流并释放与之相关联的任何系统资源。 |
void |
flush() 刷新流。 |
PrintWriter |
format(Locale l, String format, Object... args) 使用指定的格式字符串和参数将格式化的字符串写入此写入程序。 |
PrintWriter |
format(String format, Object... args) 使用指定的格式字符串和参数将格式化的字符串写入此写入程序。 |
void |
print(Object obj) 打印一个对象。 |
void |
println() 通过写入行分隔符字符串来终止当前行。 |
void |
println(Object x) 打印一个对象,然后终止该行。 |
PrintWriter |
printf(Locale l, String format, Object... args) 使用指定的格式字符串和参数将格式化的字符串写入该writer的方便方法。 |
PrintWriter |
printf(String format, Object... args) 使用指定的格式字符串和参数将格式化的字符串写入该writer的方便方法。 |
protected void |
setError() 表示发生错误。 |
void |
write(char[] buf) 写入一个字符数组。 |
void |
write(char[] buf, int off, int len) 写一个字符数组的一部分。 |
void |
write(int c) 写一个字符 |
void |
write(String s) 写一个字符串 |
void |
write(String s, int off, int len) 写一个字符串的一部分。 |
12.序列化流
序列化:将对象的状态信息转换为可以存储或传输的形式的过程。程序通过序列化把Java对象转换成二进制字节流,然后就可以把二进制字节流写入网络或磁盘。
反序列化:读取磁盘或网络中的二进制字节流数据,转化为Java对象
序列化流:ObjectOutputStream
反序列化流:ObjectInputStream
例子
测试对象:Person 类
// 序列化对象信息:必须实现序列化标记接口Serializable
public class Person implements Serializable {
// 序列化版本UID
private static final long serialVersionUID = 1L;
private String name;
private transient int age;//年龄字段不做序列化
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Person{" + "name='" + name + ", age=" + age + ", sex='" + sex + '}';
}
public Person() {
}
public Person(String name, Integer age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
}
序列化和反序列化
public class Demo2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//数据准备:集合类都实现了序列化接口Serializable
List list = new ArrayList<>();
list.add(new Person("张三",38,"男"));
list.add(new Person("李四",38,"男"));
list.add(new Person("如花",18,"女"));
// 序列化保存到普通文件
File file = new File("D:/demo2.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
objectOutputStream.writeObject(list);
objectOutputStream.close();
// 读取普通文件反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
List personList = (List) objectInputStream.readObject();
objectInputStream.close();
for (Person person:personList){
System.out.println(person);
}
}
}
执行结果:年龄没有被序列化,int默认0所以取出来为0。
Person{name='张三, age=0, sex='男} Person{name='李四, age=0, sex='男} Person{name='如花, age=0, sex='女}
几个要点:
需要实现 Serializable 标记接口才可以被序列化;
序列化版本UID:serialVersionUID,绑定对象版本,不因为对象的改变而导致序列化和反序列化失败;
被transient 修饰的成员变量不会被序列化;
Externalizable接口--外部化接口;他是Serializable接口的子接口,能手动控制序列化的方式。
12.1.序列化流
ObjectOutputStream
修饰符 | 构造方法和描述 |
---|---|
protected |
ObjectOutputStream() 为完全重新实现ObjectOutputStream的子类提供一种方法,不必分配刚刚被ObjectOutputStream实现使用的私有数据。 |
public |
ObjectOutputStream(OutputStream out) 创建一个写入指定的OutputStream的ObjectOutputStream。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
protected void |
annotateClass(类> cl) 子类可以实现此方法,以允许类数据存储在流中。 |
protected void |
annotateProxyClass(类> cl) 子类可以实现这种方法来存储流中的自定义数据以及动态代理类的描述符。 |
void |
close() 关闭流。 |
void |
defaultWriteObject() 将当前类的非静态和非瞬态字段写入此流。 |
protected void |
drain() 排除ObjectOutputStream中的缓冲数据。 |
protected boolean |
enableReplaceObject(boolean enable) 启用流来替换流中的对象。 |
void |
flush() 刷新流。 |
ObjectOutputStream.PutField |
putFields() 检索用于缓冲要写入流的持久性字段的对象。 |
protected Object |
replaceObject(Object obj) 该方法将允许ObjectOutputStream的可信子类在序列化期间将一个对象替换为另一个对象。 |
void |
reset() 复位将忽略已写入流的任何对象的状态。 |
void |
useProtocolVersion(int version) 指定在编写流时使用的流协议版本。 |
void |
write(byte[] buf) 写入一个字节数组。 |
void |
write(byte[] buf, int off, int len) 写入一个子字节数组。 |
void |
write(int val) 写一个字节。 |
void |
writeBoolean(boolean val) 写一个布尔值。 |
void |
writeByte(int val) 写入一个8位字节。 |
void |
writeBytes(String str) 写一个字符串作为字节序列。 |
void |
writeChar(int val) 写一个16位的字符。 |
void |
writeChars(String str) 写一个字符串作为一系列的字符。 |
protected void |
writeClassDescriptor(ObjectStreamClass desc) 将指定的类描述符写入ObjectOutputStream。 |
void |
writeDouble(double val) 写一个64位的双倍。 |
void |
writeFields() 将缓冲的字段写入流。 |
void |
writeFloat(float val) 写一个32位浮点数。 |
void |
writeInt(int val) 写一个32位int。 |
void |
writeLong(long val) 写一个64位长 |
void |
writeObject(Object obj) 将指定的对象写入ObjectOutputStream。 |
protected void |
writeObjectOverride(Object obj) 子类使用的方法来覆盖默认的writeObject方法。 |
void |
writeShort(int val) 写一个16位短。 |
protected void |
writeStreamHeader() 提供了writeStreamHeader方法,因此子类可以在流中附加或预先添加自己的头。 |
void |
writeUnshared(Object obj) 将“非共享”对象写入ObjectOutputStream。 |
void |
writeUTF(String str) 此字符串的原始数据写入格式为 modified UTF-8 。 |
12.2.反序列化流
ObjectInputStream
修饰符 | 构造方法和描述 |
---|---|
protected |
ObjectInputStream() 为完全重新实现ObjectInputStream的子类提供一种方法,不必分配刚刚被ObjectInputStream实现使用的私有数据。 |
public |
ObjectInputStream(InputStream in) 创建从指定的InputStream读取的ObjectInputStream。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
int |
available() 返回可以读取而不阻塞的字节数。 |
void |
close() 关闭输入流。 |
void |
defaultReadObject() 从此流读取当前类的非静态和非瞬态字段。 |
protected boolean |
enableResolveObject(boolean enable) 启用流以允许从流中读取的对象被替换。 |
int |
read() 读取一个字节的数据。 |
int |
read(byte[] buf, int off, int len) 读入一个字节数组。 |
boolean |
readBoolean() 读取布尔值。 |
byte |
readByte() 读取一个8位字节。 |
char |
readChar() 读一个16位字符。 |
protected ObjectStreamClass |
readClassDescriptor() 从序列化流读取类描述符。 |
double |
readDouble() 读64位双倍。 |
ObjectInputStream.GetField |
readFields() 从流中读取持久性字段,并通过名称获取它们。 |
float |
readFloat() 读32位浮点数。 |
void |
readFully(byte[] buf) 读取字节,阻塞直到读取所有字节。 |
void |
readFully(byte[] buf, int off, int len) 读取字节,阻塞直到读取所有字节。 |
int |
readInt() 读取一个32位int。 |
String |
readLine() 已弃用此方法无法将字节正确转换为字符。有关详细信息和替代方案,请参阅DataInputStream。 |
long |
readLong() 读64位长。 |
Object |
readObject() 从ObjectInputStream读取一个对象。 |
protected Object |
readObjectOverride() 此方法由ObjectOutputStream的受信任子类调用,该子类使用受保护的无参构造函数构造ObjectOutputStream。 |
short |
readShort() 读取16位短。 |
protected void |
readStreamHeader() 提供了readStreamHeader方法来允许子类读取和验证自己的流标题。 |
Object |
readUnshared() 从ObjectInputStream读取一个“非共享”对象。 |
int |
readUnsignedByte() 读取一个无符号的8位字节。 |
int |
readUnsignedShort() 读取无符号16位短。 |
String |
readUTF() 以 modified UTF-8格式读取字符串。 |
void |
registerValidation(ObjectInputValidation obj, int prio) 在返回图之前注册要验证的对象。 |
protected 类> |
resolveClass(ObjectStreamClass desc) 加载本地类等效的指定流类描述。 |
protected Object |
resolveObject(Object obj) 此方法将允许ObjectInputStream的受信任子类在反序列化期间将一个对象替换为另一个对象。 |
protected 类> |
resolveProxyClass(String[] interfaces) 返回一个代理类,它实现代理类描述符中命名的接口; 子类可以实现此方法从流中读取自定义数据以及动态代理类的描述符,从而允许它们为接口和代理类使用备用加载机制。 |
int |
skipBytes(int len) 跳过字节。 |
13.数据流
数据流用于实现将java基本类型转换成二进制来进行读写操作。在序列化流中便用到了数据流,通过数据流等其他处理完成对象的序列化和反序列化。
数据输入输出流相比其他流还有些特殊的方法,分别是readUTF和writeUTF方法使用了一种特殊的UTF编码/解码方式,只能用于java程序。
例子
File file = new File("D:/test.txt");
DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(file));
//dataOutputStream.write("渊渟岳".getBytes());
dataOutputStream.writeUTF("渊渟岳"); // UTF编码
dataOutputStream.close();
DataInputStream dataInputStream = new DataInputStream(new FileInputStream(file));
//int len = -1;
//byte[] bytes = new byte[1024];
//while ((len=dataInputStream.read(bytes))!=-1){
// System.out.println(new String(bytes,0,len));
//}
String s = dataInputStream.readUTF();
System.out.println(s);
dataInputStream.close();
执行结果:保存的文件数据和读取出来的数据有差异,看不见的符号分别是:"空白"+水平制表符(其二进制为00100000 00001001),对照ASCII字符代码表就知道了。
对照ASCII字符代码表结果
(图片来源于百度)
13.1.字节流--数据
DataOutputStream
构造方法和描述 |
---|
DataOutputStream(OutputStream out) 创建一个新的数据输出流,以将数据写入指定的底层输出流。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
flush() 刷新此数据输出流。 |
int |
size() 返回计数器的当前值 written ,到目前为止写入此数据输出流的字节数。 |
void |
write(byte[] b, int off, int len) 写入 len 从指定的字节数组起始于偏移 off 基础输出流。 |
void |
write(int b) 将指定的字节(参数 b 的低8位)写入底层输出流。 |
void |
writeBoolean(boolean v) 将 boolean 写入底层输出流作为1字节值。 |
void |
writeByte(int v) 将 byte 作为1字节值写入底层输出流。 |
void |
writeBytes(String s) 将字符串作为字节序列写入基础输出流。 |
void |
writeChar(int v) 将 char 写入底层输出流作为2字节值,高字节优先。 |
void |
writeChars(String s) 将字符串写入底层输出流作为一系列字符。 |
void |
writeDouble(double v) 双参数传递给转换 long 使用 doubleToLongBits 方法在类 Double ,然后写入该 long 值基础输出流作为8字节的数量,高字节。 |
void |
writeFloat(float v) 浮子参数的转换 int 使用 floatToIntBits 方法在类 Float ,然后写入该 int 值基础输出流作为一个4字节的数量,高字节。 |
void |
writeInt(int v) 将底层输出流写入 int 作为四字节,高位字节。 |
void |
writeLong(long v) 将 long 写入底层输出流,为8字节,高字节为首。 |
void |
writeShort(int v) 将 short 写入底层输出流作为两个字节,高字节优先。 |
void |
writeUTF(String str) 使用 modified UTF-8编码以机器无关的方式将字符串写入基础输出流。 |
DataInputStream
构造方法和描述 |
---|
DataInputStream(InputStream in) 创建使用指定的底层InputStream的DataInputStream。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
int |
read(byte[] b) 从包含的输入流中读取一些字节数,并将它们存储到缓冲区数组 b 。 |
int |
read(byte[] b, int off, int len) 从包含的输入流读取最多 len 个字节的数据为字节数组。 |
boolean |
readBoolean() 见的总承包 readBoolean 的方法 DataInput 。 |
byte |
readByte() 见的总承包 readByte 的方法 DataInput 。 |
char |
readChar() 见 readChar 方法的总合同 DataInput 。 |
double |
readDouble() 见 readDouble 方法 DataInput 的总体合同。 |
float |
readFloat() 见 readFloat 法 DataInput 的一般合同。 |
void |
readFully(byte[] b) 见的总承包 readFully 的方法 DataInput 。 |
void |
readFully(byte[] b, int off, int len) 见的总承包 readFully 的方法 DataInput 。 |
int |
readInt() 见 readInt 方法 DataInput 的一般合同。 |
String |
readLine() 已弃用此方法无法将字节正确转换为字符。从JDK 1.1开始,读取文本行的BufferedReader.readLine() 方法是通过BufferedReader.readLine() 方法。使用DataInputStream 类读取行的程序可以转换为使用BufferedReader 类替换以下形式的代码: DataInputStream d = new DataInputStream(in); 与: BufferedReader d = new BufferedReader(new InputStreamReader(in)); |
long |
readLong() 见的总承包 readLong 的方法 DataInput 。 |
short |
readShort() 见 readShort 方法 DataInput 的一般合同。 |
int |
readUnsignedByte() 见的总承包 readUnsignedByte 的方法 DataInput 。 |
int |
readUnsignedShort() 见 readUnsignedShort 法 DataInput 的一般合同。 |
String |
readUTF() 见 readUTF 法 DataInput 的一般合同。 |
static String |
readUTF(DataInput in) 从流in 读取以modified UTF-8格式编码的Unicode字符串的表示; 这个字符串然后作为String 返回。 |
int |
skipBytes(int n) 见 skipBytes 法 DataInput 的一般合同。 |
14.回退输入流
回退流只有输入流(读取),一般的输入流都只能按顺序读取一次流中的数据,而回退流支持读取数据后还可以回推数据。其中的原理是通过流中内置的缓冲数组和位置指针来实现数据的读取(read)和回退(unread)。意义在于多次读取流中的数据,比如:文件解压时需要读取压缩包中的文件头来判断文件类型,具体实现可阅读java.util.zip.ZipInputStream类的源码。
// zip输入流 使用 回退流,指定缓冲区为512
public ZipInputStream(InputStream in, Charset charset) {
super(new PushbackInputStream(in, 512), new Inflater(true), 512);
……
}
字节流:PushbackInputStream
字符流:PushbackReader
14.1.字节流--回退
PushbackInputStream
例子
File file = new File("D:/test.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write("Y123我不是文件头我是内容部分哈哈哈".getBytes());
fileOutputStream.close();
// 回退流缓冲区默认大小1个字节,太小,所以使用时必须指定缓冲区大小
PushbackInputStream pushbackInputStream = new PushbackInputStream(new FileInputStream(file), 512);
int len = -1;
byte[] bytes = new byte[512];
if((len=pushbackInputStream.read(bytes))!=-1){
String s = new String(bytes, 0, len);
String fileHead = s.substring(0, 4);//自行规定文件头
if ("Y123".equals(fileHead)){
//去掉文件头,回退后面的数据
pushbackInputStream.unread(s.substring(4,s.length()).getBytes());
System.out.println("文件头="+fileHead);
// 因为文件头对上了,所以继续处理后续数据
while ((len=pushbackInputStream.read(bytes))!=-1){
System.out.println(new String(bytes,0,len));
}
}
}
pushbackInputStream.close();
构造方法和描述 |
---|
PushbackInputStream(InputStream in) 创建一个 PushbackInputStream 并保存其参数,输入流 in 供以后使用。 |
PushbackInputStream(InputStream in, int size) 使用 PushbackInputStream 的推回缓冲区创建 size ,并保存其参数,输入流 in 供以后使用。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
int |
available() 返回从该输入流中可以读取(或跳过)的字节数的估计值,而不会被下一次调用此输入流的方法阻塞。 |
void |
close() 关闭此输入流并释放与流相关联的任何系统资源。 |
void |
mark(int readlimit) 标记此输入流中的当前位置。 |
boolean |
markSupported() 测试这个输入流是否支持 mark 和 reset 方法,而不是。 |
int |
read() 从该输入流读取下一个数据字节。 |
int |
read(byte[] b, int off, int len) 从该输入流读取最多 len 字节的数据为字节数组。 |
void |
reset() 将此流重新定位到最后在此输入流上调用 mark 方法时的位置。 |
long |
skip(long n) 跳过并丢弃来自此输入流的 n 字节的数据。 |
void |
unread(byte[] b) 将一个字节数组复制回推回缓冲区的前端。 |
void |
unread(byte[] b, int off, int len) 通过将字节数组复制到推回缓冲区的前端来推回一部分数组。 |
void |
unread(int b) 通过将其复制到推回缓冲区的前端来推回一个字节。 |
14.2.字符流--回退
字符流的回退和字节流的回退很相似,自行实现。
构造方法和描述 |
---|
PushbackReader(Reader in) 用一个字符的后置缓冲区创建一个新的推回阅读器。 |
PushbackReader(Reader in, int size) 使用给定大小的推回缓冲区创建一个新的推回阅读器。 |
所有方法
返回值类型 | 方法和描述 |
---|---|
void |
close() 关闭流并释放与之相关联的任何系统资源。 |
void |
mark(int readAheadLimit) 标记流中的当前位置。 |
boolean |
markSupported() 告诉这个流是否支持mark()操作,它不是。 |
int |
read() 读一个字符 |
int |
read(char[] cbuf, int off, int len) 将字符读入数组的一部分。 |
boolean |
ready() 告诉这个流是否准备好被读取。 |
void |
reset() 重置流。 |
long |
skip(long n) 跳过字符 |
void |
unread(char[] cbuf) 将一个字符数组复制回推回缓冲区的前端。 |
void |
unread(char[] cbuf, int off, int len) 通过将一部分字符复制到推回缓冲区的前端来推回。 |
void |
unread(int c) 将单个字符复制回推回缓冲区的前端。 |
15.过滤流
装饰模式的作用是动态地扩展(增强)一个对象的功能,也即是在不改变其结构同时向一个现有的对象添加新的功能。通过继承过滤流来对输入输出流的行为进行扩展,但是功能的具体实现并不是在过滤流中,而是在过滤流的子类中编写具体功能的实现。过滤流的作用是充当抽象装饰器,为了强制其子类(具体实现类)按照其构造形式传入一个IO流对象。
可能是历史原因,过滤流出现了下面三种源码设计:
- FilterInputStream:不是抽象类,构造方法为protected
- FilterOutputStream:不是抽象类,构造方法为public
- FilterReader:抽象类,构造方法为protected
- FilterWriter:抽象类,构造方法为protected
例子:以FilterReader流为例,自定义FilterReaderDemo处理流:字母自动转成大写
/**
* 自动转成大写的处理流
*/
public class FilterReaderDemo extends FilterReader {
protected FilterReaderDemo(Reader in) {
super(in);
}
@Override
public int read() throws IOException {
System.out.println("");
int i = super.read();
if(i==-1) return i;
return Character.toUpperCase(i);
}
@Override
public int read(char[] cbuf,int offset,int len) throws IOException{
int res = super.read(cbuf, offset, len);
for (int i = 0; i < res; i++) {
cbuf[i] = Character.toUpperCase(cbuf[i]);
}
return res;
}
}
测试
public static void main(String[] args) throws IOException {
File file = new File("D:/test.txt");
FileWriter fileWriter = new FileWriter(file);
fileWriter.write("qwertyufghjk");
fileWriter.close();
FilterReaderDemo filterReaderDemo = new FilterReaderDemo(new FileReader(file));
int len = -1;
char[] chars = new char[1024];
// read(char cbuf[])方法,其实是调用了read(char[] cbuf,int offset,int len)
while ((len=filterReaderDemo.read(chars))!=-1){
System.out.println(new String(chars,0,len));
}
filterReaderDemo.close();
}
执行结果:QWERTYUFGHJK
16.总结
前面仅仅总结了java.io包下的输入/输出流的体系,还有一些例如ZipInputStream、AudioInputStream 、CipherInputStream等具有压缩/解压、访问音频文件、加密/ 解密等功能的字节流,位于JDK的java.util.zip、javax.sound.sampled、javax.crypto包等,还有很多的具有特殊功能的IO流位于其它的包下。
Java往期文章
Java全栈学习路线、学习资源和面试题一条龙
我心里优秀架构师是怎样的?免费下载经典编程书籍
更多优质文章和资源
原创不易、三联支持:分享,点赞,在看