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

来源:https://www.cnblogs.com/zhanghuicheng/archive/2019/05/01/10788213.html
-Advertisement-
Play Games

7.1 包 7.1.1 看一個應用場景 現在有兩個程式員共同開發一個項目,程式員xiaoming希望定義一個類取名Dog,程式員xiaohong也想定一個類也叫Dog,兩個程式員還為此吵了起來,該怎麼辦? >使用包即可解決這個問題 7.1.2 回顧-Java包的三大作用 1) 區分相同名字的類 2) ...


7.1 包 

  7.1.1 看一個應用場景 

      現在有兩個程式員共同開發一個項目,程式員xiaoming希望定義一個類取名Dog,程式員xiaohong也想定一個類也叫Dog,兩個程式員還為此吵了起來,該怎麼辦?

      --->使用包即可解決這個問題

  7.1.2 回顧-Java包的三大作用 

      1) 區分相同名字的類

      2) 當類很多時,可以很好的管理類

      3) 控制訪問範圍

  7.1.3 回顧-Java打包命令 

      -打包基本語法

        package com.c;

      -打包的本質分析

        實際上就是創建不同的文件夾來保存類文件

  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可以是任何類

       

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一:基礎語法之--標識符,修飾符,關鍵字 1.標識符: 定義:類名、變數名以及方法名都被稱為標識符。 註意: ·所有的標識符都應該以字母(A-Z或者a-z),美元符($)、或者下劃線(_)開始·首字元之後可以是字母(A-Z或者a-z),美元符($)、下劃線(_)或數字的任何字元組合·關鍵字不能用作標 ...
  • Python基礎之參數與返回值進階,包括 函數的返回值 進階,函數的參數進階,函數的遞歸。其中,函數的返回值 進階 包括 利用元組返回多個函數值,用多個變數接收函數的返回值;函數的參數進階 包括 函數內部變數和參數的關係,列表調用+=,預設參數,多值參數,元組和字典的拆包;函數的遞歸 僅包括 函數的... ...
  • 概述 這是關於 Swoole 學習的第三篇文章:Swoole WebSocket 的應用。 "第二篇:Swoole Task 的應用" "第一篇:Swoole Timer 的應用" 什麼是 WebSocket ? WebSocket 是一種在單個TCP連接上進行全雙工通信的協議。 WebSocket ...
  • 工作一年,維護工程項目的同時一直寫CURD,最近學習DDD,結合之前自己寫的開源項目,深思我們這種CURD的編程方式的弊端,和朋友討論後,發現我們從來沒有面向對象開發,所以寫這篇文章,希望更多人去思考面向對象,不只是停留在背書上 下麵以開發一個常規的登錄模塊為例,模擬實現一個登錄功能,一步步地去說明 ...
  • 5.1自學自我總結 1.關於數據類型補充 在整列中除了int數據類型,還要long數據數據類型 ling為長數字 另外還有一種未被提到的數據類型為虛數complex在python中虛數為5j來表示 個個數字函數的也可以相互裝換 如下 2.關於字元串 字元串取值 3.不同類型變數拼接新增方法 五一快樂 ...
  • 關於Java 鎖的知識整理與回顧(個人筆記): 鎖有哪些,分別用來幹嘛? Java實現鎖有兩種方式,synchronized關鍵字和Lock (1)Lock(可判斷鎖狀態) Lock是基於JDK層面實現。Lock的實現主要有ReentrantLock、ReadLock和WriteLock(引出鎖分類 ...
  • 利用map和reduce編寫一個str2float函數,把字元串'123.456'轉換成浮點數123.456。 思路:計算小數位數 >將字元串中的小數點去掉 >字元串轉換為整數 >整數轉換為浮點數 知識點: 1、將字元串中的小數點去掉可以用切片的方法。 2、reduce把一個函數作用在一個序列[x1 ...
  • 最近在web項目中,客戶端註冊時需要通過郵箱驗證,伺服器就需要向客戶端發送郵件,我把發送郵件的細節進行了簡易的封裝: 最近在web項目中,客戶端註冊時需要通過郵箱驗證,伺服器就需要向客戶端發送郵件,我把發送郵件的細節進行了簡易的封裝: 在maven中需要導入: 1 <!--Email--> 2 <d ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...