数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。
然后,应用程序可以使用数据输入流将数据读入。
构造方法:
DataOutputStream(OutputStream out)
// 创建一个新的数据输出流,将数据写入指定基础输出流。
成员方法:
数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型
构造方法:
DataInputStream(InputStream in)
// 使用指定的底层 InputStream 创建一个 DataInputStream。
成员方法:
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
需求:
void printInt(int a)
void printIntLn(int a)
void printDouble(double a)
void printDoubleLn(double a)
void close()
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(File file) // 创建具有指定文件且不带自动行刷新的新打印流。
PrintStream(OutputStream out) // 创建新的打印流。
PrintStream(OutputStream out, boolean autoFlush) // 创建新的打印流。
PrintStream(String fileName) // 创建具有指定文件名称且不带自动行刷新的新打印流。
成员方法:
/*
使用字节打印流打印数据
*/
// 1. 创建字节打印流对象
PrintStream printStream = new PrintStream("a.txt");
// 2. 写 1000
printStream.print(1000);
// 3. 写 true
printStream.print(true);
// 4. close
printStream.close();
向文本输出流打印对象的格式化表示形式
构造方法:
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。
成员方法:
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
注意事项:
eg:
需求:利用System.in 完成Scanner的nextLine()的功能
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(System.in));
// InputStreamReader是字节流和字符流的转换桥梁
String s = bufferedReader.readLine();
System.out.println(s);
bufferedReader.close();
java.io.Serializable
接口的对象写入流中,Serializable接口是一个空接口(跟cloneable接口是一样的), 起到标记的作用writeObject
方法用于将对象写入流中继承关系:
构造方法:
ObjectOutputStream(OutputStream out) // 创建写入指定 OutputStream 的 ObjectOutputStream。
成员方法:
writeObject(Object obj) // 将指定的对象写入 ObjectOutputStream。
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
java.io.InvalidClassException: _18io03.com.cskaoyan._04serialize.Student; local class incompatible: stream classdesc serialVersionUID = -7889256375299507710, local class serialVersionUID = 7388140007375758175
static final long serialVersionUID = -7889256375299507710L;
transient
修饰不想被序列化的成员变量,就是反序列化后看不到具体的值java.io
包下,但直接继承于java.lang.Object
类,这个类既可以读也 可以写。构造方法:
RandomAccessFile(File file, String mode)
// 创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。
RandomAccessFile(String name, String mode)
// 创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。
注意:
创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指 定 RandomAccessFile 的访问模式(介绍2种常用的):
成员方法:
read
和write
方法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();
}
IO | NIO |
---|---|
面向流(Stream Oriented) | 面向缓冲区(块)(Buffer Oriented) |
阻塞IO(Blocking IO) | 非阻塞IO(Non Blocking IO) |
主要用在Netty框架里
Channel负责传输,Buffer负责存储
除了boolean之外的基本数据类型都有1个对应的Buffer
比如 IntBuffer 放int数据(除了boolean)
以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
limit
position
flip()
切换到读模式时position会被重置为0,当Buffer从position读入数据后,position会移动到下一个可读入的数据Buffer单元mark
这4个属性遵循如下关系:
put操作(存)
put(byte b) // 将给定的字节写入此缓冲区的当前位置,然后该位置递增 。 position+1
put(int index, byte b) // 将给定字节写入此缓冲区的给定索引处。position不变
put(byte[] src, int offset, int length) // 此方法将把给定源数组中的字节字传输到此缓冲区中。
put(byte[] src) // 此方法将给定的源 byte 数组的所有内容传输到此缓冲区中。
get操作(取)
get() // 读取此缓冲区当前位置的字节,然后该位置递增。
get(int index) // 读取指定索引处的字节。(position的值不变)
get(byte[] dst, int offset, int length) // 此方法将此缓冲区的字节传输到给定的目标数组中
get(byte[] dst) // 此方法将此缓冲区的字节传输到给定的目标数组中
Buffer flip()
// 反转此缓冲区。首先将limit设置为当前位置,然后将position设置为 0。
// 上面的意思就是把limit的值设置为position,然后position的值设置为0。
// 如果已定义了mark,则丢弃该mark。( 修改 position为0 limit为之前position的位置 mark = -1 )
Buffer rewind() // 重绕此缓冲区。将position设置为 0 并丢弃mark。
Buffer clear() // 清除此缓冲区,将position设置为 0,将limit设置为capacity,并丢弃mark。 mark=-1
Buffer reset() // 将位置 position 转到以前设置的 mark 所在的位置
flip() VS rewind() VS clear()
eg:
// 1. 创建缓冲区对象
ByteBuffer buffer = ByteBuffer.allocate(10);
System.out.println(buffer);
// 2. put(byte[] src)
buffer.put("abcd".getBytes());
System.out.println(buffer);
// 3. 读写模式的切换flip
buffer.flip();
System.out.println(buffer);
// 4. 取数据get方法
byte b = buffer.get();
System.out.println(b);
System.out.println(buffer);
java.nio.channels
包定义 的。Channel 表示IO 源与目标打开的连接。
Channel 类似于传统的“流” 。
只不过 Channel 本身不能直接访问数据,Channel 只能与 Buffer 进行交互。
用于读取、写入、映射和操作文件的通道
文件通道在其文件中有一个当前 position,可对其进行查询和修改。
实例化FileChannel(无法new)
RandomAccessFile
(可读可写) 双工FileInputStream
(可读 单向) 单工FileOutputStream
(可写 单向)eg:
FileChannel inChannel = new FileInputStream("a.mp4").getChannel();
FileChannel outChannel = new FileOutputStream("a.mp4").getChannel();
FileChannel rafIn = new RandomAccessFile("aa.mp4", "rw").getChannel();
FileChannel
中的map()
方法将文件的部分或全部映射到内存
映射文件的三种模式(MapMode)
READ_ONLY
READ_WRITE
PRIVATE
请求的映射模式将受到被调用map方法FileChannel对象的访问权限所限制
eg:
// 1. 创建文件映射对象
FileChannel channel =
new RandomAccessFile("D:\\Java_test\\a.txt", "rw").getChannel();
// 2. map方法进行映射
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, channel.size());
// 3. 获取内存中的数据
byte[] bytes = new byte[4];
buffer.get(bytes);
System.out.println(new String(bytes));
// 4. 修改内存中的数据
buffer.put(0,((byte) 98));
数据传输:
eg:
// 创建通道
FileChannel inChannel = new FileInputStream("a.txt").getChannel();
// 创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(64);
// 向缓冲区写数据
buffer.put("hello".getBytes());
buffer.flip();
// 向通道中写数据
inChannel.write(buffer);
// 将数据从通道读入到缓冲区
inChannel.read(buffer);
buffer.flip();
// 把数据从缓冲区取出来
byte[] bytes = new byte[1024];
buffer.get(bytes,0,buffer.limit());
System.out.println(new String(bytes,0,buffer.limit()));
eg:
/*
分散(Scatter)
*/
// 1. 创建一个通道
FileChannel inChannel = new FileInputStream("D:\\Java_test\\a.txt").getChannel();
// 2. 创建两个缓冲区
ByteBuffer buffer1 = ByteBuffer.allocate(64);
ByteBuffer buffer2 = ByteBuffer.allocate(64);
ByteBuffer[] byteBuffers = {buffer1,buffer2};
// 3. 将数据读入到缓冲区
long readCount = inChannel.read(byteBuffers);
System.out.println("readCount = " + readCount);
System.out.println("buffer1.capacity() = " + buffer1.capacity());
System.out.println("buffer1.position() = " + buffer1.position());
System.out.println("buffer2.capacity() = " + buffer2.capacity());
System.out.println("buffer2.position() = " + buffer2.position());
/*
聚集(Gather)
*/
// 创建1个通道
FileChannel channel = new FileOutputStream("D:\\Java_test\\a.txt").getChannel();
// 创建2个缓冲区
ByteBuffer buffer1 = ByteBuffer.allocate(64);
ByteBuffer buffer2 = ByteBuffer.allocate(64);
// 写入数据
buffer1.put("nihao".getBytes());
buffer1.flip();
buffer2.put("woshi".getBytes());
buffer2.flip();
ByteBuffer[] buffers = {buffer1, buffer2};
// 将多个Buffer写入Channel
channel.write(buffers);
通道之间进行传输(零拷贝:无需内核态与用户态切换)
transferForm(srcChannle,position,channelSize)
transferTo(position,channelSize,destChannel)
eg:
// transferForm(srcChannle,position,channelSize)
FileChannel inChannel = new FileInputStream("D:\\Java_test\\a.txt").getChannel();
FileChannel outChannel = new FileOutputStream("D:\\Java_test\\a_Copy.txt").getChannel();
outChannel.transferFrom(inChannel,0,inChannel.size());
// transferTo(position,channelSize,destChannel)
inChannel.transferTo(0,inChannel.size(),outChannel);
类型 | 字节输出流 | 字节输入流 | 字符输出流 | 字符输入流 |
---|---|---|---|---|
抽象基类 | OutputStream | InputStream | Writer | Reader |
文件相关 | FileOutputStream | FileInputStream | FileWriter | FileReader |
缓冲相关 | BufferedOutputStream | BufferedInputStream | BufferedWriter | BufferedReader |
转换相关 | OutputStreamWriter | InputStreamReader | ||
数据相关 | DataOutputStream | DataInputStream | ||
打印相关 | PrintStream | PrintWriter | ||
对象相关 | ObjectOutpuStream | ObjectInputStream |