7. Scala面向對象編程(中級部分)

7.1 包 

  7.1.1 看一個應用場景 

      如今有兩個程序員共同開發一個項目,程序員xiaoming但願定義一個類取名Dog,程序員xiaohong也想定一個類也叫Dog,兩個程序員還爲此吵了起來,該怎麼辦?java

      --->使用包便可解決這個問題程序員

  7.1.2 回顧-Java包的三大做用 

      1) 區分相同名字的類編程

      2) 當類不少時,能夠很好的管理類安全

      3) 控制訪問範圍性能優化

  7.1.3 回顧-Java打包命令 

      -打包基本語法框架

        package com.c;ide

      -打包的本質分析函數

        實際上就是建立不一樣的文件夾來保存類文件性能

  7.1.4 快速入門 

      使用打包技術來解決上面的問題,不一樣包下Dog類學習

public class TestTiger {
    public static void main(String[] args) {
        //使用xm的Tiger
        com.c.scala_exercise.javapackage.xm.Tiger tiger01 = new com.c.scala_exercise.javapackage.xm.Tiger();
        //使用xh的Tiger
        com.c.scala_exercise.javapackage.xh.Tiger tiger02 = new com.c.scala_exercise.javapackage.xh.Tiger();

        System.out.println("tiger01=" + tiger01 + "tiger02=" + tiger02);
    }
}

  7.1.5 Scala包的基本介紹 

      和Java同樣,Scala中管理項目可使用包,但Scala中的包的功能更增強大,使用也相對複雜些

  7.1.6 Scala包快速入門 

      使用打包技術來解決上面的問題,不一樣包下Dog類

object boke_demo01 {
  def main(args: Array[String]): Unit = {
    //使用xh的Tiger
    val tiger1 = new com.c.scala_exercise.scalapackage.xh.Tiger
    //使用xm的Tiger
    val tiger2 = new com.c.scala_exercise.scalapackage.xm.Tiger

    println(tiger1 + " " + tiger2)

  }
}

  7.1.7 Scala包的特色概述 

      -基本語法

        package 包名

      -Scala包的做用(和Java同樣)

        1) 區分相同名字的類

        2) 當類不少時,能夠很好的管理類

        3) 控制訪問範圍

        4) 能夠對類的功能進行擴展

      -Scala中包名和源碼所在的系統文件目錄結構能夠不一致,可是編譯後的字節碼文件路徑和包名會保持一致(這個工做由編譯器完成)

object boke_demo01 {
  def main(args: Array[String]): Unit = {
    //使用xh的Tiger
    val tiger1 = new com.c.scala_exercise.scalapackage.xh.Tiger
    //使用xm的Tiger
    val tiger2 = new com.c.scala_exercise.scalapackage.xm.Tiger

    println(tiger1 + " " + tiger2)

  }
}

class Employee {

}

  7.1.8 包的命名 

      -命名規則:

        只能包含數字、字母、下劃線、小圓點.,可是不能用梳子開頭,也不要使用關鍵字

        demo.class.exercise  //錯誤,由於class是關鍵字

        demo.12a  //錯誤,由於不能以梳子開頭

      -命名規範:

        通常是小寫字母+小圓點通常是

        com.公司名.項目名.業務模塊名 好比:com.baidu.io.model  com.baidu.io.controller

  7.1.9 Scala包注意事項和使用細節 

      1) Scala進行package打包時,能夠有以下形式

