Scala编程学习之七-面向对象(中级)

7.1包

7.1.1Scala包的基本介绍

和Java一样,Scala中管理项目可以使用包,但Scala中的包的功能更加强大,使用也相对复杂些,下面我们学习Scala包的使用和注意事项。

7.1.2Scala包快速入门

package com.smalltiger.chapter07.use

object TestCat {
  def main(args: Array[String]): Unit = {
    val cat = new com.smalltiger.chapter07.xm2.Cat
    val cat2 = new com.smalltiger.chapter07.xq.Cat
    println("cat1=" + cat)
    println("cat2=" + cat2)
  }
}

7.1.3Scala包的特点概述

基本语法
package 包名

Scala包的三大作用(和Java一样)

  1. 区分相同名字的类
  2. 当类很多时,可以很好的管理类
  3. 控制访问范围

Scala中包名和源码所在的系统文件目录结构可以不一致,但是编译后的字节码文件路径和包名会保持一致(这个工作由编译器完成)

7.1.4Scala包的命名

命名规则:

1)只能包含数字、字母、下划线、小圆点.,但不能用数字开头, 也不要使用关键字。
demo.class.exec1 //错误 , 因为class是关键字
demo.12a // 错误,因为不能以数字开头

命名规范:

1)一般是小写字母+小圆点一般是
com.公司名.项目名.业务模块名
比如:com.smalltiger.oa.model com.smalltiger.oa.controller
com.sina.edu.user
com.sohu.bank.order //

7.1.5Scala会自动引入的常用包

1)java.lang.*
2)scala包
3)Predef包
在这里插入图片描述
7.1.6Scala包注意事项和使用细节
1)scala进行package 打包时,可以有如下形式。

①package com.smalltiger.scala
class person{
}
②package com.smalltiger
package scala
class person{
}
③package com.smalltiger{
package scala{
class person{
}
}
}

//说明了第三种方式的详细内容
//1. 在scala中一个文件可以同时创建多个包
//2. 在scala中一个文件可以在不同的包下,创建多个class,object,trait
package com.smalltiger {

  //表示在com.smalltiger包下创建了User的class
  class User

  package scala {


    //在com.smalltiger.scala这个包下创建了 class PkgDemo03[伴生类] 和 class PkgDemo3$  [伴生对象]
    object PkgDemo03 {
      def main(args: Array[String]): Unit = {
        println("执行ok~~")
      }
    }
    //表示在com.smalltiger.scala包下创建了Person类
    class Person
    //表示在com.smalltiger.scala包下创建了User类
    class User

  }
}

2)包也可以像嵌套类那样嵌套使用(包中有包), 这个在前面的第三种打包方式已经讲过了,在使用第三种方式时的好处是:程序员可以在同一个文件中,将类(class / object)、trait 创建在不同的包中,这样就非常灵活了。[案例+反编译] / 案例参考前面的即可。
3)作用域原则:可以直接向上访问。即: Scala中子包中直接访问父包中的内容, 大括号体现作用域。(提示:Java中子包使用父包的类,需要import)。在子包和父包 类重名时,默认采用就近原则,如果希望指定使用某个类,则带上包名即可。

//说明
//1. 在scala中一个文件可以同时创建多个包
//2. 在scala中一个文件可以在不同的包下,创建多个class,object,trait
package com.smalltiger {

  //表示在com.smalltiger包下创建了User的class
  class User

  //表示在com.smalltiger包下创建了Sheep类
  class Sheep

  package scala {


    //在com.smalltiger.scala这个包下创建了 class PkgDemo03[伴生类] 和 class PkgDemo3$  [伴生对象]
    object PkgDemo03 {
      def main(args: Array[String]): Unit = {
        //子包可以直接使用父包的内容
        val sheep = new Sheep
        //如果子包和父包有相同的类,采用就近原则
        val user = new User
        //如果需要使用父包的内容,则指定包的路径
        val user1 = new com.smalltiger.User

        println("sheep=" + sheep)
        println("user=" + user)
        println("user2=" + user1)
        println("执行ok~~")
      }
    }
    //表示在com.smalltiger.scala包下创建了Person类
    class Person
    //表示在com.smalltiger.scala包下创建了User类
    class User

  }

  package scala2 {
    class Person
  }
}

