大数据8_07_Scala面向对象编程

8 面向对象编程

8.1 scala面向对象概念

scala面向对象的基本用法和java没有区别

object TestOOP{
  def main(args: Array[String]): Unit = {
    /*
    *   Java中
    *   package xxxxx
    *   import java.util.List   //导类
    *   import java.util.*  //导入util包内需要的类,并不是一次性全部导入,是用到哪个类就导入哪个类(编译器完成的)。
    *   public class XXX extends Parent implements Interface {
    *       private String name;
    *       public Int age;
    *
    *       //构造器
    *       public XXX(){
    *       }
    *       public void setName(){}
    *       public boolean login(){}
    * }
    *
    * */
    //面向对象编程
    //Scala面向对象的语法和Java基本一致
    val user = new User
    println(user.name)
    println(user.login())
  }
  //类
  class User{
    //成员变量,属性
    val name : String = "Tom"
    //成员方法
    def login() : Boolean = {
      if ("Tom".eq("Tom"))
        true
      else
        false
    }
  }
}

8.2 包package

  • 1 package关键字可以多次声明

  • 2 源码路径和包名没有关系

  • 3 package可以嵌套使用

  • 4 package包可以当对象使用

    创建包对象,包对象内可以声明属性、方法,包对象内定义声明的属性方法,在该包内所有的object和class内都可以使用。

//1 package关键字可以多次声明
package com
package codejiwei
package scala
//2 源码路径和包名没有关系,这个地方的包package可以写任何名字,这样就会编译出对应任何名字
package chaptor6{
  //3 package可以嵌套声明
  package xxx{
    object TestObject_Package {
      def main(args: Array[String]): Unit = {
        //面向对象编程-包-package
        //Java中package的作用
        // 1 管理类
        // 2 权限管理
        // 3 区分相同名称的类 Data Data
        // 4 源码路径必须要和包名相同

        //包名 : 域名 + 项目名 + 模块名 + 程序类型名
        //        com.codejiwei.test.user.util
        // UtilDate
        //Java中package语法功能比较单一,所以scala进行了扩展

        //4 将包可以当对象来用,
        //这个name就是package对象中声明的属性,那么这个包对象内的定义的属性方法,在该包内所有的object和class内都可以使用。
        println(name)
        say()
      }
    }
  }
}

8.3 导入import

  • java中import是导类,比如:import java.util.Date;

    scala中import是真正的导包。import java.util

  • scala中导入一个包中的所有类(用到的哪个类导入哪个类)

    import java.util.util._

    因为scala中*用来表示可变参数了

    import java.util.List
    import java.util._ // Scala中使用下划线代替Java中的星号
    
  • scala中的import可以放在任何位置,而java是只能放在类前面

    object ScalaImport{
        def main(args: Array[String]): Unit = {
            import java.util.ArrayList
            new  ArrayList()   
    }
    }
    
  • scala中可以在同一行中导入多个类,简化代码

    import java.util.{List, ArrayList}
    
  • scala中可以屏蔽某个包中的类

    import java.util._
    import java.sql.{ Date=>_, Array=>_, _ }
    
  • scala中可以给类起别名

    import java.util.{ArrayList=>AList}
    
    object ScalaImport{
        def main(args: Array[String]): Unit = {
            new AList()
        }
    }
    
    
  • scala中可以使用类的绝对路径而不是相对路径

    Scala中import操作默认使用相对包路径,以当前包为查找基准。

    import _root_.java.util.ArrayList
    
  • scala默认会导入如下包和对象

    import java.lang._
    import scala._
    import scala.Predef._
    
  • 导入对象

    导入对象的所有内容

    object TestImport {
      def main(args: Array[String]): Unit = {
    
        val user = new User("Tom", 23)
        //导入对象的所有内容
        //在访问这个对象的成员方法和属性时,不需要使用对象去调用
        //导入对象功能只能对val声明的对象起作用
        import user._
        println(name11)
        println(age11)
        buy()
      }
    
      class User(val name11 : String, var age11 :Int ){
        def buy() = {
          println("buy.....")
        }
      }
    }
    
package com.codejiwei.scala.chaptor6

//scala才是真正的导包
import com.codejiwei.scala.chaptor6.java.util.HashMap
import java.util


