Scala 伴生对象的实现原理

《大数据: Scala 原理介绍》 一、Scala 原理介绍 (重点) =*=*=*=*= (从现在开始学习Scala 模式是对比Java来说,相当于复习一部分Java,对比着学习也更好的说明Scala的优点)=*=*=*=*= Java:定义类,然后在类中定义一个静态的main方法 scala:没有静态的概念,创建对象,在对象中定义main方法 1.Scala执行流程分析 2.Scala程序基本结构 (先了解) 完全面向对象: scala完全面向对象,故scala去掉了java中非面向对象的元素,如static关键... 阅读详情

前言:这个面试出镜率很高,虽然咱们都会用,可是你有考虑过scala为什么这么设计么?你有考虑过大名鼎鼎的java23种设计模型,scala是怎么设计应用的么?

本文主要分析伴生类和伴生对象的实现方法。所谓伴生对象, 也是一个Scala中的单例对象, 使用object关键字修饰。 除此之外, 还有一个使用class关键字定义的同名类, 这个类和单例对象存在于同一个文件中, 这个类就叫做这个单例对象的伴生类, 相对来说, 这个单例对象叫做伴生类的伴生对象。

示例单例对象, 只不过为这个单例对象定义一个伴生类。 代码如下:

 class Test{
      var field = "field"
      def doSomeThing = println("do something")
    }
     
    object  Test {
        val a = "a string";
        def printString = println(a)
    }

 伴生类中有一个字段field和一个方法doSomething 。


编译这个文件, 同样生成两个class, 一个TEST.class和一个Test$.class 。 前文已经说过, 这个Test$.class叫做虚构类。
下面先反编译虚构类, 看看加入了伴生类之后, 编译出的虚构类是不是和上一篇博客中的相同。 下面是反编译结果。 (去掉了常量池等冗余的信息)

 public final class Test$
      SourceFile: "Test.scala"
        Scala: length = 0x0
      minor version: 0
      major version: 50
      flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
     
    {
      public static final Test$ MODULE$;
        flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
     
     
      private final java.lang.String a;
        flags: ACC_PRIVATE, ACC_FINAL
     
     
      public static {};
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=1, locals=0, args_size=0
             0: new           #2                  // class Test$
             3: invokespecial #12                 // Method "<init>":()V
             6: return
     
      public java.lang.String a();
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: getfield      #17                 // Field a:Ljava/lang/String;
             4: areturn
     
      public void printString();
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=1, args_size=1
             0: getstatic     #24                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
             3: aload_0
             4: invokevirtual #26                 // Method a:()Ljava/lang/String;
             7: invokevirtual #30                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
            10: return
     
      private Test$();
        flags: ACC_PRIVATE
        Code:
          stack=2, locals=1, args_size=1
             0: aload_0
             1: invokespecial #31                 // Method java/lang/Object."<init>":()V
             4: aload_0
             5: putstatic     #33                 // Field MODULE$:LTest$;
             8: aload_0
             9: ldc           #35                 // String a string
            11: putfield      #17                 // Field a:Ljava/lang/String;
            14: return
    }

发现虚构类没有任何变化, 源码中的单例对象中的字段和方法都在虚构类中有相应的对应字段和方法。 并且会为单例对象中的字段生成相应的方法。要说明的重点是:虽然在这个示例中加入了伴生类, 并且伴生类中也有字段和方法, 但是这个字段和方法并没有对应出现在虚构类中。 这也就说明, 虚构类中的信息只和单例对象有关, 单例对象的伴生类不会影响虚构类中的内容。 关于虚构类的实现细节, 可以参阅上一篇博客, 这里不再重复。