4)父包要访问子包的内容时,需要import对应的类等

 object Test{
    def main(args: Array[String]): Unit = {
      //使用com.smalltiger.scala.Person
      import com.smalltiger.scala.Person
      val person = new Person
    }
  }

5)可以在同一个.scala文件中,声明多个并列的package(建议嵌套的pakage不要超过3层)
6)包名可以相对路径也可以绝对路径,比如,访问BeanProperty的绝对路径是:
root. scala.beans.BeanProperty ,在一般情况下:我们使用相对路径来引入包,只有当包名冲突时,使用绝对路径来处理
package com.smalltiger.scala2
class Manager( var name : String ) {
//第一种形式
//@BeanProperty var age: Int = _
//第二种形式, 和第一种一样,都是相对路径引入
//@scala.beans.BeanProperty var age: Int = _
//第三种形式, 是绝对路径引入,可以解决包名冲突
@root. scala.beans.BeanProperty var age: Int = _
}
object TestBean {
def main(args: Array[String]): Unit = {
val m = new Manager(“jack”)
println(“m=” + m)
}}

7.1.7包对象

基本介绍:包可以包含类、对象和特质trait,但不能包含函数/方法或变量的定义。这是Java虚拟机的局限。为了弥补这一点不足,scala提供了包对象的概念来解决这个问题。

7.1.8包对象的应用案例
案例演示

//为了弥补包中不能直接定义函数/方法 和 变量的不足,scala提供包对象的概念来解决
  //说明
  //1. 在底层包对象(package object scala) 会生成两个类  class package class package$
  package object scala {
    var name = "jack" //变量
    def sayHi(): Unit = { //方法
      println("package object scala sayHI()")
    }
  }

package scala {


    //在com.smalltiger.scala这个包下创建了 class PkgDemo03[伴生类] 和 class PkgDemo3$  [伴生对象]
    object PkgDemo03 {
      def main(args: Array[String]): Unit = {
        //子包可以直接使用父包的内容
        val sheep = new Sheep
        //如果子包和父包有相同的类,采用就近原则
        val user = new User
        //如果需要使用父包的内容,则指定包的路径
        val user1 = new com.smalltiger.User

        println("sheep=" + sheep)
        println("user=" + user)
        println("user2=" + user1)
        println("执行ok~~")

        //使用对应的包对象的内容
        println("name=" + name)
        sayHi()
      }
    }

对调用包对象的变量和方法,做了底层分析
在这里插入图片描述
7.1.9包对象的注意事项
1)每个包都可以有一个包对象。你需要在父包中定义它。
2)包对象名称需要和包名一致,一般用来对包的功能补充

7.2包的可见性

7.2.1回顾-Java访问修饰符基本介绍

java提供四种访问控制修饰符号控制方法和变量的访问权限(范围):

1)公开级别:用public 修饰,对外公开
2)受保护级别:用protected修饰,对子类和同一个包中的类公开
3)默认级别:没有修饰符号,向同一个包的类公开.
4)私有级别:用private修饰,只有类本身可以访问,不对外公开.
在这里插入图片描述
7.2.2Scala中包的可见性介绍:
在Java中,访问权限分为: public,private,protected和默认。在Scala中,你可以通过类似的修饰符达到同样的效果。但是使用上有区别
案例演示

package com.smalltiger.chapter07.pkg

object Testvisit {
  def main(args: Array[String]): Unit = {
    val c = new Clerk()

    c.showInfo()
    //


  }
}

