让你再也忘不了IO相关知识-Java IO图文详解

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

可以读写对象,包含ObjectArrayList等。

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不属于InputStreamOutputStream类系的它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是自己从头开始规定的,这里面包含读写两种操作。

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】,及时获取更多最新技术干货。

猜你喜欢

转载自juejin.im/post/7098880733682335774