为什么说枚举更占内存,枚举原理是什么?

为什么说枚举更占内存,枚举原理是什么?

从以前学习java 开始就听说枚举很占内存,然后老版Android开发指南文章也指出,枚举通常需要比静态常量多两倍的内存。你应该严格避免在android上使用枚举。那么究竟为什么说枚举更占内存呢?本文就是通过这种方法来分析枚举为什么占内存的,而不是说拒绝枚举。

在阅读过程中有任何问题,请及时联系。如需转载请注明 fuchenxuan de Blog

关于 Enum

Enum 一般用来表示一组相同类型可列举的常量。如性别、日期、月份、颜色等。对这些属性用常量的好处是显而易见的,不仅可以保证单例,且比较时候可以用 ”==” 来替换 equals 。

基本使用

看看枚举的基本使用

public enum Color {   

  RED , BLUE,GREEN,BLACK ;   

}  
  • 1
  • 2
  • 3
  • 4
  • 5

原理分析

通常会使用javap -c Color.class 反编译class 文件,查看生成的字节码,如下:

Compiled from "Color.java"
public final class org.fast.clean.Color extends java.lang.Enum<org.fast.clean.Color> { public static final org.fast.clean.Color RED; public static final org.fast.clean.Color BLUE; public static final org.fast.clean.Color GREEN; public static final org.fast.clean.Color BLACK; public static org.fast.clean.Color[] values(); Code: 0: getstatic #1 // Field $VALUES:[Lorg/fast/clean/Color; 3: invokevirtual #2 // Method "[Lorg/fast/clean/Color;".clone:()Ljava/lang/Object; 6: checkcast #3 // class "[Lorg/fast/clean/Color;" 9: areturn public static org.fast.clean.Color valueOf(java.lang.String); Code: .... 9: areturn static {}; Code: 0: new #4 // class org/fast/clean/Color 3: dup 4: ldc #7 // String RED 6: iconst_0 7: invokespecial #8 // Method "<init>":(Ljava/lang/ .... 10: putstatic #9 // Field RED:Lorg/fast/clean/Color; 13: new #4 // class org/fast/clean/Color 16: dup 17: ldc #10 // String BLUE 83: return 

org.fast.clean.Color 类被编译成继承自继承自java.lang.Enumfinal类,其中的成员变量也是final常量,它们都不能被修改的。

下面使用jad 反编译生成jad源码文件, 文件显示每个方法的字节码的实际作用,与源码做出对比。

public final class Color extends Enum { public static Color[] values() { return (Color[])$VALUES.clone(); } public static Color valueOf(String s) { return (Color)Enum.valueOf(org/fast/clean/Color, s); } private Color(String s, int i) { super(s, i); } public static final Color RED; public static final Color BLUE; public static final Color GREEN; public static final Color BLACK; private static final www.hbs90.cn/  Color $VALUES[]; static { RED = new Color("RED", 0); BLUE = new Color("BLUE", 1); GREEN = new Color("GREEN", 2); BLACK = new Color("BLACK",www.wanmeiyuele.cn 3); $VALUES = (new Color[www.chushiyl.cn] { RED, BLUE, GREEN, BLACK 

通过比较字节码和源代码的区别,我们可以知道为什么枚举确实比简单的静态变量占用的内存要更多。

Android 简单替代枚举的方法

严格来讲,这个使用方法是有很多缺陷的,但是使用下面的方法就能满足需求的话,那么用Java Enum是会带来各种更大的开销。

  • 方法一:使用接口变量

接口变量默认都是public static final的,个人理解接口只是对一类事物的属性和行为更高层次的抽象。对修改关闭,对扩展(不同的实现implements)开放,接口是对开闭原则的一种体现。

public interface ErrorCode {

    int ERROR_MANUAL_EXP = 100; int ERROR_MANUAL_BACK = 101;

使用javap Color.class 反编译class 文件,查看生成的字节码,如下:

Compiled from "ErrorCode.java"
interface org.fast.clean.ErrorCode {
  public static final int ERROR_MANUAL_EXP; public static final int ERROR_MANUAL_BACK;

可以看出就是一个public static final的静态变量。

  • 方法二:使用support-annotations 注解库

Android Support Library19.1版本开始引入了一个新的注解库,使用 com.android.support:support-annotations ,这个官方的注解支持库中包含了许多很好的注解,可以帮助我们在编译的时候就找到错误。IntDef 和 StringDef 是包含在库中的两个关于常量的注解,我们可以用来代替枚举其中包括了很多有用的元注解,可以用来修饰代码,如@NonNull@StringRes@IntDef@StringDef等等

下面我们使用@IntDef来替代枚举,方法如下

  public static final int RED = 0; public static final int BLUE = 1; public static final int GREEN = 2; @IntDef({RED, BLUE,www.feihuanyule.com GREEN}) @Retention(RetentionPolicy.SOURCE) public @interface Colors { 

gradle 依赖:

dependencies {www.douniu178.com  compile ‘com.android.support:support-annotations:24.2.0’ }

等等还有其他方法。

枚举单例

枚举在单例的使用也是很平常的


    public enum SingleTon {
        INSTANCE;
    }

    //单例对象的获取:
    SingleTon instance =www.chuangyed.com/   SingleTon.INSTANCE;

 

通过查看java.lang.Enum源码如下:

 /**
     * prevent default deserialization
     */
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { throw new InvalidObjectException("can't deserialize enum"); } private void readObjectNoData(www.255055.cn/) throws ObjectStreamException { throw new InvalidObjectException("can't deserialize enum"); 

对于单例是否安全,主要考虑以下两方面:序列化和反序列方面、线程安全方面。

  • 对于序列化和反序列化,因为每一个枚举类型和枚举变量在JVM中都是唯一的,即Java在序列化和反序列化枚举时做了特殊的规定,枚举的writeObjectreadObjectreadObjectNoDatawriteReplacereadResolve等方法是被编译器禁用的,因此也不存在实现序列化接口后调用readObject会破坏单例的问题。

  • 对于线程安全方面,类似于普通的饿汉模式,通过在第一次调用时的静态初始化创建的对象是线程安全的。

所以使用枚举也是一种比较好的单例模式,通过反编译我们知道,缺点就是不能够继承,因为final了。

通过上面的分析枚举,我们只是讨论为什么说枚举更占内存,枚举原理是什么,而不是让我们拒绝使用枚举,因为就几个枚举,让应用内存开销大,那就实在是太可怕了。。。更重要的是通过比较字节码和源代码,我们可以发现很多的问题,一个很重要的作用就是了解很多编译器内部的工作机制。

更多java 学习总结,请点击下方图片哦。 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值