//说明一下伴生类和伴生对象关系
//1. 如果我们在同一个文件中,写了 class Clerk 和  object Clerk
//   就把 class Clerk 称为 伴生类, object Clerk 称为伴生对象
//2. 如果我们在同一个文件中,只写了 class Clerk ,那么Clerk就是一个普通的类
//3. 如果我们在同一个文件中,只写了 object Clerk, 那么在底层就会自动生成对应的伴生类 class Clerk, 只是这个伴生类是空..
//4. 伴生对象,可以访问到伴生类的任何方法和属性

//类Clerk(伴生类)
class Clerk {
  //var name , 底层 name是private ,但是会提供两个public方法 name name_$eq
  var name: String = "jack"
  //protected var job ,底层 job 是private , 会提供两个pulbic方法 job 和 job_$eq
  protected var job : String = "大数据工程师"
  //private var sal , 底层sal priate, 会提供两个private方法 sal 和 sal_$eq
  private var sal: Double = 9999.9

  //如果方法默认就是public
  def showInfo(): Unit = {
    println(" name " + name + " sal= " + sal)
  }
}

//伴生对象
object Clerk {
  def test(c: Clerk): Unit = {
    //这里体现出在伴生对象中,可以访问c.sal[sal是私有]
    println("test() name=" + c.name + " sal= " + c.sal) //c.sal()
  }
}


package com.smalltiger.chapter07.pkg

object Testvisit {
  def main(args: Array[String]): Unit = {
    val c = new Clerk()

    c.showInfo()
    //


  }
}

7.2.3Scala中包的可见性和访问修饰符的使用

1)当属性访问权限为默认时,从底层看属性是private的,但是因为提供了xxx_$eq()[类似setter]/xxx()[类似getter] 方法,因此从使用效果看是任何地方都可以访问)
2)当方法访问权限为默认时,默认为public访问权限
3)private为私有权限,只在类的内部和伴生对象中可用 【案例演示】
4)protected为受保护权限,scala中受保护权限比Java中更严格,只能子类访问,同包无法访问 (编译器从语法层面控制)
5)在scala中没有public关键字,即不能用public显式的修饰属性和方法。【案演】
6)小结
scala设计者将访问的方式分成三大类: (1) 处处可以访问public (2) 子类和伴生对象能访问protected (3) 本类和伴生对象访问 private
7)包访问权限(表示属性有了限制。同时包也有了限制),这点和Java不一样,体现出Scala包使用的灵活性。 当然,也可以将可见度延展到上层包private[smalltiger] val description="zhangsan"说明:private也可以变化,比如protected[smalltiger], 非常的灵活.
代码说明:

package com.smalltiger.chapter07.pkg

object TestVisitDemo02 {
  def main(args: Array[String]): Unit = {
    val house = new House
    println(house.master)
    house.sayHi()
  }
}

class House {
  //说明:
  //1. 当我们在private[pkg] 表示(1) private 是生效 (2) 同时扩大属性的访问访问,就是在
  //   com.smalltiger.chapter07.pkg  包下是可以访问的.
  //2. private[chapter07] 表示 com.smalltiger.chapter07 包和它子包都可以访问master属性.
  //3. 其它方法和属性类推
  private[pkg] var master = "smith"

  private[chapter07] def sayHi() {
    println("say Hi")
  }
}

7.3包的引入

7.3.1包的引入的注意事项和细节
1)在Scala中,import语句可以出现在任何地方,并不仅限于文件顶部,import语句的作用一直延伸到包含该语句的块末尾。这种语法的好处是:在需要时在引入包,缩小import 包的作用范围,提高效率

package com.smalltiger.chapter07.pkg



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

  }
}

class Dog {
  //import 可以放在任何的地方,同时他的作用范围就是{} 块中
  //import 如果使用到3次及以上,则可以放在文件前面,否则可以使用就近引入.
  import scala.beans.BeanProperty
  @BeanProperty var  name : String = ""
}

