javaIO学习下:javase学习(三)

压缩(ZIP文档)

Java IO类库是支持读写压缩格式的数据流的。我们可以把一个或一批文件压缩成一个zip文档。这些压缩相关的流类是按字节处理的。先看下设计压缩解压缩的相关流类。

压缩类功能
CheckedInputStreamgetCheckSum()可以为任何InputStream产生校验和(不仅是解压缩)
CheckedOutputStreamgetCheckSum()可以为任何OutputStream产生校验和(不仅是压缩)
DeflaterOutputStream压缩类的基类
ZipOutputStream继承自DeflaterOutputStream,将数据压缩成Zip文件格式
GZIPOutputStream继承自DeflaterOutputStream,将数据压缩成GZIP文件格式
InflaterInputStream解压缩类的基类
ZipInputStream继承自InflaterInputStream,解压缩Zip文件格式的数据
GZIPInputStream继承自InflaterInputStream,解压缩GZIP文件格式的数据

表格中CheckedInputStream  和 CheckedOutputStream  一般会和Zip压缩解压过程配合使用,主要是为了保证我们压缩和解压过程数据包的正确性,得到的是中间没有被篡改过的数据。

我们以CheckedInputStream  为例,它的构造器需要传入一个Checksum类型:

1    public CheckedInputStream(InputStream in, Checksum cksum) {
2        super(in);
3        this.cksum = cksum;
4    }

而Checksum 是一个接口,可以看到这里又用到了策略模式,具体的校验算法是可以选择的。Java类库给我提供了两种校验和算法:Adler32 和 CRC32,性能方面可能Adler32 会更好一些,不过CRC32可能更准确。各有优劣吧。

好了,接下来看下压缩/解压缩流的具体使用。

将多个文件压缩成zip包

 1public class ZipFileUtils {
 2    public static void compressFiles(File[] files, String zipPath) throws IOException {
 3
 4        // 定义文件输出流,表明是要压缩成zip文件的
 5        FileOutputStream f = new FileOutputStream(zipPath);
 6
 7        // 给输出流增加校验功能
 8        CheckedOutputStream checkedOs = new CheckedOutputStream(f,new Adler32());
 9
10        // 定义zip格式的输出流,这里要明白一直在使用装饰器模式在给流添加功能
11        // ZipOutputStream 也是从FilterOutputStream 继承下来的
12        ZipOutputStream zipOut = new ZipOutputStream(checkedOs);
13
14        // 增加缓冲功能,提高性能
15        BufferedOutputStream buffOut = new BufferedOutputStream(zipOut);
16
17        //对于压缩输出流我们可以设置个注释
18        zipOut.setComment("zip test");
19
20        // 下面就是从Files[] 数组中读入一批文件,然后写入zip包的过程
21        for (File file : files){
22
23            // 建立读取文件的缓冲流,同样是装饰器模式使用BufferedReader
24            // 包装了FileReader
25            BufferedReader bfReadr = new BufferedReader(new FileReader(file));
26
27            // 一个文件对象在zip流中用一个ZipEntry表示,使用putNextEntry添加到zip流中
28            zipOut.putNextEntry(new ZipEntry(file.getName()));
29
30            int c;
31            while ((c = bfReadr.read()) != -1){
32                buffOut.write(c);
33            }
34
35            // 注意这里要关闭
36            bfReadr.close();
37            buffOut.flush();
38        }
39        buffOut.close();
40    }
41
42    public static void main(String[] args) throws IOException {
43        String dir = "d:";
44        String zipPath = "d:/test.zip";
45        File[] files = Directory.getLocalFiles(dir,".*\\.txt");
46        ZipFileUtils.compressFiles(files, zipPath);
47    }
48}

在main函数中我们使用了本文中 File其实是个工具类 章节里的Directory工具类。

