《Thinking in Java》学习——18章Java I/O系统(四)

压缩

1.Java I/O类库中的类支持读写压缩格式的数据流。由于压缩类库是按字节方式处理的而不是字符方式,因此这些类不适从ReaderWriter派生而来,而是属于InputStreamOutputStream继承结构的一部分。

压缩类 功能
CheckedInputStream GetCheckSum()为任何InputStream产生校验和(不仅是解压缩)
CheckedOutputStream GetCheckSum为任何OutputStream产生校验和(不仅是压缩)
DeflaterOutputStream 压缩的基类
ZipOutputStream 一个DeflaterOutputStream ,用于将数据压缩成Zip文件格式
GZIPOutputStream 一个DeflaterOutputStream,用于将数据压缩成GZIP格式
InflaterInputStream 解压缩的基类
ZipInputStream 一个InflaterInputStream,用于解压缩Zip文件格式的数据
GZIPInputStream 一个InflaterInputStream,用于解压缩GZIP文件格式的数据
一.用GZIP进行简单压缩

1.GZIP接口非常简单,因此如果我们只想对单个数据流进行压缩,那么它可能是比较适合的选择:

public class GZIPCompress {
    public static void main(String... args) throws IOException {
        if (args.length == 0) {
            System.exit(1);
        }
        BufferedReader in = new BufferReader(new FileReader(args[0]));
        BufferedOutputStream out = new BufferedOutputStream(
            new GZIPOutputStream(new FileOutputStream("test.gz")));
        int c;
        while ((c = in.read()) != -1) {
            out.wirte(c);
        }
        in.close();
        out.close();
    }
}
二.用Zip进行多文件保存

1.支持Zip格式的Java库更加全面,利用该库可以方便地保存多个文件:

public class ZipCompress {
    public static void main(String... args) throws IOException {
        FileOutputStream f = new FileOutputStream("test.zip");
        CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
        ZipOutputStream zos = new ZipOutputStream(csum);
        BufferedOutputStream out = new BufferedOutputStream(zos);
        zos.setComment("A test of Java Zipping");
        for (String arg : args) {
            BufferedReader in = new BufferedReader(new FileReader(arg));
            zos.putNextEntity(new ZipEntity(arg));
            int c;
            while ((c = in.read()) != -1) {
                out.write(c);
            }
            in.close();
            out.flush();
        }
        out.close();
        FileInputStream fi = new FileInputStream("test.zip");
        CheckedInputStream csumi = new CheckInputStream(fi, new Adler32());
        ZipInputStream in2 = new ZipInputStream(csumi);
        BufferedInputStream bis = new BufferedInputStream(in2);
        ZipEntity ze;
        while ((ze = in2.getNextEntity) != null) {
            System.out.println("Reading file " + ze);
            int x;
            while ((x = bis.read()) != -1) {
                System.out.write(x);
            }
            bis.close();
            ZipFile zf = new ZipFile("test.zip");
            Enumeration e = zf.entries();
            while (e.hasMoreElements()) {
                ZipEntity ze2 = (ZipEntity) e.nextElement();
                System.out.println("File: " + ze2);
            }
        }
    }
}

2.虽然上面的代码只展示了一种类型,但是一共有两种Checksum类型:Adler32(它快一些)和CRC32(慢一些,但更准确)。
3.对于每一个要加入压缩档案的文件,都必须调用putNextEntity(),并将其传递给一个ZipEntity对象。ZipEntity对象包含了一个功能很广的接口,允许你获取和设置Zip文件内该特定项上所有可利用的数据:名字、压缩的和未压缩的文件大小、日期、CRC校验和、】额外字段数据、注释、压缩方法以及它是否是一个目录入口等等。
4.为了能够解压缩文件,ZipInputStream提供了一个getNextEntity()方法返回下一个ZIpEntity(如果存在的话)。解压缩文件有一个更简便的方法——利用ZipFile对象读取文件。该对象有一个entries()方法用来向ZipEntries返回一个Enumeration(枚举)。

三.Java档案文件

1.Zip格式也被应用于JAR文件格式中。这种文件格式就像Zip一样,可以将一组文件压缩到单个压缩文件中,另外一个JAR文件还有一张描述了所有文件的“文件清单”(可自行创建文件清单,也可以由jar程序自动生成)。
2.JDK自带的jar程序可根据我们的选择自动压缩文件。可以使用命令行的形式调试它:

jar [options] destination [manifest] inputfile(s)

其中option只是一个字母集合,选项有:

option 意义
c 创建一个新的或空的压缩文档
t 列出目录表
x 解压所有文件
x file 解压该文件
f 意指:“我打算指定一个文件名。”如果没有用这个选项,jar假设所有的输入都来自于标准输入;或者在创建一个文件时,输出对象也假设为标准输出
m 表示第一个参数将是用户自建的清单文件的名字
v 产生详细输出,描述jar所做的工作
O 只储存文件,不压缩文件
M 不自动创建文件清单

对象序列化

1.Java的对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,并能够将这个字节序列完全恢复为原来的对象。
2.要序列化一个对象,首先要创建某些OupputStream对象,然后将其封装在一个ObjectOutputStream对象内。这时,只需调用writeObject()即可将对象序列化,并将其发送给OutputStream。要反向进行该过程,需要将一个InputStream封装在ObjectInputStream内,然后调用readObject()。我们最后得到的是一个引用,它指向一个向上转型的Object,所以必须向下转型才能直接使用它。

二.序列化控制

1.默认序列化并不难操作,但在一些特殊情况下,需要通过实现Externalizable接口——代替实现Serializable接口——来对序列化过程进行控制。这个Exterbalizable接口继承了Serializable接口,同时添加了两个方法:writeExternal()readExternal()。着两个方法会在序列化和反序列化还原的过程中被自动调用,以便执行一些特殊操作:

class Blip implements Externalizable {
    public Blip () {
        System.out.println("Blip Constructor");
    }
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("Blip.writeExternal");
    }

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("Blip.readExternal");
    }
}

public class Blips {
    public static void main(String... args) {
        Blip b = new Blip();
        ObjectOutputStream o = new ObjectOutputStream(
            new FileOutputStream("Blips.out"));
        o.writeObject(b);
        o.close();
        ObjectInputStream in = new ObjectInputStream(
            new FileInputStream("Blip.out"));
        b = (Blip) in.readObject();
    }
}
/*
Output:
Blip Constructor
Blip.writeExternal
Blip Constructor
Blip.readExternal
*/

2.正如上面的程序所示,恢复一个Externalizable对象和恢复一个Serializable对象不同。对于Serializable对象,对象完全以它存储的二进制位为基础来构造,而不调用构造器。而对于一个Externalizable对象,所有普通的默认构造器都会被调用(包括字段定义时的初始化),然后调用readExternal()。必须注意一点——所有默认构造器都会被调用,才能使Externalizable对象产生正确的行为。
3.为了正常运行,我们不仅需要在writeExternal()方法中奖来自对象的重要信息写入,还必须在readExternal()方法中恢复数据。Externalizable对象的默认构造行为并非是某种自动发生的存储与恢复操作。
4.当我们对序列化进行控制时,可能某个特定子对象不想让Java的序列化机制自动保存 与恢复。有一种方法可防止对象的敏感部分被序列化,就是将类实现为Externalizable;另外一种情况就是当我们在操作一个Serializable对象时,为了能够予以控制,可以用transient关键字逐个字段地关闭序列化:

public class Login implements Serializable {
    private String username;
    private transient String password;
    private Login(String name, String pwd) {
        username = name;
        password = pwd;
    }
    public String toString() {
        return "login info: \n    username: " + username + 
            "\n    date: " + date + 
            "\n    password: " + password;
    }
    public static void main(String... args) {
        Login a = new Login("Hulk", "myLittlePony");
        System.out.println("login a = " + a);
        ObjectOutputStream o = new ObjectOutputStream(
            new FileOutputStream("Login.out"));
        o.writeObject(a);
        o.close();
        ObjectInputStream in = new ObjectInputStream(
            new FileInputStream("Login.out"));
        System.out.println("login a = " + a);
    }
}
/*
Output:
Login a = login info: 
    username: Hulk
    password: myLittlePony
Login a = login info:
    username: Hulk
    password: null
*/

由于Externalizable对象在默认情况下不保存它们的任何字段,所以transient关键字只能和Serializable对象一起使用。
5.如果不是特别坚持实现Externalizable接口,那么还有另一种方法。我们可以实现Serializable接口,并添加名为writeObject()readObject()的方法。这样一旦对象被序列化或者被反序列化还原,就会自动地分别调用这两个方法,而不是默认的序列化机制。这些方法必须具有准确的方法特征签名:

private void writeObject(ObjectOutputStream stream) throws IOException

private void readObject(ObjectInputStream stream) throws IOException
三.使用“持久性”

1.我们可以通过一个字节数组来使用对象序列化,从而实现任何可Serializable对象的“深度复制”——深度复制意味着我们复制的时整个对象网,而不仅仅是基本对象及其引用。
2.只要将任何对象序列化到单一流中,就可以恢复与我们写出时一样的对象网,并且没有任何意外重复复制出的对象。

XML

1.一种相比对象序列化更具互操作性的解决方案是将数据转换为XML格式,这可以使其被各种各样的平台和语言使用。
2.虽然JDK包含了javax.xml.*类库,但是《Thinking in Java》选择使用开源的XOM类库,因为它看起来最简单,同时也是最直观的用Java产生的修改XML方式,另外XOM还强调了XML的正确性,相关用法详见官网github主页

Preferences

1.Preferences API与对象序列化相比。前者与对象的持久性更密切,因为它可以自动存储和读取信息。不过它只能用于小的、受限的数据集合——我们只能存储基本类型和字符串,并且每个字符的存储长度不能超过8K。
2.Preferences是一个键-值集合,存储在一个节点层次结构中:

public class PreferencesDemo {
    public static void main(String... args) throws Exception {
        Preferences prefs = Preferences.userNodeForPackage(PreferencesDemo.class);
        prefs.put("Location", "Oz");
        prefs.put("Footwear", "Ruby Slippers");
        prefs.putInt("Companions", 4);
        prefs.putBoolean("Are there witches?", true);
        int usageCount = prefs.getInt("UsageCount", 0);
        usageCount++;
        prefs.putInt("UsageCount", usageCount);
        for (String key : prefs.keys())
            System.out.println(key + " : " + prefs.get(key, null));
        System.out.println("How many companions does Dorothy have? " + prefs.getInt("Companions", 0));
    }
}
/*
Output:
Location : Oz
Footwear : Ruby Slippers
Companions : 4
Are there witches? : true
How many companions does Dorothy have? 4
*/

3.Preferences API利用合适的系统资源完成了数据存储的任务,这些资源会随操作系统的不同而不同,所以我们不一定会在执行程序后发现存储数据的本地文件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • JAVA序列化机制的深入研究 对象序列化的最主要的用处就是在传递,和保存对象(object)的时候,保证对象的完整...
    时待吾阅读 10,825评论 0 24
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,494评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,493评论 18 399
  • 转自:http://blog.csdn.net/jackfrued/article/details/4492194...
    王帅199207阅读 8,469评论 3 93
  • 含玻尿酸,蜗牛原液,蛋清,小分子胶原蛋白 让您的肌肤由内而外水润通透!
    Cindy_Xu阅读 354评论 0 0