class User {
  import scala.beans.BeanProperty
  @BeanProperty var  name : String = ""
}

2)Java中如果想要导入包中所有的类,可以通过通配符*,Scala中采用下 _
3)如果不想要某个包中全部的类,而是其中的几个类,可以采用选取器,使用{} 括起来即可。

class Cat{
  def test(): Unit = {
    //
    import scala.collection.mutable.{HashMap,HashSet}
    val hm = new HashMap()
    val hs = new HashSet()
  }
}

4)如果引入的多个包中含有相同的类,那么可以将类进行重命名进行区分,这个就是重命名。

class Car {
  def test(): Unit = {

    //如果有多个同名的类或者trait等,可以使用scala 重命名的机制来解决.
    import java.util.{ HashMap=>JavaHashMap, List}
    import scala.collection.mutable._
    var map = new HashMap() // 此时的HashMap指向的是scala中的HashMap
    var map1 = new JavaHashMap(); // 此时使用的java中hashMap的别名


  }
}

5)如果某个冲突的类根本就不会用到,那么这个类可以直接隐藏掉

class Student {
  def test(): Unit = {
    import java.util.{ HashMap=>_, _} // 含义为 引入java.util包的所有类,但是忽略 HahsMap类.
    import scala.collection.mutable.HashMap
    var map = new HashMap() // 此时的HashMap指向的是scala中的HashMap, 而且idea工具,的提示也不会显示java.util的HashMaple 
    
  }
}

7.4面向对象编程方法-抽象

7.4.1如何理解抽象

我们在前面去定义一个类时候(oo),实际上就是把一类事物的共有的属性和行为提取出来,形成一个物理模型(模板)。这种研究问题的方法称为抽象。[见后面ppt] deposit

在这里插入图片描述
代码:

package com.smalltiger.chapter07.abstracts

object AbstractDemo {
  def main(args: Array[String]): Unit = {
    //测试
    val account = new Account("sg000001",123456,899.0)
    account.query(123456)
    account.withdraw(123456,100)
    account.query(123456)
  }
}

//主构造器,完成初始化
class Account(iAccountNo: String, iPwd: Int, iBalance: Double) {
  //属性
  var accountNo: String = iAccountNo
  private var pwd: Int = iPwd
  private var balance: Double = iBalance

  //查询
  def query(pwd: Int): Unit = {
    if (pwd != this.pwd) {
      println("密码不正确!")
      return
    }

    printf("当前卡号 %s 余额是 %.2f\n", this.accountNo, this.balance)
  }

  //取款
  def withdraw(pwd: Int, money: Double): Unit = {

    //密码校验
    if (pwd != this.pwd) {
      println("密码不正确!")
      return
    }

    if (money > this.balance) {
      println("余额不足...")
      return
    }

    this.balance -= money
    println("取款ok")

  }
}

7.5面向对象的三大特征

7.5.1基本介绍

面向对象编程有三大特征:封装、继承和多态。下面我们一一为同学们进行详细的讲解。

7.6面向对象编程-封装

7.6.1封装介绍

封装(encapsulation)就是把抽象出的数据/属性和对数据的操作/方法封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作

7.6.2封装的理解和好处

隐藏实现细节
提可以对数据进行验证,保证安全合理

如何体现封装
1)对类中的属性进行封装
2)通过成员方法,包实现封装

7.6.3封装的实现步骤

1)将属性进行私有化
2)提供一个公共的set方法,用于对属性判断并赋值
def setXxx(参数名 : 类型) : Unit = {
//加入数据验证的业务逻辑
属性 = 参数名
}

3)提供一个公共的get方法,用于获取属性的值//
def getXxx() [: 返回类型] = {
//权限,需要自己.
return 属性
}

7.6.4快速入门案例
看一个案例

那么在Scala中如何实现这种类似的控制呢?
请大家看一个小程序(TestEncap.scala),不能随便查看人的年龄,工资等隐私,并对输入的年龄进行合理的验证[要求1-120之间]。