//代碼說明
//1. package com.boke{}  表示咱們建立了包 com.boke ,在{}中
//   咱們能夠繼續寫它的子包 scala //com.boke.scala, 還能夠寫類,特質trait,還能夠寫object
//2. 即Sacla支持,在一個文件中,能夠同時建立多個包,以及給各個包建立類,trait和object
package com.boke { //包 com.boke

  //  class User { // 在com.boke包下建立個 User類
  //    def sayHello(): Unit = {
  //      //想使用 com.boke.scala2包下的 Monster
  //      import com.boke.scala2.Monster
  //      val monster = new Monster()
  //    }
  //  }
  //
  //  package scala2 { // 建立包 com.boke.scala2
  //    class User { // 在com.boke.scala2 包下建立個 User類
  //   }
  //
  //    class Monster { //
  //
  //    }
  //
  //  }


  //
  //說明
  //1. 在包中直接寫方法,或者定義變量,就錯誤==>使用包對象的技術來解決
  //2. package object scala表示建立一個包對象scala, 它是com.boke.scala這個包對應的包對象
  //3. 每個包均可以有一個包對象
  //4. 包對象的名字須要和子包同樣
  //5. 在包對象中能夠定義變量,方法
  //6. 在包對象中定義的變量和方法,就能夠在對應的包中使用
  //7. 在底層這個包對象會生成兩個類 package.class  和 package$.class
  package object scala {
    var name = "king"

    def sayHiv(): Unit = {
      println("package object scala sayHI~")
    }
  }


  package scala { //包 com.boke.scala

    class Person { // 表示在 com.boke.scala下建立類 Person
      val name = "Nick"

      def play(message: String): Unit = {
        println(this.name + " " + message)
      }
    }

    class User {
      def testUser(): Unit = {
        println("name = " + name)
        sayHiv()
      }
    }

    object Test1 { //表示在 com.boke.scala 建立object Test1
      def main(args: Array[String]): Unit = {

        println("name=" + name)
        name = "yy"
        sayHiv()

        //        println("ok")
        //        //咱們能夠直接使用父包的內容
        //        //1.若是有同名的類,則採用就近原則來使用內容(好比包)
        //        //2.若是就是要使用父包的類,則指定路徑便可
        //        val user = new User
        //        println("user=" + user) //
        //        val user2 = new com.boke.User()
        //        println("user2" + user2)

      }
    }

  }

}

      2) 包也能夠像嵌套類那樣嵌套使用(包中有包),好處:能夠在同一個文件中,將類(class/object)、trait建立在不一樣的包中,這樣就很是靈活了

      3) 做用域原則:能夠直接向上訪問。即Scala中子包中直接訪問父包的內容,大括號體現做用域。(提示:Java中子包使用父包的類,須要import)。在子包和父包類重名時,默認採用就近原則,若是但願指定使用某個類,則帶上包名便可

      4) 父包要訪問子包的內容時,須要import對應的類等

      5) 能夠在同一個.scala文件中聲明多個並列的package(建議嵌套的package不要超過3層)

      6) 包名能夠相對也能夠絕對,好比訪問 BeanProperty 的絕對路徑是: _root_.scala.beans.BeanProperty,在通常狀況下,咱們使用相對路徑來引入包,只有當包名衝突時,使用絕對路徑來處理

import scala.beans.BeanProperty

class Manager(var name: String) {
  //第一種形式 [使用相對路徑引入包]
  @BeanProperty var age: Int = _
  //第二種形式, 和第一種同樣,都是相對路徑引入
  @scala.beans.BeanProperty var age2: Int = _
  //第三種形式, 是絕對路徑引入,能夠解決包名衝突
  @_root_.scala.beans.BeanProperty var age3: Int = _
}

object TestBean {
  def main(args: Array[String]): Unit = {
    val m = new Manager("jack")
    println("m=" + m)
  }
}

  7.1.10 包對象 

      基本介紹:包能夠包含類、對象和特質(trait),但不能包含函數/方法或變量的定義。這是Java虛擬機的侷限。爲了彌補這一點,Scala提供了包對象的概念來解決這個問題

  7.1.11 包對象的應用案例

/說明
//1. 在包中直接寫方法,或者定義變量,就錯誤==>使用包對象的技術來解決
//2. package object scala表示建立一個包對象scala, 它是com.boke.scala這個包對應的包對象
//3. 每個包均可以有一個包對象
//4. 包對象的名字須要和子包同樣
//5. 在包對象中能夠定義變量,方法
//6. 在包對象中定義的變量和方法,就能夠在對應的包中使用
//7. 在底層這個包對象會生成兩個類 package.class  和 package$.class
package object scala {
  var name = "king"

  def sayHiv(): Unit = {
    println("package object scala sayHI~")
  }
}