下面反编译Test.class 。 反编译结果如下:

 public class Test
      SourceFile: "Test.scala"
      RuntimeVisibleAnnotations:
        0: #6(#7=s#8)
        ScalaSig: length = 0x3
         05 00 00
      minor version: 0
      major version: 50
      flags: ACC_PUBLIC, ACC_SUPER
     
    {
      private java.lang.String field;
        flags: ACC_PRIVATE
     
     
      public static void printString();
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=1, locals=0, args_size=0
             0: getstatic     #16                 // Field Test$.MODULE$:LTest$;
             3: invokevirtual #18                 // Method Test$.printString:()V
             6: return
     
      public static java.lang.String a();
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=1, locals=0, args_size=0
             0: getstatic     #16                 // Field Test$.MODULE$:LTest$;
             3: invokevirtual #22                 // Method Test$.a:()Ljava/lang/String;
             6: areturn
     
      public java.lang.String field();
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: getfield      #26                 // Field field:Ljava/lang/String;
             4: areturn
     
      public void field_$eq(java.lang.String);
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=2, args_size=2
             0: aload_0
             1: aload_1
             2: putfield      #26                 // Field field:Ljava/lang/String;
             5: return
     
      public void doSomeThing();
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=1, args_size=1
             0: getstatic     #37                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
             3: ldc           #39                 // String do something
             5: invokevirtual #43                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
             8: return
     
      public Test();
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=1, args_size=1
             0: aload_0
             1: invokespecial #46                 // Method java/lang/Object."<init>":()V
             4: aload_0
             5: ldc           #47                 // String field
             7: putfield      #26                 // Field field:Ljava/lang/String;
            10: return
    }


单例对象中的每个字段或方法,都对应Test类中的一个静态同名方法。 从上面的反编译结果得出, 这些静态方法仍然存在。 方法如下:

 public static void printString();
 public static java.lang.String a();

这些静态方法的行为和上篇博客分析的是一样的。 这里不再重复。


除了这两个静态方法之外, Test类中还存在一些其他字段和方法, 这些字段和方法都是成员方法, 而不是静态的。 这些字段和方法如下:

private java.lang.String field;
public java.lang.String field();
public void field_$eq(java.lang.String);
public void doSomeThing();
public Test();

这些字段和方法是和伴生类中的字段和方法相对应的。 其中会为字段field添加相关方法public java.lang.String field();和public void field_$eq(java.lang.String); 。 为字段添加同名getter方法和xxx_$eq这样的setter方法, 是scalac编译器的默认行为,这个问题在之前的博客 学习Scala:Scala中的字段和方法 中详细讲解过, 这里不再重复。

总结:

1 伴生类中定义的字段和方法,对应同类class类中的成员字段和成员方法;
2 伴生对象中定义的字段和方法,对应同名类中的静态方法, 所以可以认为Scala中的object关键字是静态的另一种表示方式, 只是scala将这些静态的东西也封装成了对象;
3 伴生对象中定义的字段和方法, 对应虚构类中的成员字段和方法。
4 同名类中的静态方法, 会访问单例的虚构类对象, 将相关的逻辑调用到虚构类中的成员方法中。 由于虚构类是单例的, 所以可以保证伴生对象中的字段都是唯一的。 也就是说虚构类的单例性, 保证了伴生对象(即scala中的object修饰的单例对象)中信息的唯一性。

下面进行验证:

    object Main {
      def main(args : Array[String]){
        var a = Test.a;
        var a1 = Test.a;
        println("a eq a1 : " + (a eq a1))
      }
    }

上面的示例中, 访问了两次单例对象的a属性, 并且比较是否是同一个对象, 输出信息如下:

a eq a1 : true


5 伴生对象中的逻辑, 都转移到虚构类中去处理
6 伴生类中的逻辑, 都转移到同名类中的成员方法中去处理。
7 需要注意,伴生类并不是单例的!!! 它仍然可以创建多个对象, 只要在其他地方能够访问到这个伴生类。下面进行验证:

    object Main {
      def main(args : Array[String]){
        var a = new Test
        var a1 = new Test
        println("a eq a1 : " + (a eq a1))
      }
    }


打印结果为:

a eq a1 : false


创建的对象不是同一个对象, 所以Test伴生类不是单例的。


8 如何在Scala中使用单例模式呢?我们上面说过, 单例对象中的属性都是永远唯一的, 所以将伴生类中的所有逻辑全部移到单例对象中, 去除伴生类, 让这个单例对象成为孤立对象, 这个孤立对象天然就是单例的。 以本例中的实例代码为例, 将伴生类和伴生对象合并到一起, 去除伴生类, 得到孤立对象如下:

    /*class Test{
      var field = "field"
      def doSomeThing = println("do something")
    }
    object  Test {
        val a = "a string";
        def printString = println(a)
    }*/
     
    object Test {
      var field = "field"
      def doSomeThing = println("do something")
     
      val a = "a string";
      def printString = println(a)
    }


9 如果必须存在伴生类, 怎么保证伴生类是单例的呢? 可以将伴生类的构造器私有, 并且在伴生对象中创建一个伴生类的对象, 这个对象就是唯一的。 代码如下所示:

    class Test private {
      var field = "field"
      def doSomeThing = println("do something")
    }
     
    object Test {
      val single = new Test
      val a = "a string"
      def printString = println(a)
    }