object TestObject_Import {
  def main(args: Array[String]): Unit = {
    /*
    * 面向对象编程-导入-import
    * java => import
    * 1. 导入类
    * 2. 静态导入
    *
    * Java中import关键字的功能比较单一,所以scala进行扩展
    * 1. 采用下划线代替了星号,代表导入一个包中所有的类(用到哪个类才导入哪个类)
    * import java.util._
    * 2. import关键可以放置在任意地方
    * import java.util.Date
    * new Date()
    *
    * 3. scala才是真正的导包
    *
    * 4. 将同一个包的类在导入时放置在一起
    *import java.util.{List, ArrayList, Map, _}
    *
    *
    * 5. 屏蔽类
    *     import java.util.Date
          import java.sql.{Date => _, _}
           new Date()
    *
    * 6. 给类起别名
    * import java.util.{HashMap => JavaHashMap}
    *
    *
    * 7. Scala中import操作使用相对包路径,以当前包为查找的基准路径
    *   如果不想使用相对包路径,需要采用特殊操作
   		var map2 = new _root_.java.util.HashMap()
    *
    * */
//    new util.ArrayList()

    //如果自己写的类和jdk的类重名了,怎么办?
    //jdk不会使用咱们自己的类,而是采用JDK的类,因为安全
    //采用了双亲委派机制
    var map = new java.util.HashMap()
    println(map)  //com.codejiwei.scala.chaptor6.java.util.HashMap@19bb089b
  }
}
package java{
  package util{
    class HashMap{
    }
  }
}
Java的双亲委派机制

Java中当出现重名的类,编译器会先从启动类加载器->扩展类加载器->应用类加载器。这个顺序去找。

启动类加载器位置:D:\Development_tools\java\jdk8\jre\classes

扩展类加载器位置:D:\Development_tools\java\jdk8\jre\lib\ext\classes

应用类加载器位置:D:\

image-20201115221419544

8.4 类

  • 面向对象编程中类可以看成一个模板,而对象可以看成是根据模板所创建的具体事物
  • Scala中一个源文件中可以声明多个类

object里面声明的属性,方法;模仿的是静态语法

package com.codejiwei.scala.chaptor6

object TestClass {
  def main(args: Array[String]): Unit = {
    //面向对象编程 - 类
    //class类名 {类体}
    //在object里面声明的类 没有静态的概念,所以它里面的方法和属性只能通过构建对象才能访问。
    //但是声明在object内的属性和方法可以直接通过对象名去访问。
    //Scala中一个源文件中可以声明多个类
    //子类可以继承父类
    val user = new User
    user.test()
    user.name

    val user1 = new User1
    user1.name1
    user1.test1()

  }
  class User extends Parent {
    val name : String = "Tom"
    def test(): Unit ={
      println("Test....")
    }
  }
  class Parent{
  }
}

8.5 属性

  • 所谓的属性就是类中声明的变量,使用var声明的其实是private;使用val声明的其实是private final

image-20201115234459025

  • 类的属性可以通过构造对象进行访问

    User.name

  • scala中下划线表示类的属性默认初始化

    var age : Int = _	//默认初始化,scala中如果不写,表示的是抽象属性。
    
  • scala中声明属性,同时生成了set、get方法

    • scala中给一个属性赋值,其实并不是赋值,而是调用属性的set方法
    • scala中访问一个属性,其实并不是访问属性,而是调用属性的get方法

image-20201116000708871

  • 因为在很多框架中需要调用类的get和set方法那么怎么办呢?

    scala是给属性添加**@BeanProperty**注解的方式,编译器自动生成符合Bean规范的set、get方法

