Android 枚举 VS 枚举注解

枚举注解替换枚举

java 虚拟机内存分配

java 内存区域可分为

  • 方法区 存放虚拟机加载的类信息,常量,静态变量等数据。
  • 虚拟机栈 java 方法执行的内存模型:每个方法在执行的时候创建的栈帧,包括存储局部变量表,操作数栈,动态链接,方法出口等信息。
  • 本地方法栈 主要与Native相关
  • 堆 存放对象实例。
  • 程序计数器 当前线程执行的字节码行号指示器。

java 数据类型占内存大小

java 数据类型分为基本数据类型和引用数据类型。

在32位系统上基本数据类型,本文中中的所有内存空间大小都在在32位系统上面。占用的内存大小为

基本数据类型内存大小
byte1 字节
short2 字节
char2 字节
int4 字节
float4 字节
double8 字节
long8 字节
boolean1 字节或者 4字节

在Java 中引用数据类型 String 也是一个类。简化源码为

public final class String{
    private final char value[];
    private int hash; //4 字节
    private static final long serialVersionUID = -6849794470754667710L; 
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0]; 
}

因此一个空的String 大小为 12(Header) + 4(char[] reference) + 4(int) + 4(Padding) = 24 bytes

关于枚举的使用

Enum需要占用比较大的内存空间,如果对内存空间敏感,请谨慎使用。

google官方文档有提到

Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android

个人在目前google官方的文档没有找到这句话,但是网络都有转发这就话,姑且认为互联网都是有记忆的,认为这句话是正确的吧。同时查看android 的源码,与枚举有关的变量,都使用枚举注解,比如说设置控件是否可见,使用的也是枚举注解。因此认为google关键在Android应用上面尽量少用枚举,并且使用枚举注解替代枚举。

// 设置控件是否可见
public void setVisibility(@Visibility int visibility) {
        setFlags(visibility, VISIBILITY_MASK);
}
// 设置布局的布局方向
public void setOrientation(@OrientationMode int orientation){}

枚举占用的内存空间

关于枚举占用的内存空间,先理解枚举的实现原理。

  1. 定义枚举

    public enum FirstWeek {
        Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
    }
    
  2. 编译枚举类

    // 编译命令 javac FirstWeek.java
    
  3. 反编译

    // 反编译命令 javap -p FirstWeek.class
    Compiled from "FirstWeek.java"
    // 类用final 修饰,保证类不能被继承
    public final class com.chenxum.dragon.utils.FirstWeek extends java.lang.Enum<com.chenxum.dragon.utils.FirstWeek> {
      // 定义枚举实例  
      public static final com.chenxum.dragon.utils.FirstWeek Monday;
      public static final com.chenxum.dragon.utils.FirstWeek Tuesday;
      public static final com.chenxum.dragon.utils.FirstWeek Wednesday;
      public static final com.chenxum.dragon.utils.FirstWeek Thursday;
      public static final com.chenxum.dragon.utils.FirstWeek Friday;
      public static final com.chenxum.dragon.utils.FirstWeek Saturday;
      public static final com.chenxum.dragon.utils.FirstWeek Sunday;
      private static final com.chenxum.dragon.utils.FirstWeek[] $VALUES;
      public static com.chenxum.dragon.utils.FirstWeek[] values();
      public static com.chenxum.dragon.utils.FirstWeek valueOf(java.lang.String);
        // 私有的构造函数, 原因是不能通过new 来实例化这个类
      private com.chenxum.dragon.utils.FirstWeek();
      static {
          // 调用父类的构造函数
            MONDAY = new FirstWeek("MONDAY", 0);
            TUESDAY = new Day("TUESDAY", 1);
            WEDNESDAY = new Day("WEDNESDAY", 2);
            THURSDAY = new Day("THURSDAY", 3);
            FRIDAY = new Day("FRIDAY", 4);
            SATURDAY = new Day("SATURDAY", 5);
            SUNDAY = new Day("SUNDAY", 6);
            $VALUES = (new Day[] {
                MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
            });
      }
    }
    

    枚举占用内存大小分析

    FirstWeek成员变量有枚举的实例引用和一个数组。一个实例引用占用4个字节,一个数组引用也占有4个字节。

    class FirstWeek{
        public static final com.chenxum.dragon.utils.FirstWeek Monday; 
      	public static final com.chenxum.dragon.utils.FirstWeek Tuesday; 
      	public static final com.chenxum.dragon.utils.FirstWeek Wednesday; 
      	public static final com.chenxum.dragon.utils.FirstWeek Thursday; 
      	public static final com.chenxum.dragon.utils.FirstWeek Friday; 
      	public static final com.chenxum.dragon.utils.FirstWeek Saturday; 
      	public static final com.chenxum.dragon.utils.FirstWeek Sunday; 
      	private static final com.chenxum.dragon.utils.FirstWeek[] $VALUES; 
    }
    

    引用指向的实例对象也要占用额外的内存空间。枚举实例引用指向一个枚举。一个枚举的成员变量有name和ordinal。参见简化源码源码

    class Enum{
        private final String name; 
        private final int ordinal;
    }
    

    一个枚举类占用空间大小为 对象头8个字节+引用4个字节+(String 引用)4个字节+(int 型数据)+4个字节 = 20字节。

    但是name是字符串,因此String 本身也需要占用空间。一个String 占用的空间为 24+n * 2;

    对枚举类计算内存空间

    class FirstWeek{
        // 对象引用4个字节
        public static final com.chenxum.dragon.utils.FirstWeek Monday; 
      	public static final com.chenxum.dragon.utils.FirstWeek Tuesday; 
      	public static final com.chenxum.dragon.utils.FirstWeek Wednesday; 
      	public static final com.chenxum.dragon.utils.FirstWeek Thursday; 
      	public static final com.chenxum.dragon.utils.FirstWeek Friday; 
      	public static final com.chenxum.dragon.utils.FirstWeek Saturday; 
      	public static final com.chenxum.dragon.utils.FirstWeek Sunday; 
        //  数组引用对象4个字节,由于数组存放的是上面的引用实例,因此 占用空间 4* n
      	private static final com.chenxum.dragon.utils.FirstWeek[] $VALUES; 
    }
     
    

    因此不计算枚举内部枚举引用指向的实例占用的空间,一个带有N个枚举值的枚举占用的空间为 n *4 + n *4 + 4;由于每个枚举引用对象的枚举实例含有一个String 对象,当枚举的命名非常长的时候,占用的内存空间是非常可怕的。