代码如下

class Person {
  var name: String = _
  //var age ; //当是public时,可以随意的进行修改,不安全
  private var age: Int = _
  private var salary: Float = _
  private var job: String = _
  //这里setAge是public 类型, 加入了我们的业务逻辑控制,实现了封装
  def setAge(age: Int): Unit = { 
    if (age >= 0 && age <= 120) {
      this.age = age
    } else {
      println("输入的数据不合理");
      //可考虑给一个默认值
      this.age = 20
    }}
} 

7.6.5Scala封装的注意事项和细节

前面讲的Scala的封装特性,大家发现和Java是一样的,下面我们看看Scala封装还有哪些特点。
1)Scala中为了简化代码的开发,当声明属性时,本身就自动提供了对应setter/getter方法,如果属性声明为private的,那么自动生成的setter/getter方法也是private的,如果属性省略访问权限修饰符,那么自动生成的setter/getter方法是public的[案例+反编译+说明]
2)因此我们如果只是对一个属性进行简单的set和get ,只要声明一下该属性(属性使用默认访问修饰符) 不用写专门的getset,默认会创建,访问时,直接对象.变量。这样也是为了保持访问一致性 [案例]
3)从形式上看 dog.food 直接访问属性,其实底层仍然是访问的方法, 看一下反编译的代码就明白
4)有了上面的特性,目前很多新的框架,在进行反射时,也支持对属性的直接反射

7.7面向对象编程-继承

7.7.1Java继承的简单回顾

class 子类名 extends 父类名 { 类体 }
子类继承父类的属性和方法
7.7.2scala继承的示意图

继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类(比如Student),在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends语句来声明继承父类即可。
在这里插入图片描述
7.7.3Scala继承的基本语法

class 子类名 extends 父类名 { 类体 }

7.7.4Scala继承快速入门

编写一个Student 继承 Person的案例,体验一下Scala继承的特点

package com.smalltiger.chapter07.myextends

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

    //创建一个Student对
    val student = new Student
    student.studying()
  }
}


//是一个Person类
class Person {
  var name : String = "jack" //private [public方法]
  var age : Int = _ //private [public 方法]
  def showInfo(): Unit = { //public 方法
    println("学生信息如下:")
    println("名字:" + this.name)
  }
}

class Student extends Person {
  def studying(): Unit = {
    //this.name 本质是 this.name()
    println(this.name + "学习 scala中....")
  }
}

上面的代码对应的.class 反编译文件是

public class Person
{
  private String name = "jack";
  private int age;

  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 void showInfo() {
    Predef..MODULE$.println("学生信息如下:");
    Predef..MODULE$.println(new StringBuilder().append("名字:").append(name()).toString());
  }
}


public class Student extends Person
{
  public void studying()
  {
    Predef..MODULE$.println(new StringBuilder().append(name()).append("学习 scala中....").toString());
  }
}

7.7.5Scala继承给编程带来的便利

1)代码的复用性提高了
2)码的扩展性和维护性提高了【面试官问:当我们修改父类时,对应的子类就会继承相应的方法和属性】

7.7.6scala子类继承了什么,怎么继承了?

子类继承了所有的属性(方法),只是私有的属性不能直接访问,需要通过公共的方法去访问【debug代码验证可以看到】
代码:

package com.smalltiger.chapter07.myextends

object Extends02 {
  def main(args: Array[String]): Unit = {
    val sub = new Sub()
    //下断点,我们可以看到,其实子类继承了所有,但是private没有权限访问
    sub.sayOk()
  }
}

//父类
class Base {
  //三种属性
  var n1: Int = 1
  protected var n2: Int = 2
  private var n3: Int = 3

  //方法
  def test100(): Unit = {
    println("base 100")
  }

  protected def test200(): Unit = {
    println("base 200")
  }

  private def test300(): Unit = {
    println("base 300")
  }
}