下面进行验证:

    object Main {
      def main(args : Array[String]){
        var a = Test.single
        var a1 = Test.single;
        println("a eq a1 : " + (a eq a1))
      }
    }


打印结果为:a eq a1 : true

从输出结果可以知道, 两次访问Test.single得到的是同一个对象。 并且这个对象是唯一的, 在外部不能创建伴生类的对象, 因为它的构造器已经私有了, 所以他是单例的。

CRISPR筛选全流程指南:从文库构建到数据分析实战 功能基因组学研究需要高效解析基因型与表型的关联,高通量筛选技术是其中的关键工具。基于CRISPR系统的筛选方法通过大规模sgRNA文库扰动基因功能,结合慢病毒递送和测序分析,能够系统鉴定影响细胞增殖、耐药、病毒感染的候选基因。与RNAi或化学小分子筛选相比,CRISPR筛选具有覆盖全基因组、脱靶率低、信号清晰等优势,已广泛应用于药物靶点发现、合成致死和免疫治疗机制研究。本文围绕实验设计与操作中的核心要点展开,涵盖文库扩增、病毒滴度控制、MOI把握、筛选模式选择以及基于MAGeCK的sgRNA丰度分析,并针对 阅读详情

相关推荐

基于CODESYS与EtherCAT的步进电机单轴控制实践

本文详细介绍了基于CODESYS平台与EtherCAT总线技术实现步进电机单轴控制的完整实践流程。从硬件选型、软件工程创建、EtherCAT主从站配置,到运动控制功能块编程与在线调试,手把手指导读者完成从零到一的搭建,并提供了常见问题的排坑指南,帮助自动化开发者快速掌握这一高效、灵活且成本可控的工业运动控制解决方案。

hh234的博客 491

mysql中的临时表怎么用的?

1.什么是临时表 内部临时表是sql语句执行过程中,用来存储中间结果的的数据表,其作用类似于:join语句执行过程中的joinbuffer,order by语句执行过程中的sortBuffer一样。这个表是mysql自己创建出来的,对客户端程序不可见。那么mysql什么时候会创建内部临时表呢?创建的内部临时表的表结构又是怎么样的呢? 2.临时表的使用场景 在mysql中常见的使用临时表的场景,有两个:unoin语句和groupby语句。 为了更好的了解内部临时表在unoin和groupby中是如何起作用的,

weixin_45701550的博客 5880

告别‘火龙’:实测BES2800/RK2108等主流芯片,聊聊AI眼镜的功耗与散热‘内卷’实战

本文深入探讨了AI智能眼镜在功耗与散热方面的挑战,重点分析了恒玄BES2800和瑞芯微RK2108等主控芯片的能效表现。通过实测数据和优化策略,揭示了在6nm工艺、异构计算和系统级散热方案下的实战经验,为AI眼镜的功耗管理提供了实用解决方案。

weixin_30839881的博客 946

Scala(总)之 小白入门扫盲知识总篇!

文章目录1.Scala入门1.1Windows安装Scala(Scala JDK)1.2 Linux安装Scala1.3 IDEA插件安装1.4 IDEA创建项目2. 变量和数据类型2.1 变量和常量2.2 字符串输出2.3 键盘输入2.4 数据类型2.5 整数类型(Byte、Short、Int、Long)2.6 浮点类型(Float、Double)2.7 字符类型(Char)2.8 布尔类型(Boolean)2.9 Unit类型、Null类型和Nothing类型2.10 类型转换2.10.1 数值类型自动

子清的博客 1886

YOLOv8全网首发:CVPR2026 MixerCSeg | DEGConv方向引导边缘门控,破解细长裂缝检测难题

DEGConv通过方向先验嵌入、边缘增强卷积和动态门控三大机制,针对性解决了YOLO在处理裂缝类细长、多方向、低对比度目标时的特征提取不足问题,显著提升了裂缝检测的精度与鲁棒性。

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 518

【数字孪生与仿真技术】14:数据驱动+机理模型:工业级混合建模实战(附MATLAB完整代码+案例解析)