package scala { //包 com.boke.scala

  class Person { // 表示在 com.boke.scala下建立類 Person
    val name = "Nick"

    def play(message: String): Unit = {
      println(this.name + " " + message)
    }
  }

  class User {
    def testUser(): Unit = {
      println("name = " + name)
      sayHiv()
    }
  }

  object Test1 { //表示在 com.boke.scala 建立object Test1
    def main(args: Array[String]): Unit = {

      println("name=" + name)
      name = "yy"
      sayHiv()

    }
  }

}

  7.1.12 包對象的底層的實現機制 

 如圖所示:一個包對象會生成兩個類package和package$

如圖所示:說明了包去使用包對象的變量或者方法的原理

  7.1.13 包對象的注意事項 

      1) 每一個包均可以有一個包對象,須要在父包中定義它

      2) 包對象名稱須要和包名一致,通常用來對包的功能補充

7.2 包的可見性問題 

  7.2.1 回顧-Java訪問修飾符基本介紹 

      Java提供四種訪問控制修飾符號來控制方法和變量的訪問權限(範圍)

        1) 公開級別:用public修飾,對外公開

        2) 受保護級別:用protected修飾,對於子類和同一包中的類公開

        3) 默認級別:沒有修飾符,向同一個包中的類公開

        4) 私有級別:用private修飾,只有類自己能夠訪問,不對外公開

  7.2.2 回顧-Java中4中訪問修飾符的訪問範圍 

  7.2.3 回顧-Java訪問修飾符使用注意事項 

      1) 修飾符能夠用來修飾類中的屬性,成員方法以及類

      2) 只有默認的和public才能修飾類!而且遵循上述訪問權限的特色

  7.2.4 Scala中包的可見性介紹 

      在Java中,訪問權限分爲:public,private,protected和默認。在Scala中,能夠經過相似的修飾符達到一樣的效果,可是使用上有所區別

      案例演示

object boke_demo01 {

  def main(args: Array[String]): Unit = {
    val c = new Clerk()
    c.showInfo()
    Clerk.test(c)

  }
}

//類
class Clerk {
  var name: String = "jack" //
  private var sal: Double = 9999.9
  protected var age = 23
  var job: String = "大數據工程師"

  def showInfo(): Unit = {
    //在本類可使用私有的
    println(" name " + name + " sal= " + sal)
  }
}

object Clerk {
  def test(c: Clerk): Unit = {
    //這裏體現出在伴生對象中,能夠訪問c.sal
    println("test() name=" + c.name + " sal= " + c.sal)
  }
}

  7.2.5 Scala中包的可見性和訪問修飾符的使用 

      1) 當屬性訪問權限爲默認時,從底層看屬性是private的,可是由於提供了xxx_$eq()[相似setter]/xxx()[相似getter]方法,所以從使用效果看是任何地方均可以訪問

      2) 當方法訪問權限爲默認時,默認爲public訪問權限

      3) private爲私有權限,只有在類的內部和伴生對象中可用

      4) protected爲受保護權限,Scala中受保護權限比Java中更爲嚴格,只能子類訪問,同包沒法訪問

      5) 在Scala中沒有public關鍵字,即不能用public顯示的修飾屬性和方法

      6) 包訪問權限(表示屬性有了限制,同時包也有了限制),這點和Java不同,體現出Scala包的靈活性

package com.scala.exercise

class Person {

  //增長包訪問權限後
  //1.private同時起做用,不只同類可使用
  //2.com.scala.exercise中包下的其它類也可使用
  private[exercise]  val name = "Jack"
  //固然,也能夠將可見度延展到上層包
  private[scala] val age = 23
  //說明:private能夠變化,好比protected[scala],很是的靈活
}

      7) 總體的案例演示

object boke_demo01 {

  def main(args: Array[String]): Unit = {
    val c = new Clerk()
    c.showInfo()
    Clerk.test(c)

  }
}

