Scala面向对象编程
1.课程目标
- 掌握Scala中面向对象编程
- 掌握Scala模式匹配
- 掌握Scala泛型高级内容
- 熟悉Scala中多线程编程模型
2.Scala 类
-
格式: class 名称{ //成员变量 //成员方法 } 使用类: 创建对象: val/var 对象名称=new 类名() //可写可不写 小括号中没有任何参数的时候 可以不用写 package cn.itcast.day02 import scala.collection.mutable.ArrayBuffer /** * 构建类 * Person * 属性: name 不可变的 * age 是可变的 * skill 数组类型的 * 方法: * add 添加技能 * scanSkill 遍历技能 */ class Person { // name val name = "itcast" var age = 10 val skill = new ArrayBuffer[String]() def add(str: String): Unit = { skill += str } def scanSkill(): Unit = { skill.foreach(x => println(x)) } } object Scala_day02_class { def main(args: Array[String]): Unit = { val person = new Person println(person.name) println(person.age) person.add("scala") person.scanSkill() } } 注意: 1.class 没有public修饰,默认的权限都是public 2.类名和文件名称没有任何关系
3.scala中setter getter
-
package cn.itcast.day02 /** * 类 Student */ class Student { //val 修饰的属性 只有getter权限 没有setter val name = "itcast" //var 属性既有getter 也有setter private var _age: Int = 10 //age的setter 方法 def age_(age: Int): Unit = { this._age = age } //age getter方法 def age = this._age } object Scala_day02_gettersetter { def main(args: Array[String]): Unit = { val student = new Student println(student.name) student.age_(30) println(student.age) //访问的是age的getter方法 } } 注意: getter方法中 省略了小阔号 省略了返回值类型。 方法定义原则: 当一个方法只返回数据,而不进行业务逻辑处理的时候 此时 方法名称后()可以省略。可以理解成 通过def 定义一个属性。
- Scala中方法调用原则
-
/** * 类 Teacher * 方法 teach 参数 是String */ class Teacher { def teach(suject: String): Unit = { println(s"教学$suject") } def teach2(suject: String = "java"): Unit = { println(s"教学$suject") } } object Scala_day02_method { def main(args: Array[String]): Unit = { val teacher = new Teacher teacher.teach("scala") teacher teach ("scala") teacher teach "scala" teacher.teach2() } } 注意:0 to 10 ,0 unitl 10 , 1 + 2 1.“对象 方法名称 参数” 这种调用形式:这种调用形式 只适用于一个参数的方法 2.方法的参数可以指定默认值,并且在方法调用的时候 省略参数的传递
5.Scala中构造函数
-
第一种: 主构造器 定义格式: class 类名(参数列表*) 第二种: 辅助构造器 定义在类的内部 定义格式: def this(参数列表){ //第一行代码必须是调用主构造器 this(参数列表) //class 类名(参数列表*) } package cn.itcast.day02 /** * Animal */ class Animal(val name: String) { var color: String = _ var legs: Int = _ //创建辅助构造器 def this(name: String, color: String, legs: Int) { this(name) this.color = color this.legs = legs } //创建多个辅助构造器 def this() { this("itcast") } } object Scala_day02_constructor { def main(args: Array[String]): Unit = { val animal = new Animal() //辅助构造器 val cat = new Animal("cat") //调用主构造器 val cat2 = new Animal("cat", "yellow", 4) //辅助构造器 println(cat) println(cat2) println(animal) println(animal.name) } } 注意: 主构造器有且仅有一个 辅助构造器可以具有多个 主构造器中参数 如果有val 修饰的话 public 如果有没有 private
- scala中单例对象
-
单例对象: 在一个应用程序中,一个类只会创建一个对象,那么这个对象就是单例对象 格式:object 对象名称{ //成员变量 //成员方法 } 使用: 对象名称.成员变量 对象名称.成员方法 这种操作 类似于java中static 修饰的方法和属性的调用 使用场景: 主要最为工具对象使用 类似于java中 Util类 object CommonUtil { val SUNDAY = "sunday" def getDate = new Date } object Scala_day02_object { def main(args: Array[String]): Unit = { println(CommonUtil.SUNDAY) println(CommonUtil.getDate) } }
7.伴生对象和伴生类
-
如果一个object (单例对象)的名称与一个class (类)的名称相同,并且object和class 在同一个文件之下,那么则这个objec 就称之为 class的伴生对象,这个class 就称之为对象伴生类 //创建类 SoftEngineer /** * 互为伴生的类与对象之间功能: * 1.能够互相访问其私有的变量和私有方法 * 2.在伴生对象中有一个特殊方法 apply.通过apply方法 可以实现伴生对象的构造函数功能 */ class SoftEngineer(val exp: Int) { private val salary = "15K" def getHasHair = SoftEngineer.hasHair } object SoftEngineer { private val hasHair = true def getSalary = { val engineer = new SoftEngineer(10) engineer.salary } def apply(exp: Int): SoftEngineer = new SoftEngineer(exp) } object Scala_day02_objectclass { def main(args: Array[String]): Unit = { val engineer = SoftEngineer(10) println(engineer.getHasHair) } }
- main函数
-
1.main 作为入口函数 存在于object中 2.scala中 提供了另外一种代替main函数的方法 extends App object Scala_day02_main extends App { println("helloword") println(args(0)) //通过args 变量接受初始化参数 }
9.scala中继承关系
-
继承关系 功能跟java一致 class 父类 继承: class 子类 extends 父类 1.子类可以继承父类非私有的属性和方法 2.子类可以覆盖父类的方法和属性 注意: var修饰的变量不能够被覆盖 可以直接修改,并且不能够使用super关键词直接调用 3.子类可以访问父类的方法和属性 super关键词 4.父类的引用可以指向子类的对象 5.scala中继承关系也是单继承 class BigDataEngineer { val exp: Int = 10 var name: String = "bigdata" def word(): Unit = { println("开发项目") } } class HadoopEngineer extends BigDataEngineer { override val exp: Int = 15 name="hadoop bigdata" override def word(): Unit = { // println(super.name) super.word() println("开发hadoop项目") } } object Scala_day02_extends { def main(args: Array[String]): Unit = { val engineer = new HadoopEngineer println(engineer.exp) println(engineer.name) engineer.word() //父类的引用指向子类的对象 val engineer2: BigDataEngineer = new HadoopEngineer println(engineer2.exp) } } 问题:子类在创建对象的时候,有没有创建父类的对象? 有的,子类创建对象的时候创建了父类的对象,子类的构造函数调用了父类的构造函数。
10.scala构造机制
-
构造机制:直接在类中执行的一些语句,在构造函数调用完毕之后直接执行构造语句 class 类名{ println("sfsdfsd") //构造语句 } class Parent{ println("我是父类") } class Son extends Parent { println("我是子类") } object Scala_day02_constructor2 { def main(args: Array[String]): Unit = { val son = new Son } }
11.子类调用父类有参的构造函数
-
第一种情况: 子类构造函数无参 父类的构造函数有参数 第二种情况: 子类构造函数有参数 父类构造函数也有参数 class PC(val memory: Int, val cpu: Int) //子类 class NotePC extends PC(memory = 100, cpu = 100) class DeskPC(memory: Int, cpu: Int) extends PC(memory, cpu) object Scala_day02_parent { def main(args: Array[String]): Unit = { val c = new NotePC println(c.memory) println(c.cpu) val dc = new DeskPC(120, 130) println(dc.memory) println(dc.cpu) } }
12.抽象类
-
定义: abstract class 类名{ 1.可以定义抽象方法和非抽象方法 2.定义抽象属性和非抽象属性 3.子类可以覆盖抽象父类的方法和属性 4.如果一个方法 使用final修饰,子类不能够覆盖 } abstract class AbstractClass(val name: String) { val exp: Int //抽象属性 def work(): Unit //抽象方法不能省略返回值类型 } class AbstractClassImpl(name: String) extends AbstractClass(name) { override val exp: Int = 10 override def work(): Unit = { println("工作") } } object Scala_day02_abstract extends App { private val impl = new AbstractClassImpl("itcast") println(impl.exp) impl.work() }
13.匿名内部类
-
匿名内部类: 匿名内部类一般都具有临时性, 在使用过程中一般解决临时性的需求。 object Scala_day02_innerclass { def main(args: Array[String]): Unit = { val engineer = new ScalaEngineer println(engineer.getClass) val scalaEngineer = new ScalaEngineer { def workByPython(): Unit = { println("开发python项目") } } scalaEngineer.workByPython() println(scalaEngineer.getClass) } }
14.强制类型转换
-
子类继承父类以后,父类的引用可以指向子类的对象,此时父类的引用具备了多态特性。多态会引起一个比较种问题,就是类型转换异常。 语法: 对象.asInstanceOf[Class类名] 判断类型: 对象.isIntanceOf[Class类名] //模糊形式的判断 对象.getClass==classOf[类名] //精准判断 class Employee { def work(): Unit = { println("工作") } } class ItEmployee extends Employee { def workByPc(): Unit = { println("开发IT项目") } } object Scala_day02_TypeTransaction { def boss(employee: Employee): Unit = { // employee.work() if (employee.getClass == classOf[Employee]) { val itEmployee = employee.asInstanceOf[ItEmployee] itEmployee.workByPc() } else { println("回家等消息") } } def main(args: Array[String]): Unit = { val employee: Employee = new ItEmployee boss(employee) } }
- 超类
-
Any: 所有类型的超级父类 AnyRef: 所有引用类型的超级父类 Person 、Map List AnyVal: 所有值类型的超级父类 (Int、Double、Float)
16.特质
-
特质功能: 1.特质可以作为java中接口使用 2.特质可以作为抽象类使用 3.特质可以扩展对象的方法功能 4.特质可以改造某个特定的方法功能 格式: trait 名称 { 内容 } - 特质可以作为java中接口使用
-
package cn.itcast.day02 /** * 特质可以作为java中接口使用 * 1.可以定义抽象方法 * 2.接口可以多实现 * 3.接口可以继承其他接口 * with:混入特质 */ trait HelloTrait { def sayHelloByChinese(): Unit } trait HelloTrait2 { def sayHelloByHanguo(): Unit } trait HelloTrait3 extends HelloTrait2 { def sayHelloByTaiguo(): Unit } class HelloTraitImpl extends HelloTrait with HelloTrait3 { override def sayHelloByChinese(): Unit = { println("你好") } override def sayHelloByTaiguo(): Unit = { println("萨瓦迪卡") } override def sayHelloByHanguo(): Unit = { println("阿尼哈撒有") } } object Scala_day02_trait1 { def main(args: Array[String]): Unit = { val impl = new HelloTraitImpl impl.sayHelloByChinese() impl.sayHelloByHanguo() impl.sayHelloByTaiguo() val impl2:HelloTrait3=new HelloTraitImpl impl2.sayHelloByHanguo() } }
-
- 特质可以作为抽象类使用
-
/** * 特质可以作为抽象类使用 * 1.定义抽象方法 * 2.定义抽象属性 * 3.定义非抽象方法和属性 */ trait AbstactTrait { val name: String val age = 10 def work(): Unit = { println("工作") } } class AbstactTraitImpl extends 类名 with AbstactTrait with 特质 { override val name: String = "itcast" override def work(): Unit = { println("覆盖方法") } } object Scala_day02_trait2 { def main(args: Array[String]): Unit = { val impl = new AbstactTraitImpl() println(impl.name) println(impl.age) impl.work() } } 与抽象类区别: 抽象类可以有构造函数,特质没有构造函数 抽象类是单继承的 特质是多混入的
-
- 特质可以扩展对象的方法功能
-
格式: val 名称=new 类名 with 特质 with 特质 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_day02_trait3 { 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() } }
-
- 特质可以改造某个特定的方法功能
-
class SuperTank { def shoot(): Unit = { println("发射") } } trait SuperAim extends SuperTank { override def shoot(): Unit = { println("瞄准") super.shoot() } } trait SuperScan extends SuperTank { override def shoot(): Unit = { println("扫描") super.shoot() } } //执行顺序是自右向左过程 class SuperTankSon extends SuperTank with SuperScan with SuperAim object Scala_day02_trait4 { def main(args: Array[String]): Unit = { val son = new SuperTankSon son.shoot() } } super:线性调用的关键词。并且super线性跟混入特质的顺序有关,并且线性调用是自右向左过程
-
17.模式匹配
-
语法: 选择器 match { case 匹配值 => { 业务逻辑 } case 匹配值 => { 业务逻辑 } case 匹配值 => { 业务逻辑 } case _ =>{默认业务逻辑} } 注意: case 一旦匹配上,直接返回,则不会贯穿到下一个case 模式匹配的数据类型: 值 数据类型 数组 列表 tuple map 自定义数据类型 - scala中模式匹配之 值匹配
-
object Scala_day01_match { def main(args: Array[String]): Unit = { val arr=Array[String]("hadoop","scala","spark","java") // val value=arr((math.random*arr.size).toInt) val value="flink" println(s"value:$value") /** * 选择器 match { * case 匹配值 => { 业务逻辑 } * case 匹配值 => { 业务逻辑 } * case 匹配值 => { 业务逻辑 } * case _ =>{默认业务逻辑} 能够匹配一切数据以及一切数据类型 * } */ value match { case "hadoop" => println("匹配上hadoop") case "scala" => println("匹配上scala") case "spark" => println("匹配上spark") case "java" => println("匹配上java") //默认匹配 case _ =>println("默认匹配") } } }
-
- 模式匹配匹配数据类型
-
/** * 模式匹配 匹配数据类型 */ object MatchDataType { def main(args: Array[String]): Unit = { val arr = Array("hadoop", 3.14, 10, true) val value = arr((math.random * arr.size).toInt) println(s"value:$value") value match { case s: String => println(s"string:$s") case d: Double => println(s"double:$d") case i: Int => println(s"int:$i") case b: Boolean => println(s"boolean:$b") } } }
-
- 匹配Array
-
// 模式匹配 Array object MatchArray { def main(args: Array[String]): Unit = { val arr = Array[String]("spark", "hadoop", "scala") arr match { case Array(_*) =>println("sfsddsfsfsdfdsffs") //可以匹配所有Array //模糊匹配 case Array(x,y,_*) => println(x+"\t"+y+" 模糊匹配") case Array("spark",_*) => println("模糊匹配") //占位符匹配 case Array(x,_,_) =>println(x+" 占位符匹配占位符匹配") case Array("spark",_,_) => println("占位符匹配占位符匹配") //精确匹配 case Array(x,y,z) =>println(x+"\t"+y+"\t"+z) case Array("spark", "hadoop", "scala") => println("精确匹配") } } }
-
- 模式匹配List
-
// 模式匹配 List object MatchList { def main(args: Array[String]): Unit = { val list = List("hadoop", 10, 3.25) list match { case List(_*) => {} //能够匹配所有的LIst case "hadoop"::10::3.25::Nil =>println(2) 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", 10, 3.25) => println("1") } } }
-
- 匹配Tuple
-
// 模式匹配 Tuple object MatchTuple { def main(args: Array[String]): Unit = { val tuple = (3.14, "hadoop", 100) tuple match { case (x, y, _) => println(x + "\t" + y) case (x, y, z) => println(x + "\t" + y + "\t" + z) } } }
-
- 匹配map
-
// 模式匹配 Map object MatchMap { def main(args: Array[String]): Unit = { val map = Map("hadoop" -> 10, "scala" -> 20) val value = map.get("java") value match { case Some(x) => println("x:" + x) case None => println("None") } } }
-
- 匹配自定义数据类型
-
注意:这种匹配方式在spark框架中 普遍存在,应用非常广泛 两种自定义数据类型 样例类 case class 类名称(参数列表) 样例对象 case object 对象名称 //样例类 case class Test(name: String, age: Int) //样例对象 case object TestObject object MatchSelfDataType { def main(args: Array[String]): Unit = { val arr = Array(Test("itcast", 13), TestObject) val value = arr((math.random * arr.size).toInt) println(s"value:$value") value match { case Test(name, age) => println(name + "\t" + age) case TestObject => println("匹配上样例对象") } } }
-
18.偏函数
-
偏函数:函数体 或者方法体 是一个模式匹配的形式 def 名称(参数) = 参数 match { case 匹配 } val 名称: PartialFunction[输入数据类型, 输出数据类型] ={ case 匹配 } /** * def 名称(参数) =参数 match { * case 匹配 * } */ def getInt(x: Int): Int = x match { case 10 => 10 * 10 case 20 => 20 * 20 * 20 case _ => 1000 } def main(args: Array[String]): Unit = { val i = getInt(30) println(i) } val fun: PartialFunction[Int, String] = { case 10 => String.valueOf(10 * 10) case 20 => String.valueOf(20 * 20 * 20) case _ => String.valueOf(1000) } val str = fun(20) println(str)
19.泛型的逆变协变和非变
-
规定类的泛型操作 class 类名[T] //非变 class 类名[+T] //泛型协变操作, 协变操作使得泛型之间有了与类相关的继承关系 class 类名[-T] //逆变操作 class Apple class RedApple extend Apple class Box[T] //泛型类 问题: RedApple 是 Apple子类, Box[RedApple] 会不会是 Box[Apple]子类? 假设: Box[RedApple] 是 Box[Apple] 子类的话,那么 val box:Box[Apple]=new Box[RedApple] 成立 答案 :假设不成立 总结: C[T]:如果B是A的子类 ,那么 C[A]与C[B] 没有关系 非变操作 C[+T] :如果B是A的子类 那么 C[A]是C[B] 的父类 协变操作 C[-T]:如果B是A的子类 那么C[A]是C[B]的子类 逆变操作
20.方法参数的上下界操作
-
通过规定方法参数的上下界操作,可以规定方法参数的传入过程 U>:A : 下界操作 U是A类本身或者其父类 S<:A : 上界操作 S是A类本身或者其子类 class Man class OldMan extends Man class OOldMan extends OldMan class YoungMan extends Man class YYoungMan extends YoungMan class Sport { //适合老年人方法 规定方法下界 U 是oldMan的本身或者是其父类 def taiji[U >: OOldMan](man: U): Unit = { println("打太极") } //适合年轻人 规定上界 S:代表的是YoungMan本身 或者其子类 def bengji[S <: YoungMan](youngMan: S): Unit = { println("年轻人游戏") } } object Scala_day02_upanddown { def main(args: Array[String]): Unit = { /* val yman = new YoungMan val yyman = new YYoungMan val sport = new Sport sport.bengji(yyman)*/ val sport = new Sport val man = new Man val yyman = new YYoungMan val oldMan = new OldMan //val ooldMan = new OOldMan sport.taiji(yyman) } } class SuperBox[+T, -S] { //上界方法 def up[P <: S](s: S): Unit = { } def down[D >: T](d: D): Unit = { } }
21.Scala中并发编程
-
并发编程:继承Actor类 实现actor中 act方法 class Myactor(val name: String) extends Actor { override def act(): Unit = { for (i <- 0 to 20) { println(name + "--------" + i) } } } object Scala_day02_actor { def main(args: Array[String]): Unit = { val myactor = new Myactor("myactor1") val myactor2 = new Myactor("myactor2") myactor.start() //启动线程 myactor2.start() } } -
Actor消息机制
-
发送消息:actor!消息内容 接受消息:act方法中 通过创建recevier 方法接受消息 class MyActor2 extends Actor { override def act(): Unit = { var flag = true while (flag) { receive { case "start" => println("启动线程") case "test" => println("测试") case "end" => flag = false } } } } object Scala_day02_Actor2 { def main(args: Array[String]): Unit = { val actor = new MyActor2 actor.start() actor ! "start" actor ! "test" actor ! "end" actor ! "test" } } 注意:while receiver 这种形式 每次接收消息的都会创建receiver对象。 loop react机制 代理while receiver机制 提高程序运行效率 class MyActor3 extends Actor { override def act(): Unit = { var flag = true loop { react { case "start" => println("启动线程") case "test" => println("测试") case "end" => exit() //退出当前进程 } } } }
-
-
actor发送消息的方式
-
!:发送异步消息 并且无返回值 !?: 发送同步消息 并且有返回值 返回值的类型就Any !! :发送异步消息 并且有返回值 Future[Any] 同步消息: 发送完消息以后,需要等待消息执行完成返回数据 异步消息: 发送完消息以后,直接返回,通过Future[Any]对象在未来的某一时刻拿到异步消息的返回值 /** * actor 发送同步和异步消息 * 消息形式:样例类 */ case class SynsMsg(id: Int, msg: String, times: Long) case class AsynMsg(id: Int, msg: String, times: Long) case class ReplayMsg(id: Int, msg: String) class MsgActor extends Actor { override def act(): Unit = { loop { react { case SynsMsg(id, msg, times) => { Thread.sleep(times) println(id + "\t" + msg) //发送返回值 sender ! ReplayMsg(id, msg + "----replay") } case AsynMsg(id, msg, times) => { Thread.sleep(times) println(id + "\t" + msg) sender ! ReplayMsg(id, msg + "----Asyn---replay") } } } } } object Scala_day02_actor3 { def main(args: Array[String]): Unit = { val actor = new MsgActor actor.start() /* val replayMsg: Any = actor !? SynsMsg(2, "黑马", 1000) println(replayMsg.asInstanceOf[ReplayMsg].msg) val replayMsg2 = actor !? SynsMsg(1, "itcast", 2000) println(replayMsg2.asInstanceOf[ReplayMsg].msg)*/ val list = new ListBuffer[Future[Any]]() val futurnReplayMsg: Future[Any] = actor !! AsynMsg(3, "传智播客", 10000) val futurnReplayMsg2: Future[Any] = actor !! AsynMsg(4, "黑马程序员", 1000) list += futurnReplayMsg list += futurnReplayMsg2 while (list.size > 0) { for (future <- list) { //判断future中是否存在数据 if (future.isSet) { val msg: Any = future.apply() //拿到future中数据 val tmpReplayMsg = msg.asInstanceOf[ReplayMsg] println(tmpReplayMsg.msg) //将已经完成任务future剔除掉 list -= future } } } } }
-
22.基于Actor的wordcount案例
- 需求:用 actor 并发编程写一个单机版的 WordCount,将多个文件作为输入,计算完成后将多 个任务汇总,得 到终的结果
- 实现步骤:
- 编写读取文件,并统计文件wordcount的工具方法
- 实现actor 并通过loop react方式接受消息。利用 case class 样例类去匹配对应的操作 并将结果封装在样 例类中,通过sender返回消息
- 主线程汇总返回结果,并将结果保存在ListBuffer中
- 后对ListBuffer中数据进行全局汇总
- 汇总完成后,关闭所有线程
-
package cn.itcast.day02 import java.io.File import scala.actors.{Actor, Future} import scala.collection.mutable.ListBuffer import scala.io.Source /** 需求:3个文件 * 启动三个Actor 读取3个文件 * 对这个三个文件中数据 word 进行统计 * 打印统计结果 * */ case class Task(filePath: String) case class ReplayMap(map: Map[String, Int]) class MyActor28 extends Actor { override def act(): Unit = { loop { react { case Task(filePath) => { val lines = Source.fromFile(new File(filePath)).getLines().toArray val groupByResult = lines.flatMap(x => x.split(",")).map(w => (w, 1)).groupBy(t => t._1) val result = groupByResult.mapValues(a => a.length) sender ! ReplayMap(result) } } } } } object Scala_28_Actor { def main(args: Array[String]): Unit = { val arr = Array("D://a.txt", "D://b.txt", "D://c.txt") //装返回数据 val list = new ListBuffer[Future[Any]]() val listReplayMap = new ListBuffer[ReplayMap]() for (filePath <- arr) { val a = new MyActor28() a.start() val result = a !! Task(filePath) list += result } //循环遍历接受数据的操作 while (list.size > 0) { val flterReulst = list.filter(f => f.isSet) for (f <- flterReulst) { listReplayMap += f.apply().asInstanceOf[ReplayMap] list -= f } } val groupByListBuffer = listReplayMap.map(rm => rm.map).flatten.groupBy(t => t._1) //lb 是listBuffer (tuple) val wordcount = groupByListBuffer .mapValues(lb => { //t 代表 tuple // map(t=>t._2) 拿到tuple的value数据 // x:初始值 0 y 第一个元素 lb.map(t => t._2).reduce((x, y) => x + y) }) for (wc <- wordcount) { println(wc) } } }
浙公网安备 33010602011771号