解压缩zip包到目标文件夹

 1    public static void unConpressZip(String zipPath, String destPath) throws IOException {
 2        if(!destPath.endsWith(File.separator)){
 3            destPath = destPath + File.separator;
 4            File file = new File(destPath);
 5            if(!file.exists()){
 6                file.mkdirs();
 7            }
 8        }
 9        // 新建文件输入流类,
10        FileInputStream fis = new FileInputStream(zipPath);
11
12        // 给输入流增加检验功能
13        CheckedInputStream checkedIns = new CheckedInputStream(fis,new Adler32());
14
15        // 新建zip输出流,因为读取的zip格式的文件嘛
16        ZipInputStream zipIn = new ZipInputStream(checkedIns);
17
18        // 增加缓冲流功能,提高性能
19        BufferedInputStream buffIn = new BufferedInputStream(zipIn);
20
21        // 从zip输入流中读入每个ZipEntry对象
22        ZipEntry zipEntry;
23        while ((zipEntry = zipIn.getNextEntry()) != null){
24            System.out.println("解压中" + zipEntry);
25
26            // 将解压的文件写入到目标文件夹下
27            int size;
28            byte[] buffer = new byte[1024];
29            FileOutputStream fos = new FileOutputStream(destPath + zipEntry.getName());
30            BufferedOutputStream bos = new BufferedOutputStream(fos, buffer.length);
31            while ((size = buffIn.read(buffer, 0, buffer.length)) != -1) {
32                bos.write(buffer, 0, size);
33            }
34            bos.flush();
35            bos.close();
36        }
37        buffIn.close();
38
39        // 输出校验和
40        System.out.println("校验和:" + checkedIns.getChecksum().getValue());
41    }
42
43    // 在main函数中直接调用
44    public static void main(String[] args) throws IOException {
45        String dir = "d:";
46        String zipPath = "d:/test.zip";
47//        File[] files = Directory.getLocalFiles(dir,".*\\.txt");
48//        ZipFileUtils.compressFiles(files, zipPath);
49
50        ZipFileUtils.unConpressZip(zipPath,"F:/ziptest");
51    }

这里解压zip包还有一种更加简便的方法,使用ZipFile对象。该对象的entries()方法直接返回ZipEntry类型的枚举。看下代码片段:

1        ZipFile zipFile = new ZipFile("test.zip");
2        Enumeration e = zipFile.entries();
3        while (e.hasMoreElements()){
4            ZipEntry zipEntry = (ZipEntry) e.nextElement();
5            System.out.println("file:" + zipEntry);
6        }

对象序列化

什么是序列化和反序列化呢?

序列化就是将对象转成字节序列的过程,反序列化就是将字节序列重组成对象的过程。

在这里插入图片描述

为什么要有对象序列化机制

程序中的对象,其实是存在有内存中,当我们JVM关闭时,无论如何它都不会继续存在了。那有没有一种机制能让对象具有“持久性”呢?序列化机制提供了一种方法,你可以将对象序列化的字节流输入到文件保存在磁盘上。

序列化机制的另外一种意义便是我们可以通过网络传输对象了,Java中的 远程方法调用(RMI),底层就需要序列化机制的保证。

在Java中怎么实现序列化和反序列化

首先要序列化的对象必须实现一个Serializable接口(这是一个标识接口,不包括任何方法)

1public interface Serializable {
2}

其次需要是用两个对象流类:ObjectInputStream 和ObjectOutputStream主要使用ObjectInputStream对象的readObject方法读入对象、ObjectOutputStream的writeObject方法写入对象到流中

下面我们通过序列化机制将一个简单的pojo对象写入到文件,并再次读入到程序内存。

 1public class User implements Serializable {
 2    private String name;
 3    private int age;
 4
 5    public User(String name, int age) {
 6        this.name = name;
 7        this.age = age;
 8    }
 9
10    @Override
11    public String toString() {
12        return "User{" +
13                "name='" + name + '\'' +
14                ", age='" + age + '\'' +
15                '}';
16    }
17
18    public static void main(String[] args) throws IOException, ClassNotFoundException {
19        User user = new User("二营长",18);
20        ObjectOutputStream objectOps = new ObjectOutputStream(new FileOutputStream("f:/user.out"));
21        objectOps.writeObject(user);
22        objectOps.close();
23
24        // 再从文件中取出对象
25        ObjectInputStream objectIns = new ObjectInputStream(new FileInputStream("f:/user.out"));
26
27        // 这里要做一次强转
28        User user1 = (User) objectIns.readObject();
29        System.out.println(user1);
30        objectIns.close();
31    }
32}
33

程序运行结果:

1User{name='二营长', age='18'}

不想序列化的数据使用transient(瞬时)关键字屏蔽

如果我们上面的user对象有一个password字段,属于敏感信息,这种是不能走序列化的方式的,但是实现了Serializable 接口的对象会自动序列化所有的数据域,怎么办呢?在password字段上加上关键字transient就好了。

1 private transient String password;

序列化机制就简单介绍到这里吧。这是Java原生的序列化,现在市面上有好多序列化协议可以选择,比如Json、FastJson、Thrift、Hessian 、protobuf等

I/O流的典型使用方式

IO流种类繁多,可以通过不同的方式组合I/O流类,但平时我们常用的也就几种组合。下盘通过示例的方式盘点几种I/O流的典型用法。