//類
class Clerk {
  var name: String = "jack" //
  private var sal: Double = 9999.9
  protected var age = 23
  var job: String = "大數據工程師"

  def showInfo(): Unit = {
    //在本類可使用私有的
    println(" name " + name + " sal= " + sal)
  }
}

//當一個文件中出現了 class Clerk 和 object Clerk
//1. class Clerk 稱爲伴生類
//2. object Clerk 的伴生對象
//3. 由於Scala設計者將static拿掉, 他就是設計了 伴生類和伴生對象的概念
//4. 伴生類 寫非靜態的內容 伴生對象 就是靜態內容
//5.
object Clerk {
  def test(c: Clerk): Unit = {
    //這裏體現出在伴生對象中,能夠訪問c.sal
    println("test() name=" + c.name + " sal= " + c.sal)
  }
}

class Person {
  //這裏咱們增長一個包訪問權限
  //下面private[scala] : 1,仍然是private 2. 在scala包(包括子包)下也可使用name ,至關於擴大訪問範圍

  protected[scala] val name = "Jack"
}

7.3 包的引入 

  7.3.1 Scala引入包基本介紹 

      Scala引入包也是使用import,基本的原理跟機制和Java同樣,可是Scala中的import功能更佳強大,也更靈活。由於Scala語言源自Java,因此java.lang包中的類會自動引入到當前環境中,而Scala中的scala包和predef包的類也會自動引入到當前環境中,即起其下的類能夠直接使用。若是想要把其它包中的類引入到當前環境中,須要使用import

  7.3.2 Scala引入包的細節和注意事項 

      1) 在Scala中,import語句能夠出如今任何地方,並不只限於文件頂部,import語句的做用一直延伸到包含該語句的塊末尾。這種語法的好處是:在須要時引入包,縮小import包的做用範圍,提升效率

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

class Dog {
  @BeanProperty var name : String = "" //能夠嗎?  No
}

      2) Java中若是想要導入包中全部的類,能夠經過通配符*,Scala中採用_(下劃線)

      3) 若是不想要某個包中所有的類,而是其中幾個類,能夠採用選取器(大括號)

def test(): Unit = {
    //可使用選擇器,選擇引入包的內容,這裏,咱們只引入 HashMap, HashSet
    import scala.collection.mutable.{HashMap, HashSet}
    var map = new HashMap()
    var set = new HashSet()
  }

      4) 若是引入的多個包中含有相同的類,那麼能夠將不須要的類進行重命名進行區分,這個就是重命名

def test2(): Unit = {
    //下面的含義是 將 java.util.HashMap 重命名爲 JavaHashMap
    import java.util.{HashMap => JavaHashMap}
    import scala.collection.mutable._
    var map = new HashMap() // 此時的 HashMap 指向的是 scala 中的 HashMap
    var map1 = new JavaHashMap(); // 此時使用的 java 中 hashMap 的別名

}

      5) 若是某個衝突的類根本就不會用到,那麼這個類能夠直接隱藏掉

 import java.util.{ HashMap=>_, _} // 含義爲 引入 java.util 包的全部類,可是忽略 HahsMap 類.
 var map = new HashMap()

7.4 面向對象編程方法-抽象 

      -如何理解抽象

        咱們在前面去定義一個類的時候,實際上就是把一類事物的共有的屬性和行爲提取出來,造成一個物理模型(模版),這種研究問題的方法稱爲抽象

                        

7.5 面向對象編程三大特徵 

  7.5.1 基本介紹 

      面向對象有三大特徵:封裝、繼承、多態

  7.5.2 封裝介紹 

      封裝(encapsulation)就是把抽象出的數據和對數據的操做封裝在一塊兒,數據被保護在內部,程序的其它部分只有經過被受權的操做(成員變量),才能對數據進行操做

  7.5.3 封裝的理解和好處 

      1) 隱藏實現細節

      2) 能夠對數據進行驗證,保證安全合理

      3) 同時能夠加入業務邏輯

  7.5.4 如何體現封裝 

      1) 對類中的屬性進行封裝

      2) 經過成員方法,包實現封裝

  7.5.5 封裝的實現步驟 

      1) 將屬性進行私有化

      2) 提供一個公共的set方法,用於對屬性判斷並賦值

