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

压缩

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

压缩类

功能

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利用合适的系统资源完成了数据存储的任务,这些资源会随操作系统的不同而不同,所以我们不一定会在执行程序后发现存储数据的本地文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值