枚举注解

枚举注解类定义

@IntDef({
        SecondWeek.Monday,
        SecondWeek.Tuesday,
        SecondWeek.Wednesday,
        SecondWeek.Thursday,
        SecondWeek.Friday,
        SecondWeek.Saturday,
        SecondWeek.Sunday,
})
public @interface SecondWeek {
    int Monday = 1;
    int Tuesday = 2;
    int Wednesday = 3;
    int Thursday = 4;
    int Friday = 5;
    int Saturday = 6;
    int Sunday = 7;
}

在这里无法使用java 自带的命令进行编译 javac SecondWeek.java,报错无法找到IntDef,这个注解了。因此使用IDE编译后的Class文件反编译回java文件

反编译后为

// javap -p -c SecondWeek.class 
Compiled from "SecondWeek.java"
public interface com.chenxum.dragon.utils.SecondWeek extends java.lang.annotation.Annotation {
  public static final int Monday;

  public static final int Tuesday;

  public static final int Wednesday;

  public static final int Thursday;

  public static final int Friday;

  public static final int Saturday;

  public static final int Sunday;
}

从反编译后的文件可以看出,这是存在语法问题的,final没有赋值,因此继续反编译,将汇编指令也反编译处理。


// javap -v classxx,不仅会输出行号、本地变量表信息、反编译汇编代码,还会输出当前类用到的常量池等信息
Classfile /H:/dragon/app/build/intermediates/javac/debug/classes/com/chenxum/dragon/utils/SecondWeek.class
  Last modified 2020-5-20; size 403 bytes
  MD5 checksum 895b02673e1cde9588d38aaf51a999f8
  Compiled from "SecondWeek.java"
public interface com.chenxum.dragon.utils.SecondWeek extends java.lang.annotation.Annotation
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
Constant pool:
   #1 = Class              #22            // com/chenxum/dragon/utils/SecondWeek
   #2 = Class              #23            // java/lang/Object
   #3 = Class              #24            // java/lang/annotation/Annotation
   #4 = Utf8               Monday
   #5 = Utf8               I
   #6 = Utf8               ConstantValue
   #7 = Integer            1
   #8 = Utf8               Tuesday
   #9 = Integer            2
  #10 = Utf8               Wednesday
  #11 = Integer            3
  #12 = Utf8               Thursday
  #13 = Integer            4
  #14 = Utf8               Friday
  #15 = Integer            5
  #16 = Utf8               Saturday
  #17 = Integer            6
  #18 = Utf8               Sunday
  #19 = Integer            7
  #20 = Utf8               SourceFile
  #21 = Utf8               SecondWeek.java
  #22 = Utf8               com/chenxum/dragon/utils/SecondWeek
  #23 = Utf8               java/lang/Object
  #24 = Utf8               java/lang/annotation/Annotation
{
  public static final int Monday;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 1

  public static final int Tuesday;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 2

  public static final int Wednesday;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 3

  public static final int Thursday;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 4

  public static final int Friday;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 5

  public static final int Saturday;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 6

  public static final int Sunday;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 7

}
SourceFile: "SecondWeek.java"

这样就可以确定,确实是赋值了。

枚举注解占用空间

枚举注解占用的空间比较好计算,带有N个枚举值的注解类占用的空间为 n *4 。

总结

单纯考虑内存空间的情况下,枚举值为n。枚举占用的空间为 4 *n +4 *n +4 = 8 * n +4。(没有将String计算在内)。枚举注解占用的空间为 n * 4 。所以google 官方说Enums often require more than twice as much memory as static constants.

当我们定义枚举,只是为了编码方便,将枚举值在入参,switch中使用,完全可以使用枚举注解替代。枚举注解已有这些功能。

当然枚举还有其他功能,这些功能是无法使用枚举注解进行替换的。

  • 情况1,android 目前只提供了枚举注解@StringDef和@IntDef,如果枚举值,不是int类型或者String 类型,则无法使用android 官方提供的枚举注解。

  • 情况2。在某些情况下,自定义枚举,将多个变量有机的结合起来,这时候无法使用枚举注解。比如说常用的将错误码和错误描述结合起来,这时候使用枚举方便。

    enum TransState{
            STATE1(1,"交易成功"),
            STATE2(1,"交易失败");
            private int code;
            private String message;
            TransState(int code, String message) {
                this.code = code;
                this.message = message;
            }
    
        }
    
    • 情况3。因为枚举实现了Serializable,说明枚举本身是可以序列化的,如果枚举值需要保存在介质中或者通过Intent进行传递,使用枚举方便。

参考博客连接:

https://www.jianshu.com/p/6052cd4ea9ae

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值