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一樣)
- 區分相同名字的類
- 當類很多時,可以很好的管理類
- 控制訪問範圍
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; }
}