• Scala核心编程_第06章 面向对象编程(基础部分)


    面向对象的Scala

    Java是面向对象的编程语言,由于历史原因,Java中还存在着非面向对象的内容:基本类型 ,null,静态方法等。

    Scala语言来自于Java,所以天生就是面向对象的语言,而且Scala是纯粹的面向对象的语言,即在Scala中,一切皆为对象。

    如何定义类

    基本语法

    [修饰符] class 类名 {
    类体
    }

    注意:

    scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public),[修饰符在后面再详解].

    一个Scala源文件可以包含多个类.

    //定义一个类Cat
    //一个class Cat 对应的字节码文件只有一个 Cat.class ,默认是public
    class Cat {
      //定义/声明三个属性
      //说明
      //1. 当我们声明了 var name :String时, 在底层对应 private name
      //2. 同时会生成 两个public方法 name() <=类似=> getter  public name_$eq() => setter
      var name: String = "" //给初始值
      var age: Int = _ // _ 表示给age 一个默认的值 ,如果Int 默认就是0
      var color: String = _ // _ 给 color 默认值,如果String ,默认是就是""
      def meng(): Unit ={
    
      }
    }
    

    底层逻辑:

    public class Cat
    {
      private String name = "";
      private int age;
      private String color;
    
      public String name()
      {
        return this.name; }
      public void name_$eq(String x$1) { this.name = x$1; }
      public int age() { return this.age; }
      public void age_$eq(int x$1) { this.age = x$1; }
      public String color() { return this.color; }
      public void color_$eq(String x$1) { this.color = x$1; }
      public void meng() {}  
    }

    属性/成员变量

    1. 属性的定义语法:同变量:[访问修饰符] varl 属性名称 [:类型] = 属性值
    2. 属性的定义类型:属性是类的一个组成部分,属性的定义类型可以为任意类型,包含值类型或引用类型
    3. 属性的初始化:
      • Scala中声明一个属性,必须显示的初始化,然后根据初始化数据的类型自动推断,属性类型可以省略(这点和Java不同)。
      • 如果赋值为null,则一定要加类型,因为不加类型, 那么该属性的类型就是Null类型.
      • 如果在定义属性时,暂时不赋值,也可以使用符号_(下划线),让系统分配默认值.

     

    class Person {
      var age: Int = 10 //给属性赋初值,省略类型,会自动推导
      var sal = 8090.9
      var Name = null //  是 null类型
      var address1: String = null //ok null=>""
      var address2 = "" //ok 会自动推导=>"" String
    }
    
    class A {
      var var1 :String = _  // null
      var var2 :Byte = _  // 0
      var var3 :Double = _  //0.0
      var var4 :Boolean = _  //false
    }
    

     如何创建对象

    val | var 对象名 [:类型] = new 类型()
    1. 如果我们不希望改变对象的引用(即:内存地址), 应该声明为val 性质的,否则声明为var, scala设计者推荐使用val ,因为一般来说,在程序中,我们只是改变对象属性的值,而不是改变对象的引用。
    2. scala在声明对象变量时,可以根据创建对象的类型自动推断,所以类型声明可以省略,但当类型和后面new 对象类型有继承关系即多态时,就必须写【:类型】

    方法

    基本说明

    Scala中的方法其实就是函数,声明规则请参考函数式编程中的函数声明。

    基本语法

    def 方法名(参数列表) [:返回值类型] = { 
            方法体...
    }    
    

    方法的调用机制原理

    1. 当我们scala开始执行时,先在栈区开辟一个main栈。main栈是最后被销毁
    2. 当scala程序在执行到一个方法时,总会开一个新的栈。
    3. 每个栈是独立的空间,变量(基本数据类型)是独立的,相互不影响(引用类型除外)
    4. 当方法执行完毕后,该方法开辟的栈就会被jvm机回收。

    构造器

    基本介绍

    构造器(constructor)又叫构造方法,是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。

    Scala构造器的介绍

    和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法(即scala中构造器也支持重载)。
    Scala类的构造器包括: 主构造器 和 辅助构造器

    Scala构造器的基本语法

    class 类名(形参列表) { // 主构造器
        // 类体
        def this(形参列表) { // 辅助构造器
        }
        def this(形参列表) { //辅助构造器可以有多个...
        }
    }
    

    Scala构造器注

    1. Scala构造器作用是完成对新对象的初始化,构造器没有返回值。
    2. 主构造器的声明直接放置于类名之后
    3. 主构造器会执行类定义中的所有语句,即:构造器也是方法(函数),传递参数和使用方法和前面的函数部分内容没有区别
    4. 如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略
    5. 辅助构造器名称为this(这个和Java是不一样的),多个辅助构造器通过不同参数列表进行区分,辅助构造器需要在内部直接或者间接调用柱构造器,本质上辅助构造器在底层就是构造器重载。
    6. 如果想让主构造器变成私有的,可以在()之前加上private,这样用户只能通过辅助构造器来构造对象。
    7. 辅助构造器的声明不能和主构造器的声 明一致,会发生错误(即构造器名重复)

    代码案例:

    object CatDemo {
      def main(args: Array[String]): Unit = {
        var cat=new Cat("wqbin",10)
    
    //    print(cat.name,cat.age) //报错
        println(cat.cname)
        println(cat.cage)
      }
    }
    
    //定义一个类Cat
    class Cat(name:String,age:Int) {
      var cname: String =name
      var cage: Int =age
      var csex: String =_
      var chobby: String =_
    
      def this(name:String,age:Int,sex:String){
        this(name,age)//主构造器或者其他构造器
        this.csex=sex
      }
      def this(name:String,age:Int,sex:String,hobby:String){
        this(name,age,sex) //主构造器或者其他构造器
        this.chobby=hobby
      }
    }

    底层逻辑:

    public class Cat
    {
      private String cname;
      private int cage;
      private String csex;
      private String chobby;
      
      public Cat(String name, int age)
      {
        this.cname = name;
        this.cage = age;
      }
      
      public Cat(String name, int age, String sex)
      {
        this(name, age);
        csex_$eq(sex);
      }
      
      public Cat(String name, int age, String sex, String hobby)
      {
        this(name, age, sex);
        chobby_$eq(hobby);
      }
      
      public String cname()
      {
        return this.cname;
      }
      
      public void cname_$eq(String x$1)
      {
        this.cname = x$1;
      }
      
      public int cage()
      {
        return this.cage;
      }
      
      public void cage_$eq(int x$1)
      {
        this.cage = x$1;
      }
     
      public String csex()
      {
        return this.csex;
      }
      
      public void csex_$eq(String x$1)
      {
        this.csex = x$1;
      }
      
      public String chobby()
      {
        return this.chobby;
      }
      
      public void chobby_$eq(String x$1)
      {
        this.chobby = x$1;
      }
      
    }
    

    私有主构造器,或者辅助构造器:

    构造器参数

    • Scala类的主构造器的形参未用任何修饰符修饰,那么这个参数是局部变量。
    • 如果参数使用val关键字声明,那么Scala会将参数作为类的私有的只读属性使用 。
    • 如果参数使用var关键字声明,那么那么Scala会将参数作为类的成员属性使用,并会提供属性对应的xxx()[类似getter]/xxx_$eq()[类似setter]方法,即这时的成员属性是私有的,但是可读写。
    //1. 如果 主构造器是Worker1(inName: String)
    // inName就是一个局部变量
    class Worker1(inName: String) {
            var name: String = inName
            }
    //. 如果 主构造器是Worker2(val inName: String)
    // inName就是Worker2的一个private的只读属性
    class Worker2(val inName: String) {
            var name: String = inName
            }
    
    // 如果 主构造器是Worker3(var inName: String)
    // inName就是Worker3的一个private 的可以读写属性
    class Worker3(var inName: String) {
            var name: String = inName
            }

    继承主构造器带参的父类

    父类

    class China(var name){}

    子类

    class Japan extends China(name="son"){} 

    注意: 我们查看Scala文件编译后的class文件,然后再通过反编译为Java文件可以看到以下代码: 在Japan的默认构造中调用了父类的带参数的构造方法。

    public Japan(){super("son");}

    如果我们想在Japan中定义和父类相同的属性(覆盖override)则需要注意父类 属性的参数的修饰符必须为val (var 也不行), 否者会报错,在Scala中属性覆盖针对不易变的属性,子类必须加上 override val XXX: Tpye

    修改父类:

    class China(val name: String){}

    修改子类:

    class Japan(override val name: String) extends China(name="XX") 

    这里父类上的参数必须加上,否者编译不通,如果不想赋值则可以:Chian(name) 直接这样也可以。

    以上为原理在开发时不注意这些的话,可以直接简写:

    class Japan( name: String ) extends China(name) {}
    

    把 override val 去掉, 变量前面 不要加任何的 权限修饰符。应为Scala的构造方法 是与class写在一起的

    看成java的话:

    public Japan( name: String ){super.(name)}

    总:

    Scala的语言设计的初衷就是少写代码,如果父类中有了非val的属性,我们的子类直接就可以在创建对象的时候,默认的构造中可以传入对应的属性值,并且使用该对象也可以访问的到,所以针对于非val的属性没必要override,而val修饰符在编译后的JAVA文件中是final修饰 是最终变量不可变,所以该值在子类中想引用这个变量并且想改变他的值,则需要override val,想想Scala这样设计也是挺合理的

    对象创建的流程分析

    对象创建的流程分析
    看一个案例
    class Person {
      var age: Short = 90
      var name: String = _
      def this(n: String, a: Int) {
        this()
        this.name = n
        this.age = a
      }
    }
    var p : Person = new Person("小倩",20)
    
    1.加载类的信息(属性信息,方法信息)
    2.在内存中(堆)开辟空间
    4.使用父类的构造器(主和辅助)进行初始
    5.使用主构造器对属性进行初始化  
    6.使用辅助构造器对属性进行初始化  
    7.将开辟的对象的地址赋给 p这个引用
    

    Bean属性

    JavaBeans规范定义了Java的属性是像getXxx()和setXxx()的方法。许多Java工具(框架)都依赖这个命名习惯。

    为了Java的互操作性。将Scala字段加@BeanProperty时,这样会自动生成规范的 setXxx/getXxx 方法。这时可以使用 对象.setXxx() 和 对象.getXxx() 来调用属性。

    注意:给某个属性加入@BeanPropetry注解后,会生成getXXX和setXXX的方法并且对原来底层自动生成类似xxx(),xxx_$eq()方法,没有冲突,二者可以共存。

    class Worker1(inName: String) {
            @BeanProperty var name: String = inName
            }

     

    • @beanGetter
    • @beanSetter
  • 相关阅读:
    11个网站后台模版 | Admin Dashboards | Bootstrap
    Replace Pioneer
    Unity扩展编辑器--类型3:Custom Editors
    Unity扩展编辑器--类型1:Editor Windows
    Android新项目GBSS:第1篇 搭建开发环境
    树莓派(jessie)制作服务并开机启动
    Jqgrid动态拖拽
    看得见的百亿脱贫投入,看不见的阿里技术“脱贫代码”
    OceanBase迁移服务:向分布式架构升级的直接路径
    支付宝工程师创造出了一个可以“拷贝”支付宝的神器
  • 原文地址:https://www.cnblogs.com/wqbin/p/12931711.html
Copyright © 2020-2023  润新知