object TestField {
  def main(args: Array[String]): Unit = {
    //面向对象编程 - 属性
    //所谓的属性,其实就是类中声明的变量,private final / private
    //使用val声明的普通的变量,那么不会增加final
    //使用val声明的类的属性,那么会增加final


    //scala中声明属性,等同于java中声明私有的类属性
    //外部之所以能够使用,因为编译器在声明属性时,同时生成了属性对应的set、get方法

    val user = new User

    //scala中给一个属性赋值,其实并不是赋值,而是调用属性的set方法
    user.name = "Tom"
    //使用val声明的属性,编译器在编译的时候不会提供对应的set方法!!!
//    user.password = "root"  //声明为val

    //scala中获取一个属性,其实并不是访问属性,而是调用属性的get方法
    println(user.name)

    //如果给属性添加@BeanProperty注解,编译器在生成类时,会自动添加符合Bean规范的set、get方法

  }
  class User{
    @BeanProperty
    var name : String = "zhangsan"
    @BeanProperty
    val password : String = "zhangsan"
  }
}
  • 声明为var和val的属性其实编译器编译的时候就会生成private和private final的属性,那么在给scala的属性添加private呢

    private var name : String = "zhangsan"
    private val password : String = "zhangsan"
    

image-20201116001902758

  • 如何初始化属性呢?

    可以在构造器的参数列表中传递参数,在参数前面添加var或val关键字;scala会将这个参数作为属性来用

  class User(var name :String){
    
  }
  class User2(){
    var name : String = _
    def this(name :String){
      this()
    }
  }

8.6 访问权限

  • private : 私有访问权限
  • private[包名] : 包访问权限
  • protected : 受保护权限
  • ​ :公共的
package com.codejiwei.scala.chaptor6

import com.codejiwei.scala.chaptor6.test.Parent

object TestObjectAccess {
  def main(args: Array[String]): Unit = {
    //访问权限
    /*
    * Java的访问权限
    * 1 private :同类
    * 2 (default) : 同包同类
    * 3 protected : 同类同包子类
    * 4 public : 任意
    *
    * .点:不是调用的意思,表示从属关系
    * User user = new User();
    * user.name = "zhangsan";
    * 1) user对象调用name属性,赋值给zhangsan
    * 2) 给user对象的name属性赋值为zhangsan
    *
    * */

    /*
    * Scala中的访问权限
    * 1 private : 私用
    * 2 private[包名] : 包私有访问权限
    * 3 protected : 受保护的,只能同类和子类使用,同包不能使用
    * 4 (default) : 公共的
    *
    * */
    val p = new Parent
    p.sex
    
  }

}
package test{
  class Parent {
    private val email = "zhang@163.com"
    //包私有访问权限的包可以更改
    private[chaptor6] val sex = "M"
    protected val money = 293.3

    def testxx(): Unit ={
      println(email)
      println(sex)
      println(money)
    }
  }
  class Emp extends Parent {
    def test1() = {
      println(sex)
      println(money)
    }
  }
  class User {
    def test2() = {
      val p = new Parent
      println(p.sex)
//      println()
    }
  }
}

8.7 动态绑定机制

动态绑定机制:当调用一个对象的成员方法时,JVM会将这个方法和对象的实际内存进行绑定,然后调用。

属性没有动态绑定机制。

public class Scala07_Object_Method_Java {
    public static void main(String[] args) throws Exception {
        // 方法的重写
        // 子类重写父类的方法
        // 动态绑定机制:当调用一个对象的成员方法时,JVM会将这个方法和对象的实际内存进行绑定
        //               然后调用,属性没有动态绑定机制
        C c = new D();
        System.out.println(c.sum());
    }
}

class C {
    public int i = 10;
    public int sum() {
        return getI() + 10;
    }
    public int getI() {
        return i;
    }
}
class D extends C {
    public int i = 20;
//    public int sum() {
//        return i + 20;
//    }
    public int getI() {
        return this.i;//此时D内有两个i,默认是this.i
    }
}

8.8 方法

①方法的重写

子类重写父类的方法

Scala中重写完整方法,需要增加override关键字,但是抽象方法不需要

object Scala11_Object_Abstract {
    def main(args: Array[String]): Unit = {
        // 抽象 : 不完整
        // 不完整的类就是抽象类 : 使用abstract声明的类
        // 不完整的方法就是抽象方法 : 只有声明没有实现
        //new User()
        // 抽象类不能直接构建对象,但是可以由子类继承后,重写抽象方法创建对象
        new Child();
        // Scala中重写完整方法,需要增加override关键字,但是抽象方法不需要(也可以加,而且更好)
    }
    abstract class User {
        // 抽象方法
        def test():Unit
        def aaa(): Unit = {}
    }
    class Child extends User {
        override def test():Unit = {}
        override def aaa(): Unit = {}
    }
}
②方法的重载

