有一羣小孩在玩堆雪人,不時有新的小孩加入,請問如何知道如今共有多少人在玩?請使用面向對象的思想,編寫程序解決java
-Scala中靜態的概念-伴生對象mysql
Scala語言是徹底面向對象(萬物皆對象)的語言,因此並無靜態的操做(即在Scala中沒有靜態的概念)。可是爲了可以和Java語言交互(由於Java中有靜態概念),就產生了一種特殊的對象來模擬類對象,咱們稱之爲類的伴生對象。這個類的全部靜態內容均可以放置在它的伴生對象中聲明和調用sql
object boke_demo01 { def main(args: Array[String]): Unit = { println(ScalaPerson.sex) //true 在底層等價於 ScalaPerson$.MODULE$.sex() ScalaPerson.sayHi() //在底層等價於 ScalaPerson$.MODULE$.sayHi() } } //說明 //1. 當在同一個文件中,有 class ScalaPerson 和 object ScalaPerson //2. class ScalaPerson 稱爲伴生類,將非靜態的內容寫到該類中 //3. object ScalaPerson 稱爲伴生對象,將靜態的內容寫入到該對象(類) //4. class ScalaPerson 編譯後底層生成 ScalaPerson類 ScalaPerson.class //5. object ScalaPerson 編譯後底層生成 ScalaPerson$類 ScalaPerson$.class //6. 對於伴生對象的內容,咱們能夠直接經過 ScalaPerson.屬性 或者方法 //伴生類 class ScalaPerson { // var name: String = _ } //伴生對象 object ScalaPerson { // var sex: Boolean = true def sayHi(): Unit = { println("object ScalaPerson sayHI~~") } }
-對快速入門的案例的源碼分析數據庫
1) Scala中伴生對象採用object關鍵字聲明,伴生對象中聲明的全是「靜態」內容,能夠經過伴生對象名稱直接調用設計模式
2) 伴生對象對應的類稱之爲伴生類,伴生對象的名稱應該和伴生類名一致oracle
3) 伴生對象中的屬性和方法均可以經過伴生對象名(類名)直接調用訪問app
4) 從語法角度來說,所謂的伴生對象其實就是類的靜態方法和成員的集合ide
5) 從技術角度來說,Scala仍是沒有生成靜態的內容,只不過是將伴生對象生成了一個新的類,實現屬性和方法調用[反編譯看源碼]源碼分析
6) 從底層原理看,伴生對象實現靜態特性是依賴 public static final MOUDLE$ 實現的學習
7) 伴生對象的聲明應該和伴生類的聲明在同一個源碼文件中(若是不在同一個文件中會運行錯誤),可是若是沒有伴生類,也就沒有所謂的伴生對象了,因此放在哪裏就無所謂了
8) 若是 class A 獨立存在,那麼A就是一個類,若是 Object A 獨立存在,那麼A就是一個「靜態」性質的對象[即類對象],在 Object A 中聲明的屬性和方法能夠經過 A.屬性和A.方法 來實現調用
9) 當一個文件中,存在半生類和伴生對象時,文件的圖標會發生變化
設計一個var total Int 表示總人數,咱們在建立一個小孩時,就把total加1,而且total是全部對象共享的就ok了,使用伴生對象來解決
object boke_demo01 { def main(args: Array[String]): Unit = { //建立三個小孩 val child0 = new Child("鐵蛋") val child1 = new Child("狗蛋") val child2 = new Child("熊大") Child.joinGame(child0) Child.joinGame(child1) Child.joinGame(child2) Child.showNum() } } class Child(cName: String) { var name = cName } object Child { //統計共有多少小孩的屬性 var totalChildNum = 0 def joinGame(child: Child): Unit = { printf("%s 小孩加入了遊戲\n", child.name) //totalChildNum 加1 totalChildNum += 1 } def showNum(): Unit = { printf("當前有%d小孩玩遊戲\n", totalChildNum) } }
在伴生對象中定義apply方法,能夠實現:類名(參數)方式來建立對象實例
object boke_demo01 { def main(args: Array[String]): Unit = { val list = List(1, 2, 5) println(list) val pig = new Pig("狗蛋") //使用apply方法來建立對象 val pig2 = Pig("鐵蛋") //自動 apply(pName: String) val pig3 = Pig() // 自動觸發 apply() println("pig2.name=" + pig2.name) //小黑豬 println("pig3.name=" + pig3.name) //匿名豬豬 } } //案例演示apply方法. class Pig(pName: String) { var name: String = pName } object Pig { //編寫一個apply def apply(pName: String): Pig = new Pig(pName) def apply(): Pig = new Pig("匿名") }
這個部分將在Scala設計模式專題進行介紹
-聲明接口
interface接口名
-實現接口
class 類名 implements 接口1,接口2
-Java接口的使用小結
1) 在Java中,一個類能夠實現多個接口
2) 在Java中,接口之間支持多繼承
3) 接口中屬性都是常量
4) 接口中的方法都試抽象的
1) 從面向對象來看,接口並不屬於面向對象的範疇,Scala是純面向對象的語言,在Scala中,沒有接口
2) Scala語言中,採用特質trait(特徵)來代替接口的概念,也就是說,多個類具備相同的特質(特徵)時,就能夠將這個特質(特徵)獨立出來,採用關鍵字trait聲明。理解trait等價於(interface+abstract class)
3) Scala繼承特質(trait)的示意圖
trait 特質名 {
trait 體
}
1) trait 命名 通常首字母大寫 Cloneable,Serializable
object T1 extends Serializable {
}
Serializable:就是Scala的一個特質
-在Scala中,Java中的接口能夠當作特質使用
object boke_demo01 { def main(args: Array[String]): Unit = { } } //trait Serializable extends Any with java.io.Serializable //在scala中,java的接口均可以當作trait來使用(如上面的語法) object T1 extends Serializable { } object T2 extends Cloneable { }
一個類具備某種特質(特徵),就意味着這個類知足了這個特質(特徵)的全部要素,因此在使用時,也採用了extends關鍵字,若是有多個特質或存在父類,那麼須要採用with關鍵字鏈接
1) 沒有父類
class 類名 extends 特質1 with 特質2 with 特質3...
2) 有父類
class 類名 extends 父類 with 特質1 with 特質2 with 特質3...
Scala引入trait特質,第一能夠替代Java的接口,第二也是對單繼承機制的一種補充
object boke_demo01 { def main(args: Array[String]): Unit = { val c = new C() val f = new F() c.getConnect() // 鏈接mysql數據庫... f.getConnect() // 鏈接oracle數據庫.. } } //按照要求定義一個trait trait Trait { //定義一個規範 def getConnect() } //先將六個類的關係寫出 class A {} class B extends A {} class C extends A with Trait { override def getConnect(): Unit = { println("鏈接mysql數據庫...") } } class D {} class E extends D {} class F extends D with Trait { override def getConnect(): Unit = { println("鏈接oracle數據庫..") } }
1) Scala提供了特質(trait),特質能夠同時擁有抽象方法和具體方法,一個類能夠實現/繼承多個特質
object boke_demo01 { def main(args: Array[String]): Unit = { //建立sheep val sheep = new Sheep sheep.sayHi() sheep.sayHello() } } //當一個trait有抽象方法和非抽象方法時 //1. 一個trait在底層對應兩個 Trait.class 接口 //2. 還對應 Trait$class.class Trait$class抽象類 trait Trait { //抽象方法 def sayHi() //實現普通方法 def sayHello(): Unit = { println("say Hello~~") } } //當trait有接口和抽象類是 //1.class Sheep extends Trait 在底層 對應 //2.class Sheep implements Trait //3.當在 Sheep 類中要使用 Trait的實現的方法,就經過 Trait$class class Sheep extends Trait { override def sayHi(): Unit = { println("小羊say hi~~") } }
2) 特質中沒有實現的方法就是抽象方法。類經過extends繼承特質,經過with關鍵字能夠繼承多個特質
3) 全部的Java接口均可以當作Scala特質使用
1) 除了能夠在類聲明時繼承特質之外,還能夠在構建對象時混入特質,擴展目標類的功能
2) 此種方法也能夠應用於對抽象類功能進行擴展
3) 動態混入是Scala特有的方式(Java沒有動態混入),可在不修改類聲明/定義的狀況下,擴展類的功能,很是的靈活,耦合性低
4) 動態混入能夠在不影響原有的繼承關係的基礎上,給指定的類擴展功能
5) 同時要注意動態混入時,若是抽象類有抽象方法,如何混入
6) 案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { //在不修改類的定義基礎,讓它們可使用trait方法 val oracleDB = new OracleDB with Operate oracleDB.insert(100) // val mySQL = new MySQL with Operate mySQL.insert(200) //若是一個抽象類有抽象方法,如何動態混入特質 val mySql_ = new MySQL_ with Operate { override def say(): Unit = { println("say") } } mySql_.insert(999) mySql_.say() } } trait Operate { //特質 def insert(id: Int): Unit = { //方法(實現) println("插入數據 = " + id) } } class OracleDB { //空 } abstract class MySQL { //空 } abstract class MySQL_ { //空 def say() }
-在Scala中建立對象的4種方式
1) new 對象
2) apply 建立
3) 匿名子類方式
4) 動態混入
-基本介紹
構建對象的同時若是混入多個特質,稱之爲疊加特質,那麼特質聲明順序從左到右,方法執行順序從右到左
-疊加特質應用案例
目的:分析疊加特質時,對象的構建順序,和執行方法的順序
案例演示:
object boke_demo01 { def main(args: Array[String]): Unit = { //說明 //1. 建立 MySQL實例時,動態的混入 DB 和 File //研究第一個問題,當咱們建立一個動態混入對象時,其順序是怎樣的 //總結一句話 //Scala在疊加特質的時候,會首先從後面的特質開始執行(即從左到右) //1.Operate... //2.Data //3.DB //4.File val mysql = new MySQL with DB with File println(mysql) //研究第2個問題,當咱們執行一個動態混入對象的方法,其執行順序是怎樣的 //順序是,(1)從右到左開始執行 , (2)當執行到super時,是指的左邊的特質 (3) 若是左邊沒有特質了,則super就是父特質 //1. 向文件" //2. 向數據庫 //3. 插入數據 100 mysql.insert(100) println("===================================================") //練習題 val mySQL = new MySQL with File with DB mySQL.insert(999) //構建順序 //1.Operate... //2.Data //3.File //4.DB //執行順序 //1. 向數據庫 //2. 向文件 //3. 插入數據 = 999 } } trait Operate { //特色 println("Operate...") def insert(id: Int) //抽象方法 } trait Data extends Operate { //特質,繼承了Operate println("Data") override def insert(id: Int): Unit = { //實現/重寫 Operate 的insert println("插入數據 = " + id) } } trait DB extends Data { //特質,繼承 Data println("DB") override def insert(id: Int): Unit = { // 重寫 Data 的insert println("向數據庫") super.insert(id) } } trait File extends Data { //特質,繼承 Data println("File") override def insert(id: Int): Unit = { // 重寫 Data 的insert println("向文件") //super.insert(id) //調用了insert方法(難點),這裏super在動態混入時,不必定是父類 //若是咱們但願直接調用Data的insert方法,能夠指定,以下 //說明:super[?] ?的類型,必須是當前的特質的直接父特質(超類) super[Data].insert(id) } } class MySQL {} //普通類
-疊加特質注意事項和細節
1) 特質聲明順序從左到右
2) Scala 在執行疊加對象的方法時,會首先從後面的特質(從右向左)開始執行
3) Scala 中特質中若是調用 super,並非表示調用父特質的方法,而是向前面(左邊)繼續 查找特質,若是找不到,纔會去父特質查找
4) 若是想要調用具體特質的方法,能夠指定:super[特質].xxx(...).其中的泛型必須是該特質的直接超類類型
富接口:即該特質中既有抽象方法,又有非抽象方法
trait Operate { def insert(id: Int) //抽象 def pageQuery(pageno: Int, pagesize: Int): Unit = { //實現 println("分頁查詢") } }
特質中能夠定義具體字段,若是初始化了就是具體字段,若是不初始化就是抽象字段,混入該特質的類就具備了該字段,字段不是繼承,而是直接加入類,成爲本身的字段
object boke_demo01 { def main(args: Array[String]): Unit = { val mySQL = new MySQL with DB { override var sal = 10 } } } trait DB { var sal: Int //抽象字段 var opertype: String = "insert" def insert(): Unit = { } } class MySQL {}
-反編譯後的代碼
特質中未被初始化的字段在具體的子類中必須被重寫
-介紹
特質也是有構造器的,構造器中的內容由「字段的初始化」和一些其餘語句構成
-第一種特質構造順序(聲明類的同時混入特質)
1) 調用當前類的超類構造器
2) 第一個特質的父特質構造器
3) 第一個特質構造器
4) 第二個特質構造器的父特質構造器,若是已經執行過就再也不執行
5) 第二個特質構造器
6) ......重複4,5的步驟(若是有第3個,第4個特質)
7) 當前類構造器
-第二種特質構造順序(在構建對象時,動態混入特質)
1) 調用當前類的超類構造器
2) 當前類構造器
3) 第一個特質構造器的父特質構造器
4) 第一個特質構造器
5) 第二個特質構造器的父特質構造器,若是已經執行過就再也不執行
6) 第二個特質構造器
7) ......重複4,5的步驟(若是有第3個,第4個特質)
8) 當前類構造器
-兩種方式對構造順序的影響
1) 第一種方式實際是構建類對象,在混入特質時,該對象尚未建立
2) 第二種方式實際是構造匿名子類,能夠理解成在混入特質時,對象已經建立了
-案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { //這時FF是這樣 形式 class FF extends EE with CC with DD /* 調用當前類的超類構造器 第一個特質的父特質構造器 第一個特質構造器 第二個特質構造器的父特質構造器, 若是已經執行過,就再也不執行 第二個特質構造器 .......重複4,5的步驟(若是有第3個,第4個特質) 當前類構造器 [案例演示] */ //1. E... //2. A... //3. B.... //4. C.... //5. D.... //6. F.... val ff1 = new FF() println(ff1) //這時咱們是動態混入 /* 先建立 new KK 對象,而後再混入其它特質 調用當前類的超類構造器 當前類構造器 第一個特質構造器的父特質構造器 第一個特質構造器. 第二個特質構造器的父特質構造器, 若是已經執行過,就再也不執行 第二個特質構造器 .......重複5,6的步驟(若是有第3個,第4個特質) 當前類構造器 [案例演示] */ //1. E... //2. K.... //3. A... //4. B //5. C //6. D println("=======================") val ff2 = new KK with CC with DD println(ff2) } } trait AA { println("A...") } trait BB extends AA { println("B....") } trait CC extends BB { println("C....") } trait DD extends BB { println("D....") } class EE { //普通類 println("E...") } class FF extends EE with CC with DD { //先繼承了EE類,而後再繼承CC 和DD println("F....") } class KK extends EE { //KK直接繼承了普通類EE println("K....") }
-特質能夠繼承類,以用來拓展該特質的一些功能
trait LoggedException extends Exception { def log(): Unit = { println(getMessage()) // 方法來自於Exception類 } }
-全部混入該特質的類,會自動成爲那個特質所繼承的超類的子類
//1. LoggedException 繼承了 Exception //2. LoggedException 特質就能夠 Exception 功能 trait LoggedException extends Exception { def log(): Unit = { println(getMessage()) // 方法來自於Exception類 } }
-若是混入該特質的類,已經繼承了另外一個類(A類),則要求A類是特質超類的子類,不然就會出現了多繼承現象,發生錯誤
object boke_demo01 { def main(args: Array[String]): Unit = { println("h~~") } } //說明 //1. LoggedException 繼承了 Exception //2. LoggedException 特質就能夠 Exception 功能 trait LoggedException extends Exception { def log(): Unit = { println(getMessage()) // 方法來自於Exception類 } } //由於 UnhappyException 繼承了 LoggedException //而 LoggedException 繼承了 Exception //UnhappyException 就成爲 Exception子類 class UnhappyException extends LoggedException { // 已是Exception的子類了,因此能夠重寫方法 override def getMessage = "錯誤消息!" } // 若是混入該特質的類,已經繼承了另外一個類(A類),則要求A類是特質超類的子類, // 不然就會出現了多繼承現象,發生錯誤。 class UnhappyException2 extends IndexOutOfBoundsException with LoggedException { // 已是Exception的子類了,因此能夠重寫方法 override def getMessage = "錯誤消息!" } class CCC {} //錯誤的緣由是 CCC 不是 Exception子類 //class UnhappyException3 extends CCC with LoggedException{ // // 已是Exception的子類了,因此能夠重寫方法 // override def getMessage = "錯誤消息!" //}
-說明
自身類型:主要是爲了解決特質的循環依賴問題,同時能夠確保特質在不擴展某個類的狀況下,依然能夠作到限制混入該特質的類的類型
-應用案例
舉例說明自身類型特質,以及如何使用自身類型特質
object boke_demo01 { def main(args: Array[String]): Unit = { } } //Logger就是自身類型特質,當這裏作了自身類型後,那麼 // trait Logger extends Exception,要求混入該特質的類也是 Exception子類 trait Logger { // 明確告訴編譯器,我就是Exception,若是沒有這句話,下面的getMessage不能調用 this: Exception => def log(): Unit = { // 既然我就是Exception, 那麼就能夠調用其中的方法 println(getMessage) } } //class Console extends Logger {} //對嗎? 錯誤 //class Console extends Exception with Logger {}//對嗎? 正確
編寫程序,在內部類中訪問外部類的屬性
-方式1
內部類若是想要訪問外部類的屬性,能夠經過外部類對象訪問
即訪問形式:外部類名.this.屬性名
案例演示
//外部類 //內部類訪問外部類的屬性的方法1 外部類名.this.屬性 class ScalaOuterClass { //定義兩個屬性 var name = "Jack" private var sal = 199.6 class ScalaInnerClass { //成員內部類, def info() = { // 訪問方式:外部類名.this.屬性名 // 怎麼理解 ScalaOuterClass.this 就至關因而 ScalaOuterClass 這個外部類的一個實例, // 而後經過 ScalaOuterClass.this 實例對象去訪問 name 屬性 // 只是這種寫法比較特別,學習java的同窗可能更容易理解 ScalaOuterClass.class 的寫法. println("name = " + ScalaOuterClass.this.name + " sal =" + ScalaOuterClass.this.sal) } } }
-方式2
內部類若是想要訪問外部類的屬性,也能夠經過外部類別名訪問(推薦)
即訪問方式:外部類名別名.屬性名
案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { //測試1. 建立了兩個外部類的實例 val outer1: ScalaOuterClass = new ScalaOuterClass(); val outer2: ScalaOuterClass = new ScalaOuterClass(); //在scala中,建立成員內部類的語法是 //對象.內部類 的方式建立, 這裏語法能夠看出在scala中,默認狀況下內部類實例和外部對象關聯 val inner1 = new outer1.ScalaInnerClass val inner2 = new outer2.ScalaInnerClass //測試一下使用inner1 去調用 info() inner1.info() //這裏咱們去調用test inner1.test(inner1) //在默認狀況下,scala的內部類的實例和建立該內部類實例的外部對象關聯. // inner1.test(inner2) inner2.test(inner2) //建立靜態內部類實例 val staticInner = new ScalaOuterClass.ScalaStaticInnerClass() } } //外部類 //內部類訪問外部類的屬性的方法2 使用別名的方式 //1. 將外部類屬性,寫在別名後面 class ScalaOuterClass { myouter => //這裏咱們能夠這裏理解 外部類的別名 看作是外部類的一個實例 class ScalaInnerClass { //成員內部類, def info() = { // 訪問方式:外部類別名.屬性名 // 只是這種寫法比較特別,學習java的同窗可能更容易理解 ScalaOuterClass.class 的寫法. println("name~ = " + myouter.name + " sal~ =" + myouter.sal) } } //定義兩個屬性 var name = "Jack" private var sal = 999.9 } object ScalaOuterClass { //伴生對象 class ScalaStaticInnerClass { //靜態內部類 } }
-案例演示
//外部類 //內部類訪問外部類的屬性的方法2 使用別名的方式 //1. 將外部類屬性,寫在別名後面 class ScalaOuterClass { myouter => //這裏咱們能夠這裏理解 外部類的別名 看作是外部類的一個實例 class ScalaInnerClass { //成員內部類, def info() = { // 訪問方式:外部類別名.屬性名 // 只是這種寫法比較特別,學習java的同窗可能更容易理解 ScalaOuterClass.class 的寫法. println("name~ = " + myouter.name + " sal~ =" + myouter.sal) } //這裏有一個方法,能夠接受ScalaInnerClass實例 //下面的 ScalaOuterClass#ScalaInnerClass 類型投影的做用就是屏蔽 外部對象對內部類對象的 //影響 def test(ic: ScalaOuterClass#ScalaInnerClass): Unit = { System.out.println("使用了類型投影" + ic) } } //定義兩個屬性 var name = "Jack" private var sal = 999.9 }
-解決方式-類型投影
類型投影是指:在方法聲明上,若是使用 外部類#內部類 的方式,表示忽略內部類的對象關係,等同於Java中內部類的語法操做,咱們將這種方法稱之爲 類型投影(即:忽略對象的建立方式,只考慮類型)