分类 JAVA 下的文章

Java9新特性——模块化

Java9引入了模块化的特性,利用这个特性可以创建小型的JRE来发布应用程序,减少JRE体积,提高资源利用率

将应用打包成模块

  1. 在源码根路径新建module-info.java来配置模块信息

    module 模块名(用于其它应用导入,以及运行环境打包) {
        //requires声明依赖模块
        requires java.base;
        //transitive关键字将依赖的模块传递出去,导入当前模块的时,无需再次导入即可以使用这个依赖的模块
        requires transitive java.desktop;
        //exports声明导出的模块,即外部工程导入本模块之后,可见的包名,其他包名将对外隐藏
        exports top.dreagonmon.app.test;
    }
  2. 编译源码,生成字节码.class文件(生成的字节码中有module-info.class文件)
  3. 将字节码打包成jmod模块包

    jmod create --class-path <字节码所在的路径> <输出的jmod文件路径>
  4. 根据jmod生成运行环境

    jlink --output <输出目录名> --add-modules <应用模块名> --module-path <jmod文件所在的路径>

    可以不包含java自带模块的路径,正确配置的JAVA环境会自动发现这些模块

  5. 运行应用的主类

    cd <输出目录>/bin/java 主类类名(一定要将主类导出,才可以从外部执行)

java对象序列化学习

由于开发中的需要,要将数据变成字节流之后分片传输,学习一下Java里面将对象序列化的方法。

1.简单序列化的实现

最简单的序列化实现的方法,就是自定义数据类的时候继承java.io.Serializable接口。
所有继承了这个接口的类都可以被序列化,用ObjectOutputStream和ObjectInputStream可以分别将对象输出和输入。

public class myObject implements java.io.Serializable{
    //这是一个空接口,所以不需要任何实现,只是告诉别人这个类可以序列化
    //所有基本类型默认实现了Serializable接口,所以都是可以序列化的
    int arg1;
    String msg;
    
    //可以序列化的类型的数组也可以序列化
    byte[] data;
    float[] d3;
    
    //如果下面的类实现了Serializable接口,则可以序列化
    MyAnotherClass obj;

    //对于不知道是否可以序列化的类要慎用,不然会出错
    UnknowClass uobj;//可能会在序列化时报错!!!
}

如果一个类声明实现了Serializable接口,那么它里面的所有属性都必须可以序列化,否则会在序列化的时候报错!

2.自己控制序列化的实现

很多时候,在对象序列化的时候,我们不希望某些敏感属性被序列化,或者某些属性不能被自动序列化,这时候就需要我们手动实现序列化了。
手动实现序列化需要自定义类实现java.io.Externalizable接口,然后重写相关的方法,来完成对象的序列化与反序列化。

int arg1;
long time;
String msg;
@Override
public void readExternal(ObjectInput in)
    throws IOException, ClassNotFoundException {
    //在这里对对象进行反序列化
    //注意调用readXXX()与writeXXX()的先后顺序!
    time = in.readLong();
    msg = (String) in.readObject();//String用Object来读
    arg1 = in.readInt();
}

@Override
public void writeExternal(ObjectOutput out) throws IOException {
    //在这里对对象进行序列化
    //注意调用readXXX()与writeXXX()的先后顺序!
    out.writeLong(time);
    out.writeObject(msg);//String用Object来写
    out.writeInt(arg1);
}

手动序列化的时候,按照一定的顺序调用writeXXX()和readXXX()的方法,将需要的属性序列化。
序列化与反序列化时,先序列化的就先反序列化,上面的例子中,序列化的顺序是long、String、int,所以反序列化的顺序就是long、String、int。
序列化生成的对象,并不会调用类的构造方法,所以要在这两个方法中对其他属性进行必要的初始化。

3.serialVersionUID的特殊用处

在自定义类的时候,只要更改了一点点的源文件,编译出来的类便与之前序列化出来的数据流不兼容了,无法完成反序列化。
serialVersionUID的作用就是标识当前类的版本号。
版本号相同的类可以序列化与反序列化,反之则不行。
所以如果有可能,自定义的所有需要序列化的类都应该定义一个serialVersionUID属性

private static final long serialVersionUID = -5314860580001L;

通过控制serialVersionUID来控制序列化的流与类的兼容性。
只要serialVersionUID相同,无论类里的方法如何变,对应的属性总能正确地完成序列化与反序列化。

4.可以序列化的属性

并不是所有的属性都可以用Serializable接口自动序列化,自动序列化的属性需要满足以下条件:

  • 该属性的类本身可以序列化
  • 该属性不是静态(static)属性
  • 该属性不是瞬时(transient)属性