def setXxx(參數名 : 類型) : Unit = { 
    //加入數據驗證的業務邏輯
    屬性 = 參數名
}

      3) 提供一個公共的get方法,用於獲取屬性的值

def getXxx() [: 返回類型] = {
    return 屬性 
}

  7.5.6 Scala封裝的注意事項的小結 

      1) Scala中爲了簡化代碼的開發,當聲明屬性var時,自己就自動提供了對應setter/getter方法,若是屬性聲明爲private的,那麼自動生成的setter/getter方法也是private的,若是屬性省略訪問權限修飾符,那麼自動生成的setter/getter方法時public的

      2) 所以咱們若是隻是對一個屬性進行簡單的set和get,只要聲明一下該屬性(屬性使用默認訪問修飾符),不用寫專門的set和get,默認會建立,訪問時,直接對象.變量。這樣也是爲了保持訪問一致性

      3) 從形式上看 dog.food 直接訪問屬性,其實底層仍然是訪問的方法,看一下反編譯的代碼就會明白

      4) 有了上面的特性,目前不少新的框架,在進行反射時,也支持對屬性的直接反射

7.6 面向對象編程-繼承 

  7.6.1 Java繼承的簡單回顧

class 子類名 extends 父類名 { 類體 }

  7.6.2 繼承基本介紹和示意圖 

      繼承能夠解決代碼複用,讓咱們的編程更佳靠近人類的思惟,當多個類存在相同的屬性(變量)和方法時,能夠從這些類中抽象出父類(好比Student),在父類中定義這些相同的屬性和方法,全部的子類不須要重複定義這些屬性和方法,只須要經過extends語句來聲明繼承父類便可。和Java同樣,Scala也支持類的單繼承

  7.6.3 Scala繼承的基本語法 

class 子類名 extends 父類名 { 類體 }

  7.6.4 Scala繼承快速入門 

object boke_demo01 {

  def main(args: Array[String]): Unit = {
    //使用
    val student = new Student
    student.name = "jack" //調用了student.name()
    student.studying()
    student.showInfo()
  }
}

class Person { //Person類
  var name: String = _
  var age: Int = _

  def showInfo(): Unit = {
    println("學生信息以下:")
    println("名字:" + this.name)
  }
}

//Student類繼承Person
class Student extends Person {
  def studying(): Unit = {
    //這裏可使用父類的屬性
    println(this.name + "學習 scala中....")
  }
}

  7.6.5 Scala繼承給編程帶來的便利 

      1) 代碼的複用性提升了

      2) 代碼的擴展性和維護性提升了

  7.6.6 Scala子類繼承了什麼,怎麼繼承了 

      子類繼承了全部的屬性,只是私有的屬性不能直接訪問,須要經過公共的方法去訪問[debug代碼驗證能夠看到]

//說明
//1. 在scala中,子類繼承了父類的全部屬性
//2. 可是private的屬性和方法沒法訪問

object boke_demo01 {

  def main(args: Array[String]): Unit = {
    val sub = new Sub()
    sub.sayOk()
    //sub.test200() //編譯器不讓過.
  }
}

//父類(基類)
class Base {
  var n1: Int = 1 //public n1() , public n1_$eq()
  protected var n2: Int = 2
  private var n3: Int = 3 // private n3() , private n3_$eq()

  def test100(): Unit = { // 默認 public test100()
    println("base 100")
  }

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

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

  //編譯原理->業務邏輯->性能優化
}

//Sub 繼承 Base
class Sub extends Base {

  def sayOk(): Unit = {
    this.n1 = 20 //這裏訪問本質this.n1_$eq()
    this.n2 = 40

    println("範圍" + this.n1 + this.n2)

    test100() //
    test200() //在子類中使用protected
  }
}

 

  7.6.7 Scala重寫方法 

      說明:Scala明確規定,重寫一個非抽象方法須要用override修飾符,調用超類的方法使用super關鍵字

