美文网首页
JAVA 压缩和序列化

JAVA 压缩和序列化

作者: 简单一点点 | 来源:发表于2019-04-27 21:03 被阅读0次

压缩和序列化主要用在数据的存储和传输上,二者都是由IO流相关知识实现,这里统一介绍下。

全部章节传送门:

压缩

Java I/O类支持读写压缩格式的数据流,你可以用他们对其他的I/O流进行封装,以提供压缩功能。

GZIP

GZIP接口比较简单,适合对单个数据流进行压缩,在Linux系统中使用较多。

public static void main(String[] args) throws IOException {
    String[] paths = new String[] {"123.txt", "qqq.txt", "file.txt"};
    FileOutputStream f = new FileOutputStream("test.zip");
    ZipOutputStream zos = new ZipOutputStream(f);
    BufferedOutputStream out = new BufferedOutputStream(zos);
    
    zos.setComment("A test of java Zipping");
    for(String path : paths) {
        System.out.println("Write file " + path);
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(path));
        zos.putNextEntry(new ZipEntry(path));
        byte[] b = new byte[1024];
        int len = 0;
        while((len = in.read(b, 0, b.length)) != -1) {
            out.write(b, 0, len);
        }
        in.close();
        out.flush();
    }
    out.close();
    
    System.out.println("Read file ");
    FileInputStream fi = new FileInputStream("test.zip");
    ZipInputStream in2 = new ZipInputStream(fi);
    BufferedInputStream bis = new BufferedInputStream(in2);
    ZipEntry ze;
    while((ze = in2.getNextEntry()) != null) {
        System.out.println("Read file " + ze);
        int x;
        while((x = bis.read()) != -1) {
            System.out.write(x);
        }
    }
    bis.close();
}

ZIP

ZIP格式可以压缩多个文件,而且可以和压缩工具进行协作,是经常使用的压缩方法。

public static void main(String[] args) throws IOException {
    String[] paths = new String[] {"123.txt", "qqq.txt", "file.txt"};
    FileOutputStream f = new FileOutputStream("test.zip");
    ZipOutputStream zos = new ZipOutputStream(f);
    BufferedOutputStream out = new BufferedOutputStream(zos);
    
    zos.setComment("A test of java Zipping");
    for(String path : paths) {
        System.out.println("Write file " + path);
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(path));
        zos.putNextEntry(new ZipEntry(path));
        byte[] b = new byte[1024];
        int len = 0;
        while((len = in.read(b, 0, b.length)) != -1) {
            out.write(b, 0, len);
        }
        in.close();
        out.flush();
    }
    out.close();
    
    System.out.println("Read file ");
    FileInputStream fi = new FileInputStream("test.zip");
    ZipInputStream in2 = new ZipInputStream(fi);
    BufferedInputStream bis = new BufferedInputStream(in2);
    ZipEntry ze;
    while((ze = in2.getNextEntry()) != null) {
        System.out.println("Read file " + ze);
        int x;
        while((x = bis.read()) != -1) {
            System.out.write(x);
        }
    }
    bis.close();
}

JAR和WAR

JAR(Java Archive,Java 归档文件)是与平台无关的文件格式,它允许将许多文件组合成一个压缩文件。为 J2EE 应用程序创建的 JAR 文件是 EAR 文件(企业 JAR 文件)。

JAR 文件格式以流行的 ZIP 文件格式为基础。与 ZIP 文件不同的是,JAR 文件不仅用于压缩和发布,而且还用于部署和封装库、组件和插件程序,并可被像编译器和 JVM 这样的工具直接使用。在 JAR 中包含特殊的文件,如 manifests 和部署描述符,用来指示工具如何处理特定的 JAR。

