文章目录
scala 进阶
1.高阶函数
1.1 函数作为参数的函数
使用函数作为参数的方法,叫做高阶函数
package cn.itcast.scala
object ScalaTest {
val myFunc = (x:Int) => {x*x}
def main(args: Array[String]): Unit = {
//方法就是函数,函数就是对象
def myMethod(x:Int) : Int = {x*x}
val myArray0 = Array(1,3,5,7,9).map(x => x*x) //匿名函数优先使用
val myArray1 = Array(1,3,5,7,9).map(myFunc) //函数就是对象
val myArray2 = Array(1,3,5,7,9).map(myMethod) //方法就是函数
val myArray3 = Array(1,3,5,7,9).map(_*2) //实现不了x*x功能 因为x出现了两次,不能使用_
println(myArray0.mkString(","))
println(myArray1.mkString(","))
println(myArray2.mkString(","))
println(myArray3.mkString(","))
}
}
1.2 匿名函数
package cn.itcast.scala
object ScalaTest {
def main(args: Array[String]): Unit = {
println((x:Int,y:Int) => {x * y}) // <function2>
}
}
1.3 高阶函数
package cn.itcast.scala
object ScalaTest {
def main(args: Array[String]): Unit = {
val func3: (String, Int) => (Int, String) = {
(x, y) => (y + 2, x + "hello")
}
//函数匿名函数定义第二种方式
def myMethod3(hello: (String, Int) => (Int, String)): Int = {
val result: (Int, String) = hello("zhangsan", 60)
result._1
}
val result: Int = myMethod3(func3)
println(result) //62
}
}
1.4 高阶函数作为方法的返回类型
package cn.itcast.scala
object ScalaTest {
def main(args: Array[String]): Unit = {
def myFunc4(x:Int) = {
//方法的最后返回值,是一个匿名函数
(y:String) =>y + x
}
println(myFunc4(50)) //<function1>
}
}
1.5 参数类型推断
package cn.itcast.scala
object ScalaTest {
def main(args: Array[String]): Unit = {
val myArray = Array(1, 3, 5, 7, 9)
//map当中需要传入一个函数,我们可以直接定义一个函数
println(myArray.map((x: Int) => x * 2).mkString("==="))
//进一步化简 参数推断省去类型信息
println(myArray.map((x) => x * 2).mkString("==="))
//进一步化简 单个参数可以省去括号
println(myArray.map(x => x * 2).mkString("==="))
///进一步化简 如果变量只在=>右边只出现一次,可以用_来代替
println(myArray.map(_ * 2).mkString("==="))
/*
2===6===10===14===18
2===6===10===14===18
2===6===10===14===18
2===6===10===14===18
*\
}
}
1.6 闭包与柯里化
package cn.itcast.scala
object ScalaTest {
def main(args: Array[String]): Unit = {
//柯里化 柯里化定义形式
def kery(x:Int)(y:Int) = {
x+y
}
val kery1: Int = kery(30)(20)
println(kery1)
val keryResult = (x:Int) => { (y:Int) => {x + y} } // 函数的返回值也是函数,为function1,切内部是匿名函数无法赋值
println(keryResult(10))
//keryResult(20) => 匿名函数 y:Int => 20+y <=1,3,5,7,9
val myArray = Array(1,3,5,7,9).map(keryResult(20))
println(myArray.toBuffer) //ArrayBuffer(21, 23, 25, 27, 29)
//柯里化推导过程
//柯里化守门Scala面向函数式编程,必然导致的一种结果
def keryMethod(x:Int) = {
//里面的函数,将外面的参数包起来了,叫做闭包
y:Int => {y + x}
}
keryMethod(30)
}
}
总结:
函数可以用来干嘛 ==>
1.作为一个方法的参数
2.函数可以直接调用
3.匿名函数无法调用,但是可以通过作为一个方法的参数进行赋值调用如
Array(1,3,5,7,9).map(keryResult(20))
2.scala当中的类
2.1 类的定义与创建
package cn.itcast.scala
class Person {
//定义一个属性,叫做name的,使用val不可变量来进行修饰
// 用val修饰的变量是可读属性,有getter但没有setter(相当与Java中用final修饰的变量)自动生成该方法
val name:String ="zhangsan"
//定义一个属性,叫做age的,使用var可变量来进行修饰
//用var修饰的变量都既有getter,又有setter 自动生成该方法
var age:Int = 28
//类私有字段,只能在类的内部使用或者伴生对象中访问
private val address:String = "地球上"
//类私有字段,访问权限更加严格的,该字段在当前类中被访问
//在伴生对象里面也不可以访问
private[this] var pet = "小强"
//在类当中定义了一个方法,
def hello(first:Int,second:String):Int ={
println(first+"\t"+second)
250
}
/**
* 定义了一个函数
*/
val func1 =(x:Int,y:Int) =>{
x+y
}
}
2.2 类的实例化以及使用
package cn.itcast.scala
object ScalaClass {
def main(args: Array[String]): Unit = {
val person = new Person
val name: String = person.name
val age: Int = person.age
println(name)
println(age)
person.age = (30) // 这个是赋值操作
person.age= (30) // age= 这个是set方法,.age=连起来写
person.age_= (80) // age_= 这个是set方法
println(person.age) //这个是get方法
//调用函数或者方法
val hello: Int = person.hello(120,"zhangsan")
println(hello) //250
}
}
2.3 属性的getter和setter方法
对于scala类中的每一个属性,编译后,会有一个私有的字段和相应的getter、setter方法生成
//getter方法
println(person age)
//setter方法
println(person age_= (18))
//getter方法
println(person.age)
当然了,你也可以不使用自动生成的方式,自己定义getter和setter方法
class Dog2 {
private var _leg = 4
def leg = _leg
def leg_=(newLeg: Int) {
_leg = newLeg
}
}
使用之:
val dog2 = new Dog2
dog2.leg_=(10)
println(dog2.leg)
规范提示:自己手动创建变量的getter和setter方法需要遵循以下原则:
1) 字段属性名以“_”作为前缀,如:_leg
2) getter方法定义为:def leg = _leg
3) setter方法定义时,方法名为属性名去掉前缀,并加上后缀,后缀是:“leg_=”,如例子所示
2.4 类的构造器
scala当中类的构造器分为两种:主构造器和辅助构造器
主构造器,写在类名后面的。
所有的辅助构造器,在最后都必须调用另外一个构造器
java中构造器
public class Dog{
public Dog(String name,int age){
System.out.println("xxxx")
}
}
new Dog("小黑",3)
scala中构造器
package cn.itcast.scala
/**
* 顶一个class类,主构造器带有两个参数,name与age
*
* @param name
* @param age
* scala的执行代码,可以直接将代码写在class当中,而在java当中不能
* 在Scala编译之后成为.class文件
* 将我们所有class类的代码都进入到了主构造器方法当中去了
* class Dog{
* public Dog(String name,int age){
* println(name)
* println(age)
* }
* }
*/
class Dog(name: String, age: Int) {
println(name)
println(age)
var gender: String = "";
//使用this关键字来定义我们的构造器
def this(name: String, age: Int, gender: String) {
//每个辅助构造器,都必须以其他辅助构造器,或者主构造器的调用作为第一句
this(name: String, age: Int)
this.gender = gender
}
var color = "";
/**
* 我们也可以通过private来进行修饰我们的构造器,
*
* @param name
* @param age
* @param color
* @param gender
*/
private def this(name: String, age: Int, color: String, gender: String) {
this(name: String, age: Int)
this.color = color
}
}
两个构造器之间不能相互调用
Object通过构造器生成对象
package cn.itcast.scala
object ScalaClass {
def main(args: Array[String]): Unit = {
val xiaoheigou: Dog = new Dog("xiaoheigou",3)
val xiaobaigou: Dog = new Dog("xiaobaigou",2,"cixing")
}
}
3.scala当中的对象
Java中
public class MyClass{
public static String name="xxx"
public String age = "xxx"
public static String getMyAge(){
return "xxx"
}
public int getMyRealAge(){
return 1;
}
}
Scala中
将static和非static的东西全部分开
object 里面装的东西,可以简单的认为,全部都是static修饰的
class里面装的东西,可以简单的任务,全部都是没有static修饰的(需要new对象)
3.1 scala当中的Object
定义一个class类,然后在class类当中定义一个Object的对象。object对象当中的所有属性或者方法都是静态的
class Session {
def hello(first:Int):Int={
println(first)
first
}
}
object SessionFactory{
val session = new Session
def getSession():Session ={
session
}
def main(args: Array[String]): Unit = {
for(x <- 1 to 10){
//通过直接调用,产生的对象都是单列的
val session = SessionFactory.getSession()
println(session)
}
}
}
3.2 伴生类与伴生对象
特点:
1.如果有一个class文件,还有一个与class同名的object文件,那么就称这个object是class的伴生对象,
class是object的伴生类;
2.伴生类和伴生对象必须存放在一个.scala文件中;
3.伴生类和伴生对象的最大特点是,可以相互访问;
package cn.itcast.scala
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
//访问私有的字段name
p.name = "123"
p.printName()
}
}
3.3 scala当中的apply方法
通过apply方法来创建我们的伴生类,可以通过伴生对象来创建我们的伴生类,更加简洁和方便
package cn.itcast.scala
//定义一个class类,主构造器传入一个参数
class ApplyClassObject(name:String) {
println(name)
}
object ApplyClassObject{
def apply(name: String): ApplyClassObject = new ApplyClassObject(name)
def main(args: Array[String]): Unit = {
val applyClassObject1 = new ApplyClassObject("张三")
//通过伴生对象,直接调用调用apply方法,来获取我们的一个伴生类
val applyClassObject2 = ApplyClassObject("lisi")
//底层还是调用了apply方法,apply还是new Array[Int](5)
val myArray = Array(1,2,3,4,5)
val myArray2 = new Array[Int](10)
myArray2(0)=1
myArray2(1)=2
}
}
3.4 scala当中的main方法
//1.在object中定义main方法
object Main_Demo1 {
def main(args: Array[String]) {
if(args.length > 0){
println("Hello, " + args(0))
}else{
println("Hello World1!")
}
}
}
//2.使用继承App Trait ,将需要写在 main 方法中运行的代码
// 直接作为 object 的 constructor 代码即可,
// 而且还可以使用 args 接收传入的参数。
object Main_Demo2 extends App{
if(args.length > 0){
println("Hello, " + args(0))
}else{
println("Hello World2!")
}
}
3.5 枚举
java
public enum{
HELLO,ABC,123
}
enum.HELLO
public static String CONSTANTS = "HELLO"
Scala中没有枚举类型,但是我们可以通过定义一个扩展Enumeration类的对象,并以value调用初始化枚举中的所有可能值:
4.scala当中的继 承
4.1 Scala中继承(extends)的概念
ackage cn.itcast.extends_demo
class Person {
val name="super"
def getName=this.name
}
class Student extends Person{
//继承加上关键字
override
val name="sub"
//重写方法
override def getName = "xxx"
//子类可以定义自己的field和method
val score="A"
def getScore=this.score
}
4.2 Scala中override 和 super 关键字
class Person1 {
private val name = "leo"
val age=50
def getName = this.name
}
class Student1 extends Person1{
private val score = "A"
//子类可以覆盖父类的 val field,使用override关键字
override
val age=30
def getScore = this.score
//覆盖父类非抽象方法,必须要使用 override 关键字
//同时调用父类的方法,使用super关键字
override def getName = "your name is " + super.getName
}
4.3 Scala中isInstanceOf 和 asInstanceOf
Class B extends Class A
Class C
B instanceOf A => 如果判断 B是A的子类 (A)B
C instanceOf A ==> 如果判断 C不是A的子类 不能强转
java当中只提供了子类判断是否属于父类形 instanceOf,没有提供判断一个类是否是另外一个类的父类的方法
isInstanceOf 和 asInstanceOf
isInstanceOf ==> 类似于java当中的instanceOf 做类型判断的
如果 判断 A isInstanceOf B 为true
可以调用 A asInstanceOf B 做类型转换
asInstanceOf ==> 做类型转换的
package cn.itcast.extends_demo
class Person3 {}
class Student3 extends Person3
object Student3{
def main (args: Array[String] ) {
val p: Person3 = new Student3
var s: Student3 = null
//如果对象是 null,则 isInstanceOf 一定返回 false
println (s.isInstanceOf[Student3])
// 判断 p 是否为 Student3 对象的实例
if (p.isInstanceOf[Student3] ) {
//把 p 转换成 Student3 对象的实例
s = p.asInstanceOf[Student3]
}
println (s.isInstanceOf[Student3] )
}
}
4.4 Scala中getClass 和 classOf
传入一个Any 是不是Array 这一个确定的类型
package cn.itcast.extends_demo
class Person4 {}
class Student4 extends Person4
object Student4{
def main(args: Array[String]) {
val p:Person4=new Student4
//判断p是否为Person4类的实例
println(p.isInstanceOf[Person4])//true
//判断p的类型是否为Person4类
println(p.getClass == classOf[Person4])//false
//判断p的类型是否为Student4类
println(p.getClass == classOf[Student4])//true
}
}
4.5 Scala中使用模式匹配进行类型判断
package cn.itcast.extends_demo
class Person5 {}
class Student5 extends Person5
object Student5{
def main(args: Array[String]) {
val p:Person5=new Student5
p match {
// 匹配是否为Person类或其子类对象
case per:Person5 => println("This is a Person5's Object!")
// 匹配所有剩余情况
case _ =>println("Unknown type!")
}
}
}
4.6 Scala中protected (了解)
package cn.itcast.extends_demo
class Person6{
protected var name:String="tom"
protected[this] var hobby:String ="game"
protected def sayBye=println("再见...")
}
class Student6 extends Person6{
//父类使用protected 关键字来修饰 field可以直接访问
def sayHello =println("Hello "+name)
//父类使用protected 关键字来修饰method可以直接访问
def sayByeBye=sayBye
def makeFriends(s:Student6)={
println("My hobby is "+hobby+", your hobby is UnKnown")
}
}
object Student6{
def main(args: Array[String]) {
val s:Student6=new Student6
s.sayHello
s.makeFriends(s)
s.sayByeBye
}
}
4.7 Scala中调用父类的constructor
package cn.itcast.extends_demo
class Person7(val name:String,val age:Int){
var score :Double=0.0
var address:String="beijing"
def this(name:String,score:Double)={
//每个辅助constructor的第一行都必须调用其他辅助constructor或者主constructor代码
//主constructor代码
this(name,30)
this.score=score
}
//其他辅助constructor
def this(name:String,address:String)={
this(name,100.0)
this.address=address
}
}
class Student7(name:String,score:Double) extends Person7("abc",23 ){
var myaddress = "上海"
//注意:子类的辅助构造器,只能够调用子类的主构造器,或者子类的辅助构造器
//不能够调用父类的辅助构造器或者,或者父类的主构造器
def this(name:String,myaddress:String){
//this(name,"tianjin")
this(name,23.0)
}
}
object Student7{
def main(args: Array[String]): Unit = {
val zhangsan = new Student7("zhangsan","lisi")
println(zhangsan)
}
}
4.8 Scala中抽象类
package cn.itcast.extends_demo
abstract class Person9(val name:String) {
//必须指出返回类型,不然默认返回为Unit
def sayHello:String
def sayBye:String
val age:Int //变量的初始化
}
class Student9(name:String) extends Person9(name){ //随便传一个与构造器类型一直的
//必须指出返回类型,不然默认
def sayHello: String = "Hello,"+name
def sayBye: String ="Bye,"+name
val age: Int = 50 //变量集成后赋值
}
object Student9{
def main(args: Array[String]) {
val s = new Student9("tom")
println(s.sayHello)
println(s.sayBye)
}
}
5.scala当中的特质trait
5.1 将trait作为接口使用
1.将特质当作接口使用
package cn.itcast.triat
trait HelloTrait {
def sayHello(): Unit
}
trait MakeFriendsTrait {
def makeFriends(c: Children): Unit
}
//多重继承 trait
class Children(val name: String) extends HelloTrait with MakeFriendsTrait with Cloneable with Serializable{
def sayHello() =println("Hello, " + this.name)
def makeFriends(c: Children) = println("Hello, my name is " + this.name + ", your name is " + c.name)
}
object Children{
def main(args: Array[String]) {
val c1=new Children("tom")
val c2=new Children("jim")
c1.sayHello()//Hello, tom
c1.makeFriends(c2)//Hello, my name is tom, your name is jim
}
}
5.2 在trait中定义具体的方法
2.特质当中的方法如果实现了方法体,在子类当中就不用覆写了,直接可以拿过来调用
package cn.itcast.triat
/**
* 比如 trait 中可以包含很多子类都通用的方法,例如打印日志或其他工具方法等等。
* spark就使用trait定义了通用的日志打印方法;
*/
trait Logger {
def log(message: String): Unit = println(message)
}
class PersonForLog(val name: String) extends Logger {
def makeFriends(other: PersonForLog) = {
println("Hello, " + other.name + "! My name is " + this.name + ", I miss you!!")
this.log("makeFriends method is invoked with parameter PersonForLog[name = " + other.name + "]")
}
}
object PersonForLog{
def main(args: Array[String]) {
val p1=new PersonForLog("jack")
val p2=new PersonForLog("rose")
p1.makeFriends(p2)
//Hello, rose! My name is jack, I miss you!!
//makeFriens method is invoked with parameter PersonForLog[name = rose]
}
}
5.3 在trait中定义具体field
如果trait当中已经定义好了一个字段,且字段赋值了,在子类当中可以直接使用
package cn.itcast.triat
trait PersonForField {
val age:Int=50
}
//继承 trait 获取的field直接被添加到子类中
class StudentForField(val name: String) extends PersonForField {
def sayHello = println("Hi, I'm " + this.name + ", my age is "+ age)
}
object StudentForField{
def main(args: Array[String]) {
val s=new StudentForField("tom")
s.sayHello
}
}
5.4 在trait中定义抽象field
如果特制当中的字段没有实现,那么我们必须在子类当中给实现了
trait SayHelloTrait {
//注意,这个字段,没有实现
val msg:String
def sayHello(name: String) = println(msg + ", " + name)
}
class PersonForAbstractField(val name: String) extends SayHelloTrait {
//必须覆盖抽象 field
val msg = "Hello"
def makeFriends(other: PersonForAbstractField) = {
this.sayHello(other.name)
println("I'm " + this.name + ", I want to make friends with you!!")
}
}
object PersonForAbstractField{
def main(args: Array[String]) {
val p1=new PersonForAbstractField("Tom")
val p2=new PersonForAbstractField("Rose")
p1.makeFriends(p2)
}
}
5.5 在实例对象指定混入某个trait
在对象当中混入某个特质—>利于在不改变代码的情况下进行功能扩展
trait LoggedTrait {
// 该方法为实现的具体方法
def log(msg: String) = {}
}
trait MyLogger extends LoggedTrait{
// 覆盖 log() 方法
override def log(msg: String) = println("log: " + msg)
}
trait MyLogger2 {
def MyNewMethod(name:String) ={
println("hahhaha")
}
}
class PersonForMixTraitMethod(val name: String) extends LoggedTrait {
def sayHello = {
println("Hi, I'm " + this.name)
log("sayHello method is invoked!")
}
}
/*class PersonForMixTraitMethod(val name: String) extends MyLogger {
def sayHello = {
println("Hi, I'm " + this.name)
log("sayHello method is invoked!")
}
}*/
object PersonForMixTraitMethod{
def main(args: Array[String]) {
val tom= new PersonForMixTraitMethod("Tom").sayHello //结果为:Hi, I'm Tom
// 使用 with 关键字,指定混入MyLogger trait
val rose = new PersonForMixTraitMethod("Rose") with MyLogger
rose.sayHello
val rose2 = new PersonForMixTraitMethod("Rose") with MyLogger2
// 结果为: Hi, I'm Rose
// 结果为: log: sayHello method is invoked!
}
}
5.6 trait 调用链
package cn.itcast.triat
trait HandlerTrait {
def handle(data: String) = {println("last one")}
}
trait DataValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("check data: " + data)
super.handle(data)
}
}
trait SignatureValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("check signature: " + data)
super.handle(data)
}
}
class PersonForRespLine(val name: String) extends SignatureValidHandlerTrait with DataValidHandlerTrait {
def sayHello = {
println("Hello, " + this.name)
this.handle(this.name)
}
}
object PersonForRespLine{
def main(args: Array[String]) {
val p=new PersonForRespLine("tom")
p.sayHello
//执行结果:
// Hello, tom
// check data: tom
// check signature: tom
// last one
}
}
5.7 混合使用 trait 的具体方法和抽象方法
package cn.itcast.triat
trait ValidTrait {
//抽象方法
def getName: String
//具体方法,具体方法的返回值依赖于抽象方法
def valid: Boolean = {"Tom".equals(this.getName)
}
}
class PersonForValid(val name: String) extends ValidTrait {
def getName: String = this.name
}
object PersonForValid{
def main(args: Array[String]): Unit = {
val person = new PersonForValid("Rose")
println(person.valid)
}
}
5.8 trait的构造机制
package cn.itcast.triat
class Person_One {
println("Person's constructor!")
}
trait Logger_One {
println("Logger's constructor!")
}
trait MyLogger_One extends Logger_One {
println("MyLogger's constructor!")
}
trait TimeLogger_One extends Logger_One {
println("TimeLogger's contructor!")
}
class Student_One extends Person_One with MyLogger_One with TimeLogger_One {
println("Student's constructor!")
}
object exe_one {
def main(args: Array[String]): Unit = {
val student = new Student_One
//执行结果为:
// Person's constructor!
// Logger's constructor!
// MyLogger's constructor!
// TimeLogger's contructor!
// Student's constructor!
}
}
5.9 trait 继承 class (java中接口不能)
package cn.itcast.triat
class MyUtil {
def printMsg(msg: String) = println(msg)
}
trait Logger_Two extends MyUtil {
def log(msg: String) = this.printMsg("log: " + msg)
}
class Person_Three(val name: String) extends Logger_Two {
def sayHello {
this.log("Hi, I'm " + this.name)
this.printMsg("Hello, I'm " + this.name)
}
}
object Person_Three{
def main(args: Array[String]) {
val p=new Person_Three("Tom")
p.sayHello
//执行结果:
// log: Hi, I'm Tom
// Hello, I'm Tom
}
}
5.10 scala当中的特质总结
1、特质可以作为一个接口使用,里面定义的方法没有方法体
2、特质里面的字段Field,如果不实现,就是一个抽象字段,子类一定要实现
3、特质里面定义好的已经实现了的方法,子类可以拿过来直接使用
4、特质里面定义好的field,已经赋值了,子类可以拿过来直接使用
5、在实例对象中混入某个trait,扩展一些功能
6、Trait的调用链 从右往左边调用
7、Trait的构造器 从左往右边进行实例化
8、Trait继承class类
6.模式匹配和样例类
6.1 字符匹配
def main(args: Array[String]): Unit = {
val charStr = '6'
charStr match {
case '+' => println("匹配上了加号")
case '-' => println("匹配上了减号")
case '*' => println("匹配上了乘号")
case '/' => println("匹配上了除号")
//注意。所有的模式匹配都必须最终匹配上一个值,如果没有匹配上任何值,就会报错
case _ => println("都没有匹配上,我是默认值")
}
}
6.2 匹配字符串
def main(args: Array[String]): Unit = {
val arr = Array("hadoop", "zookeeper", "spark")
val name = arr(Random.nextInt(arr.length))
name match {
case "hadoop" => println("大数据分布式存储和计算框架...")
case "zookeeper" => println("大数据分布式协调服务框架...")
case "spark" => println("大数据分布式内存计算框架...")
case _ => println("我不认识你...")
}
}
6.3 守卫
模式匹配当中,我们也可以通过条件进行判断
def main(args: Array[String]): Unit = {
var ch = "500"
var sign = 0
ch match {
case "+" => sign = 1
case "-" => sign = 2
case _ if ch.equals("500") => sign = 3
case _ => sign = 4
}
println(ch + " " + sign)
}
6.4 匹配类型
注意在map当中会存在泛型擦除的情况。注意在进行非数组的类型匹配的时候,类型都会进行擦除
def main(args: Array[String]): Unit = {
//注意泛型擦除,在模式匹配当中的类型匹配中,除了Array类型以外,所有的其他的数据类型都会被擦除掉
val a = 3
val obj = if(a == 1) 1
else if(a == 2) "2"
else if(a == 3) BigInt(3)
else if(a == 4) Map("aa" -> 1)
else if(a == 5) Map(1 -> "aa")
else if(a == 6) Array(1, 2, 3)
else if(a == 7) Array("aa", 1)
else if(a == 8) Array("aa")
val r1 = obj match {
case x: Int => x
case s: String => s.toInt
case BigInt => -1 //不能这么匹配
case _: BigInt => Int.MaxValue
case m: Map[String, Int] => "Map[String, Int]类型的Map集合"
case m: Map[_, _] => "Map集合"
case a: Array[Int] => "It's an Array[Int]"
case a: Array[String] => "It's an Array[String]"
case a: Array[_] => "It's an array of something other than Int"
case _ => 0
}
println(r1 + ", " + r1.getClass.getName)
}
6.5 匹配数组、元组、集合
def main(args: Array[String]): Unit = {
val arr = Array(0, 3, 5)
arr match {
case Array(0, x, y) => println(x + " " + y)
case Array(0) => println("only 0")
//匹配数组以1 开始作为第一个元素
case Array(1, _*) => println("0 ...")
case _ => println("something else")
}
val lst = List(3, -1)
lst match {
case 0 :: Nil => println("only 0")
case x :: y :: Nil => println(s"x: $x y: $y")
case 0 :: tail => println("0 ...")
case _ => println("something else")
}
val tup = (1, 3, 7)
tup match {
case (1, x, y) => println(s"1, $x , $y")
case (_, z, 5) => println(z)
case _ => println("else")
}
}
6.6 样例类
case class SubmitTask(id:String,name:String)
case class HeartBeat(time:Long)
case object CheckTimeOutTask
object CaseDemo04 {
def main(args: Array[String]): Unit = {
val arr = Array(CheckTimeOutTask, HeartBeat(12333), SubmitTask("0001", "task-0001"))
val myInt: Int = Random.nextInt(arr.length)
println(myInt)
val arrayResult = arr(1)
arrayResult match {
case CheckTimeOutTask => println("匹配上了CheckTimeOut")
case SubmitTask(x,y) => println(s"$x....$y")
case HeartBeat(x) => println("心跳时间为 " + x)
}
}
}
6.7 偏函数
val func1: PartialFunction[String, Int] = {
case "one" => 1
case "two" => 2
// case _ => -1
}
def func2(num: String) : Int = num match {
case "one" => 1
case "two" => 2
case _ => -1
}
def main(args: Array[String]) {
println(func1("one"))
println(func2("one"))
//如果偏函数当中没有匹配上,那么就会报错,我们可以通过isDefinedAt来进行判断
// println(func1("three"))
println(func1.isDefinedAt("three"))
if(func1.isDefinedAt("three")){
println("hello world")
}else{
println("world hello")
}
}
7.scala当中的类型参数(了解)
7.1 scala当中的类的泛型
object Demo8 {
def main(args: Array[String]): Unit = {
val result1 = new MyClass("hello",50)
val result2 = new MyClass[Any,Any]("zhangsan","Lisi");
}
}
/**
* 定义一个class类,接收两个参数,但是两个参数都是泛型,泛型的类型,会根据我们
* 创建类的实例化对象的时候,动态的传递进行动态的推断
* @param first
* @param second
* @tparam T
* @tparam B
*/
class MyClass[T,B](first:T,second:B){
println(first+","+second)
}
7.2 函数的泛型
object methodType{
def getMiddle[T](canshu:T) ={
canshu
}
def main(args: Array[String]): Unit = {
// 从参数类型来推断类型
println(getMiddle(Array("Bob", "had", "a", "little", "brother")).getClass.getTypeName)
//指定类型,并保存为具体的函数。
val f = getMiddle[String] _
println(f("Bob"))
}
}
7.3 scala当中的上下界之泛型类型的限定
泛型的上界限定(必须是该类的子类)
class Pair1[T <: Comparable[T]](val first: T, val second: T) {
def smaller = if (first.compareTo(second) < 0) first else second
}
object Main1 extends App{
override def main(args: Array[String]): Unit = {
val p = new Pair1("hello", "Brooks")
println(p.smaller)
}
}
泛型的下界限定(必须是该类的父类)
class Pair2[T](val first: T, val second: T) {
def replaceFirst[R >: T](newFirst: R) = new Pair2[R](newFirst, second)
override def toString = "(" + first + "," + second + ")"
}
object Main2 extends App{
override def main(args: Array[String]): Unit = {
val p = new Pair2("Nick", "Alice")
println(p)
println(p.replaceFirst("Joke"))
println(p)
}
}
7.4 scala当中的视图界定
/**
* 使用 <% 来实现我们类型的隐式转换
* @param first
* @param second
* @tparam T
*/
class Pair3[T <% Comparable[T]](val first: T, val second: T) {
def smaller = if (first.compareTo(second) < 0) first else second
override def toString = "(" + first + "," + second + ")"
}
object Main3 extends App {
val p = new Pair3(4, 2) //RichInt有CompareTo方法
println(p.smaller)
}
7.5 scala当中的协变,逆变和非变
Java当中,这两个玩意儿没有关系
class Anmial {}
class Cat extends Anmial{}
List[Cat] List[Anmial]
在 scala当中
List[cat] extends List[Anmial] 协变
List[Anmial] extends List[Cat] 逆变
List[Cat] = List[Cat] 非变
package cn.itcast.scala.enhance.covariance
class Super
class Sub extends Super
//协变
class Temp1[+A](title: String)
//逆变
class Temp2[-A](title: String)
//非变
class Temp3[A](title: String)
object Covariance_demo{
def main(args: Array[String]) {
//支持协变 Temp1[Sub]还是Temp1[Super]的子类
val t1: Temp1[Super] = new Temp1[Sub]("hello scala!!!")
//支持逆变 Temp1[Super]是Temp1[Sub]的子类
val t2: Temp2[Sub] = new Temp2[Super]("hello scala!!!")
//支持非变 Temp3[Super]与Temp3[Sub]没有从属关系,如下代码会报错
//val t3: Temp3[Sub] = new Temp3[Super]("hello scala!!!")
//val t4: Temp3[Super] = new Temp3[Sub]("hello scala!!!")
println(t1.toString)
println(t2.toString)
}
}
8.scala当中的Actor并发编程
8.1 scala当中的Actor介绍
8.2 Actor实战
怎么实现actor并发编程:
1、定义一个class或者是object继承Actor特质,注意导包import scala.actors.Actor
2、重写对应的act方法
3、调用Actor的start方法执行Actor
4、当act方法执行完成,整个程序运行结束
第一个例子
package cn.itcast.scala_actor
import scala.actors.Actor
class Actor1 extends Actor{
override def act(): Unit = {
for (x <- 1 to 10){
println(x)
}
}
}
object Actor2 extends Actor{
override def act(): Unit = {
for (j <- 1 to 10){
println("j"+j)
}
}
}
object Actor1{
def main(args: Array[String]): Unit = {
val actor: Actor1 = new Actor1
actor.act()
Actor2.act()
}
}
第二个例子 实现发送,接收消息
package cn.itcast.scala_actor
import scala.actors.Actor
class MyActor2 extends Actor{
//通过act方法当中,使用receive来进行消息接收
override def act(): Unit = {
receive{
case "start" => println("start.....")
}
}
}
object MyActor2{
def main(args: Array[String]): Unit = {
val actor = new MyActor2
//启动我们的actor,类似我们启动线程
actor.start()
//发送一个异步没有返回值的消息
actor ! "xxx"
actor ! "start"
}
}
第三个例子 实现actor可以不断地接受消息
怎么实现actor可以不断地接受消息:
在act方法中可以使用while(true)的方式,不断的接受消息。
class MyActor3 extends Actor{
override def act(): Unit = {
while (true){
receive{ //每次来一条消息都会启动一个线程去执行,线程开销太大
case "start" => println("starting")
case "stop" =>println("stopping")
}
}
}
}
object MyActor3{
def main(args: Array[String]): Unit = {
val actor = new MyActor3
actor.start()
actor ! "start"
actor ! "stop"
}
}
第四个例子 使用react方法代替receive方法去接受消息
使用react方法代替receive方法去接受消息
好处:react方式会复用线程,避免频繁的线程创建、销毁和切换。比receive更高效
注意: react 如果要反复执行消息处理,react外层要用loop,不能用while
class MyActor4 extends Actor{
override def act(): Unit = {
loop{
react{
case "start" => println("starting")
case "stop" => println("stopping")
}
}
}
}
object MyActor4{
def main(args: Array[String]): Unit = {
val actor = new MyActor4
actor.start()
actor ! "start"
actor ! "stop"
}
}
第五个例子 结合case class样例类发送消息和接受消息
结合case class样例类发送消息和接受消息
1、将消息封装在一个样例类中
2、通过匹配不同的样例类去执行不同的操作
Actor可以返回消息给发送方。通过sender方法向当前消息发送方返回消息
//异步消息
case class AsyncMessage(id:Int,message:String)
//同步消息
case class SyncMessage(id:Int,message:String)
//返回消息
case class ReplyMessage(id:Int,message:String)
class MyActor5 extends Actor{
override def act(): Unit = {
loop{
react{
case AsyncMessage(id,message) => {
println(s"$id,$message")
sender ! ReplyMessage(2,"异步有返回值的消息处理成功")
}
case SyncMessage(id,message) =>{
println(s"$id,$message")
sender ! ReplyMessage(id,"我是同步消息的返回值,等到我返回之后才能继续下一步的处理")
}
}
}
}
}
object MyActor5{
def main(args: Array[String]): Unit = {
val actor: MyActor5 = new MyActor5
actor.start()
//发送异步无返回值消息
actor ! AsyncMessage(1,"我是一个异步无返回值的消息不要回答")
//发送异步有返回值消息
val asyncMessage: Future[Any] = actor !! AsyncMessage(2,"actorSend")
val apply: Any = asyncMessage.apply()
println(apply)
println("helloworld22222")
//同步等待返回值消息
val syncMessage: Any = actor !? SyncMessage(3,"我是同步阻塞消息")
println(syncMessage)
}
}
练习实战 用actor并发编程写一个单机版的WordCount
package cn.itcast.scala_actor
import scala.actors.{Actor, Future}
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.io.{BufferedSource, Source}
case class FilePath(fileNamePath:String)
case class ReplyWord(returnMap:Map[String,Int])
class WordCountActor extends Actor{
override def act(): Unit = {
//loop react不断接收数据
loop{
react{
case FilePath(fileNamePath) =>{
//获取到文件路径之后,读取文件内容
val fileContent: BufferedSource = Source.fromFile(fileNamePath)
//获取文件流中所用内容
val fileContentStr: String = fileContent.mkString
println(fileContentStr)
//文件内容按照换行符切分
// win-\r\n linux-\r mac-\n
val split: Array[String] = fileContentStr.split("\r\n")
/*
方法1:
//将Array中每个元素取出来,按照空格切
val map: Array[Array[String]] = split.map(x => x.split(" "))
//Array(Array(hello world hadoop hive),Array(sqoop hadoop hello world))
println(map.toBuffer)
val flatten: Array[String] = map.flatten //压平
println(flatten.toBuffer)
//ArrayBuffer(hello, world, hadoop, hive, sqoop, hadoop, hello, world)
//给每一个单词记做1 hello 1 world 1 hadoop 1 hello 1
//然后再给每一个单词进行分组 hello (1,1)
*/
// 方法2:
val words: Array[String] = split.flatMap(x => x.split(" "))
println(words.toBuffer)
//如何将每个Array中的每个元素取出来,编程如下形式
//Array((hello,1),(hadoop,1))
//ArrayBuffer((hello,1), (world,1), (hadoop,1), (hive,1), (sqoop,1), (hadoop,1), (hello,1), (world,1))
val wordAndOne: Array[(String, Int)] = words.map(x => (x,1))
println(wordAndOne.toBuffer)
//将Array中每个元素,取出来,作用再groupBy上面
val by: Map[String, Array[(String, Int)]] = wordAndOne.groupBy(x => x._1)
for ((k,v) <- by){
println(k)
println(v.toBuffer)
}
/*
world
ArrayBuffer((world,1), (world,1))
sqoop
ArrayBuffer((sqoop,1))
hadoop
ArrayBuffer((hadoop,1), (hadoop,1))
hive
ArrayBuffer((hive,1))
hello
ArrayBuffer((hello,1), (hello,1))
*/
//获取每个map中每一个value的长度,就是我们单词出现的次数
//将map当中每一个value取出来,作用在下面这个函数,然后返回新的map
//新map的key还是原来的key,value改变
val values: Map[String, Int] = by.mapValues(x => x.length)
/*
for ((x,y) <- values){
println(x,y)
}
ArrayBuffer((hello,1), (hello,1))
(world,2)
(sqoop,1)
(hadoop,2)
(hive,1)
(hello,2)
*/
//将我们得到的每个文件结果返回
sender ! ReplyWord(values)
}
}
}
}
}
object WordCountActor{
def main(args: Array[String]): Unit = {
//一个文件
val filePath = "E:\\大数据资料\\大数据实时资料\\4、Scala\\2、Scala第二天\\wordCount\\1.txt"
//多个文件
val files = Array("E:\\大数据资料\\大数据实时资料\\4、Scala\\2、Scala第二天\\wordCount\\1.txt","E:\\大数据资料\\大数据实时资料\\4、Scala\\2、Scala第二天\\wordCount\\2.txt","E:\\大数据资料\\大数据实时资料\\4、Scala\\2、Scala第二天\\wordCount\\3.txt")
val mySet: mutable.HashSet[Future[Any]] = new mutable.HashSet[Future[Any]]()
val replyWords: ListBuffer[ReplyWord] = new ListBuffer[ReplyWord]
for (f <- files){
val actor = new WordCountActor
actor.start()
//获取到异步发送的返回值
val value: Future[Any] = actor !! FilePath(f)
mySet.add(value)
}
while (mySet.size>0){
//一定要过滤掉我们set集合中没有数据的可能性
val filter: mutable.HashSet[Future[Any]] = mySet.filter(x => x.isSet)
for (eachFuture <- filter){
val apply: Any = eachFuture.apply()
val word: ReplyWord = apply.asInstanceOf[ReplyWord]
//将真正的值转换成样例类,再加入到ListBuffer里去
replyWords.+=(word)
//避免死循环不退出
mySet.remove(eachFuture)
}
}
val map: ListBuffer[Map[String, Int]] = replyWords.map(x => x.returnMap)
/*
println(map)
ListBuffer(Map(world -> 2, sqoop -> 1, hadoop -> 2, hive -> 1, hello -> 2), Map(world -> 2, sqoop -> 1, hadoop -> 2, hive -> 1, hello -> 2), Map(world -> 2, sqoop -> 1, hadoop -> 2, hive -> 1, hello -> 2))
*/
/*map.map(x=>println(x.toBuffer))
ArrayBuffer((world,2), (sqoop,1), (hadoop,2), (hive,1), (hello,2))
ArrayBuffer((world,2), (sqoop,1), (hadoop,2), (hive,1), (hello,2))
ArrayBuffer((world,2), (sqoop,1), (hadoop,2), (hive,1), (hello,2))
*/
val flatten: ListBuffer[(String, Int)] = replyWords.map(x => x.returnMap).flatten
/*
println(flatten)
ListBuffer((world,2), (sqoop,1), (hadoop,2), (hive,1), (hello,2), (world,2), (sqoop,1), (hadoop,2), (hive,1), (hello,2), (world,2), (sqoop,1), (hadoop,2), (hive,1), (hello,2))
*/
//按照k进行分组
val by: Map[String, ListBuffer[(String, Int)]] = flatten.groupBy(x => x._1)
/*
for ((x,y) <- by){
println(x,y)
}
(world,ListBuffer((world,2), (world,2), (world,2)))
(sqoop,ListBuffer((sqoop,1), (sqoop,1), (sqoop,1)))
(hadoop,ListBuffer((hadoop,2), (hadoop,2), (hadoop,2)))
(hive,ListBuffer((hive,1), (hive,1), (hive,1)))
(hello,ListBuffer((hello,2), (hello,2), (hello,2)))
*/
//每个value都是ListBuffer,将每个
val values: Map[String, Int] = by.mapValues(x => x.foldLeft(0)(_ + _._2))
// by.mapValues(x => x.foldLeft(0)((x,y) => x + y._2))
//(0)((x,y)) => x = 0 + 2 => 2+2 => 4+2
for ((x,y) <- values){
println(x,y)
}
}
}
9.scala当中的文件操作和网络请求
9.1 读取文件当中每一行的数据
package cn.itcast.scala_actor
import scala.io.{BufferedSource, Source}
object Test {
def main(args: Array[String]): Unit = {
val file: BufferedSource = Source.fromFile("E:\\大数据资料\\大数据实时资料\\4、Scala\\3、Scala第三天\\files\\file.txt","GBK")
val lines: Iterator[String] = file.getLines()
//法1
for(line <- lines){
println(line)
}
println("===========")
//法2
while(lines.hasNext){
println(lines.next())
}
//注意关闭文件
file.close()
}
}
如果要将文件内容转数组,直接调用toArray即可
9.2 读取词法单元和数字
如果想将以某个字符或某个正则表达式分开的字符成组读取,可以这么做:
def main(args: Array[String]): Unit = {
val file: BufferedSource = Source.fromFile("F:\\scala与spark课件资料教案\\3、scala第三天\\files\\file2.txt","GBK");
val split: Array[String] = file.mkString.split(" ")
println(split.mkString("\t"))
file.close()
}
9.3 读取网络资源、文件写入、控制台操作
1、读取网络资源
def main(args: Array[String]): Unit = {
val source: BufferedSource = Source.fromURL("http://www.baidu.com")
val string: String = source.mkString
println(string)
source.close()
}
2、文件写入操作
def main(args: Array[String]): Unit = {
val writer = new PrintWriter("E:\\大数据资料\\大数据实时资料\\4、Scala\\3、Scala第三天\\filesprintWriterx.txt")
for(i <- 1 to 100){
writer.println(i)
writer.flush()
}
writer.close()
}
3、控制台交互操作
def main(args: Array[String]): Unit = {
//控制台交互--老API
print("请输入内容:")
val consoleLine1 = Console.readLine()
println("刚才输入的内容是:" + consoleLine1)
//控制台交互--新API
print("请输入内容(新API):")
val consoleLine2 = StdIn.readLine()
println("刚才输入的内容是:" + consoleLine2)
}
9.4 scala当中的序列化
@SerialVersionUID(1L)
class Person extends Serializable{
override def toString = name + "," + age
val name = "Nick"
val age = 20
}
object PersonMain extends App{
override def main(args: Array[String]): Unit = {
import java.io.{FileOutputStream, FileInputStream, ObjectOutputStream, ObjectInputStream}
val nick = new Person
val out = new ObjectOutputStream(new FileOutputStream("Nick.obj"))
out.writeObject(nick)
out.close()
val in = new ObjectInputStream(new FileInputStream("Nick.obj"))
val saveNick = in.readObject()
in.close()
println(saveNick)
}
}
9.5 scala当中的正则表达式
我们可以通过正则表达式匹配一个句子中所有符合匹配的内容,并输出:
object Test extends App{
override def main(args: Array[String]): Unit = {
import scala.util.matching.Regex
val pattern1 = new Regex("(S|s)cala")
val pattern2 = "(S|s)cala".r
val str = "Scala is scalable and cool"
println((pattern2 findAllIn str).mkString(","))
println((pattern1 findAllIn str).mkString(","))
}}
10.隐式转换和隐式参数
10.1 隐式转换
10.2 隐式参数
10.3 隐式转换作用域与时机
10.4 隐式转换和隐式参数案例
隐式转换案例一(将我们的Double类型的数据自动转换成Int类型)
object Chapter14 {
implicit def ConvertDoubleToInt(first:Double):Int= first.toInt
}
object Convert{
//导入隐式转换的方法
import Chapter14._
def main(args: Array[String]): Unit = {
val first:Int = 3.5
println(first) //3
}
}
例如我们也可以定义猫和狗,并且让狗学会抓老鼠的技能
object CatAndDog {
implicit def dogCatchMouse(dog:Dog) = new Cat()
def main(args: Array[String]): Unit = {
val dog = new Dog
dog.catMouse("大黄狗")
}
}
class Cat{
def catMouse(name:String): Unit ={
println(name+"catch a mouse")
}
}
class Dog{
def wangwangwang(name:String) ={
println(name+"看门汪汪汪")
}
}
隐式转换案例二(让File类具备RichFile类中的read方法)
import java.io.File
import scala.io.Source
object MyPredef{
//定义隐式转换方法
implicit def file2RichFile(file: File)=new RichFile(file)
}
class RichFile(val f:File) {
def read()=Source.fromFile(f,"GBK").mkString
}
object RichFile{
def main(args: Array[String]) {
val f=new File("F:\\scala与spark课件资料教案\\3、scala第三天\\files\\file.txt")
//使用import导入隐式转换方法
import MyPredef._
//通过隐式转换,让File类具备了RichFile类中的方法
val content=f.read()
println(content)
}
}
隐式转换案例三(超人变身)
class Man(val name:String)
class SuperMan(val name: String) {
def heat=print("超人打怪兽")
}
object SuperMan{
//隐式转换方法
implicit def man2SuperMan(man:Man)=new SuperMan(man.name)
def main(args: Array[String]) {
val hero=new Man("hero")
//Man具备了SuperMan的方法
hero.heat
}
}
隐式转换案例四(一个类隐式转换成具有相同方法的多个类)
class A(c:C) {
def readBook(): Unit ={
println("A说:好书好书...")
}
}
class B(c:C){
def readBook(): Unit ={
println("B说:看不懂...")
}
def writeBook(): Unit ={
println("B说:不会写...")
}
}
class C
object AB{
//创建一个类的2个类的隐式转换
implicit def C2A(c:C)=new A(c)
implicit def C2B(c:C)=new B(c)
}
object B{
def main(args: Array[String]) {
//导包
//1. import AB._ 会将AB类下的所有隐式转换导进来
//2. import AB._C2A 只导入C类到A类的的隐式转换方法
//3. import AB._C2B 只导入C类到B类的的隐式转换方法
import AB._
val c=new C
//由于A类与B类中都有readBook(),只能导入其中一个,否则调用共同方法时代码报错
//c.readBook()
//C类可以执行B类中的writeBook()
c.writeBook()
}
}
隐式参数案例五(员工领取薪水)
object Company{
//在object中定义隐式值 注意:同一类型的隐式值只允许出现一次,否则会报错
implicit val aaa="zhangsan"
implicit val bbb=10000.00
}
class Boss {
//注意参数匹配的类型 它需要的是String类型的隐式值
def callName()(implicit name:String):String={
name+" is coming !"
}
//定义一个用implicit修饰的参数
//注意参数匹配的类型 它需要的是Double类型的隐式值
def getMoney()(implicit money:Double):String={
" 当月薪水:"+money
}
}
object Boss extends App{
//使用import导入定义好的隐式值,注意:必须先加载否则会报错
import Company._
val boss =new Boss
println(boss.callName()+boss.getMoney())
}
11.scala编程实战
11.1 使用Akka实现一个简易版的spark通信框架
11.2 项目概述
11.3 项目实现
实战一 利用Akka的actor编程模型,实现2个进程间的通信。
架构图
重要类介绍
Actor
具体代码
第一步:创建maven工程,导入jar包
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.11</artifactId>
<version>2.3.14</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-remote_2.11</artifactId>
<version>2.3.14</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<!-- 限制jdk的编译版本插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!--编译Scala代码必须要用到的一个包-->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<args>
<arg>-dependencyfile</arg>
<arg>${project.build.directory}/.scala_dependencies</arg>
</args>
</configuration>
</execution>
</executions>
</plugin>
<!-- 打包时候的插件,将我们其他一些依赖的jar包全部打包到一起去 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>reference.conf</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass></mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
第二步:master进程代码开发
package cn.itcast.scala_akka
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import com.typesafe.config.{Config, ConfigFactory}
class Master extends Actor{
//这个receive方法会反复不断的被调用,一旦有消息就会收得到
//类似loop{react{}}
println("我是构造器,最先调用")
override def preStart(): Unit = {
println("我是初始化的方法,master对象创建完成后就会调用")
}
override def receive: Receive = {
case "connect" => {
println("a client connected")
sender ! "success"
}
}
}
object Master{
def main(args: Array[String]): Unit = {
val host = "127.0.0.1"
val port = 8888
val configStr:String =
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "$host"
|akka.remote.netty.tcp.port = "$port"
""".stripMargin
val config: Config = ConfigFactory.parseString(configStr)
//获取我们的ActorSystem,通过ActorSystem实现获取一个actor
val actorSystem = ActorSystem("actorSystem",config)
//创建master的actor,并且给master的actor起一个名字叫做master
//props里面需要传入一个actor的子类
val master: ActorRef = actorSystem.actorOf(Props(new Master),"master")
//不需要调用start直接发
}
}
第三步:worker进程代码开发
package cn.itcast.scala_akka
import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props}
import com.typesafe.config.{Config, ConfigFactory}
class Worker extends Actor{
override def preStart(): Unit = {
println("我是初始化调用的方法")
//在这里实现worker给master发送消息
//通过context上下文对象,使用actorSelection方法,向指定的actor发送数据
//注意:akka.tcp://actorSystem@127.0.0.1:8888 是我们ActorSystem
//通过ActorSystem创建了一个子actor 名字叫做master 通过/user/master对应我们的actor
val master: ActorSelection = context.actorSelection("akka.tcp://actorSystem@127.0.0.1:8888/user/master")
master ! "connect"
}
override def receive: Receive = {
case "connect" => println("a client connected ")
case "success" => println("successful")
}
}
object Worker{
def main(args: Array[String]): Unit = {
val host = "127.0.0.1"
val port = 6666
val configStr:String =
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "$host"
|akka.remote.netty.tcp.port = "$port"
""".stripMargin
val config: Config = ConfigFactory.parseString(configStr)
//获取我们的ActorSystem,通过ActorSystem实现获取一个actor
val actorSystem = ActorSystem("actorSystem",config)
val worker: ActorRef = actorSystem.actorOf(Props(new Worker),"worker")
}
}
实战二 使用Akka实现一个简易版的spark通信框架
架构图
具体代码
① Master类
package cn.itcast.scala_spark
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import cn.itcast.scala_spark.util.{RegisterMessage, RegisteredMessage, SendHeartBeat, WorkerInfo}
import com.typesafe.config.{Config, ConfigFactory}
import scala.collection.mutable
class SparkMaster extends Actor{
println("master构造器")
override def preStart(): Unit = {
}
//将我们worker注册信息封装到Map里面去,map的key就是我们的workid,map的value就是我们的WorkerInfo对象
private val workMsgMap = new mutable.HashMap[String,WorkerInfo]()
override def receive: Receive = {
case RegisterMessage(workerId,memory,cores) => {
//得到了worker注册信息,并保存到Map里去
val workerInfo = new WorkerInfo(workerId,memory,cores)
workMsgMap.put(workerId,workerInfo)
//保存好注册成功信息后,master向worker反馈一下注册成功信息
sender ! RegisteredMessage("恭喜你注册成功!")
}
case SendHeartBeat(workerId) => {
//获取到了心跳信息
println("接收心跳信息的workId为" + workerId)
//判断我们保存的注册信息是否在map当中存在
if(workMsgMap.contains(workerId)){
val workerInfo: WorkerInfo = workMsgMap(workerId)
//更新我们的心跳时间
workerInfo.lastHeartBeatTime = System.currentTimeMillis()
}
}
}
}
object SparkMaster{
def main(args: Array[String]): Unit = {
val host = "127.0.0.1"
val port = 8888
val configStr:String =
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "$host"
|akka.remote.netty.tcp.port = "$port"
""".stripMargin
val config: Config = ConfigFactory.parseString(configStr)
val masterActorSystem = ActorSystem("masterActorSystem",config)
val sparkMaster: ActorRef = masterActorSystem.actorOf(Props(new SparkMaster),"sparkMaster")
}
}
② Worker类
package cn.itcast.scala_spark
import java.util.UUID
import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props}
import cn.itcast.scala_spark.util.{HeartBeat, RegisterMessage, RegisteredMessage, SendHeartBeat}
import com.typesafe.config.{Config, ConfigFactory}
import scala.concurrent.duration._
class SparkWorker extends Actor{
println("worker构造器调用")
private val workId: String = UUID.randomUUID().toString
private val memory: Int = 128
private val cores: Int =16
//将我们master的引用提到全局变量,外部都可以使用
var selection: ActorSelection = _
override def preStart(): Unit = {
println("前置执行方法")
//worker向master发送注册信息
//获取sparkMaster的actor的引用
selection= context.actorSelection("akka.tcp://masterActorSystem@127.0.0.1:8888/user/sparkMaster")
selection ! RegisterMessage(workId,memory,cores)
}
override def receive: Receive = {
//将master反馈的信息打印出来
case RegisteredMessage(msg) => {
println(msg)
//继续调用定时模块,定时向master发送心跳信息
/*
initialDelay
interval
receiver 这里没有办法直接向sparkMaster发送数据,只能向自己发送数据
message
*/
import context.dispatcher
context.system.scheduler.schedule(0 millis,10000 millis,self,HeartBeat)
}
case HeartBeat =>{
//如果收到自己的心跳,向sparkMaster发送心跳
println("准备发送心跳信息")
//sender ! SendHeartBeat(workId)
selection ! SendHeartBeat(workId)
}
}
}
object SparkWorker{
def main(args: Array[String]): Unit = {
val host = "127.0.0.1"
val port = 6666
val configStr:String =
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "$host"
|akka.remote.netty.tcp.port = "$port"
""".stripMargin
val config: Config = ConfigFactory.parseString(configStr)
val workerActorSystem = ActorSystem("workerActorSystem",config)
val sparkWorder: ActorRef = workerActorSystem.actorOf(Props(new SparkWorker),"sparkWorder")
}
}
③ WorkerInfo类
package cn.itcast.scala_spark.util
//封装worker信息
class WorkerInfo(val workerId:String,val memory:Int,val cores:Int) {
//定义一个变量用于存放worker上一次心跳时间
var lastHeartBeatTime:Long=_ //占位符
override def toString: String = {
s"workerId:$workerId , memory:$memory , cores:$cores"
}
}
④ 样例类
package cn.itcast.scala_spark.util
trait RemoteMessage extends Serializable{
}
//worker向master发送注册信息,由于不在同一进程中,需要实现序列化
case class RegisterMessage(val workerId:String,val memory:Int,val cores:Int) extends RemoteMessage
//master反馈注册成功信息给worker,由于不在同一进程中,也需要实现序列化
case class RegisteredMessage(message:String) extends RemoteMessage
//worker向worker发送心跳 由于在同一进程中,不需要实现序列化
case object HeartBeat
//worker向master发送心跳,由于不在同一进程中,需要实现序列化
case class SendHeartBeat(val workerId:String) extends RemoteMessage
//master自己向自己发送消息,由于在同一进程中,不需要实现序列化
case object CheckOutTime