object boke_demo01 {

  def main(args: Array[String]): Unit = {
    val emp = new Emp
    emp.printName()
  }
}

//Person類
class Person {
  var name: String = "tom"

  def printName() { //輸出名字
    println("Person printName() " + name)
  }

  def sayHi(): Unit = {
    println("sayHi...")
  }
}

//這裏咱們繼承Person
class Emp extends Person {
  //這裏須要顯式的使用override
  override def printName() {
    println("Emp printName() " + name)
    //在子類中須要去調用父類的方法,使用super
    super.printName()
    sayHi()
  }
}

  7.6.8 Scala中類型檢查和轉換 

      -基本介紹

        要測試某個對象是否屬於某個給定的類,能夠用isInstanceOf方法。用asInstanceOf方法將引用轉換爲子類的引用。classOf獲取對象名

        classOf[String]就如同Java的String.class

        obj.isInstanceOf[T]就如同Java的 obj instanceofT 判斷obj是否是T類型

        obj.asInstanceOf[T] 就如同Java的(T)obj 將obj強轉成T類型

      -案例演示

object boke_demo01 {

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

    //ClassOf的使用,能夠獲得類名
    println(classOf[String]) // 輸出
    val s = "king"
    println(s.getClass.getName) //使用反射機制

    //isInstanceOf asInstanceOf
    var p1 = new Person
    var emp = new Emp
    //將子類引用給父類(向上轉型,自動)
    p1 = emp
    //將父類的引用從新轉成子類引用(多態),即向下轉型
    var emp2 = p1.asInstanceOf[Emp]
    emp2.sayHello()

  }
}

//Person類
class Person {
  var name: String = "tom"

  def printName() { //輸出名字
    println("Person printName() " + name)
  }

  def sayHi(): Unit = {
    println("sayHi...")
  }
}

//這裏咱們繼承Person
class Emp extends Person {
  //這裏須要顯式的使用override
  override def printName() {
    println("Emp printName() " + name)
    //在子類中須要去調用父類的方法,使用super
    super.printName()
    sayHi()
  }

  def sayHello(): Unit = {

  }
}

      -最佳實踐

        類型檢查和轉換的最大價值在於:能夠判斷傳入對象的類型,而後轉成對應的子類對象,進行相關操做,這裏也體現出多態的特色

  7.6.9 Java中超類的構造  

      說明:從代碼能夠看出,在Java中,建立子類對象時,子類的構造器老是去調用一個父類的構造器(顯示或者隱式調用)

public class JavaBaseConstractor {

    public static void main(String[] args) {

        //1.A()
        //2.B()
        B b = new B();

        //1.A(String name) jack
        //2.B(String name) jack
        B b2 = new B("jack");

    }
}

class A {
    public A() {
        System.out.println("A()");
    }

    public A(String name) {
        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.6.10 Scala中超類的構造 

      1) 類有一個主構造器和任意數量的輔助構造器,而每一個輔助構造器都必須先調用主構造器(也能夠是間接調用)

      2) 只有主構造器能夠調用父類的構造器,輔助構造器不能直接調用父類的構造器,在Scala的構造器中,不能調用super(params)

      

      3) 案例演示

object boke_demo01 {

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

    //分析執行的順序
    //1.Person...
    //2.Emp ....
    //3.Emp 輔助構造器~
    val emp1 = new Emp("smith")

    println("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%")
    //Person.. 
    //Emp ....
    val emp2 = new Emp("terry", 10)
    emp2.showInfo() // 僱員的名字 terry

  }
}

//父類Person
class Person(pName: String) {
  var name = pName
  println("Person...")

  def this() {
    this("默認的名字")
    println("默認的名字")

  }
}

//子類Emp繼承Person
class Emp(eName: String, eAge: Int) extends Person(eName) {

  println("Emp ....")

  //輔助構造器
  def this(name: String) {

    this(name, 100) // 必須調用主構造器
    this.name = name
    println("Emp 輔助構造器~")
  }