方法的重载:方法名相同,凡是参数列表不一样(个数、顺序、类型)

如果指定类型的参数不存在,那么会按照类型下限的方式往上查找类型

public class Scala07_Object_Method_Java_1 {
    public static void main(String[] args) throws Exception {

        // 方法重载:方法名相同,但是参数列表不一样(个数,顺序,类型)
        // 所谓的方法重载,其实就是不同的方法,只是巧了,方法名一样,靠参数列表来区分
        // 如果指定类型的参数不存在,那么会按照类型下限的方式往上查找类型。
        B a = new B();
        test(a);
    }
    public static void test( A a ) {
        System.out.println("aaaaa");
    }
//    public static void test( B b ) {
//        System.out.println("bbbbb");
//    }
//    public static void test( byte b ) {
//        System.out.println("bbbbb");
//    }
//    public static void test( short s ) {
//        System.out.println("sssss");
//    }
//    public static void test( char c ) {
//        System.out.println("cccc");
//    }
//    public static void test( int i ) {
//        System.out.println("iiiii");
//    }
}
class A {
}
class B extends A {
}

8.9 对象的创建

构造对象的方式有几种?

  • new 类名()

    val user = new User()
    
  • 反射

  • clone

    new java.util.ArrayList().clone()
    
  • 反序列化

8.10 构造方法

  • scala中方法名如果和类名相同,并不是构造方法,就是普通方法

  • scala中万物皆对象,万物皆函数

  • 类也是函数,在类名后面增加参数列表,其实就是构造函数

  • scala中构造方法主要分为两类:主构造方法(类的初始化)& 辅助构造方法(辅助)

    • 1 类名后面的构造方法就是主构造方法
    • 2 辅助构造方法主要使用this关键字
    • 3 辅助构造方法在执行时,必须显示调用(直接、间接)主构造方法,完成类的初始化
    • 4 辅助构造方法存在重载的概念
object TestObjectInstance {
  def main(args: Array[String]): Unit = {
    //如果构造方法有参数,在构建对象时必须传递构造参数
    val user = new User("tom")
    //构造方法分为两大类:主构造方法(类的初始化)& 辅助构造方法(辅助)
    //1 类名后面的构造方法就是主构造方法
    //2 辅助构造方法主要使用this关键字
    //3 辅助构造方法在执行时,必须显示调用(直接、间接)主构造方法,完成类的初始化
    //4 辅助构造方法存在重载的概念

  }
  class User(name : String){
    println("no para instance...")
    def this(){
      this(name)
    }
    def this(i : Int){
      this
    }
  }
}
①存在继承关系的构造方法:
object Scala09_Object_Instance_1 {
    def main(args: Array[String]): Unit = {
        new User("zhangsan")
    }
    class Parent(name:String) {
        println(name)
    }
    class User(name:String) extends Parent(name){
    }
}
②题目:

结果是:?

object Scala09_Object_Instance_2 {
    def main(args: Array[String]): Unit = {
        new User()

    }
    class Parent(name:String) {
        println("111111")
        def this() {
            this("")
            println("222222")
        }
    }
    class User(name:String) extends Parent(){
        def this() {
            this("")
            println("33333")
        }
        println("444444")
    }
}
③构造方法私有化

单例模式创建的对象不会释放,除非显示为null

object Scala09_Object_Instance_3 {

    def main(args: Array[String]): Unit = {

        // 单例模式: 创建对象不会释放,除非显示为null
        //            构造方法私有化
        //new User()

    }
     class User private() {

    }
}

8.11 单例对象&伴生对象

伴生对象可以直接使用,而无需构建,所以没有构造方法

package com.codejiwei.scala.chaptor6

object TestObjectInstance4 {
  def main(args: Array[String]): Unit = {
    //单例:scala中采用特殊的方式(object)创建单例对象

    //scala中采用object关键字声明类,编译时会编译为2个类
    //一个就是当前类本身,另一个就是单例对象的类

    //当调用CCC这个object的test()方法时,其实就是调用单例对象CCC$的MODULE$.test()的方法;所以模拟了静态语法
    //一般情况下,CCC$这个类称为伴生类,将这个单例对象object称为伴生对象
    //一般情况下,将成员方法声明在半生类中
    //          将所谓的静态方法声明在半生对象中。
    CCC.test()
  }
}

