Java I/O

时间:2019-11-05
本文章向大家介绍Java I/O,主要包括Java I/O使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

I/O分类

流的作用:为数据源和目的地建立一个输送通道。


磁盘操作:File
    File 类可以用于表示文件和目录的信息,但是它不表示文件的内容。


字节操作:InputStream 和 OutputStream(二进制格式操作)
    InputStream:抽象类。基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。
    OutputStream:抽象类。基于字节的输出操作,是所有输出流的父类。定义了所有输出流都具有的共同特征。


字符操作:Reader 和 Writer(文件格式操作)
    Reader:抽象类,基于字符的输入操作。
    Writer:抽象类,基于字符的输出操作。


RandomAccessFile(随机文件操作):

    一个独立的类,直接继承至Object.它的功能丰富,可以从文件的任意位置进行存取(输入输出)操作。

对象操作:Serializable


网络操作:Socket


新的输入/输出:NIO

I/O流分类

根据处理数据类型的不同分为:字符流和字节流

据数据流向不同分为:输入流和输出流

按数据来源(去向)分类:

    1、File(文件): FileInputStream, FileOutputStream, FileReader, FileWriter

    2、byte[]: ByteArrayInputStream, ByteArrayOutputStream

    3、Char[]:  CharArrayReader,CharArrayWriter

    4、管道操作: PipedInputStream、PipedOutputStream、PipedReader、PipedWriter

    5、网络数据流: InputStream,OutputStream, Reader, Writer

    6、打印:PrintStream、PrintWriter

    7、对象序列化反序列化:ObjectInputStream、ObjectOutputStream

    8、转换:InputStreamReader、OutputStreWriter

    9、缓冲操作:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter

字符流与字节流

字节流读取单个字节,字符流读取单个字符。


字符转字节:
    byte[] bytes = string.getBytes("UTF-8");
字节转字符:
    String s = new String(byte bytes[], int offset, int length, String charsetName);


字符流使用了缓冲区(关闭字符流时会强制性地将缓冲区中的内容进行输出),而字节流没有使用缓冲区(不关闭字节流也可以输出):
    在字符流的操作中,所有的字符都是在内存中形成的,在输出前会将所有的内容暂时保存在内存之中,所以使用了缓冲区暂存数据。
    如果想在不关闭时也可以将字符流的内容全部输出,则可以使用Writer类中的flush()方法完成。


InputStreamReader(InputStream in):将字节流以字符流输入。
OutputStreamWriter(OutputStream out):将字节流以字符流输出。

将UTF-8转换成GBK:

    FileInputStream fis = new FileInputStream("a.txt");
    InputStreamReader isr = new InputStreamReader(fis, "UTF-8");

    FileOutputStream fos = new FileOutputStream("b.txt");
    OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");


只要是处理纯文本数据,就优先考虑使用字符流。除此之外都使用字节流。

装饰(Decorator)设计模式

在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。

适用性:
    需要扩展一个类的功能,或给一个类添加附加职责。
    当不能采用生成子类来实现,比如final类。


装饰者和被装饰者之间必须是一样的类型,也就是要有共同的超类


抽象构件(Component)

public abstract class InputStream implements Closeable {
    public abstract int read() throws IOException;
}


具体构件(Concrete Component)

public class FileInputStream extends InputStream {
    public int read() throws IOException {
        return read0();
    }
    private native int read0() throws IOException;
}


抽象装饰(Decorator):内部一定要有一个指向具体构件对象的引用

public class FilterInputStream extends InputStream {
    protected volatile InputStream in;

    protected FilterInputStream(InputStream in) {
        this.in = in;
    }

    public int read(byte b[], int off, int len) throws IOException {
        return in.read(b, off, len);
    }
}


具体装饰(ConcreteDecorator):添加附加的责任

public class BufferedInputStream extends FilterInputStream {
    private static int DEFAULT_BUFFER_SIZE = 8192;
    protected volatile byte[] buf;

    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
}

文件复制

public class CopyTest {

    public static void main(String[] args) throws IOException {
        File file1 = new File("E:\\PE系统.rar");
        File file2 = new File("E:\\PE系统_copy.rar");
        copyTest(file1, file2);
    }

    private static void copyTest(File file1, File file2) throws IOException {
        FileInputStream fis = null; //输入流
        FileOutputStream fos = null;    //输出流

        try {
            fis = new FileInputStream(file1);
            fos = new FileOutputStream(file2);

            //创建缓冲流,提高字节流的操作效率
            BufferedInputStream bis = new BufferedInputStream(fis);
            BufferedOutputStream bos = new BufferedOutputStream(fos);

            int len = 0;
            byte[] bytes = new byte[4096];
            //返回 -1 的时候表示读到 eof,即文件尾
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes, 0, len);
                bos.flush();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (fis != null)
                fis.close();
            if (fos != null)
                fos.close();
        }
    }
}

文件分割

class SplitFile {
    public static void main(String[] args) throws Exception {
        /**
         * 文件切割器
         */
        File file = new File("E:\\1.mp4");
        File desFile = new File("E:\\split");
        if (!desFile.exists()) {
            desFile.mkdirs();
        }
        splitFile(file, desFile);
    }

