scala object 单例实现——字节码分析

scala object 单例实现——字节码分析

scala中天然支持单例,一个类可以声明为class或object。当声明为object后我们就可以拿它当单例使用了。虽然可以简单将object中的字段或方法理解为java中的static,还是想了解下字节码层面如何做到的。

scala

定义如下单例测试

object Single {
  var f1=10
  val f2="Test"
  def add(a: Int, b: Int): Int = a + b
  def sayHi(name: String): Unit = println(s"hi,$name")
}

字节码

将这个类编译后,产生Single.class和Singe$.class两个文件。使用javap -c -v -p 反编译后,关键部分的字节码如下

Single.class 仅仅提供了static了方法

public final class detail.object1.Single{
  public static void sayHi(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #16                 // Field detail/object1/Single$.MODULE$:Ldetail/object1/Single$;
         3: aload_0
         4: invokevirtual #18                 // Method detail/object1/Single$.sayHi:(Ljava/lang/String;)V
         7: return

  public static int add(int, int);
    descriptor: (II)I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=2, args_size=2
         0: getstatic     #16                 // Field detail/object1/Single$.MODULE$:Ldetail/object1/Single$;
         3: iload_0
         4: iload_1
         5: invokevirtual #22                 // Method detail/object1/Single$.add:(II)I
         8: ireturn

  public static java.lang.String f2();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #16                 // Field detail/object1/Single$.MODULE$:Ldetail/object1/Single$;
         3: invokevirtual #26                 // Method detail/object1/Single$.f2:()Ljava/lang/String;
         6: areturn

  public static void f1_$eq(int);
    descriptor: (I)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #16                 // Field detail/object1/Single$.MODULE$:Ldetail/object1/Single$;
         3: iload_0
         4: invokevirtual #30                 // Method detail/object1/Single$.f1_$eq:(I)V
         7: return

  public static int f1();
    descriptor: ()I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #16                 // Field detail/object1/Single$.MODULE$:Ldetail/object1/Single$;
         3: invokevirtual #34                 // Method detail/object1/Single$.f1:()I
         6: ireturn
}

Single$.class真正的对象类

public final class detail.object1.Single$
{
  public static final detail.object1.Single$ MODULE$;
    descriptor: Ldetail/object1/Single$;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

  private int f1;
    descriptor: I
    flags: ACC_PRIVATE

  private final java.lang.String f2;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_FINAL

  public static {};
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: new           #2                  // class detail/object1/Single$
         3: invokespecial #12                 // Method "<init>":()V
         6: return

  public int f1();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #19                 // Field f1:I
         4: ireturn
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ldetail/object1/Single$;
      LineNumberTable:
        line 11: 0

  public void f1_$eq(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: iload_1
         2: putfield      #19                 // Field f1:I
         5: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  this   Ldetail/object1/Single$;
            0       6     1   x$1   I
      LineNumberTable:
        line 11: 0

  public java.lang.String f2();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #26                 // Field f2:Ljava/lang/String;
         4: areturn
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ldetail/object1/Single$;
      LineNumberTable:
        line 12: 0

  public int add(int, int);
    descriptor: (II)I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: iload_1
         1: iload_2
         2: iadd
         3: ireturn
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  this   Ldetail/object1/Single$;
            0       4     1     a   I
            0       4     2     b   I
      LineNumberTable:
        line 13: 0

  public void sayHi(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=8, locals=2, args_size=2
         0: getstatic     #37                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
         3: new           #39                 // class scala/StringContext
         6: dup
         7: getstatic     #37                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
        10: iconst_2
        11: anewarray     #41                 // class java/lang/String
        14: dup
        15: iconst_0
        16: ldc           #43                 // String hi,
        18: aastore
        19: dup
        20: iconst_1
        21: ldc           #45                 // String
        23: aastore
        24: checkcast     #47                 // class "[Ljava/lang/Object;"
        27: invokevirtual #51                 // Method scala/Predef$.wrapRefArray:([Ljava/lang/Object;)Lscala/collection/mutable/WrappedArray;
        30: invokespecial #54                 // Method scala/StringContext."<init>":(Lscala/collection/Seq;)V
        33: getstatic     #37                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
        36: iconst_1
        37: anewarray     #4                  // class java/lang/Object
        40: dup
        41: iconst_0
        42: aload_1
        43: aastore
        44: invokevirtual #58                 // Method scala/Predef$.genericWrapArray:(Ljava/lang/Object;)Lscala/collection/mutable/WrappedArray;
        47: invokevirtual #62                 // Method scala/StringContext.s:(Lscala/collection/Seq;)Ljava/lang/String;
        50: invokevirtual #66                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
        53: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      54     0  this   Ldetail/object1/Single$;
            0      54     1  name   Ljava/lang/String;
      LineNumberTable:
        line 14: 0

  private detail.object1.Single$();
    descriptor: ()V
    flags: ACC_PRIVATE
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #68                 // Method java/lang/Object."<init>":()V
         4: aload_0
         5: putstatic     #70                 // Field MODULE$:Ldetail/object1/Single$;
         8: aload_0
         9: bipush        10
        11: putfield      #19                 // Field f1:I
        14: aload_0
        15: ldc           #72                 // String Test
        17: putfield      #26                 // Field f2:Ljava/lang/String;
        20: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      21     0  this   Ldetail/object1/Single$;
      LineNumberTable:
        line 16: 0
        line 11: 8
        line 12: 14
}

java伪代码

将以上代码复现为java代码

# scala类Single反编译后的java版

//真正的单例类,实现类饿汉模式
public final class Single${
    public static Single$ MODULE=new Single$();
    private int f1;
    private final String f2;

    private Single$(){
        f1=10;
        f2="test";
    }
    //f1 getter
    public int f1(){
        return f1;
    }
    //f1 setter
    public void f1_$eq(int a){
        f1=a;
    }
    //f2 getter
    public String f2(){
        return f2;
    }

    public int add(int a,int b){
        ...
    }

    public void sayHi(String name){
        ...
    }
}

public final class Single{
    public static void sayHi(String name){
        Single$.MODULE.sayHi(name);
    }

    public static int add(int a, int b){
        return Single$.MODULE.add(a+b);
    }
     public static java.lang.String f2(){
        return Single$.MODULE.f2();
     }

    public static void f1_$eq(int a){
        Single$.MODULE.f1_$eq(a);
    }
    public static int f1(){
        return Single$.MODULE.f1();
    }
}

总结

scala的object总体实现机制为,假设object A,则首先定义一个类A , 这 个 类 就 是 真 正 的 单 例 类 , 和 j a v a 中 的 饿 汉 模 式 的 实 现 一 样 , 然 后 还 有 一 个 类 A 为 代 理 类 , 其 提 供 类 A ,这个类就是真正的单例类,和java中的饿汉模式的实现一样,然后还有一个类A为代理类,其提供类A java饿AA所有方法的static版,并实际调用类A$对应的方法。

另外,类都有final关键字修饰,这点和scala中object不能继承一致。

还有一个问题,如果对object Single调用getClass,返回的是哪个呢?答案Single$,的确,因为这个类才是真正的主体类,Single只是代理而已。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值