摘要 本文针对工业建模中机理模型精度不足和数据驱动模型缺乏物理约束的痛点,提出混合建模技术作为解决方案。文章系统阐述了混合建模的3种主流架构(参数校正型、残差补偿型、组件替换型),结合清华大学催化裂化装置的实际案例,分析混合建模的工业应用价值。同时以连续搅拌反应釜(CSTR)为例,提供MATLAB环境下残差补偿型混合模型的完整实现代码,详细展示从机理模型构建到数据驱动补偿的全流程,并给出工程实践中的应对策略,为工业建模提供可落地的技术方案。 关键词 混合建模、机理模型、数据驱动、工业建模、MATLAB

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 622

大数据学习04 Scala的学习

大数据学习04 Scala的学习 在Spark学习之前,先进行对Scala的学习 提示:已经有Scala基础的同学可以先跳过这部分 文章目录大数据学习04 Scala的学习Scala是什么一、Scala入门1.为什么学习Scala2.Scala的发展历史二、Scala与Java的关系1.Scala是对java的增强2.Scala语言特点2.Scala环境搭建总结 Scala是什么 Scala===Java++ 1.Scala基于JVM,和JAVA完全兼容,同样具有跨平台、可移植性好,方便的垃圾回收

yumiao的博客 1713

【笔记篇】Davinci Configurator OS模块(上)

主要讲解了AUTOSAR OS配置项和Vector MICROSAR OS的特性

weixin_47571898的博客 2274

优化算法——梯度下降法

最近一直在看机器学习的材料,归纳起来就是把一个学习的问题转化为优化的问题,机器学习算法的本质就是如何对问题抽象建模,使一个学习的问题变为一个优化的问题。 优化的算法有很多种,从最基本的梯度下降法到现在的一些启发式算法,如遗传算法(GA),差分演化算法(DE),粒子群算法(PSO)和人工蜂群算法(ABC)。 梯度下降法又被称为最速下降法(Steepest descend method),其理论基

null的专栏 3万+

LVGL core disp显示设计(lv_disp.c)

lv_disp.c当前显示页面的配置scr_act在原始的LVGL是以screen的概念存在,但我们还是习惯叫它为一个页面提供获取top_layer、sys_layer、prev_scr图层接口配置主题管理默认的主题管理是递归查找的,实际项目中因此页面的复杂性,虽然也兼容了此主题管理的方案,但还是推荐使用单页面自行管理主题的方案壁纸设置多页面切换的动画支持计算无用户操作时间清理显示缓存。

Bigan(安)的博客 2331

【SystemVerilog基础】SystemVerilog Assertion 概述

断言主要是描述复杂的设计行为;是传统验证方法学一个很好的补充,可以检查到电路内部细节,相当于一种白盒验证,传统的验证方法学是纯粹的黑盒验证!property跟上面的sequence差别不大,唯一有差别可能多了两个implication(|->,|=>)注意,当有前置条件时,不满足前置条件不会fail,而是伪真!上面这个因为没有前置条件,每个周期都会去检查!注意:当没有前置条件时,不满足条件会直接fail!

MoorePlus的博客 333

随机种子 seed

np.random模块下的其它随机数生成函数如randn、randint等在设置了seed后也会产生固定的随机数。np.random.seed()这个函数的作用是设置numpy随机数生成器的种子,以产生可复现的随机数序列。设置seed可以使得结果可复现,便于debug和测试。在科学计算中需要保证结果一致时,设置seed也很重要。可以看到,当设置相同的seed时,每次生成的随机数序列都是一样的。而不设置seed时,每次生成的随机数序列都会不同。

qq_40812236的博客 550

T507 buildroot linux4.9之RLT8211F 1000M以太网开发调试

调试T507的gmac,解决该RTL8211F 1000m以太网中调试遇到的问题

目前从事linux驱动开发,多交流多学习吧 688

学习Scala:伴生对象的实现原理

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34254823的博客 73

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

scala中的伴生对象实现原理

孤立对象是只有一个object关键字修饰的对象。 该对象会编译成两个class文件, 一个是以孤立对象的名字命名的class,  一个是以孤立对象的名字后面加上一个$字符命名的class, 这个class又叫做虚构类。 源码中的孤立对象中的字段和方法, 都被编译成以孤立对象的名字命名的class中的静态方法, 这些静态方法都会访问单例的虚构类对象。 虚构了是传统意义上的单例模式, 并且在类初始化的

2048

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Scala 之伴生对象总结

Scala 中伴生对象采用 object 关键字声明,伴生对象中声明的全是 "静态"内容,可以通过伴生 对象名称直接调用。 伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。 伴生对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问 从语法角度来讲,所谓的伴生对象其实就是类的静态方法和成员的集合 从技术角度来讲,scala 还是没有生成静态的内容,只不过是将伴生对象生成了一个...