  def showInfo(): Unit = {
    println("僱員的名字 ", name)
  }
}

  7.6.11 覆寫字段 

      -基本介紹

        在Scala中,子類改寫父類的字段,咱們稱之爲覆寫/重寫字段。覆寫字段須要使用override修飾 

      -回顧

        在Java中只有方法的重寫,沒有屬性/字段的重寫,準確的講,是隱藏字段代替了重寫

      -回顧-Java另外一重要特性:動態綁定機制

        動態綁定機制:

        1) 若是調用的是方法,則JVM機會將改方法和對象的內存地址綁定

        2) 若是調用的是一個屬性,則沒有動態綁定機制,在哪裏調用就返回對應值

public class JavaDaynamicBind {
    public static void main(String[] args) {

        //將一個子類的對象地址,交給了一個AA(父類的)引用
        //java的動態綁定機制的小結
        //1.若是調用的是方法,則Jvm機會將該方法和對象的內存地址綁定
        //2.若是調用的是一個屬性,則沒有動態綁定機制,在哪裏調用,就返回對應值
        AA obj = new BB();
        System.out.println(obj.sum());  // 30
        System.out.println(obj.sum1()); // 20

    }
}

class AA {
    public int i = 10;

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

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

    public int getI() {
        return i;
    }
}

class BB extends AA {
    public int i = 20;

    public int getI() {
        return i;
    }

}

       -Scala覆寫字段快速入門

object ScalaFiledOverride {

  def main(args: Array[String]): Unit = {
    val obj1: AAA = new AAA
    val obj2: BBB = new BBB
    //obj1.age => obj1.age() //動態綁定機制
    //obj2.age => obj2.age()
    println("obj1.age=" + obj1.age + "\t obj2.age=" + obj2.age)
  }
}

class AAA {
  val age: Int = 10 // 會生成 public age()
}

class BBB extends AAA {
  override val age: Int = 20 // 會生成 public age()
}

       反編譯後的代碼:

      -覆寫字段的注意事項和細節

        1) def只能重寫另外一個def(即:方法只能重寫另外一個方法)

        2) val只能重寫另外一個val屬性 或 重寫不帶參數的def

        -案例演示1(val只能重寫另外一個val屬性)

object ScalaFiledOverride {

  def main(args: Array[String]): Unit = {
    val obj1: AAA = new AAA
    val obj2: BBB = new BBB
    //obj1.age => obj1.age() //動態綁定機制
    //obj2.age => obj2.age()
    println("obj1.age=" + obj1.age + "\t obj2.age=" + obj2.age)
  }
}

//若是 val age 改爲 var 報錯
class AAA {
  val age: Int = 10 // 會生成 public age()
}

class BBB extends AAA {
  override val age: Int = 20 // 會生成 public age()
}

      -案例演示2(重寫不帶參數的def)

object boke_demo01 {

  def main(args: Array[String]): Unit = {
    val b1 = new BB()
    println(b1.sal) // 0
    val b2: AA = new BB()
    println("b2.sal=" + b2.sal()) // 0
  }
}

class AA {
  def sal(): Int = {
    return 10
  }
}

class BB extends AA {
  override val sal: Int = 0 //底層 public sal
}

        3) var只能重寫另外一個抽象的var屬性

object boke_demo01 {
  
  def main(args: Array[String]): Unit = {
    println("hello~")
  }
}

//在AA中,有一個抽象的字段(屬性)
//1. 抽象的字段(屬性):就是沒有初始化的字段(屬性)
//2. 當一個類含有抽象屬性時,則該類須要標記爲abstract
//3. 對於抽象的屬性,在底層不會生成對應的屬性聲明,而是生成兩個對應的抽象方法(name name_$eq)
abstract class AA {
  var name: String //抽象
  var age: Int = 10
}