image-20201116015637710

8.12 抽象方法

  • 抽象方法:不完整的方法,只有声明没有实现

  • 抽象方法不能直接创建对象,但是可以子类继承后重写抽象方法,然后创建子类对象。

    • 重写抽象方法:可以不加override,但是加上更好
    • 重写完整的方法(非抽象方法):必须要加override
object Scala11_Object_Abstract {
    def main(args: Array[String]): Unit = {
        // 抽象 : 不完整
        // 不完整的类就是抽象类 : 使用abstract声明的类
        // 不完整的方法就是抽象方法 : 只有声明没有实现
        //new User()
        // 抽象类不能直接构建对象,但是可以由子类继承后,重写抽象方法创建对象
        new Child();
        // Scala中重写完整方法,需要增加override关键字,但是抽象方法不需要(也可以加,而且更好)
    }
    abstract class User {
        // 抽象方法
        def test():Unit
        def aaa(): Unit = {}
    }
    class Child extends User {
        override def test():Unit = {}
        override def aaa(): Unit = {}
    }
}

8.13 抽象属性

  • 不完整的属性就称为抽象属性,只有声明,没有初始化。

    抽象属性所在的类是抽象类。

    抽象属性其实在编译时,编译成了属性对应的抽象的get、set方法。

  • 子类继承父类的抽象属性,需要将属性补充完整

    子类在实现父类的抽象属性其实编译时是生成了真正的属性,和两个get和set方法

image-20201116130236950

  • 如果希望scala类的属性java一样,有系统默认初始化怎么做?

使用var age : Int = _;使用下划线代替系统默认值

image-20201116130414037

  • 完整属性的重写,需要增加override关键字,抽象属性的重写也可以加override,并且最好加上

    完整属性的重写,必须声明为val的;抽象属性的重写可以不是val的

object Scala11_Object_Abstract_1 {

    def main(args: Array[String]): Unit = {

        // 抽象属性
        // 不完整的属性就称之为抽象属性:只有声明,没有初始化
        // 如果属性不完整,就是抽象属性,对应的类就是抽象类
        // 子类继承抽象类,将属性补充完整

        // java没有抽象属性的概念,那么scala中抽象属性到底是什么?
        // 1. 抽象属性在编译时,不会产生属性的,而是产生了2个抽象的set,get方法
        // 2. 子类重写父类的抽象属性时,其实就是产生属性以及实现set,get方法


        // 如果希望scala类的属性和java一样,有系统默认初始化怎么做?

        // 完整属性的重写,需要增加override关键字
        // 完整属性的重写,需要声明为val
        new Child()
    }
    abstract class User {
        var name : String
        val email : String = null
        var age : Int = _

        def test(): Unit = {
            //email = "wangwu@xxx.com" // setEmail

            println(email)  //getEmail
        }
    }
    class Child extends User {
        var name : String = "zhangsan"
        override val email : String = "lisi@xxx.com"
    }

}

8.14 apply

  • 伴生对象可以访问伴生类的私有属性和方法。

    注意:私有的伴生类的形式:

    //伴生类 私有的主构造方法
      class User private (){
        private var name: String = _
        var age: Int = _
    	//伴生类 私有的辅助构造方法
        private def this(name: String) = {
          this()
          this.name = name
        }
    
        def this(name: String, age: Int) = {
          this()
          this.name = name
          this.age = age
        }
      }
    
  • 几种创建对象的方式的区别

        //    val user = new User //伴生类
        //    val user1 = new User()  //伴生类
        //    val user2 = User  //伴生对象	
    
        //    val value = User.apply()	//.apply()和下面这是一种方式,通过类名.apply()创建对象
    	//	  val user = User() //
    
  • apply创建对象

    apply()方法声明在伴生对象中,可以通过类名.apply()或者类名()的方式创建对象

      val user1 = User.apply()
      val user2 = User()
    
    object User {
        def apply(name: String): User = new User(name)
        def apply(): User = new User()
    }
    
object Scala12_Object {

