1. 程式人生 > >Scala程式設計學習之七-面向物件(中級)

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; }

}