class Sub_AA extends AA {
  //說明
  //1. 若是咱們在子類中去重寫父類的抽象屬性,本質是實現了抽象方法
  //2. 所以這裏咱們能夠寫override ,也能夠不寫
  override var name: String = ""

}

      -抽象屬性:聲明未初始化的變量就是抽象的屬性,抽象屬性在抽象類中

      -var重寫抽象的var屬性小結

        1) 一個屬性沒有初始化,那麼這個屬性就是抽象屬性

        2) 抽象屬性在編譯成字節碼文件時,屬性並不會聲明,可是會自動生成抽象方法,因此類必須聲明爲抽象類

        3) 若是是覆寫一個父類的抽象,那麼override關鍵字能夠省略[緣由:父類的抽象屬性,生成的是抽象方法,所以不涉及到方法重寫的概念,override能夠省略]

  7.6.12 抽象類 

      -基本介紹

        在Scala中,經過abstract關鍵字標記不能被實力化的類。方法不用標記abstract,只要省掉方法體便可,抽象類能夠擁有抽象字段,抽象字段/屬性就是沒有初始值的字段

      -快速入門案例

        將Animal作成抽象類,包含一個抽象方法cry()

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

//抽象類
abstract class Animal {
  var name: String //抽象的字段
  var age: Int // 抽象的字段
  var color: String = "black" //普通屬性
  def cry() //抽象方法,不須要標記 abstract
}

  7.6.13 Scala抽象類使用的注意事項和細節 

      1) 抽象類不能被實例

//默認狀況下,一個抽象類是不能實例化的,可是你實例化時,動態的實現了抽象類的全部 //抽象方法,也能夠,以下
val animal = new Animal {
  override def sayHello (): Unit = {
  println ("say hello~~~~")
  }
}
animal.sayHello ()

      2) 抽象類不必定要包含abstract方法,也就是說,抽象類能夠沒有abstract方法

abstract class Animal { 
  //在抽象類中能夠有實現的方法 
  def sayHi (): Unit = {
  println("hello") }
}

      3) 一旦類包含了抽象方法或者抽象屬性,則這個類必須聲明爲abstract

      4) 抽象方法不能有主體,不容許使用abstract修飾

      5) 若是一個類繼承了抽象類,則它必須實現抽象類中全部的抽象方法和抽象屬性,除非它本身也聲明爲abstract類

abstract class Animal {

  def sayHello()
  var food: String
}

class Dog extends Animal {

  override def sayHello(): Unit = {
    println("小狗汪汪叫!")
  }

  override var food: String = _
}

      6) 抽象方法和抽象屬性不能使用private、final來修飾,由於這些關鍵字都是和重寫/實現相違背的

      7) 抽象類中能夠有實現的方法

      8) 子類重寫抽象方法不須要override,寫上也不會錯

  7.6.14 匿名子類

      -基本介紹

        和Java同樣,能夠經過包含帶有定義或重寫的代碼塊的方式建立一個匿名的子類

      -回顧-Java中匿名子類的使用

public class NoNameDemo01 {
    public static void main(String[] args) {
        //在java中去建立一個匿名子類對象
        A a = new A() {
            @Override
            public void cry() {
                System.out.println("cry...");
            }
        };
        a.cry();
    }
}

abstract class A {
    abstract public void cry();
}

      -Scala匿名子類的使用 

object boke_demo01 {
  
  def main(args: Array[String]): Unit = {
    val monster = new Monster {
      override def cry(): Unit = {
        println("...:)")
      }

      override var name: String = _
    }
    monster.cry()
  }
}

abstract class Monster {
  var name: String

  def cry()
}

  7.6.15 繼承層級 

      -Scala繼承層級一覽圖

      -對上圖的一個小結

        1) 在Scala中,全部其它類都是AnyRef的子類,相似Java的Obiect

        2) AnyVal和AnyRef都擴展自Any類,Any類是子節點

        3) Any中定義了isInstanceOf、asInstanceOf方法,以及哈希方法等

        4) Null類型的惟一實例就是null對象,能夠將null賦值給任何引用,但不能賦值給值類型的變量

        5) Nothing類型沒有實例,它對於泛型結構是有用處的,舉例:空列表Nil的類型就是List[Nothing],它是List[T]的子類型,T能夠是任何類

相關文章
相關標籤/搜索