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