如果一个Web应用程序的目录和文件非常多,那么将这个Web应用程序部署到另一台机器上,就不是很方便了,我们可以将Web应用程序打包成Web 归档(WAR)文件,这个过程和把Java类文件打包成JAR文件的过程类似。利用WAR文件,可以把Servlet类文件和相关的资源集中在一起进行发布。在这个过程中,Web应用程序就不是按照目录层次结构来进行部署了,而是把WAR文件作为部署单元来使用。

一个WAR文件就是一个Web应用程序,建立WAR文件,就是把整个Web应用程序(不包括Web应用程序层次结构的根目录)压缩起来,指定一个.war扩展名。下面我们将第2章的Web应用程序打包成WAR文件,然后发布

要注意的是,虽然WAR文件和JAR文件的文件格式是一样的,并且都是使用jar命令来创建,但就其应用来说,WAR文件和JAR文件是有根本区别的。JAR文件的目的是把类和相关的资源封装到压缩的归档文件中,而对于WAR文件来说,一个WAR文件代表了一个Web应用程序,它可以包含 Servlet、HTML页面、Java类、图像文件,以及组成Web应用程序的其他资源,而不仅仅是类的归档文件。

在命令行输入jar即可查看jar命令的使用方法。

PS C:\> jar
用法: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files ...
选项:
    -c  创建新档案
    -t  列出档案目录
    -x  从档案中提取指定的 (或所有) 文件
    -u  更新现有档案
    -v  在标准输出中生成详细输出
    -f  指定档案文件名
    -m  包含指定清单文件中的清单信息
    -n  创建新档案后执行 Pack200 规范化
    -e  为捆绑到可执行 jar 文件的独立应用程序
        指定应用程序入口点
    -0  仅存储; 不使用任何 ZIP 压缩
    -P  保留文件名中的前导 '/' (绝对路径) 和 ".." (父目录) 组件
    -M  不创建条目的清单文件
    -i  为指定的 jar 文件生成索引信息
    -C  更改为指定的目录并包含以下文件
如果任何文件为目录, 则对其进行递归处理。
清单文件名, 档案文件名和入口点名称的指定顺序
与 'm', 'f' 和 'e' 标记的指定顺序相同。

示例 1: 将两个类文件归档到一个名为 classes.jar 的档案中:
       jar cvf classes.jar Foo.class Bar.class
示例 2: 使用现有的清单文件 'mymanifest' 并
           将 foo/ 目录中的所有文件归档到 'classes.jar' 中:
       jar cvfm classes.jar mymanifest -C foo/ .

序列化

把对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为对象的过程称为对象的反序列化。

对象的序列化主要有两种用途:

  1. 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
  2. 在网络上传送对象的字节序列。

序列化API

java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

只有实现了Serializable的对象才能被序列化。对象序列化包括如下步骤:

  1. 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
  2. 通过对象输出流的writeObject()方法写对象。

对象反序列化的步骤如下:

  1. 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
  2. 通过对象输入流的readObject()方法读取对象。

创建一个可以可以序列化的对象。

package medium.io.serialize;

import java.io.Serializable;

public class Person implements Serializable{

    private static final long serialVersionUID = -7702331032373972217L;
    
    private int age;
    private String name;
    private String sex;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }   

}

然后进行序列化和反序列化测试。

package medium.io.serialize;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class TestObjSerializeAndDeserialize {

    public static void main(String[] args) throws Exception{
        SerializePerson();
        Person p = DeserializePerson();
        System.out.printf("name={%s},age={%d},sex={%s}", p.getName(), 
                p.getAge(), p.getSex());

    }
    
    private static void SerializePerson() throws FileNotFoundException,
        IOException {
        Person person = new Person();
        person.setName("wyk");
        person.setAge(25);
        person.setSex("男");
        
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
                new File("wyk.txt")));
        oo.writeObject(person);
        System.out.println("Person对象序列化成功!");
        oo.close();
    }
    
    private static Person DeserializePerson() throws Exception, IOException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
                new File("wyk.txt")));
        Person person = (Person)ois.readObject();
        System.out.println("Person对象序列化成功!");
        return person;
    }

}

