Scala (二)面向對象

第一章 面向對象

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 中實現單例模式

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章