    def main(args: Array[String]): Unit = {

        // 构造对象
        // new => 构造方法

        // 伴生对象可以访问伴生类的私有方法和属性的
        //val user = new User()
        //val user = User.instance()

        // Scala提供了其他创建对象的方式:特殊的方法 apply
        // apply方法可以被编译器识别,所以可以省略
        // apply方法可以重载
        //val user1 = User.apply()
        //val user2 = User()
        //val user3 = User("zhangsan")

        //Range(1, 5)

        val user1 = new User("zhangsan") // 构造方法
        val user2 = User // 伴生对象
        val user3 = User() // User.apply()

        //println(user1)
        //println(user2)
        //println(user3)

        // apply方法不仅仅可以声明在伴生对象中,还可以声明在伴生类中
        // 类名+()表示调用伴生对象的apply方法,对象+()表示调用伴生类的apply方法。
        user1.apply()
        user1()

    }
    class User (s:String) {
        def : Unit = {
            println("kkkkkkkkkkkk")
        }
    }
    object User {
        def instance() = {
            new User("zhangsan")
        }
        def apply() = {
            println("xxxxxx")
            new User("zhangsan")
        }
        def apply(name : String) = {
            println("yyyyyy")
            new User(name)
        }
    }
}

8.15 接口

①面向接口编程

image-20201116093213731

②特征-trait

Scala多个类可能存在相同的特征,那么可以将这个特征从类中剥离出来,形成特殊的结构,然后如果类符合这个特征,那么可以混入这个特征。

Scala采用trait关键字来实现这个特征。

  • trait关键字会在编译的时候被编译为interface

    image-20201116194207633

  • 如果一个类符合这个特征,可以使用extends的方式将这个特征混入(实现)到类中,在编译的时候会被编译为implements

object TestInterface {
  def main(args: Array[String]): Unit = {
    val person = new Person
    person.run()
  }
  //run特征
  trait run{
    //抽象的run方法
    def run(): Unit
  }
  //混入的方式:extends
  class Person extends run {
    override def run(): Unit = println("person run....")
  }
}
  • scala中的trait可以简单的理解为接口和抽象类的结合体
 //run特征
  trait run{
    //抽象的run方法
    def run(): Unit
    //理解为抽象类
    def test() = {
      println("test....")
    }


  }
  //混入的方式:extends
  class Person extends run {
    override def run(): Unit = println("person run....")
	//那么子类可以重写该抽象方法
    override def test(): Unit = println("Test1111")
  }
}
③多次混入 with
object TestInterface3 {
  def main(args: Array[String]): Unit = {
    //如果没有父类可以直接使用extends混入特质
    //如果有父类的情况下,可以使用with关键字来混入特质,而且可以多次混入
}
  trait run{}
  class Parent{}
  class User extends Parent with run {}
}
④动态混入功能 with

既然scala编译器编译到最后还是使用的时interface和implements的方式去实现接口,只不过是改变了关键字为trait和extends(with);那么到底还有什么好处呢? — 动态混入功能

  • 有一个场景如果想给User增加一个功能,有几种方法呢?

    • OCP原则:open-close-principle开发原则

    方式1:直接在User内添加新的功能。这样需要修改源码,违背了OCP原则

    方式2:给User创建一个trait特征,让User去混入。但是这样改变了类的体系结构

    方式3:动态混入功能!这样可以在不改变类的体系结构的情况下,扩展类的功能。

  • 怎么样动态混入的呢?

    首相创建一个新功能的trait特征。然后在创建对象的时候使用动态混入的with关键字加上新功能的trait特征。

object TestInterface2 {
  def main(args: Array[String]): Unit = {
    /*
    * 如果想给User增加一个updateUser的功能
    * 方式1:直接在User里面添加一个updateUser()方法,但是这样需要修改源码,违背了OCP原则
    * 方式2:给User创建一个trait特征,让User去混入,但是这样改变了类的体系结构
    * 方式3:动态混入功能!!!
    * */
  
    //方式3 使用with,动态混入原则,可以在不改变类的体系结构的情况下,扩展类的功能,符合OCP开发原则
    val user = new User with updateUser
    user.updateUser()
  }
  trait updateUser{
    def updateUser() = {
      println("updateUser....2")
    }
  }
   //方式2
//  class User extends updateUser{
  class User {
    def insertUser() = {
      println("insert user...")
    }
	//方式1
//    def updateUser() = {
//      println("update user...")
//    }
  }
}
⑤ 特质的初始化顺序
  • 特质只和当前类相关,和父类、子类都无关,所以父类初始化优先于当前的特质
  • 如果将特质理解为抽象类,那么初始化优先于当前类
  • 如果一个类混入多个特质,那么多个特质的初始化顺序从左到右
  • 特质的初始化只会初始化一次
