Scala面向对象编程
1.课程目标
- 掌握Scala中面向对象编程
- 掌握Scala模式匹配
- 掌握Scala泛型高级内容
- 熟悉Scala中多线程编程模型
2.面向对象
2.1 类
-
定义: class 类名{ 成员变量 val/var 名称=值 成员方法 def 方法名称(参数列表):返回值={ 方法体 } } 创建对象: val 对象名称=new 类名() //() 可写可不写 ()没有参数的时候可以省略 /** * Person * name 不可变 * age 可变 * skill 数组 * 方法: * def addSkill * def scanSkill */ class Person { val name = "itcast" //不可变 不需要setter var age = 13 //可变 需要setter getter val skill = new ArrayBuffer[String]() // def addSkill def addSkill(subject: String): Unit = { skill += subject } def scanSkill(): Unit = { skill.foreach((x: String) => println(x)) } } object Scala_01_class { def main(args: Array[String]): Unit = { val person = new Person println(person.name) person.age=14 println(person.age) person.addSkill("scala") person.addSkill("spark") person.scanSkill() } }
2.2 setter getter
-
/** * Student类 * name 不可变 * age 可变 * * 注意: * 1.如果一个方法 只返回结果 在方法内部不进行任何业务逻辑处理的话,就可以省略方法名称后边的小括号“()” * 2.通过def 定义属性 */ class Student { val name = "itcast" //不需要提供setter 和getter private var _age = 10 //可变变量定义格式 : _名称 //setter def age_(age: Int) = this._age = age //getter def age=this._age } object Scala_02_class { def main(args: Array[String]): Unit = { val student = new Student student.age_(10) println(student.age) } }
2.3 方法的调用原则
-
/** * Teacher 类 * teach方法 * * 注意:teacher teach "spark" 这种调用方式只适用于一个参数的方法 */ class Teacher{ def teach(suject:String): Unit ={ println(s"教学:$suject") } } object Scala_03_class { def main(args: Array[String]): Unit = { val teacher = new Teacher teacher.teach("hadoop") teacher teach("scala") teacher teach "spark" 0 to 10 0 until 10 1 + 2 } }
2.4 Scala构造器
-
两种构造器方式 主构造器 class 类名(参数*) 辅助构造器 定义在类当中 def this(参数*){ //第一行代码必须代用主构造 this(参数) } /** * 定义: Animal * 主构造器 (name) * 辅助构造(color,legs) */ class Animal(val name: String) { var _color = "red" var _legs = 2 //辅助构造(name,color,legs) def this(name: String, color: String, legs: Int) { this(name) //调用主构造器 this._color = color this._legs = legs } } object Scala_04_class { def main(args: Array[String]): Unit = { val a=new Animal("dog") val b=new Animal("cat","white",2) println(a.name) println(b._legs) } }
2.5 Scala中单例对象
-
创建方式: object 名称{ 成员变量 成员方法 } 使用方式: 可以通过 名称.成员变量/成员方法 形式使用。相当于java中static修饰变量和方法的调用 应用场景: 单例对象多应用于工具类或者工具对象 //创建工具对象 object CommonUtil { val PI = 3.14 def getDate = new Date } object Scala_05_object { def main(args: Array[String]): Unit = { println(CommonUtil.PI) println(CommonUtil.getDate) } }
2.6 伴生对象
-
当单例对象的名称跟类名相同的时候,并且单例对象和类同时存在于同一个文件中, 这里这个对象就称之为类的伴生对象。类是对象的伴生类 /** * SoftEngineer 类 * SoftEngineer object * 1.伴生类和伴生对象之间可以互相访问 private属性和方法 * 2.apply存于伴生对象中 能够实现伴生类创建对象的功能 ,相当于java中的工厂模式的定义 */ class SoftEngineer(val exp:Int){ private var salary=1000 def getHasGF()={ SoftEngineer.hasGF //访问伴生对象中私有变量 } val hasHair=SoftEngineer.hasHair() } object SoftEngineer{ private var hasGF=false //定义方法 访问伴生类的私有变量 def getSalary()={ val engineer = new SoftEngineer(10) engineer.salary //访问私有变量 } private def hasHair()={ true } def apply(exp:Int): SoftEngineer = new SoftEngineer(exp) } object Scala_06_object { def main(args: Array[String]): Unit = { val engineer = new SoftEngineer(10) println(engineer.getHasGF()) println(engineer.hasHair) println( SoftEngineer.getSalary()) val engineer2 = SoftEngineer(10) //Array(“dd”) List("ddd") } }
2.7 main函数
-
//通过继承App的方式可以省略掉main //本质上 还是没有省略 App中包含main函数 object Scala_07_object extends App { println("hello app") println(args(0)) }
2.8 继承
-
格式:class 类名 extends 父类 1.scala单继承 2.子类可以继承父类的非私有的属性和方法 3.子类可以重写父类的方法和属性 4.子类可以通过super关键词调用父类的属性和方法 /** * 父类 BigDataEngineer * 不可变 name * 可变 age * 方法 study * 子类 HadoopEngineer * */ class BigDataEngineer{ val name="BigData" var age=10 def study():Unit={ println("学习和开发") } } class HadoopEngineer extends BigDataEngineer{ //子类覆盖父类中的study方法 override override def study(): Unit = { age=12 super.study() println("开发hadoop") } } object Scala_08_extends extends App{ val engineer = new HadoopEngineer println(engineer.name) engineer.study() } 问题:子类在创建对象的时候,是否调用了父类的构造器? scala中子类创建对象的时候也同样调用了父类的构造器
-
Scala中构造机制
-
/** * 类 Person_09 * 子类 Sun_09 */ class Person_09{ println("Person_09") //主构造器调用完成之后直接调用 } class Sun_09 extends Person_09{ println("Sun_09") } object Scala_09_extends extends App { val sun_0 = new Sun_09 //根据打印结果 //在子类创建对象的过程中,调用了父类的构造器的 }
-
2.9 子类调用父类的构造器
-
子类主构造器没有参数,父类主构造器有参数 子类主构造器有参数,父类主构造器也有参数 /** * 父类PC 电脑(cup memory) * 子类 NotePC * 台式机:DeskPC(cup memory) */ class PC(val cpu:Int,val memory:Int) class NotePC extends PC(cpu = 10,memory = 20) class DeskPC(cpu:Int,memory:Int) extends PC(cpu,memory) object Scala_10_extends { def main(args: Array[String]): Unit = { val n = new NotePC println(n.cpu) println(n.memory) println("----------------------") val deskPC=new DeskPC(30,60) println(deskPC.cpu) println(deskPC.memory) } }
2.10 抽象类
-
定义: abstract class 类名 1.可以定义抽象方法和抽象属性(没有初始化的属性) 2.可以有非抽象的方法和非抽象属性 3.如果方法或者属性被final修饰,不能够被重写 /** * Employee 员工 * val name 抽象 * var age 非抽象的 * 抽象方法 work * 非抽象方法 study * final 方法:sleep */ abstract class Employee { val name: String //抽象属性 数据类型不能够省略 var age = 25 def work(): Unit //抽象方法返回值类型不能够省略 def study(): Unit = { println("学习公司文化") } final def sleep(): Unit = { println("休息") } } class ScalaEmployee extends Employee { /** * override 如果覆盖的是非抽象的方法或者属性 这里不能够省略 * 如果覆盖的是抽象方法或者属性 这里可以省略 */ override val name: String = "scala" override def work(): Unit = { println("开发scala应用程序") } } object Scala_11_extends extends App { private val employee = new ScalaEmployee println(employee.name) employee.work() employee.study() }
2.11匿名内部类
-
应用场景: ScalaProgramer 只会开发scala应用程序。python class ScalaProgrammer { def workerByScala(): Unit = { println("开发scala应用程序") } } class pyAndScaProgrammer extends ScalaProgrammer{ def workerByPython(): Unit = { println("学习并开发python") } } object Scala_12_innerclass { def main(args: Array[String]): Unit = { //python项目 临时性需求 val scala= new ScalaProgrammer { def workerByPython(): Unit = { println("学习并开发python") } } scala.workerByPython() scala.workerByScala() println(scala.getClass) } }
2.12 超级父类
-
Any 超级分类 AnyRef 所有引用类型对象的超级父类 ScalaProgrammer AnyVal 所有值类型对象的超级父类 Int Double...
2.13 类型转换和类型判断
-
子类继承父类,父类的引用可以指向子类对象。父类的引用是多态的。多态的问题 :类型转换异常 * val worker = w.asInstanceOf[Worker] 1. 对象.isInstanceOf[类型] 模糊判断 不精准 * 2. 对象.getClass=classOf[类型] class Worker { def work(): Unit = { println("工作") } } class JavaWorker extends Worker { def workByJava(): Unit = { println("开发java项目") } } object Scala_13_change { def boss(w: Any): Unit = { //将any对象转换成 worker对象 //判断对象的类型 /** * 1. 对象.isInstanceOf[类型] 模糊判断 不精准 * 2. 对象.getClass=classOf[类型] */ if (w.isInstanceOf[Worker]) { val worker = w.asInstanceOf[Worker] worker.work() } else { println("回家等消息") } } def boss2(w: Any): Unit = { //将any对象转换成 worker对象 //判断对象的类型 /** * 1. 对象.isInstanceOf[类型] 模糊判断 不精准 * 2. 对象.getClass=classOf[类型] */ if (w.getClass()==classOf[JavaWorker]) { val worker = w.asInstanceOf[JavaWorker] worker.workByJava() } else { println("回家等消息") } } def main(args: Array[String]): Unit = { val worker = new Worker /*boss(worker) boss(new Student)*/ val javaWorker = new JavaWorker boss2(worker) } }
2.14特质
-
特质的功能
- 格式:trait 名称
- 功能:
- 特质可以作为java中的接口使用
- 特质可以作为抽象类使用
- 特质可以扩展对象的方法(1 个方法 --》多个方法)
- 特质可以扩展特定方法的功能
-
特质作为接口使用
-
/** * 定义 * trait 名称 * 可以作为接口使用 * 1.可以定抽象方法 * 2.接口可以多实现 */ 格式:类 extends Tarit名称 with Tarit名称2 with Tarit名称3 trait HelloTrait { def sayHellByChina(): Unit } trait HelloTraitHanguo { def sayHellByHanguo(): Unit } trait HelloTraitTaiguo { def sayHellByTaiguo(): Unit } class HelloTraitImpl extends HelloTrait with HelloTraitHanguo with HelloTraitTaiguo { override def sayHellByChina(): Unit = { println("你好") } override def sayHellByHanguo(): Unit = { println("阿尼哈沙有") } override def sayHellByTaiguo(): Unit = { println("萨瓦迪卡") } } object Scala_14_trait { def main(args: Array[String]): Unit = { val impl = new HelloTraitImpl impl.sayHellByChina() impl.sayHellByHanguo() impl.sayHellByTaiguo() } }
-
-
特质作为抽象类使用
-
trait AbstractTrait { //非抽象属性和方法 val name = "itcast" def study(): Unit = { println("学习") } //抽象属性和方法 val age: Int def work(): Unit } class AbstractTraitImpl extends AbstractTrait { override val age: Int = 10 override def work(): Unit = { println("工作") } } object Scala_15_trait extends App { private val impl = new AbstractTraitImpl println(impl.age) println(impl.name) impl.work() impl.study() } 特质和抽象类的区别 1.抽象类可以有自己的主构造器以及初始化参数 2.特质可以多实现但是抽象类只能进行单继承或者多重继承
-
-
特质扩展对象的功能
-
package cn.itcast.day02 class Tank { def shoot(): Unit = { println("发射子弹") } } trait Aim { def aim(): Unit = { println("瞄准") } } trait Scan { def scan(): Unit = { println("扫描") } } trait Fly{ def fly():Unit } object Scala_16_trait { def main(args: Array[String]): Unit = { val tank = new Tank with Aim with Scan with Fly{ override def fly(): Unit = { println("飞上天") } } tank.scan() tank.aim() tank.shoot() tank.fly() } }
-
-
特质扩展特定方法的功能
-
super: 线性调用的标志符,线性是动态过程 是根据特质的混入顺序有关的 线性执行顺序:自右向左的过程 class Tank2 { def shoot(): Unit = { println("发射") } } trait Aim2 extends Tank2 { override def shoot(): Unit = { println("瞄准") super.shoot() } } trait Scan2 extends Tank2 { override def shoot(): Unit = { println("扫描") super.shoot() } } class SuperTank extends Tank2 with Aim2 with Scan2 object Scala_17_trait { def main(args: Array[String]): Unit = { val tank = new SuperTank tank.shoot() } }
-
-
特质构造机制
-
构造机制的执行原则: 从左往右 自上而下 trait T0{ println(0) } trait T1{ println(1) } trait T2{ println(2) } trait T3 extends T1{ println(3) } class Timpl extends T0 with T3 with T2 with T1{ println("Timpl") } object Scala_18_trait extends App { //1 2 1 3 0 // Timpl 1 2 1 3 0 new Timpl // 从左往右 自上而下 }
-
3. 模式匹配
-
功能类似于java中switch结构,但是要比swith强大的多
- 数值
- 数据类型
- Array
- List
- Tuple
- Map
- 自定义数据类型
-
结构: value match { case 需要匹配值或者类型 =>{ 执行逻辑} case _ => {执行逻辑} //默认的匹配 }
-
模式匹配值匹配
-
//值匹配 val arr = Array("hadoop", "scala", "spark") var value = arr((math.random * arr.size).toInt) //println("value:" + value) value="java" /** * value match { * case 需要匹配值或者类型 =>{ 执行逻辑} * case _ => {执行逻辑} //默认的匹配 * } */ value match { case "hadoop" => println("匹配Hadoop") case "scala" => println("匹配scala") case "spark" => println("匹配spark") case _ =>println("默认匹配") }
-
-
数据类型匹配
-
object DataType { def main(args: Array[String]): Unit = { val arr=Array("hadoop",1.34,10,true) var value = arr((math.random * arr.size).toInt) println("value:"+value) value match { case s:String => println(s+"-------------") case d:Double => println(d+"-------------") case i:Int => println(i+"-------------") case b:Boolean =>println(b+"-------------------") } } }
-
-
模式匹配数组
-
object MatchArray{ def main(args: Array[String]): Unit = { val arr=Array("hadoop",1.34,10,true) arr match { //能否有个配所有array //非精确匹配 case Array("hadoop1",_*) =>println("333333") //占位符匹配 case Array("hadoop",1.34,_,_) =>println("222222222") //精确匹配 case Array("hadoop",1.34,10,true) =>println("111111") case Array(_*) =>println("4444444") } } }
-
-
模式匹配列表
-
object MatchList{ def main(args: Array[String]): Unit = { val list=List("hadoop","spark","scala") list match { case "hadoop"::"spark"::"scala"::Nil =>println("sfsfsfdsfsdsdfd") //模糊匹配 case List(x,_*) =>println(x) case List(x,y,_) =>println(x+"\t"+y) //精确匹配 case List(x,y,z) =>println(x+"\t"+y+"\t"+z) case List("hadoop","spark","scala") => println("精确匹配list") } } }
-
-
模式匹配Tuple
-
object MatchTuple { def main(args: Array[String]): Unit = { val t = ("hadoop", 1.23, 10, true) t match { case (x,_,_,_) =>println("sdfdsfds") case _ =>println("默认匹配。。。。") //case ("hadoop",_*) =>println("") case ("hadoop",_,_,_) =>println("匹配占位符tuple") case ("hadoop", 1.23, 10, true) =>println("精确匹配tuple") } } }
-
-
模式匹配Map
-
object MatchMap{ def main(args: Array[String]): Unit = { val map=Map("hadoop"->100,"scala"->50) val value=map.get("java") value match { case Some(x) =>println(x) case None =>println("没有值") } } }
-
-
模式匹配自定数据类型
-
样例类 case class 名称
- 样例类跟普通类区别:样例类能够自动构建apply方法
-
样例对象
- case object 名称 :应用场景 只是作为标志
-
case class Task(name: String) case object TaskObject object MatchCaseClass { def main(args: Array[String]): Unit = { val arr = Array(Task("itcast"), TaskObject) val value = arr((math.random * arr.size).toInt) println("value:" + value) Task("lisi") match { case Task(name) => println("name:" + name) case TaskObject => println("匹配样例对象") } } }
-
4. 偏函数 x=>y
-
把模式匹配这种形式 放在一个方法或者一个函数中,就构成一种新的函数,偏函数 //方法 def 名称(参数)= value math{ case 操作 } //函数 val 名称:PartialFunction[输入数据类型,返回值数据类型] ={ case 操作 } object Scala_20_pianhanshu { def intMatch(x: Int) = x match { case 10 => println("10") case 100 => 100 } def main(args: Array[String]): Unit = { intMatch(10) println(intMatch(100)) val matchFuncion: PartialFunction[Int, String] = { case x:Int => x + " is even" } println(matchFuncion(10)) } }
5.逆变协变非变
-
class Apple class RedApple extends Apple class Box[T] 问题:RedApple 是 Apple 子类, Box[RedApple] 是不是 Box[Apple]子类? 假如 Box[RedApple] 是 Box[Apple]子类 val box:Box[Apple] =new Box[RedApple] 则成立 ,反之则不成立 答案 :以上假设不成立 要想让以上操作成立 需要协变操作 RedApple extends Apple class Box[T] a=Box[Apple] b=Box[RedApple] class Box[+T] a=Box[Apple] b=Box[RedApple] class Box[-T] a=Box[Apple] b=Box[RedApple] 逆变:class Box[-T] val box:Box[RedApple] =new Box[Apple] class RedApple extends Apple class Box[+T] //协变 class Box2[-T] object Scala_21_fanxin { def main(args: Array[String]): Unit = { val box: Box[Apple] = new Box[RedApple] val box2:Box2[RedApple] =new Box2[Apple] } } Son extends Person 总结:a=ArrayList<Person> b=ArrayList<Son> C[T]: A是B的子类 C[A]与C[B]之间没有任何关系 非变操作 C[+T]: A是B的子类 C[A]是C[B]的子类 协变操作 C[-T]: A是B的子类 C[A]是C[B]的父类 逆变操作
6.上下界
-
上下界 影响的是方法的入参 U>:Class U 带表的class本身以及其父类 下界操作 下界限制不住 U<:Class U 代表class 本身以及其子类 上界操作 class OldMan extends Man class OldOldMan extends OldMan class YoungMan extends Man class ChildMan extends YoungMan class Sport { //添加下界操作 U 代表的是 OldMan 本身 或者是其父类 下界操作 其实是无下界 def guangchangwu[U >: OldMan](man: U): Unit = { println("广场舞") } //U 带表示 YoungMan本身以及其子类 def qiaodaima[U<:YoungMan](man: U): Unit = { println("敲代码") } } object Scala_22_upanddown { def main(args: Array[String]): Unit = { val sport = new Sport val yman = new YoungMan val oman = new OldMan val ooman=new OldOldMan val cman = new ChildMan /* sport.guangchangwu(yman) sport.guangchangwu(oman) sport.guangchangwu(ooman)*/ sport.qiaodaima(cman) } }
7.Scala并发操作
-
格式: class 线程类 extends Actor{ def act() } //启动线程 actor.start() /** * 创建线程类 MyActor */ class MyActor(val name: String) extends Actor { override def act(): Unit = { for (i <- 0 to 10) { println(name + ":" + i) } } } object Scala_23_actor { def main(args: Array[String]): Unit = { val itcast = new MyActor("itcast") val tian = new MyActor("Tian") itcast.start() tian.start() } }
-
Actor基于消息机制的并发
-
actor能够发送消息 和接受消息,根据消息的不同,去执行不同的业务逻辑
-
! 发送消息
-
class MyActor2 extends Actor { override def act(): Unit = { var flag = true while (flag) receive { //阻塞 case "start" => println("启动成功") //执行逻辑完成后自动退出 case "test" => println("测试") case "end" => { println("退出") flag = false } } } } object Scala_24_actor { def main(args: Array[String]): Unit = { val actor = new MyActor2() actor.start() actor ! "start" actor!"test" actor ! "end" } }
-
-
loop react
-
class MyActor2 extends Actor { override def act(): Unit = { loop{ react{ case "start"=>println("启动成功过") case "test" =>println("测试") case "end"=> exit() } } } } object Scala_24_actor { def main(args: Array[String]): Unit = { val actor = new MyActor2() actor.start() actor ! "start" actor!"test" actor ! "end" } }
-
-
Actor发送消息的三种方式
-
! 异步消息 并且无返回值
-
!? 同步消息 有返回值
-
!! 异步消息 有返回值 Future[Any]
-
同步消息和异步消息
- 同步消息:发送消息后,会等待消息执行逻辑完成,再返回
- 异步消息:发送消息后直接返回,返回会在将来的某一时刻返回 Future[Any]
-
总和案例:
- 结合样例类方式 来整同步消息和异步消息以及返回值
- 需要构建3个样例类
-
/** * 消息模型 * 同步消息 * 异步消息 * 返回值消息 */ case class SynMsg(msg: String, time: Long) case class AsynMsg(msg: String, time: Long) case class ReplayMsg(msg: String) class MsgActor extends Actor { override def act(): Unit = { loop { react { case SynMsg(msg, time) => { Thread.sleep(time) println(msg) sender ! ReplayMsg(msg + "---SynMsg-----") } case AsynMsg(msg, time) => { Thread.sleep(time) println(msg) sender ! ReplayMsg(msg + "---AsynMsg-----") } } } } } object Scala_25_actor { def main(args: Array[String]): Unit = { val actor = new MsgActor actor.start() /*val msg1=actor!?SynMsg("itcast",5000) val msg2=actor!?SynMsg("itcast2",1000) println(msg1) println(msg2)*/ val msg1 = actor !! AsynMsg("itcast", 5000) val msg2 = actor !! AsynMsg("itcast2", 1000) val list = ListBuffer[Future[Any]]() list += msg1 list += msg2 while (list.size > 0) { for (msg <- list) { //msg:Future[Any] //判断future是否接收到数据 if (msg.isSet) { //ture 代表已经接受到数据 val replayMsg = msg.apply().asInstanceOf[ReplayMsg] println(replayMsg.msg) list -= msg } } } } }
-
-
基于Actor的wordcount案例
-
操作步骤:
- 准备文件 a.txt b.txt c.txt
- 构建actor
- 通过样例类 构建消息的载体 case FilePathMsg(path:String)
- actor接受消息 并且处理文件数据
- 接受返回值 并且将返回值放在ListBuffer进行管理
- 对ListBuffer进行循环操作,拿到文件处理数据,把文件处理数据放在一个集合中
- 对集合进行汇总处理
-
package cn.itcast.day02 import java.io.File import scala.actors.{Actor, Future} import scala.collection.mutable.{ArrayBuffer, ListBuffer} import scala.io.Source /** * 样例类方式构建消息载体 */ case class FilePathMsg(path: String) //返回值消息 case class FileResultMsg(result: Map[String, Int]) class FileActor extends Actor { override def act(): Unit = { loop { react { case FilePathMsg(path) => { //读取文件数据 并且进行处理 Map val map: Map[String, Int] = Source.fromFile(new File(path)).getLines().mkString(",").split(",") .map((x: String) => (x, 1)).groupBy((t: Tuple2[String, Int]) => t._1) .mapValues((arr: Array[Tuple2[String, Int]]) => arr.length) sender ! FileResultMsg(map) } } } } } object Scala_26_wordcount { def main(args: Array[String]): Unit = { val arr = Array("d:/a.txt", "d:/b.txt", "d:/c.txt") val list = ListBuffer[Future[Any]]() //数组封装了每个文件处理好的数据 val fileDataArray = ArrayBuffer[Map[String, Int]]() for (path <- arr) { val actor = new FileActor actor.start() //发送异步消息 并且接受到每个文件的处理结果 val result = actor !! FilePathMsg(path) list += result } while (list.size > 0) { for (f <- list if f.isSet) { val fileResult = f.apply().asInstanceOf[FileResultMsg] fileDataArray += fileResult.result list -= f } } val wordcount = fileDataArray.flatten.groupBy((t: Tuple2[String, Int]) => t._1) .mapValues((arr: ArrayBuffer[Tuple2[String, Int]]) => { arr.map((t: Tuple2[String, Int]) => t._2).sum }) println(wordcount) } }
-