缓冲输入文件

 1public class BufferedInutFile {
 2    public static String readFile(String fileName) throws IOException {
 3        BufferedReader bf = new BufferedReader(new FileReader(fileName));
 4        String s;
 5
 6        // 这里读取的内容存在了StringBuilder,当然也可以做其他处理
 7        StringBuilder sb = new StringBuilder();
 8        while ((s = bf.readLine()) != null){
 9            sb.append(s + "\n");
10        }
11        bf.close();
12        return sb.toString();
13    }
14
15    public static void main(String[] args) throws IOException {
16        System.out.println(BufferedInutFile.readFile("d:/1.txt"));
17    }
18}

格式化内存输入

要读取格式化的数据,可以使用DataInputStream。

 1public class FormattedMemoryInput {
 2    public static void main(String[] args) throws IOException {
 3        try {
 4            DataInputStream dataIns = new DataInputStream(
 5                    new ByteArrayInputStream(BufferedInutFile.readFile("f:/FormattedMemoryInput.java").getBytes()));
 6            while (true){
 7                System.out.print((char) dataIns.readByte());
 8            }
 9        } catch (EOFException e) {
10            System.err.println("End of stream");
11        }
12    }
13}

上面程序会在控制台输出当前类本身的所有代码,并且会抛出一个EOFException异常。抛出异常的原因是已经到留的结尾了还在读数据。这里可以使用available()做判断还有多少可以的字符。

 1package com.herp.pattern.strategy;
 2
 3import java.io.ByteArrayInputStream;
 4import java.io.DataInputStream;
 5import java.io.IOException;
 6
 7public class FormattedMemoryInput {
 8    public static void main(String[] args) throws IOException {
 9        DataInputStream dataIns = new DataInputStream(
10                new ByteArrayInputStream(BufferedInutFile.readFile("FormattedMemoryInput.java").getBytes()));
11        while (true){
12            System.out.println((char) dataIns.readByte());
13        }
14    }
15}

基本的文件输出

FileWriter对象可以向文件写入数据。首先创建一个FileWriter和指定的文件关联,然后使用BufferedWriter将其包装提供缓冲功能,为了提供格式化机制,它又被装饰成为PrintWriter

 1public class BasicFileOutput {
 2    static String file = "BasicFileOutput.out";
 3
 4    public static void main(String[] args) throws IOException {
 5        BufferedReader in = new BufferedReader(new StringReader(BufferedInutFile.readFile("f:/BasicFileOutput.java")));
 6        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
 7
 8        int lineCount = 1;
 9        String s;
10        while ((s = in.readLine()) != null){
11            out.println(lineCount ++ + ": " + s);
12        }
13        out.close();
14        in.close();
15    }
16}

下面是我们写出的BasicFileOutput.out文件,可以看到我们通过代码字节加上了行号

 11: package com.herp.pattern.strategy;
 22: 
 33: import java.io.*;
 44: 
 55: public class BasicFileOutput {
 66:     static String file = "BasicFileOutput.out";
 77: 
 88:     public static void main(String[] args) throws IOException {
 99:         BufferedReader in = new BufferedReader(new StringReader(BufferedInutFile.readFile("f:/BasicFileOutput")));
1010:         PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
1111: 
1212:         int lineCount = 1;
1313:         String s;
1414:         while ((s = in.readLine()) != null){
1515:             out.println(lineCount ++ + ": " + s);
1616:         }
1717:         out.close();
1818:         in.close();
1919:     }
2020: }

数据的存储和恢复

为了输出可供另一个“流”恢复的数据,我们需要使用DataOutputStream写入数据,然后使用DataInputStream恢复数据。当然这些流可以是任何形式(这里的形式其实就是我们前面说过的流的两端的类型),比如文件。

 1public class StoringAndRecoveringData {
 2    public static void main(String[] args) throws IOException {
 3        DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("data.txt")));
 4        out.writeDouble(3.1415926);
 5        out.writeUTF("三连走起");
 6        out.writeInt(125);
 7        out.writeUTF("点赞加关注");
 8        out.close();
 9
10        DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("data.txt")));
11        System.out.println(in.readDouble());
12        System.out.println(in.readUTF());
13        System.out.println(in.readInt());
14        System.out.println(in.readUTF());
15        in.close();
16    }
17}

输出结果:

13.1415926
2三连走起
3125
4点赞加关注

需要注意的是我们使用writeUTF()和readUTF()来写入和读取字符串。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

only-qi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值