李昊轩的博客 976

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

scala伴生类与伴生对象分析

object AccompanyObject { def main(args: Array[String]): Unit = { println(ScalaPerson.sex) //true 在底层等价于 ScalaPerson$.MODULE$.s...

yimeng1994的博客 870

从java中理解scala静态,从反编译中理解伴生对象与伴生类

一.java静态属性与静态方法 1.1 Java中静态方法并不是通过对象调用的,而是通过类对象调用的,所以静态操作并不是面向对象的。(因为没有显式地使用对象这一概念) 二.scala静态属性与静态方法 2.1 scala中没有关键字 ”static” 2.2为了达到面向对象目的,scala设计者设计出伴生对象和伴生类来满足静态属性与静态方法的需求 Scala语言是完全面向对象(万物皆对象...

weixin_42767147的博客 591

解析scala的伴生类和伴生对象所属类

@目录说在前面运行原理代码体现解析说在前面 scala是纯面向对象的语言,C是纯面向过程的语言,Java就是面向过程+面向对象的语言。 在说到伴生对象之前,要先说到java的static关键字,static关键字修饰的变量、方法可以使类对象共享,而非某个对象单独拥有,所以是通过类名.变量名/方法名去调用。而这违背了scala面向对象的初衷,因为这没有通过对象去调用。 运行原理 sca...

Sun's Blog 301

scala object 与class 区别

原博客:http://dreamhead.blogbus.com/logs/60217908.html 准备涉水Scala的Java程序员请注意,Scala里没有static。在大多数情况下,static真不该是static的。像Scala这样想在面向对象上更进一步的程序设计语言,取消static是一种进取的表现,这样得以保证了其面向对象模型的完整性。好吧,我承认,有时候,我们还是需要类一级的...

freespace 180

Scala对象的所属类

在说到伴生对象之前,要先说到java的static关键字,static关键字修饰的变量、方法可以使类对象共享,而非某个对象单独拥有,所以是通过类名.变量名/方法名去调用。Scala是纯面向对象的,去除了java中的static关键字,通过伴生对象模拟static的效果(类名.变量名/方法名)当我们对源文件进行编译之后,默认会生成两个字节码文件,一个是伴生类,另一个是伴生对象所属类。如果不想默认生成伴生类,可以手动生成,要求伴生类名称和伴生对象名称一致。真正的伴生对象是伴生对象所属类中创建的单例对象。

2401_87076475的博客 231

第66讲scala 中伴生对象的隐式转换

信息来源于 DT大数据梦工厂微信公众号:DT_Spark代码如下:/** * 伴生对象 中的隐式方法 * Created by zhiwang on 2015/7/24. */import java.io.File import scala.io.Sourceclass RichFile2(val file:File) { def read = Source.fromFile(file.

749

scala中的object和class

近来一直在阅读spark的源码,发现一个问题,一直不太理解为什么在同一个 .scala文件中可以同时存在名字相同的class和object 经过一番研究,终于明白原来是在scala中没有定义static类型的变量,取而代之的是单例对象:singleton object 除了object关键字取代了class,单例对象看着就像是类的定义。 一个单例对象与一个类共享同一个名称时,他被称作是这个类

oYiShiMiLi的专栏 2974

用一个例子告诉你 什么是scala中的伴生对象(单例对象)

用一个🌰告诉你 什么是scala中的伴生对象(单例对象)

向热爱自己一样,热爱这个世界。 273

学习Scala:孤立对象的实现原理

在关于Scala的第一篇文章 学习Scala:从HelloWorld开始 中, 我们讲述了Scala的HelloWorld程序的执行原理。在Scala中,程序的入口使用孤立对象来实现, 在这篇博客中, 我们讲述了孤立对象是如何实现程序入口的, 不管Scala和Java的语法差别多大, 只要能以一定的方式实现标准的class文件入口类和入口函数, 就能被JVM执行。感兴趣的读者可以移步这篇博客。 在《Scala编程》这本书中, 把孤立对象和伴生对象都叫做单例对象。孤立对象指的是只有一个使用object关

昨夜星辰的博客 9009
上一篇: 【机器学习】 SVD矩阵分解 整理
下一篇: 精细化营销推广渠道分析
CWS_chen
博客等级 码龄11年 220粉丝 51原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值