1 装饰模式
Java中IO使用的是装饰模式,装饰模式在Android中很常见,比如系统的Context
。
装饰模式的模型
-
Component
:抽象构建接口。 -
ConcreteComponent
:具体的构建对象,实现组件对象接口,通常就是被装饰的原始对象。就对这个对象添加功能。 -
Decorator
:所有装饰器的抽象父类,需要定义 一个与组件接口一致的接口,内部持有一个Component
对象,就是持有一个被装饰的对象。 -
ConreteDecoratorA/ConreteDecoratorB
:实际的装饰器对象,实现具体添加功能。
2 流式部分
2.1 I/O体系
IO流需要站在内存的角度去理解:读入写出。
读入:将文件从外部读入内存中。
写出:从内存中生成一个文件。
2.2 字节流
BufferInputStream+FileInputStream
Buffer
:缓存,作用是提升性能,原理是减少磁盘的磁头操作次数。
只能读写byte
类型的数据。
File file = new File("txt/BufferedStreamTest.txt");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
bis.read();
BufferOutputStream+FileOutputStream
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();
完整示例:
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
可以读写所有基本类型的数据。
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
File file = new File("txt/DataStreamTest.txt");
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
DataOutputStream dos = new DataOutputStream(bos);
完整示例:
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
等。
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
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
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
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
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
方法,而这个方法也只适用于文件。
功能
可以用于多线程分段下载,断点续传。
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
,省去了非批量操作时的重复中间操作,操纵大文件时可 以显著提高效率。
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】,及时获取更多最新技术干货。