⑥ 功能叠加顺序

功能的叠加问题,其实就是多个相同功能的调用顺序问题

  • 多个特质的初始化顺序是从左到右,但是调用的顺序是从右到左
    • super不是父类引用的意思,而是在编译时起到了标记的作用
    • scala的功能叠加时,super表示的不是上一级的对象,而是上一个

image-20201116203926098

object TestInterface4 {
  def main(args: Array[String]): Unit = {

    //功能叠加问题:其实就是多个相同功能的调用顺序问题
    val mysql = new MySQL()
    mysql.operDate()
    /*结果:
    * 向数据库中
      操作数据
      向日志中
      操作数据
    * */
  }

  trait Oper {
    def operDate() = {
      println("操作数据")
    }
  }

  trait DB extends Oper {
    override def operDate(): Unit = {
      println("向数据库中")
      //这样就可以调用两次,一次是直接去调用Oper的operDate方法
      super[Oper].operDate()
      //另一次就是调用上一个对象也就是Log的operDate方法
      super.operDate()
    }
  }

  trait Log extends Oper {
    override def operDate(): Unit = {
      println("向日志中")
      super.operDate()
    }
  }
    //声明的顺序是从左到右,调用的顺序是从右到左。
  class MySQL extends Log with DB {
  }
}

8.16 拓展

①判断两个对象是否相等一定要重写hashcode和equals方法吗?

不一定!一般情况下只需要重写equals方法就可以了!

只有在往hashmap中放数据的时候,在需要重写hashcode和equals方法。

isInstanceOf[]

这就相当于java中的 instanceof

asInstanceOf[]

这就相当于java中的强制类型转换

//java中判断两个对象是否相等:
public class TestExt {
    public static void main(String[] args) {
        Dept d1 = new Dept();
        d1.id = 1;
        Dept d2 = new Dept();
        d2.id = 1;
        System.out.println(d1 == d2);
        System.out.println(d1.equals(d2));
    }
}

class Dept{
    public int id;
    //在hashmap中放数据的时候,才会重写hashcode和equals方法
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Dept){
            Dept other = (Dept)obj;
            return id == other.id;
        }else {
            return false;
        }
    }
}
//scala中判断两个对象是否相等
object TestEquals {
  def main(args: Array[String]): Unit = {
    val user = new User
    user.name = "Tom"
    val user1 = new User
    user1.name = "Tom"
    println(user==user1)
  }
  class User{
    var name : String = _
    override def equals(obj: Any): Boolean = {
      //判断obj是否是一个类型的
      if (obj.isInstanceOf[User]){
        //将obj转换成User类型,赋值给一个新变量
        val temp = obj.asInstanceOf[User]
        this.name.equals(temp.name)
      }else{
        false
      }
    }
  }
}
②获取类的信息
object TestClassInfo {
  def main(args: Array[String]): Unit = {
    //获取类的信息
    //类似于Java中User.class和getClass
    val value = classOf[User]
    val value1 : Class[User] = classOf[User]

  }
  class User{
  }
}
③ 枚举类
object TestEnum {
  def main(args: Array[String]): Unit = {
    println(Color.BLUE)
  }
  //scala中的枚举类使用伴生对象object声明;需要继承Enumeration
  object Color extends Enumeration{
    var RED = Value(1, "red")
    var YELLOW = Value(2, "yellow")
    var BLUE = Value(3, "blue")
  }
}
④ Type定义新类型

使用Type关键字可以定义新的数据类型名称,本质上就是类型的起别名

object Test {
    def main(args: Array[String]): Unit = {
        type S = String
        var v : S = "abc"
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

最佳第六六六人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值