    private static void splitFile(File file, File desFile) throws IOException {
        // 读取源文件
        FileInputStream fis = new FileInputStream(file);
        // 创建目的(输出流每生成一个文件都需要重新构建一下)
        FileOutputStream fos = null;

        // 创建一个缓冲区
        byte[] by = new byte[1024 * 1024 * 10];
        int len = 0;
        int count = 0;
        while ((len = fis.read(by)) != -1) {
            // 创建输出流,明确写入文件的对象
            File partFile = new File(desFile, (++count) + ".part");
            fos = new FileOutputStream(partFile);
            fos.write(by, 0, len);
            fos.close();
        }

        // 创建配置文件
        File configFile = new File(desFile, "config.properties");
        if (!configFile.exists()) configFile.createNewFile();
        // 载入配置文件
        Properties pro = new Properties();
        pro.setProperty("partcount", Integer.toString(count));
        pro.setProperty("filename", file.getAbsolutePath());
        //输出配置文件
        fos = new FileOutputStream(configFile);
        pro.store(fos, "切割文件配置信息");
        fos.close();
        fis.close();
    }
}

压缩文件

class ZipTest {
    public static void main(String[] args) throws IOException {
        File file1 = new File("E:\\表白神器1");  //源文件
        String path = file1.getName();  //源文件名
        File file2 = new File("E:\\表白神器.zip");  //目的文件

        //打开输出流
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(file2));

        //需要用到递归
        zip(zos, file1, path);
        zos.close();
    }

    private static void zip(ZipOutputStream zos, File file1, String path) throws IOException {
        if (file1.isDirectory()) {
            File[] filer = file1.listFiles();   //当前目录下的所有文件or目录
            if (filer.length == 0) {    //当前目录是空目录
                zos.putNextEntry(new ZipEntry(path + "/"));    //如果文件夹为空,则只需写入一个目录
            } else {    //如果文件夹不为空,则递归调用,文件夹中的每一个文件(或文件夹)进行压缩
                for (File f : filer) {
                    zip(zos, f, path + "/" + f.getName());
                    System.out.println(path + "/" + f.getName());
                }
            }
        } else {
            zos.putNextEntry(new ZipEntry(path));   //写入文件(表白神器/新建文件夹/表白神器.TXT)

            //打开输入流
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file1));
            //输出到Zip文件
            byte[] by = new byte[1024];
            int len;
            while ((len = bis.read(by)) != -1) {
                zos.write(by, 0, len);
                zos.flush();
            }
            bis.close();
        }
    }
}

装饰者模式

Java I/O 使用了装饰者模式来实现。

InputStream 是抽象组件;

FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作;

FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能:
    例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。


FileInputStream fileInputStream = new FileInputStream(filePath);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

字符操作

编码与解码:
    编码就是把字符转换为字节,而解码是把字节重新组合成字符。


char 类型占 16 位,也就是两个字节,Java 使用这种双字节编码是为了让一个中文或者一个英文都能使用一个 char 来存储。


Reader 与 Writer:
    不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符。
    但是在程序中操作的通常是字符形式的数据,因此需要提供对字符进行操作的方法。

InputStreamReader 实现从字节流解码成字符流。
OutputStreamWriter 实现字符流编码成为字节流。


逐行读取文件内容

    public static void readFileContent(String filePath) throws IOException {

        FileReader fileReader = new FileReader(filePath);
        BufferedReader bufferedReader = new BufferedReader(fileReader);

        String line;
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }

        // 装饰者模式使得 BufferedReader 组合了一个 Reader 对象
        // 在调用 BufferedReader 的 close() 方法时会去调用 Reader 的 close() 方法
        // 因此只要一个 close() 调用即可
        bufferedReader.close();
    }

对象操作

序列化:
    序列化就是将一个对象转换成字节序列,方便存储和传输。

序列化:ObjectOutputStream.writeObject()
反序列化:ObjectInputStream.readObject()


不会对静态变量进行序列化,因为序列化只是保存对象的状态,静态变量属于类的状态。


Serializable:
    序列化的类需要实现 Serializable 接口。


transient 关键字可以使一些属性不会被序列化。

ArrayList 中存储数据的数组 elementData 是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,
因此就不需要所有的内容都被序列化。
通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。
private transient Object[] elementData;

网络操作

InetAddress:用于表示网络上的硬件资源,即 IP 地址
    没有公有的构造函数,只能通过静态方法来创建实例:
        InetAddress.getByName(String host);
        InetAddress.getByAddress(byte[] address);

URL:统一资源定位符

Sockets:使用 TCP 协议实现网络通信

Datagram:使用 UDP 协议实现网络通信


URL:可以直接从 URL 中读取字节流数据。

public static void main(String[] args) throws IOException {

    URL url = new URL("http://www.baidu.com");

    /* 字节流 */
    InputStream is = url.openStream();

    /* 提供缓存功能 */
    BufferedReader br = new BufferedReader(new InputStreamReader(is, "utf-8"));

    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }

    br.close();
}

原文地址:https://www.cnblogs.com/loveer/p/11797891.html