让你再也忘不了IO相关知识-Java IO图文详解
theme: cyanosis highlight: a11y-dark
1 装饰模式
Java中IO使用的是装饰模式,装饰模式在Android中很常见,比如系统的Context
。
装饰模式的模型
Component
:抽象构建接口。ConcreteComponent
:具体的构建对象,实现组件对象接口,通常就是被装饰的原始对象。就对这个对象添加功能。Decorator
:所有装饰器的抽象父类,需要定义 一个与组件接口一致的接口,内部持有一个Component
对象,就是持有一个被装饰的对象。ConreteDecoratorA/ConreteDecoratorB
:实际的装饰器对象,实现具体添加功能。
2 流式部分
2.1 I/O体系
IO流需要站在内存的角度去理解:读入写出。
读入:将文件从外部读入内存中。
写出:从内存中生成一个文件。
2.2 字节流
BufferInputStream+FileInputStream
Buffer
:缓存,作用是提升性能,原理是减少磁盘的磁头操作次数。
只能读写byte
类型的数据。
Java
File file = new File("txt/BufferedStreamTest.txt");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
bis.read();
BufferOutputStream+FileOutputStream
Java
File file = new File("txt/BufferedStreamTest.txt");
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
bos.write(byteArray[0]);
bos.write(byteArray, 1, byteArray.length - 1);
//flush()的作用是强制进行一次IO,比如最后写出的数据不够缓存指定的长度,就需要强制的执行一次IO
bos.flush();
bos.close();
完整示例:
```Java package site.exciter.learn.io;
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException;
public class BufferedStreamTest { private static final byte[] byteArray = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A };
public static void main(String[] args) {
testBufferedInputStream();
// testBufferedOutputStream();
}
private static void testBufferedInputStream() {
try {
File file = new File("txt/BufferedStreamTest.txt");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
//读取前10个字节并输出在控制台
for (int i = 0; i < 10; i++) {
if (bis.available() >= 0) {
System.out.println(byteToString((byte) bis.read()));
}
}
//在此输入流中标记当前的位置,对reset方法的后续调用会在最后标记的位置重新定位此流,以便后续重新读取相同的字节。
bis.mark(2000);
//跳过并从输入流中丢弃10字节的数据。
bis.skip(10);
//跳10个字节之后读取剩余的部分
byte[] b = new byte[1024];
int n1 = bis.read(b, 0, b.length);
System.out.println("n1的值为:" + n1);
printByteValue(b);
//调用reset之后,再次读取会从mark标记的位置开始,也就是从第10个字节开始读取。
bis.reset();
int n2 = bis.read(b, 0, b.length);
System.out.println("n2的值为:" + n2);
printByteValue(b);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void testBufferedOutputStream() {
try {
File file = new File("txt/BufferedStreamTest.txt");
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
bos.write(byteArray[0]);
bos.write(byteArray, 1, byteArray.length - 1);
bos.flush();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static String byteToString(byte b) {
byte[] bArray = {b};
return new String(bArray);
}
private static void printByteValue(byte[] buf) {
for (byte b : buf) {
if (b != 0) {
System.out.println(byteToString(b) + " ");
}
}
}
} ```
DataInputStream+BufferedInputStream+FileInputStream
可以读写所有基本类型的数据。
Java
File file = new File("txt/DataStreamTest.txt");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
DataOutputStream+BufferedOutputStream+FileOutputStream
Java
File file = new File("txt/DataStreamTest.txt");
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
DataOutputStream dos = new DataOutputStream(bos);
完整示例:
```Java package site.exciter.learn.io;
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException;
public class DataStreamTest { public static void main(String[] args) { // testDataInputStream(); testDataOutputStream(); }
private static void testDataInputStream() {
try {
File file = new File("txt/DataStreamTest.txt");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
System.out.println(Long.toHexString(dis.readLong()));
System.out.println(dis.readBoolean());
System.out.println(byteToString(dis.readByte()));
System.out.println(charToString(dis.readChar()));
System.out.println(shortToString(dis.readShort()));
System.out.println(Integer.toHexString(dis.readInt()));
System.out.println(Long.toHexString(dis.readLong()));
System.out.println(dis.readUTF());
System.out.println(Long.toHexString(dis.readLong()));
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void testDataOutputStream() {
try {
File file = new File("txt/DataStreamTest.txt");
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
DataOutputStream dos = new DataOutputStream(bos);
dos.writeBoolean(true);
dos.writeByte((byte) 0x41);
dos.writeChar((char) 0x4243);
dos.writeShort((short) 0x4445);
dos.writeInt(0x12345678);
dos.writeLong(0x987654321L);
dos.writeUTF("abcdefg");
dos.writeLong(0x23433L);
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static String byteToString(byte val) {
return Integer.toHexString(val & 0xff);
}
private static String charToString(char val) {
return Integer.toHexString(val);
}
private static String shortToString(short val) {
return Integer.toHexString(val & 0xffff);
}
} ```
ObjectInputStream+BufferedInputStream+FileInputStream
可以读写对象,包含Object
、Array
、List
等。
```Java FileInputStream fis = new FileInputStream(newFile("txt/Object.txt")); BufferedInputStream bis = new BufferedInputStream(fis); ObjectInputStream ois = new ObjectInputStream(bis);
while (ois.available() != -1) { Object object = ois.readObject(); Book book = (Book) object; System.out.println(book.toString()); }
ois.close(); ```
ObjectOutputStream+BufferedOutputStream+FileOutputStream
```Java FileOutputStream fos = new FileOutputStream("txt/Object.txt"); BufferedOutputStream bos = new BufferedOutputStream(fos); ObjectOutputStream oos = new ObjectOutputStream(bos);
for (int i = 0; i < 10; i++) { oos.writeObject(new Book("三国演义", 102.0f)); }
oos.close(); ```
2.3 字符流
BufferedReader+InputStreamReader+FileInputStream
```Java FileInputStream fis = new FileInputStream("txt/InputStreamWriterTest.txt"); InputStreamReader isr = new InputStreamReader(fis); BufferedReader br = new BufferedReader(isr);
String str; while ((str = br.readLine()) != null) { System.out.println(str); } ```
BufferedWriter+OutputStreamWriter+FileOutputStream
```Java File file = new File("txt/OutputStreamWriterTest.txt"); FileOutputStream fos = new FileOutputStream(file); OutputStreamWriter osw = new OutputStreamWriter(fos); BufferedWriter bw = new BufferedWriter(osw);
bw.write("new bee"); bw.flush(); bw.close(); ```
使用上述方式就可以读写文件,但是过程过于繁琐,可以使用FileReader/FileWriter来简化流程。
BufferedReader+FileReader/BufferedWriter+FileWriter
```Java File srcFile = new File("txt/BufferedReaderTest.txt"); File dstFile = new File("txt/BufferedWriterTest.txt");
FileReader fr = new FileReader(srcFile); BufferedReader br = new BufferedReader(fr); FileWriter fw = new FileWriter(dstFile); BufferedWriter bw = new BufferedWriter(fw);
char[] str = new char[1024];
while ((br.read(str)) != -1) { bw.write(str); }
br.close(); bw.flush(); bw.close(); ```
2.4 InputStream-Reader
2.5 OutputStream-Writer
3 非流式部分
3.1 RandomAccessFile
特点
1、既可以读也可以写。
RandomAccessFile
不属于InputStream
和OutputStream
类系的它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是自己从头开始规定的,这里面包含读写两种操作。
2、可以指定位置读写。
RandomAccessFile
能在文件里面前后移动,在文件里移动用的seek()
,所以它的行为与其它的I/O类 有些根本性的不同。总而言之,它是一个直接继承Object
的,独立的类。只有RandomAccessFile
才有seek
方法,而这个方法也只适用于文件。
功能
可以用于多线程分段下载,断点续传。
```Java RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.seek(10); raf.setLength(1000);
raf.writeBoolean(true); raf.writeChar('a'); ```
RandomAccessFile(File file, String mode)
参数 mode 的值可选 "r":可读,"w" :可写,"rw":可读性。
seek(int index)
:可以将指针移动到某个位置开始读写。
setLength(long len)
:给写入文件预留空间。
3.2 NIO-FileChannel
Channel是对I/O操作的封装。
FileChannel
配合着ByteBuffer
,将读写的数据缓存到内存中,然后以批量/缓 存的方式read/write
,省去了非批量操作时的重复中间操作,操纵大文件时可 以显著提高效率。
```Java package site.exciter.learn.io;
import android.os.Build;
import androidx.annotation.RequiresApi;
import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.time.Duration; import java.time.Instant;
public class FileChannelTest { @RequiresApi(api = Build.VERSION_CODES.O) public static void main(String[] args) { File sourceFile = new File("/Users/exciter/Desktop/test-01.zip"); File targetFile = new File("/Users/exciter/Desktop/test-02.zip"); targetFile.deleteOnExit(); try { targetFile.createNewFile(); } catch (Exception e) { e.printStackTrace(); }
copyFileByFileChannel(sourceFile, targetFile);
}
@RequiresApi(api = Build.VERSION_CODES.O)
private static void copyFileByFileChannel(File sourceFile, File targetFile) {
Instant begin = Instant.now();
RandomAccessFile sourceRaf = null;
RandomAccessFile targetRaf = null;
try {
sourceRaf = new RandomAccessFile(sourceFile, "r");
targetRaf = new RandomAccessFile(targetFile, "rw");
} catch (Exception e) {
e.printStackTrace();
}
FileChannel sourceFileChannel = sourceRaf.getChannel();
FileChannel targetFileChannel = targetRaf.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024);
try {
while (sourceFileChannel.read(byteBuffer) != -1) {
byteBuffer.flip();
targetFileChannel.write(byteBuffer);
byteBuffer.clear();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
sourceFileChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
targetFileChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("time:" + Duration.between(begin, Instant.now()).toMillis());
}
} ```
关注木水小站 (zhangmushui.cn)和微信公众号【木水Code】,及时获取更多最新技术干货。
- Handler详解和源码分析
- SpringMVC的@RequestMapping用法
- JSON的4种解析方式使用和对比
- ColorTransferTextView:一个颜色可以变化的TextView
- 自定义一个JSON解析库
- 并发编程-线程的并发工具类
- JVM内存管理
- Java泛型详解
- 并发编程-看完这篇,所有Java并发编程的问题你都能应对自如!
- 并发编程-线程的启动、死锁、线程安全、ThreadLocal
- Android虚拟机与类加载机制
- Jetpack系列-WorkManager使用和源码分析
- 让你再也忘不了IO相关知识-Java IO图文详解
- Jetpack系列-Navigation使用和源码分析
- Jetpack系列-Room使用和源码分析
- Jetpack系列-DataBinding使用和源码分析
- Jetpack系列-LiveData二次封装,去除粘性数据
- 使用反射和动态代理实现一个View注解绑定库
- Jetpack系列-LiveData使用和源码分析
- Jetpack系列-Lifecycle使用和源码分析