第一章 面向對象
Scala 的面向對象思想和 Java 的面向對象思想和概念是一致的。
Scala 中語法和 Java 不同,補充了更多的功能。
1.1 Scala 包
1)基本語法
package 包名
2)Scala 包的三大作用(和 Java 一樣)
(1)區分相同名字的類
(2)當類很多時,可以很好的管理類
(3)控制訪問範圍
1.1.1 包的命名
1)命名規則
只能包含數字、字母、下劃線、小圓點.,但不能用數字開頭,也不要使用關鍵字。
2)案例實操
demo.class.exec1 //錯誤,因爲 class 關鍵字 demo.12a //錯誤,數字開頭
3)命名規範
一般是小寫字母+小圓點
com.公司名.項目名.業務模塊名
4)案例實操
com.atguigu.oa.model
com.atguigu.oa.controller
com.sohu.bank.order
1.1.2 包說明(包語句)
1)說明
Scala 有兩種包的管理風格,一種方式和 Java 的包管理風格相同,每個源文件一個包(包名和源文件所在路徑不要求必須一致),包名用“.”進行分隔以表示包的層級關係,如com.atguigu.scala。另一種風格,通過嵌套的風格表示層級關係,如下
package com{ package atguigu{ package scala{ } } }
第二種風格有以下特點:
(1)一個源文件中可以聲明多個 package
(2)子包中的類可以直接訪問父包中的內容,而無需導包
2)案例實操
package com{ import com.atguigu.scala.Inner//父包訪問子包需要導包 // 在外層包中定義單例對象 object Outer{ var out: String = "out" def main(args: Array[String]): Unit = { println(Inner.in) } } package atguigu{ package scala{ // 內層包中定義單例對象 object Inner{ var in: String = "in" def main(args: Array[String]): Unit = { println(Outer.out)//子包訪問父包無需導包 Outer.out = "outer" println(Outer.out) } } } } } // 在同一文件中定義不同的包 package aaa{ package bbb{ object Test01_Package{ def main(args: Array[String]): Unit = { import com.atguigu.scala.Inner println(Inner.in) } } } }
1.1.3 包對象
在 Scala 中可以爲每個包定義一個同名的包對象,定義在包對象中的成員,作爲其對應包下所有 class 和 object 的共享變量,可以被直接訪問。
1)定義
package object com{ val shareValue="share" def shareMethod()={} }
2)說明
(1)若使用 Java 的包管理風格,則包對象一般定義在其對應包下的 package.scala文件中,包對象名與包名保持一致。
(2)如採用嵌套方式管理包,則包對象可與包定義在同一文件中,但是要保證包對象與包聲明在同一作用域中。
// 定義一個類 class Student { // 定義屬性 private var name: String = "alice" @BeanProperty var age: Int = _ var sex: String = _ } package com { object Outer { val out: String = "out" def main(args: Array[String]): Unit = { println(name) } } } package object com { val name: String = "com" }
1.1.4 導包說明
1)和 Java 一樣,可以在頂部使用 import 導入,在這個文件中的所有類都可以使用。
2)局部導入:什麼時候使用,什麼時候導入。在其作用範圍內都可以使用
3)通配符導入:import java.util._
4)給類起名:import java.util.{ArrayList=>JL}
5)導入相同包的多個類:import java.util.{HashSet, ArrayList}
6)屏蔽類:import java.util.{ArrayList =>_,_}
7)導入包的絕對路徑:new _root_.java.util.HashMap
package java { package util { class HashMap { } } }
說明:
2)注意
Scala 中的三個默認導入分別是
import java.lang._ import scala._ import scala.Predef._
1.2 類和對象
類:可以看成一個模板
對象:表示具體的事物
1.2.1 定義類
1)回顧:Java 中的類,如果類是 public 的,則必須和文件名一致。
一般,一個.java 有一個 public 類
注意:Scala 中沒有 public,一個.scala 中可以寫多個類。
1)基本語法
[修飾符] class 類名 { 類體 }
說明
(1)Scala 語法中,類並不聲明爲 public,所有這些類都具有公有可見性(即默認就是public)
(2)一個 Scala 源文件可以包含多個類
2)案例實操
package com.atguigu.chapter06 //(1)Scala 語法中,類並不聲明爲 public,所有這些類都具有公有可見性(即默認就是 public) class Person { } //(2)一個 Scala 源文件可以包含多個類 class Teacher{ }
1.2.2 屬性
屬性是類的一個組成部分
1)基本語法
[修飾符] var|val 屬性名稱 [:類型] = 屬性值
注:Bean 屬性(@BeanPropetry),可以自動生成規範的 setXxx/getXxx 方法
2)案例實操
package com.atguigu.scala.test import scala.beans.BeanProperty class Person { var name: String = "bobo" //定義屬性 var age: Int = _ // _表示給屬性一個默認值 //Bean 屬性(@BeanProperty) @BeanProperty var sex: String = "男" //val 修飾的屬性不能賦默認值,必須顯示指定 } object Person { def main(args: Array[String]): Unit = { var person = new Person() println(person.name) person.setSex("女") println(person.getSex) } }
1.3 封裝
封裝就是把抽象出的數據和對數據的操作封裝在一起,數據被保護在內部,程序的其它部分只有通過被授權的操作(成員方法),才能對數據進行操作。
Java 封裝操作如下,
(1)將屬性進行私有化
(2)提供一個公共的 set 方法,用於對屬性賦值
(3)提供一個公共的 get 方法,用於獲取屬性的值
Scala 中的 public 屬性,底層實際爲 private,並通過 get 方法(obj.field())和 set 方法(obj.field_=(value))對其進行操作。所以 Scala 並不推薦將屬性設爲 private,再爲其設置public 的 get 和 set 方法的做法。
但由於很多 Java 框架都利用反射調用 getXXX 和 setXXX 方法,有時候爲了和這些框架兼容,也會爲 Scala 的屬性設置 getXXX 和 setXXX 方法(通過@BeanProperty 註解實現)。
1.3.1 訪問權限
1)說明
在 Java 中,訪問權限分爲:public,private,protected 和默認。在 Scala 中,你可以通過類似的修飾符達到同樣的效果。但是使用上有區別。
(1)Scala 中屬性和方法的默認訪問權限爲 public,但 Scala 中無 public 關鍵字。
(2)private 爲私有權限,只在類的內部和伴生對象中可用。
(3)protected 爲受保護權限,Scala 中受保護權限比 Java 中更嚴格,同類、子類可以訪問,同包無法訪問。
(4)private[包名]增加包訪問權限,包名下的其他類也可以使用
2)案例實操
package com.atguigu.scala.test class Person { private var name: String = "bobo" protected var age: Int = 18 private[test] var sex: String = "男" def say(): Unit = { println(name) } } object Person { def main(args: Array[String]): Unit = { val person = new Person person.say() println(person.name) println(person.age) } } class Teacher extends Person { def test(): Unit = { this.age this.sex } } class Animal { def test: Unit = { new Person().sex } }
1.3.2 方法
1)基本語法
def 方法名(參數列表) [:返回值類型] = {
方法體
}
2)案例實操
class Person { def sum(n1:Int, n2:Int) : Int = { n1 + n2 } } object Person { def main(args: Array[String]): Unit = { val person = new Person() println(person.sum(10, 20)) } }
1.3.3 創建對象
1)基本語法
val | var 對象名 [:類型] = new 類型()
2)案例實操
(1)val 修飾對象,不能改變對象的引用(即:內存地址),可以改變對象屬性的值。
(2)var 修飾對象,可以修改對象的引用和修改對象的屬性值
(3)自動推導變量類型不能多態,所以多態需要顯示聲明
class Person { var name: String = "canglaoshi" } object Person { def main(args: Array[String]): Unit = { //val 修飾對象,不能改變對象的引用(即:內存地址),可以改變對象屬性的值。 val person = new Person() person.name = "bobo" // person = new Person()// 錯誤的 println(person.name) } }
1.3.4 構造器
和 Java 一樣,Scala 構造對象也需要調用構造方法,並且可以有任意多個構造方法。
Scala 類的構造器包括:主構造器和輔助構造器
1)基本語法
class 類名(形參列表) {// 主構造器 // 類體 def this(形參列表) { // 輔助構造器 } def this(形參列表) { //輔助構造器可以有多個... } }
說明:
(1)輔助構造器,函數的名稱 this,可以有多個,編譯器通過參數的個數及類型來區分。
(2)輔助構造方法不能直接構建對象,必須直接或者間接調用主構造方法。
(3)構造器調用其他另外的構造器,要求被調用構造器必須提前聲明。
2)案例實操
(1)如果主構造器無參數,小括號可省略,構建對象時調用的構造方法的小括號也可以省略。
//(1)如果主構造器無參數,小括號可省略 //class Person (){ class Person { var name: String = _ var age: Int = _ def this(age: Int) { this() this.age = age println("輔助構造器") } def this(age: Int, name: String) { this(age) this.name = name } println("主構造器") } object Person { def main(args: Array[String]): Unit = { val person2 = new Person(18) } }
1.3.5 構造器參數
1)說明
Scala 類的主構造器函數的形參包括三種類型:未用任何修飾、var 修飾、val 修飾
(1)未用任何修飾符修飾,這個參數就是一個局部變量
(2)var 修飾參數,作爲類的成員屬性使用,可以修改
(3)val 修飾參數,作爲類只讀屬性使用,不能修改
2)案例實操
class Person(name: String, var age: Int, val sex: String) { } object Test { def main(args: Array[String]): Unit = { var person = new Person("bobo", 18, "男") // (1)未用任何修飾符修飾,這個參數就是一個局部變量 // printf(person.name) // (2)var 修飾參數,作爲類的成員屬性使用,可以修改 person.age = 19 println(person.age) // (3)val 修飾參數,作爲類的只讀屬性使用,不能修改 // person.sex = "女" println(person.sex) } }
1.4 繼承和多態
1)基本語法
class 子類名 extends 父類名 { 類體 }
(1)子類繼承父類的屬性和方法
(2)scala 是單繼承
2)案例實操
(1)子類繼承父類的屬性和方法
(2)繼承的調用順序:父類構造器->子類構造器
3)動態綁定
Scala 中屬性和方法都是動態綁定,而 Java 中只有方法爲動態綁定。
動態綁定是指在“執行期間”(而非編譯期間)判斷所引用的實際對象類型,根據其實際的類型調用其相應的方法。所以實際當中找要調用的方法時是動態的去找的,new的是誰就找誰的方法,這就叫動態綁定。動態綁定幫助我們的程序的可擴展性達到了極致。
案例實操(對比 Java 與 Scala 的重寫)
Scala
class Person { val name: String = "person" def hello(): Unit = { println("hello person") } } class Teacher extends Person { override val name: String = "teacher" override def hello(): Unit = { println("hello teacher") } } object Test { def main(args: Array[String]): Unit = { val teacher: Teacher = new Teacher() println(teacher.name) teacher.hello() val teacher1:Person = new Teacher println(teacher1.name) teacher1.hello() } }
Java
class Person { public String name = "person"; public void hello() { System.out.println("hello person"); } } class Teacher extends Person { public String name = "teacher"; @Override public void hello() { System.out.println("hello teacher"); } } public class TestDynamic { public static void main(String[] args) { Teacher teacher = new Teacher(); Person teacher1 = new Teacher(); System.out.println(teacher.name); teacher.hello(); System.out.println(teacher1.name); teacher1.hello(); } }
結果對比
1.5 抽象類
1.5.1 抽象屬性和抽象方法
1)基本語法
(1)定義抽象類:abstract class Person{} //通過 abstract 關鍵字標記抽象類
(2)定義抽象屬性:val|var name:String //一個屬性沒有初始化,就是抽象屬性
(3)定義抽象方法:def hello():String //只聲明而沒有實現的方法,就是抽象方法
案例實操
abstract class Person { val name: String def hello(): Unit } class Teacher extends Person { val name: String = "teacher" def hello(): Unit = { println("hello teacher") } }
2)繼承&重寫
(1)如果父類爲抽象類,那麼子類需要將抽象的屬性和方法實現,否則子類也需聲明爲抽象類
(2)重寫非抽象方法需要用 override 修飾,重寫抽象方法則可以不加 override。
(3)子類中調用父類的方法使用 super 關鍵字
(4)子類對抽象屬性進行實現,父類抽象屬性可以用 var 修飾;
子類對非抽象屬性重寫,父類非抽象屬性只支持 val 類型,而不支持 var。
因爲 var 修飾的爲可變變量,子類繼承之後就可以直接使用,沒有必要重寫
1.5.2 匿名子類
1)說明
和 Java 一樣,可以通過包含帶有定義或重寫的代碼塊的方式創建一個匿名的子類。
2)案例實操
abstract class Person { val name: String def hello(): Unit } object Test { def main(args: Array[String]): Unit = { val person = new Person { override val name: String = "teacher" override def hello(): Unit = println("hello teacher") } } }
1.6 單例對象(伴生對象)
Scala語言是完全面向對象的語言,所以並沒有靜態的操作(即在Scala中沒有靜態的概念)。但是爲了能夠和Java語言交互(因爲Java中有靜態概念),就產生了一種特殊的對象來模擬類對象,該對象爲單例對象。若單例對象名與類名一致,則稱該單例對象這個類的伴生對象,這個類的所有“靜態”內容都可以放置在它的伴生對象中聲明。
1.6.1 單例對象語法
1)基本語法
object Person{ val country:String="China" }
2)說明
(1)單例對象採用 object 關鍵字聲明
(2)單例對象對應的類稱之爲伴生類,伴生對象的名稱應該和伴生類名一致。
(3)單例對象中的屬性和方法都可以通過伴生對象名(類名)直接調用訪問。
3)案例實操
//(1)伴生對象採用 object 關鍵字聲明 object Person { var country: String = "China" } //(2)伴生對象對應的類稱之爲伴生類,伴生對象的名稱應該和伴生類名一致。 class Person { var name: String = "bobo" } object Test { def main(args: Array[String]): Unit = { //(3)伴生對象中的屬性和方法都可以通過伴生對象名(類名)直接調用訪問。 println(Person.country) } }
1.6.2 apply 方法
1)說明
(1)通過伴生對象的 apply 方法,實現不使用 new 方法創建對象。
(2)如果想讓主構造器變成私有的,可以在()之前加上 private。
(3)apply 方法可以重載。
(4)Scala 中 obj(arg)的語句實際是在調用該對象的 apply 方法,即 obj.apply(arg)。用以統一面向對象編程和函數式編程的風格。
(5)當使用 new 關鍵字構建對象時,調用的其實是類的構造方法,當直接使用類名構建對象時,調用的其實時伴生對象的 apply 方法。
2)案例實操
object Test { def main(args: Array[String]): Unit = { //(1)通過伴生對象的 apply 方法,實現不使用 new 關鍵字創建對象。 val p1 = Person() println("p1.name=" + p1.name) val p2 = Person("bobo") println("p2.name=" + p2.name) } } //(2)如果想讓主構造器變成私有的,可以在()之前加上 private class Person private(cName: String) { var name: String = cName } object Person { def apply(): Person = { println("apply 空參被調用") new Person("xx") } def apply(name: String): Person = { println("apply 有參被調用") new Person(name) } //注意:也可以創建其它類型對象,並不一定是伴生類對象 }
擴展:在 Scala 中實現單例模式