目錄
面向對象編程
類
- 這部分可以類比Java中的很多概念
- 在IDEA中新建object
import java.util.Date
class Customer {
var name:String = _ // 下劃線表示佔位,傳入相應類型的默認值
var sex:String = _
val registerDate:Date = new Date
def sayHi(msg:String):Unit = { // Unit即void
println(msg)
}
}
object Main {
// main方法必須要放在一個scala的object(單例對象)中才能執行
def main(args: Array[String]): Unit = {
val customer = new Customer
//給對象的成員變量賦值
customer.name = "張三"
customer.sex = "男"
println(s"姓名: ${customer.name}, 性別:${customer.sex}, 註冊時間: ${customer.registerDate}") // 使用${}格式化輸出變量,s表示字符串
//對象調用方法
customer.sayHi("你好!")
}
}
構造器
- 主構造器:指在類名的後面跟上一系列參數
- 輔助構造器:在類中使用this來定義
class Student(val name:String, val age:Int) { // 類的默認成員屬性都是val,可以使用var聲明
val address:String="beijing"
// 定義一個參數的輔助構造器
def this(name:String) {// 子類主構造器(傳參即可)
// 第一行必須調用主構造器、其他輔助構造器或者super父類的構造器
this(name, 20) // 調用主構造器
}
def this(age:Int) {
this("某某某", age)
}
}
- 這裏的主構造器類似Java的父類構造器,但是傳參即可
- 輔助構造器類似子類構造器,但必須使用
this
調用父類的構造方法
注:只是類比學習
對象
- scala中是沒有靜態(static)成員
- 需要實現static變量、static方法的效果,要通過scala中的單例對象(object)
- scala主要進行函數式編程,這裏可以類比JavaScript中的模式,靜態屬性/方法可以藉助對象實現
import java.text.SimpleDateFormat
object DateUtils {
// 在object中定義的成員變量,相當於Java中定義一個靜態變量
// 定義一個SimpleDateFormat日期時間格式化對象
val simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
// 構造代碼
println("構造代碼")
// 相當於Java中定義一個靜態方法
def format(date:Date) = simpleDateFormat.format(date)
// main是一個靜態方法,所以必須要寫在object中
def main(args: Array[String]): Unit = {
println { DateUtils.format(new Date()) }; // 通過 . 調用
}
}
伴生對象
- class和object具有同樣的名字,那麼就稱這個object是class的伴生對象,class是object的伴生類
- 最大特點是:可以相互訪問,包括私有變量
class ClassObject {
val id = 1
private var name = "itcast"
def printName(): Unit ={
//在Dog類中可以訪問伴生對象Dog的私有屬性
println(ClassObject.CONSTANT + name )
}
}
object ClassObject{
//伴生對象中的私有屬性
private val CONSTANT = "汪汪汪 : "
def main(args: Array[String]) {
val p = new ClassObject // 實例化類
p.name = "123" //訪問私有的字段name
p.printName()
}
}
apply方法
- 我們可以使用如下代碼新建數組,不需要關鍵字
new+類名
,如何實現?
// 創建一個Array對象
val a = Array(1,2,3,4)
- 通過下面的scala源碼可以發現,其實默認調用了Array對象的apply()方法
- 伴生對象的apply方法用來快速地創建一個伴生類的對象(實例)
class Person(var name:String, var age:Int) {
override def toString = s"Person($name, $age)"
}
object Person {
// 實現apply方法
// 返回的是伴生類的對象
// 有沒有點像Java自定義異常?必須實現所有構造方法
def apply(name:String, age:Int): Person = new Person(name, age)
// apply方法支持重載
def apply(name:String):Person = new Person(name, 20)
def apply(age:Int):Person = new Person("某某某", age)
def apply():Person = new Person("某某某", 20)
}
object Main2 {
def main(args: Array[String]): Unit = {
val p1 = Person("張三", 20) // Person對象的第一個apply()方法
val p2 = Person("李四")
val p3 = Person(100)
val p4 = Person()
println(p1)
println(p2)
println(p3)
println(p4)
}
}
main方法
- 我們在對象中實現main方法
- 也可以繼承自App Trait(特質),可以理解爲Java中的接口
object Main2 extends App {
println("hello, scala")
}
繼承
- 使用
extends
關鍵字實現繼承,基本和Java相同
class Person1 {
var name = "super"
def getName = this.name
}
class Student1 extends Person1
object Main1 {
def main(args: Array[String]): Unit = {
val p1 = new Person1()
val p2 = new Student1()
p2.name = "張三"
println(p2.getName)
}
}
// 必須在子類的主構造器中調用父類的構造器
class Person8(var name:String){
println("name:"+name)
}
// 傳參,調用父類的構造器
class Student8(name:String, var clazz:String) extends Person8(name)
object Main8 {
def main(args: Array[String]): Unit = {
val s1 = new Student8("張三", "三年二班")
println(s"${s1.name} - ${s1.clazz}")
}
}
// output:
// name:張三
// 張三 - 三年二班
- 對象也可以繼承類油!
class Person2 {
var name = "super"
def getName = this.name
}
object Student2 extends Person2 // 單例對象繼承
object Main2 {
def main(args: Array[String]): Unit = {
println(Student2.getName) // 相當於調用靜態方法(object中的方法)
}
}
- 與Java不同,子類要覆蓋父類中的一個非抽象方法,必須要使用
override
關鍵字 - 相同的是,使用
super
訪問父類成員
class Person3 {
val name = "super"
def getName = name
}
class Student3 extends Person3 {
// 重寫val字段
override val name: String = "child"
// 重寫getName方法
override def getName: String = "hello, " + super.getName
}
object Main3 {
def main(args: Array[String]): Unit = {
println(new Student3().getName)
}
}
抽象類
- 就是這麼
abstract
,爲了更好的實現多態? - 沒有方法體的方法稱爲抽象方法,沒有初始化的變量稱爲抽象字段
abstract class Person(val name:String) {// 還有個構造方法呢
//抽象方法
def sayHello:String
def sayBye:String
//抽象字段
val address:String
}
class Student(name:String) extends Person(name){
//重寫抽象方法,不需要使用override
def sayHello: String = "Hello,"+name
def sayBye: String ="Bye,"+name
//重寫抽象字段,需要使用override
override val address:String ="beijing "
}
object Main{
def main(args: Array[String]) {
val s = new Student("tom")
println(s.sayHello)
println(s.sayBye)
println(s.address)
}
}
匿名內部類
- 這個非常常用,配合匿名類使用
- 匿名內部類是沒有名稱的子類,直接用來創建實例對象
abstract class Person {
//抽象方法
def sayHello:Unit
}
object Main {
def main(args: Array[String]): Unit = {
// 直接用new來實例化一個匿名內部類對象
val p1 = new Person { // 通過new實例化,但是實現了抽象方法,還是屬於子類
override def sayHello: Unit = println("我是一個匿名內部類")
}
p1.sayHello
}
}
isInstanceOf
- 在代碼中要經常進行類型的判斷和類型的轉換,在scala中
isInstanceOf
判斷對象是否爲指定類的對象asInstanceOf
將對象轉換爲指定類型
class Person4
class Student4 extends Person4
object Main4 {
def main(args: Array[String]): Unit = {
val s1:Person4 = new Student4 // Person4 類型
// 判斷s1是否爲Student4類型
if(s1.isInstanceOf[Student4]) {
// 將s1轉換爲Student3類型
val s2 = s1.asInstanceOf[Student4]
}
}
}
getClass
- isInstanceOf 只能判斷出對象是否爲指定類以及其子類的對象,而不能精確的判斷
- 要求精確地判斷出對象就是指定類的對象,那麼就只能使用
getClass
和classOf
class Person
class Student extends Person
object Student{
def main(args: Array[String]) {
val p:Person=new Student
//判斷p是否爲Person5類的實例
println(p.isInstanceOf[Person])//true
//判斷p的類型是否爲Person5類
println(p.getClass == classOf[Person])//false
//判斷p的類型是否爲Student5類
println(p.getClass == classOf[Student])//true
}
}
訪問修飾符
- 可以在成員前面添加
private/protected
關鍵字來控制成員的可見性 - 但在scala中,任何沒有被標爲private或protected的成員都是公共的,沒有public關鍵字
private[this]
- 被修飾的成員只能在當前類中被訪問
class Person6 {
// 只有在當前對象中能夠訪問
private[this] var name = "super"
def getName = this.name // 正確!
def sayHelloTo(p:Person6) = {
println("hello" + p.name) // 報錯!無法訪問
}
}
object Person6 {
def showName(p:Person6) = println(p.name) // 報錯!外部無法訪問
}
protected[this]
- 可以通過
this.
訪問或者子類通過this.訪問,和java很像
class Person7 {
// 只有在當前對象以及繼承該類的當前對象中能夠訪問
protected[this] var name = "super"
def getName = {
// 正確!
this.name
}
def sayHelloTo1(p:Person7) = {
// 編譯錯誤!無法訪問
println(p.name) // 只能this.
}
}
class Student7 extends Person7 {
def showName = {
// 正確!
println(name) // 默認使用this.
}
def sayHelloTo2(p:Person7) = {
// 編譯錯誤!無法訪問
println(p.name)
}
}
特質
- 特質
trait
是scala中代碼複用的基礎單元 - 它可以將方法和字段定義封裝起來,然後添加到類中,我們叫混入
- 一個類可以添加任意數量的特質,像Java接口吧!!!
- 使用
extends
來繼承trait,如果要繼承多個trait,則使用with
關鍵字
trait Logger {
// 抽象方法
def log(msg:String)
}
trait MessageSender {
def send(msg:String)
}
class ConsoleLogger extends Logger with MessageSender {
override def log(msg: String): Unit = println(msg)
override def send(msg: String): Unit = println(s"發送消息:${msg}")
}
object LoggerTrait {
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger2
logger.log("控制檯日誌: 這是一條Log")
logger.send("你好!")
}
}
- trait中也可以定義具體的方法
- 實例對象混入trait
trait LoggerMix {
def log(msg:String) = println(msg)
}
class UserService
object FixedInClass {
def main(args: Array[String]): Unit = {
// 使用with關鍵字直接將特質混入到對象中
val userService = new UserService with LoggerMix// 只有這個實例的對象有
userService.log("你好")
}
}
- 可以瞭解一下trait調用鏈
- 參考如下代碼,理解一下trait的優勢
// 支付數據處理
trait HandlerTrait {
def handle(data: String) = {
println("處理數據...")
}
}
// 數據校驗處理
trait DataValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("驗證數據...")
super.handle(data)
}
}
// 簽名校驗處理
trait SignatureValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("檢查簽名...")
super.handle(data)
}
}
// 支付服務
class PaymentService extends DataValidHandlerTrait with SignatureValidHandlerTrait {
def pay(data:String) = {
println("準備支付...")
this.handle(data)
}
}
object PaymentService {
def main(args: Array[String]) {
val payService = new PaymentService()
payService.pay("signature:10233123||md5:123889a3d5s1f6123||data:{order:001,money:200}")
}
}
// 程序運行輸出如下:
// 準備支付...
// 檢查簽名...
// 驗證數據...
// 處理數據...
模板模式
- 使用具體方法依賴於抽象方法,而抽象方法可以放到繼承trait的子類中實現
trait Logger {
// 抽象方法
def log(msg:String)
// 具體方法,依賴於抽象方法log
def info(msg:String) = log("INFO:" + msg)
def warn(msg:String) = log("WARN:" + msg)
def error(msg:String) = log("ERROR:" + msg)
}
class ConsoleLogger extends Logger {
// 實現抽象方法
override def log(msg: String): Unit = println(msg)
}
object LoggerTrait {
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger3.info("這是一條普通信息")
logger3.warn("這是一條警告信息")
logger3.error("這是一條錯誤信息")
}
}
trait構造機制
- trait也有構造代碼,但和類不一樣,特質不能有構造器參數
- 每個特質只有一個無參數的構造器
- 一個類繼承另一個類、以及多個trait,當創建該類的實例時,它的構造順序如下:
- 執行父類的構造器
- 從左到右依次執行trait的構造器
- 如果trait有父trait,先構造父trait,如果多個trait有同樣的父trait,則只初始化一次
- 執行子類構造器
class Person_One {
println("執行Person構造器!")
}
trait Logger_One {
println("執行Logger構造器!")
}
trait MyLogger_One extends Logger_One {
println("執行MyLogger構造器!")
}
trait TimeLogger_One extends Logger_One {
println("執行TimeLogger構造器!")
}
class Student_One extends Person_One with MyLogger_One with TimeLogger_One {
println("執行Student構造器!")
}
object exe_one {
def main(args: Array[String]): Unit = {
val student = new Student_One
}
}
// 程序運行輸出如下:
// 執行Person構造器!
// 執行Logger構造器!
// 執行MyLogger構造器!
// 執行TimeLogger構造器!
// 執行Student構造器!
模式匹配
- scala有一個十分強大的模式匹配機制,可以應用到很多場合
- 並且scala還提供了樣例類,對模式匹配進行了優化,可以快速進行匹配
- 類似於Java中的switch這裏使用
match
//todo:匹配字符串
object CaseDemo01 extends App{
//定義一個數組
val arr=Array("hadoop","zookeeper","spark","storm")
//隨機取數組中的一位,使用Random.nextInt
val name = arr(Random.nextInt(arr.length))
println(name)
name match {
case "hadoop" => println("大數據分佈式存儲和計算框架...")
case "zookeeper" => println("大數據分佈式協調服務框架...")
case "spark" => println("大數據分佈式內存計算框架...")
//表示以上情況都不滿足纔會走最後一個
case _ => println("我不認識你")
}
}
//todo:匹配類型
object CaseDemo02 extends App{
//定義一個數組
val arr=Array("hello",1,-2.0,CaseDemo02)
//隨機獲取數組中的元素
val value=arr(Random.nextInt(arr.length))
println(value)
value match {
case x:Int => println("Int=>"+x)
case y:Double if(y>=0) => println("Double=>"+y)
case z:String => println("String=>"+z)
case _ => throw new Exception("not match exception")
}
}
//匹配數組
object CaseDemo03 extends App{
//匹配數組
val arr=Array(1,3,5)
arr match{
case Array(1,x,y) =>println(x+"---"+y) // 會分別將數組中的元素複製給x,y
case Array(1,_*) =>println("1...")
case Array(0) =>println("only 0")
case _ =>println("something else")
}
}
//匹配集合
object CaseDemo04 extends App{
val list=List(0,3,6)
list match {
case 0::Nil => println("only 0") // Nil表示空
case 0::tail => println("0....") // :: 表示追加到列表,tail表示列表尾部
case x::y::z::Nil => println(s"x:$x y:$y z:$z")
case _ => println("something else") // _ 表示啥也沒匹配到,即default
}
}
//匹配元組
object CaseDemo05 extends App{
val tuple=(1,3,5)
tuple match{
case (1,x,y) => println(s"1,$x,$y")
case (2,x,y) => println(s"$x,$y")
case _ => println("others...")
}
}
樣例類
- 樣例類是一種特殊類,它可以用來快速定義一個用於保存數據的類
- 使用
case class
關鍵字
// 定義一個樣例類
// 樣例類有兩個成員name、age
case class CasePerson(name:String, age:Int)
// 使用var指定可變成員變量
case class CaseStudent(var name:String, var age:Int)
object CaseClassDemo {
def main(args: Array[String]): Unit = {
// 1. 使用new創建實例
val zhangsan = new CasePerson("張三", 20)// 一般不這樣做(意思是也可以)
println(zhangsan)
// 2. 使用類名直接創建實例
val lisi = CasePerson("李四", 21)
println(lisi)
val xiaohong = CaseStudent("小紅", 23)
xiaohong.age = 24
println(xiaohong)
}
}
- 樣例對象時可以序列化的,先了解即可
case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
case object CheckTimeOutTask
object CaseDemo06 extends App{ // 繼承App特質
val arr = Array(CheckTimeOutTask,
HeartBeat(10000),
SubmitTask("0001", "task-0001"))
arr(Random.nextInt(arr.length)) match {
case SubmitTask(id, name) => println(s"id=$id, name=$name")
case HeartBeat(time) => println(s"time=$time")
case CheckTimeOutTask => println("檢查超時")
case _ => println("what???")
}
}
Option類型
- Option[T] 是一個類型爲 T 的可選值的容器
- 如果值存在, Option[T] 就是一個 Some[T] ,如果不存在, Option[T] 就是對象 None
- 包含兩個子類:
- Some包裝了某個值
- None表示沒有值
val myMap: Map[String, String] = Map("key1" -> "value")
val value1: Option[String] = myMap.get("key1") // 我希望是String類型的
val value2: Option[String] = myMap.get("key2")
println(value1) // Some("value1")
println(value2) // None
object TestOption {
def main(args: Array[String]) {
val map = Map("a" -> 1, "b" -> 2)
val value: Option[Int] = map.get("b") // 我希望是Int類型的
val v1 =value match {
case Some(i) => i
case None => 0
}
println(v1) // 2
//更好的方式
val v2 = map.getOrElse("c", 0)
println(v2) // 0
}
}
偏函數
- 定義一個函數,只接受和處理其參數定義域範圍內的子集,對於參數範圍外的參數則拋出異常
- 它是
PartialFunction[A, B]
的一個實例 - A代表輸入參數類型
- B代表返回結果類型
- 實例得到的偏函數是一個特質trait
object TestPartialFunction {
// func1是一個輸入參數爲Int類型,返回值爲String類型的偏函數
val func1: PartialFunction[Int, String] = {// 使用定義方法的格式
case 1 => "一"
case 2 => "二"
case 3 => "三"
case _ => "其他"
}
func1(1) // String: 一
def main(args: Array[String]): Unit = {
val list=List(1,2,3,4,5,6)
// filter繼承PartialFunction的特質
val result=list.filter{
case x if x >3 => true
case _ => false
}
println(result) // List(4,5,6)
}
}
小結
這裏主要介紹了scala面向對象編程中經常用到的方法,每種特性都有適用的場合。還有很多可以擴展的地方,可以根據場景再次拓寬。下一節從異常處理開始,總結其基本概念和用法。