文章目录
- 数据流
- DataOutputStream数据输出流
- DataInputStream数据输入流
- 打印流
- PrintStream字节打印流
- PrintWriter字符打印流
- 标准输入输出流
- 标准输入流
- 标准输出流
- 对象流(序列化与反序列化流)
- ObjectOutputStream序列化流
- ObjectInputStream反序列化流
- RandomAccessFile随机访问文件流
- NIO
- NIO核心
- Buffer
- Buffer的实例化
- buffer的存取数据
- 总结
数据流
DataOutputStream数据输出流
数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。
然后,应用程序可以使用数据输入流将数据读入。
构造方法:
DataOutputStream(OutputStream out)
// 创建一个新的数据输出流,将数据写入指定基础输出流。
成员方法:
注:
- 每种数据类型都有1个对应的write方法
DataInputStream数据输入流
数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型
构造方法:
DataInputStream(InputStream in)
// 使用指定的底层 InputStream 创建一个 DataInputStream。
成员方法:
注:
- 每种数据类型都有1个对应的read方法
eg:
/*
通过数据流写数据
*/
// 1. 创建数据输出流对象
DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("a.txt"));
// 2. writeInt
dataOutputStream.writeInt(1000);
// 3. writeDouble
dataOutputStream.writeDouble(3.14);
// 4. close
dataOutputStream.close();
/*
通过数据流读取数据
*/
// 1. 创建数据输入流对象
DataInputStream dataInputStream = new DataInputStream(new FileInputStream("a.txt"));
// 2. readInt
int readint = dataInputStream.readInt();
System.out.println(readint);
// 3. readDouble
double readdouble = dataInputStream.readDouble();
System.out.println(readdouble);
// 4. close
dataInputStream.close();
注意事项:
- 读取的顺序要跟写的顺序保持一致
打印流
核心思想: 把不同的数据类型转换成String
需求:
- 定义一个类Printer
- 定义成员变量OutputStream
- 定义5个方法
- 写int的方法
void printInt(int a)
- 写int并且换行的方法
void printIntLn(int a)
- 写double的方法
void printDouble(double a)
- 写double并且换行的方法
void printDoubleLn(double a)
- 写一个close方法
void close()
- 写int的方法
eg:
class Printer{
// 定义成员变量OutputStream
OutputStream out;
public Printer(OutputStream out){
this.out = out;
}
// 写int的方法 void printInt(int a)
public void printInt(int a) throws IOException {
// int 转换成 String
String s = String.valueOf(a);
out.write(s.getBytes());
}
// 写int并且换行的方法 void printIntLn(int a)
public void printIntLn(int a) throws IOException{
// int 转换成 String
String s = String.valueOf(a);
out.write(s.getBytes());
out.write(System.lineSeparator().getBytes());
}
// 写double的方法 void printDouble(double a)
public void printDouble(double a) throws IOException{
// int 转换成 String
String s = String.valueOf(a);
out.write(s.getBytes());
}
// 写double并且换行的方法 void printDoubleLn(double a)
public void printDoubleLn(double a) throws IOException{
// int 转换成 String
String s = String.valueOf(a);
out.write(s.getBytes());
out.write(System.lineSeparator().getBytes());
}
// 写一个close方法 void close()
public void close() throws IOException{
out.close();
}
}
PrintStream字节打印流
PrintStream
为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式
继承关系:
构造方法:
PrintStream(File file) // 创建具有指定文件且不带自动行刷新的新打印流。
PrintStream(OutputStream out) // 创建新的打印流。
PrintStream(OutputStream out, boolean autoFlush) // 创建新的打印流。
PrintStream(String fileName) // 创建具有指定文件名称且不带自动行刷新的新打印流。
成员方法:
注:
- 每个数据类型都有一个相对应的print方法
/*
使用字节打印流打印数据
*/
// 1. 创建字节打印流对象
PrintStream printStream = new PrintStream("a.txt");
// 2. 写 1000
printStream.print(1000);
// 3. 写 true
printStream.print(true);
// 4. close
printStream.close();
PrintWriter字符打印流
向文本输出流打印对象的格式化表示形式
构造方法:
PrintWriter(File file) // 使用指定文件创建不具有自动行刷新的新 PrintWriter。
PrintWriter(OutputStream out) // 根据现有的 OutputStream 创建不带自动行刷新的新 PrintWriter。
PrintWriter(OutputStream out, boolean autoFlush)
// 通过现有的 OutputStream 创建新的 PrintWriter。
PrintWriter(String fileName) // 创建具有指定文件名称且不带自动行刷新的新 PrintWriter。
PrintWriter(Writer out) // 创建不带自动行刷新的新 PrintWriter。
PrintWriter(Writer out, boolean autoFlush) // 创建新 PrintWriter。
成员方法:
注:
- 每个数据类型都有一个相对应的print方法
eg:
// 1.
PrintWriter printWriter = new PrintWriter("a.txt");
// 2.
printWriter.println(100);
printWriter.print("a");
// 3.
printWriter.flush();
// 4.
printWriter.close();
打印流特点:
- 只能操作目的地,不能操作数据来源。
- 没有与之相对应的输入流
- 可以操作任意类型的数据。
- 把任意类型的数据—>
String(String.valueOf(不同类型的数据))
- 把任意类型的数据—>
- 如果启动了自动刷新,能够自动刷新。
autoFlush
如果为 true,则println
、printf
或format
方法将刷新输出缓冲区- 下面是
println
源码举例:
public void write(String s) { write(s, 0, s.length()); } private void newLine() { try { synchronized (lock) { ensureOpen(); out.write(System.lineSeparator()); if (autoFlush) out.flush(); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } }
- 可以操作文件的流
- 构造方法里可以传
File
对象或者String fileName
- 构造方法里可以传
标准输入输出流
标准输入流
- System.in
- 默认输入设备是键盘
- 本质: InputStream 普通的字节输入流
标准输出流
- System.out
- 默认输出设备是显示器
- 本质: PrintStream 字节打印流
注意事项:
- read方法是一个阻塞方法
eg:
需求:利用System.in 完成Scanner的nextLine()的功能
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(System.in));
// InputStreamReader是字节流和字符流的转换桥梁
String s = bufferedReader.readLine();
System.out.println(s);
bufferedReader.close();
对象流(序列化与反序列化流)
- 序列化: 把对象数据转为二进制数据, 存到文件的过程
- 反序列:(序列化的逆过程) :把二进制数据还原回对象数据的过程
ObjectOutputStream序列化流
- ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。
可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。
如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。 - 只能将支持
java.io.Serializable
接口的对象写入流中,Serializable接口是一个空接口(跟cloneable接口是一样的), 起到标记的作用 writeObject
方法用于将对象写入流中
继承关系:
构造方法:
ObjectOutputStream(OutputStream out) // 创建写入指定 OutputStream 的 ObjectOutputStream。
成员方法:
writeObject(Object obj) // 将指定的对象写入 ObjectOutputStream。
ObjectInputStream反序列化流
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化
构造方法:
ObjectInputStream(InputStream in) // 创建从指定 InputStream 读取的 ObjectInputStream。
成员方法
readObject() // 从 ObjectInputStream 读取对象。
eg:
public class Demo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
/*
使用对象流(序列化和反序列化)
写入对象
读取对象
*/
// 序列化流操作
// 1. 创建学生对象
Student stu = new Student("zs",10);
// 2. 创建序列化流的对象
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("a.txt"));
// writeObject(Object obj)
out.writeObject(stu);
// close
out.close();
// 反序列化流操作
// 1. 创建反序列化流对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("a.txt"));
// 2. readObject()
Object o = in.readObject();
System.out.println(o);
// 3. close
in.close();
}
}
/*
java.io.NotSerializableException 没有实现Serializable接口
*/
class Student implements Serializable {
String name;
int age;
// transient修饰不想被序列化的成员变量
transient int score;
static final long serialVersionUID = -7889256375299507710L;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
注意事项:
java.io.NotSerializableException
- 没有实现Serializable接口
java.io.InvalidClassException: _18io03.com.cskaoyan._04serialize.Student; local class incompatible: stream classdesc serialVersionUID = -7889256375299507710, local class serialVersionUID = 7388140007375758175
- 设置为:
static final long serialVersionUID = -7889256375299507710L;
- SerialVersionUID不匹配
- 设置为:
transient
修饰不想被序列化的成员变量,就是反序列化后看不到具体的值
RandomAccessFile随机访问文件流
- RandomAccessFile 声明在
java.io
包下,但直接继承于java.lang.Object
类,这个类既可以读也 可以写。 - 支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读、写文件
- 支持只访问文件的部分内容
- 可以向已存在的文件后追加内容
- RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。
构造方法:
RandomAccessFile(File file, String mode)
// 创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。
RandomAccessFile(String name, String mode)
// 创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。
注意:
创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指 定 RandomAccessFile 的访问模式(介绍2种常用的):
- r: 以只读方式打开(不会创建文件,读取已经存在的文件)
- rw:可读可写(文件不存在会创建,存在不会创建)
成员方法:
- 常规的
read
和write
方法 - RandomAccessFile 类对象可以自由移动记录指针
long getFilePointer()
:获取文件记录指针的当前位置void seek(long pos)
:将文件记录指针定位到 pos 位置
eg:
@Test
public void Test() throws IOException {
File file = new File("D:\\java_test\\a.txt");
RandomAccessFile randomAccessFile =
new RandomAccessFile("D:\\java_test\\a.txt", "rw");
// 获取当前文件的指针
long filePointer = randomAccessFile.getFilePointer();
// write写数据
randomAccessFile.write("abjdefg".getBytes());
// 从文件的某个位置进行写数据
// 移动指针
randomAccessFile.seek(4);
randomAccessFile.write("999".getBytes());
// 从文件的末尾开始写
randomAccessFile.seek(file.length());
randomAccessFile.write("1998".getBytes());
// 从文件中读取数据
randomAccessFile.seek(0);
byte[] bytes = new byte[1024];
int readCount = randomAccessFile.read(bytes);
System.out.println(new String(bytes,0,readCount));
randomAccessFile.close();
}
NIO
- Java NIO(New IO, Non Blocking IO),可以替代标准的Java IO API。
- NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,
- NIO支持面向缓冲区buffer的、基于通道channel的IO操作。
- NIO将以更加高效的方式进行文件的读写操作。
IO | NIO |
---|---|
面向流(Stream Oriented) | 面向缓冲区(块)(Buffer Oriented) |
阻塞IO(Blocking IO) | 非阻塞IO(Non Blocking IO) |
NIO核心
- Buffer
- Channel
Channel负责传输,Buffer负责存储
Buffer
除了boolean之外的基本数据类型都有1个对应的Buffer
比如 IntBuffer 放int数据(除了boolean)
-
ByteBuffer 最常用
-
IntBuffer
-
ShortBuffer
-
LongBuffer
-
DoubleBuffer
-
FloatBuffer
-
CharBuffer
Buffer的实例化
以ByteBuffer
为例:
public static ByteBuffer allocate(int capacity)
public static ByteBuffer allocateDirect(int capacity)
public static ByteBuffer wrap(byte[] array)
public static ByteBuffer wrap(byte[] array, int offset, int length)
eg:
@Test
public void Test2(){
ByteBuffer allocate = ByteBuffer.allocate(10);
System.out.println(allocate);
}
其中:
-
capacity
- 表示Buffer最大数据容量,创建后不能更改.一旦Buffer满了,需要将其清空(通过读数据或者清除数据, 才能继续往里写数据)
-
limit
- 是第一个不应该读取或写入的元素的索引。缓冲区的限制不能为负,并且不能大于其容量
- 写数据到Buffer时,limit表示可对Buffer最多写入多少个数据(写模式下 limit = capacity)
- 读数据时,limit表示Buffer里有多少可读数据,可以读取到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)
-
position
- 写数据时,position表示写入数据的当前位置,初始值为0.当数据写入到Buffer中后,position会向后移动到1个可插入数据的Buffer单元.position最大值可为capacity
- 读数据时,position表示读入数据的当前位置,如position=2时,表示已经读入了2个值,或从第2个位置开始读取.通过
flip()
切换到读模式时position会被重置为0,当Buffer从position读入数据后,position会移动到下一个可读入的数据Buffer单元
-
mark
- 跟reset()结合进行标记,通过Buffer中的mark()方法,指定一个Buffer的特定的position,之后可以通过reset()方法恢复到这个position.
-
这4个属性遵循如下关系:
- 0<=mark<=position<=limit<=capacity
buffer的存取数据
put操作(存)
put(byte b) // 将给定的字节写入此缓冲区的当前位置,然后该位置递增 。 position+1
put操作
总结
类型 | 字节输出流 | 字节输入流 | 字符输出流 | 字符输入流 |
---|---|---|---|---|
抽象基类 | OutputStream | InputStream | Writer | Reader |
文件相关 | FileOutputStream | FileInputStream | FileWriter | FileReader |
缓冲相关 | BufferedOutputStream | BufferedInputStream | BufferedWriter | BufferedReader |
转换相关 | OutputStreamWriter | InputStreamReader | ||
数据相关 | DataOutputStream | DataInputStream | ||
打印相关 | PrintStream | PrintWriter | ||
对象相关 | ObjectOutpuStream | ObjectInputStream |