serialVersionUID

s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D​:​ ​字​面​意​思​上​是​序​列​化​的​版​本​号​,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量。

JAVA序列化的机制是通过判断类的serialVersionUID来验证的版本一致的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID于本地相应实体类的serialVersionUID进行比较。如果相同说明是一致的,可以进行反序列化,否则会出现反序列化版本一致的异常,即是InvalidCastException。

为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。

Externalizable接口

控制序列化字段还可以使用Externalizable接口替代Serializable借口。此时需要定义一个默认构造器,否则将为得到一个异常(java.io.InvalidClassException: Person; Person; no valid constructor);还需要定义两个方法(writeExternal()和readExternal())来控制要序列化的字段。

如下为将Person类修改为使用Externalizable接口。


public class Person implements Externalizable {

    private String name;
    private int age;
    /**
     * 必须有无参数的构造器
     */
    public Person() {
        System.out.println("无参数的构造器");
    }
    public Person(String name,int age) {
        System.out.println("带参数的构造器");
        this.name=name;
        this.age=age;
    }
    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "姓名是"+name+"年龄是"+age;
    }
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        // TODO Auto-generated method stub

        out.writeObject(name);
        out.writeObject(age);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException,
            ClassNotFoundException {
        // TODO Auto-generated method stub
        this.name=(String)in.readObject();
        this.age=(int)in.readObject();
    }

}

transient 关键字

transient修饰符仅适用于变量,不适用于方法和类。在序列化时,如果我们不想序列化特定变量以满足安全约束,那么我们应该将该变量声明为transient。执行序列化时,JVM会忽略transient变量的原始值并将默认值(引用类型就是null,数字就是0)保存到文件中。因此,transient意味着不要序列化。

静态变量不是对象状态的一部分,因此它不参与序列化。所以将静态变量声明为transient变量是没有用处的。

package medium.io.serialize;

import java.io.Serializable;

public class Person implements Serializable{

    private static final long serialVersionUID = -7702331032373972217L;
    
    private transient int age; //无论值是什么,都会被序列化为0
    private String name;
    private String sex;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }   

}

相关文章

  • JAVA 压缩和序列化

    压缩和序列化主要用在数据的存储和传输上,二者都是由IO流相关知识实现,这里统一介绍下。 全部章节传送门: JAVA...

  • Java-序列化-反序列化

    Thanks Java基础学习总结——Java对象的序列化和反序列化java序列化反序列化原理Java 序列化的高...

  • Java序列化

    Java序列化的几种方式以及序列化的作用 Java基础学习总结——Java对象的序列化和反序列化

  • JDK 序列化

    序列化和分序列化概念 什么是序列化和反序列化 Java序列化是指把Java对象转换为字节序列的过程,而Java反序...

  • Spark序列化

    Java序列化 有关Java对象的序列化和反序列化也算是Java基础的一部分,首先对Java序列化的机制和原理进行...

  • JAVA反序列化漏洞

    目录 反序列化漏洞序列化和反序列化JAVA WEB中的序列化和反序列化对象序列化和反序列范例JAVA中执行系统命令...

  • Java序列化机制

    Java序列化机制 序列化和反序列化 Java序列化是Java内建的数据(对象)持久化机制,通过序列化可以将运行时...

  • Protostuff序列化和反序列化

    Java序列化和反序列化 序列化和反序列化是在应对网络编程最常遇到的问题之一。序列化就是将Java Object转...

  • Java 序列化

    参考链接:Java对象的序列化和反序列化Java transient关键字使用小记 1. Java序列化概念 把对...

  • 总结

    java基础 Java中多态的理解 反射 Java序列化与反序列化 Volatile和Synchronized e...

网友评论

      本文标题:JAVA 压缩和序列化

      本文链接:https://www.haomeiwen.com/subject/ozdlnqtx.html