//子类Sub继承了Base父类
class Sub extends Base {
  //方法
  def sayOk(): Unit = {
    //这里子类中,可以访问到父类的 默认和protected的属性和方法(本质都是通过继承方法来实现)
    this.n1 = 20 // n1_$eq()
    this.n2 = 40 //...
    //this.n3 = 90
    println("范围" + this.n1 + this.n2)
  }
}

7.7.7重写方法

说明: scala明确规定,重写一个非抽象方法需要用override修饰符,调用超类的方法使用super关键字 【案例演示+反编译+注释】
代码:

package com.smalltiger.chapter07.myextends

object OverrideDemo {
  def main(args: Array[String]): Unit = {
    println("ok~~")
    val emp = new Emp
    emp.printName()
  }
}

//Person2类
class Person2 {
  var name: String = "tom"
  //父类方法
  def printName() {
    println("Person printName() " + name)
  }
  def sayHi(): Unit = {
    println("sayHi")
  }
}

class Emp extends Person2 {

  //想去重写Person-printName方法,必须显式的声明 override
  //
 override def printName() {
    println("Emp printName() " + name)
    sayHi()
    //如果希望调用父类的printName,则需要使用super.printName()
    super.printName()
  }
}

7.7.8Scala中类型检查和转换
基本介绍
要测试某个对象是否属于某个给定的类,可以用isInstanceOf方法。用asInstanceOf方法将引用转换为子类的引用。classOf获取对象的类名。

1)classOf[String]就如同Java的 String.class 。
2)obj.isInstanceOf[T]就如同Java的obj instanceof T 判断obj是不是T类型。
3)obj.asInstanceOf[T]就如同Java的(T)obj 将obj强转成T类型。
4)测试案例

package com.smalltiger.chapter07.myextends

object TypeConvert {
  def main(args: Array[String]): Unit = {
    // 获取对象类型
    println(classOf[String])
    val s = "zhangsan"
    println(s.getClass.getName) //这种是Java中反射方式得到类型


    println(s.isInstanceOf[String]) //判断类型 true
    println(s.asInstanceOf[String]) //将s 显示转换成String

    //看一个简单应用
    val p: Person3 = new Emp3 //子类对象给了一个父类的引用
    p.printName()

    //如果希望使用的子类的方法say
    p.asInstanceOf[Emp3].say()
  }
}

class Person3 {
  var name = "king"
  def printName(): Unit = {
    println("name=" + name)
  }
}

class Emp3 extends Person3 {

  def say(): Unit = {
    println("Emp3=" + name)
  }
}

最佳实践
类型检查和转换的最大价值在于:可以判断传入对象的类型,然后转成对应的子类对象,进行相关操作,这里也体现出多态的特点。
在这里插入图片描述
代码演示

package com.smalltiger.chapter07.myextends

object TypeConvertDemo02 {
  def main(args: Array[String]): Unit = {
    val person = new Person4
    val emp = new Emp4
    val worker = new Worker4

    test(person)
    test(emp)//向上转型
    test(worker)//向上转型

  }

  def test(p:Person4): Unit = {

    if (p.isInstanceOf[Emp4]) {
      p.asInstanceOf[Emp4].sayOk() //向下转型
    } else if (p.isInstanceOf[Worker4]) {
      p.asInstanceOf[Worker4].sayHi()  //向下转型
    } else {
      p.printName()
    }

  }

}

class Person4 {
  var name = "scott"
  def printName(): Unit = {
    println(this.name + " printName..")
  }
}
class Emp4 extends Person4 {
  def sayOk(): Unit = {
    println(this.name + " sayok")
  }
}
class Worker4 extends Person4 {
  def sayHi(): Unit = {
    println(this.name + " sayHi")
  }
}

7.7.9Scala中超类的构造
回顾-Java中超类的构造
说明:
1)从代码可以看出:在Java中,创建子类对象时,子类的构造器总是去调用一个父类的构造器(显式或者隐式调用)。
2)java的子类是可以指定使用父类的哪个构造器完成对父类初始化.
代码:

class A {
    public A() {
       super()
        System.out.println("A()");
    }
    public A(String name) {
        super()
        System.out.println("A(String name)" + name);
    }
}
class B extends A{
    public B() {
        //这里会隐式调用super(); 就是无参的父类构造器A()
        super()
        System.out.println("B()");
    }
    public B(String name) {
        super(name);
        System.out.println("B(String name)" + name);
    }
}

7.7.10Scala中超类的构造

Scala超类的构造说明
1)类有一个主构器和任意数量的辅助构造器,而每个辅助构造器都必须先调用主构造器(也可以是间接调用.),这点在前面我们说过了。

class Person {
    var name = "zhangsan"
    println("Person...")}
class Emp extends Person {
      println("Emp ....")
def this(name : String) {
    this // 必须调用主构造器
    this.name = name
     println("Emp 辅助构造器~")
}}

2)只有主构造器可以调用父类的构造器。辅助构造器不能直接调用父类的构造器。在Scala的构造器中,你不能调用super(params)

package com.smalltiger.chapter07.myextends

object SuperDemo {
  def main(args: Array[String]): Unit = {
    new Emp5()
  }
}


class Person5(name: String) { //父类的构造器
  println("Person5 主构造器" + name)
}
class Emp5 (name: String) extends Person5(name) {// 将子类参数传递给父类构造器,这种写法√

  println("子类的主构造器=" + name)

  //super(name), 错误不能在主构造器中显式的调用super

  def  this() {
    this("xx")
    //super("abc") // (×)不能在辅助构造器中调用显式父类的构造器
  }
}

编写程序,创建一个学生对象。体会Scala中超类的构造流程。
在这里插入图片描述
7.7.11覆写字段

基本介绍
在Scala中,子类改写父类的字段,我们称为覆写/重写字段。覆写字段需使用 override修饰。

回顾:在Java中只有方法的重写,没有属性/字段的重写,准确的讲,是隐藏字段代替了重写。

java的动态绑定机制

package com.smalltiger.chapter07.myextends;

public class Exercise {
    public static void main(String[] args) {
        //动态绑定机制
        //1. 当调用对象方法的时候,该方法会和该对象的内存地址绑定【这个就是动态绑定机制】
        //2. 当对象调用属性是,没有动态绑定机制,在哪里调用,就返回哪里的值
        A a = new B();
        System.out.println(a.sum());  //40 -》 30
        System.out.println(a.sum1()); //30 -> 20
    }
}

class A { //A类
    public int i = 10;

    public int sum() {
        return getI() + 10;
    }

    public int sum1() {
        return i + 10;
    }

    public int getI() {
        return i;
    }
}

class B extends A { //B类
    public int i = 20;

//    public int sum() {
//        return i + 20;
//    }

    public int getI() {
        return i;
    }

//    public int sum1() {
//        return i + 10;
//    }
}

Scala覆写字段快速入门
我们看一个关于覆写字段的案例[底层仍然遵守动态绑定机制]。

package com.smalltiger.chapter07.myextends

object ScalaFieldOverride {
  def main(args: Array[String]): Unit = {
    val obj : AA = new BB()
    val obj2 : BB = new BB()
    println("obj.age=" + obj.age + " obj2.age=" + obj2.age) //本质 obj.age => obj.age() obj2.age=>obj2.age()
    println(obj.asInstanceOf[AA].age) // 20

  }
}

class AA {
  val age: Int = 10
}

class BB extends AA {
  override val age: Int = 20
}



底层的.class代码

public class AA
{
  private final int age = 10;

  public int age() { return this.age; }

}

public class BB extends AA
{
  private final int age = 20;

  public int age() { return this.age; }

}

猜你喜欢

转载自blog.csdn.net/smsmtiger/article/details/84778409