scala基础学习篇

scala
一: 简介
1. scala是运行在JVM上的多范式编程语言,同时支持面向对象和面向函数编程
2. scala的优点:
开发大数据应用程序(Spark程序、Flink程序)
表达能力强,一行代码抵得上Java多行,开发速度快
兼容Java,可以访问庞大的Java类库,例如:操作mysql、redis、freemarker、activemq等等
3. scala安装
1. scala SDK的安装

  1. List item

    	步骤:1. 下载,安装SDK 
    		2. 测试是否安装成功		=> 打开控制台,输入 scala -v
    2. 安装Idea scala插件
    	步骤: 1. 下载指定版本Idea scala插件(打开idea进行查找后下载)
    		2. IDEA配置scala插件
    		3. 重新启动Idea
    

二: 声明变量
在scala中,可以使用 val 或者 var 来定义变量,语法格式如下:
val/var 变量标识:变量类型 = 初始值
其中: val 定义的是不可重新被赋值的变量
var 定义的是可以重新赋值的变量
scala中定义变量类型写在变量名后面
scala的语句最后不需要添加分号
变量在定义后必须初始化

使用类型推断来定义变量: 就是可以省略不写变量类型
惰性赋值:
	当有一些变量保存的数据较大时,但是不需要马上加载到JVM内存。可以使用惰性赋值来提高效率。
	 语法格式: lazy val/var 变量名 = 表达式
		例如: lazy val sql = """insert overwrite table adm.itcast_adm_personas
				| select
				| a.user_id,
				....
				| left join gdm.itcast_gdm_user_buy_category c on a.user_id=c.user_id
				| left join gdm.itcast_gdm_user_visit d on a.user_id=d.user_id;"""

三: 字符串
scala提供多种定义字符串的方式,将来我们可以根据需要来选择最方便的定义方式。
1. 使用双引号
2. 使用插值表达式
3. 使用三引号

使用双引号的语法: val/var 变量名 = "字符串"
使用插值表达式: 可以有效的避免大量字符串的拼接
		语法: val/var 变量名 = s"${变量/表达式}字符串"
	注意事项: 1. 在定义字符串之前需要添加s
			2. 在字符串中,可以使用 ${}  来引用变量或者编写表达式
	例如: val info = s"name=${name}, age=${age}, sex=${sex}"
使用三引号:如果有大段的文本需要保存,就可以使用三引号来定义字符串。例如:保存一大段的SQL语句。三

个引号中间的所有字符串都将作为字符串的值。
语法: val/var 变量名 = “”“字符串1,字符串2"”"

四: 数据类型与操作符
1. 数据类型
基本类型 类型说明
Byt 8位带符号整数
Short 16位带符号整数
Int
Long
Char
String
Float
Double
Boolean
注意事项:
1. scala中所有的类型都使用大写字母开头
2. 整型使用的是 Int 而不是 Integer
3. scala中定义变量可以不写类型,让scala编译器自动推断
2. 运算符
类别 操作符
算术运算符 +、-、*、/
关系运算符 >、<、==、!=、>=、<=
逻辑运算符 &&、||、!
位运算符 &、||、^、<<、>>

	注意事项:
	1. scala中没有,++、--运算符
	2. 可以直接使用 ==  、 !=  进行比较,它们与 equals  方法表示一致。而比较两个对象的引用值,使用 eq
		例如: val str1 = "abc"
				val str2 = str1 + ""
				str1 == str2 	=> true  两个字符串的值相等
				str1.eq(str2)	=> false 两个字符串的引用值不相等
				
3. 	类架构
	Any:所有类的父类
		|--AnyVal:所值类型的父类
			|--Int、Char、Short、Long、Float、Double、Boolean
			|--Unit:无返回值,类似java的void
		|--AnyRef:所有引用类型父类
			|--String
			|--scala对象
			|--java对象
				|--Null:所有引用类型子类,实例是null
		
					|--Nothing:所有类型的子类

五: 表达式
1. 条件表达式
条件表达式就是if表达式,if表达式可以根据给定的条件是否满足,根据条件的 结果(真或假)决定执行对应的操作。
有返回值的if
1. 在scala中,条件表达式也是有返回值的
2. 在scala中,没有三元表达式,可以使用if表达式替代三元表达式
例如:
val sex = “male”
val result = if(sex == “male”) 1 else 0 => result = 1
2. 块表达式
1、定义:{}包裹的就是块表达式[for与while中的{}除外]
2、结果值: {}中最后一行表达式的结果值

六: 循环
1. for表达式
语法:
for( i <- 表达式/数组/集合) {
表达式
}
简单循环: 例如打印1-10的数字
for( i <- 1 to 10) println(i)

	2. 嵌套循环
		例如: 是否for表达式打印3行,5列星星, 每行打印5个星星,换行
			for( i <- 1 to 3;j <- 1 to 5) {
			println("*");if(j == 5) println("")
		}
	3. 守卫
		for表达式中,可以添加if判断语句,这个if判断就称之为守卫。我们可以使用守卫让for表达式更简洁。
			语法:
				for(i <- 表达式/数组/集合 if 表达式) {
					表达式
				}
		示例:
			for ( i <- 1 to 10 if i%3 == 0) println(i) 		// 添加守卫,打印能够整除3的数字
			1、for循环
	1、常用方法: to [前后包含]、until[包左不包右]
	2、单层循环: for(变量名<-列表)
	3、双层循环:for(变量名<-列表;变量名<-列表)
	4、守卫: for(变量名<-列表 if 布尔表达式;变量名<-列表)
	5、yeild: 生成新的列表
	
2. while 循环
	示例: 打印1-10数字
		var i=1
		while(i <= 10) {
		println(i)
		i=i+1
		}
3. break 与 continue 
	1. scala 中没有break与continue关键字
	2. 在scala 中实现break :
		1. 导包: import scala.util.control,Breaks._
		2.breakable{
		for(...){
			if(..) break()
			}
		} 
	3、在scala中实现continue:
	for(...){
		breakable{
			if(..) break()
		}
	}

七: 方法
1. 定义方法
1. 标准语法: def methodName(参数名:参数类型,参数名:参数类型):[return type] = {
//实现体
}
注意事项:1. 参数列表的参数类型不能省略
2. 返回值类型可以省略
3. 返回值可以不写return,默认就是{}块表达式的值
2. 其他方法定义方式
1、对于非递归方法,方法的返回值可以省略
def 方法名(参数名:参数类型,…)={方法体}
2、如果方法体只有一行语句,{}可以省略
def 方法名(参数名:参数类型,…)=方法体
3、方法没有返回值的时候,=可以省略
def 方法名(参数名:参数类型,…){方法体}
4、如果方法没有参数,()可以省略
def 方法名={方法体}
5、递归的方法,返回值类型必须定义
def 方法名(参数名:参数类型,…):返回值类型={方法体}
6、方法参数可以设置默认值
def 方法名(参数名:参数类型=默认值,…):返回值类型={方法体}
7、可以参数的方法定义【可以参数放在参数列表的最后】
def 方法名(参数名:参数类型,参数名:参数类型*):返回值类型={方法体}

	3. 默认参数 : 在定义方法时给参数定义一个默认值
		例如: def add( x:Int = 0 ,y:Int = 0 ) = x+y
				add()
		变长参数 : 如果方法的参数是不固定的,可以定义一个方法的参数是变长参数。
			def 方法名(参数名:参数类型*):返回值类型 = {
			方法体
		}
		在参数类型后面加一个 *  号,表示参数可以是0个或者多个
	
	4. 方法调用
		1、对象.方法名(参数)
		2、对象 方法名 (参数) 【如果只有一个参数,()可以省略】
		3、对象 方法名 {} 【如果方法只有一个参数,可以通过大括号的方式调用】
		4、对象.方法名 【方法没有参数的时候可以省略()】

八:函数
1. 语法: val 函数变量名 = (参数名:参数类型,参数名:参数类型…) => 函数体
注意事项:函数是一个对象(变量)
类似于方法,函数也有输入参数和返回值
函数定义不需要使用 def 定义
无需指定返回值类类型
2. 方法转函数: 方法名 _
3. 方法与函数的区别:
1、函数是对象,可以赋值给变量
2、函数可以作为方法的参数或者返回值

九: 数组
scala中有两种数组,一种是定长数组,一种是变长数组
定长数组: 语法 val/var 变量名 = new Array元素类型
val/var 变量名 = Array(元素1,元素2,元素3…)
取值方式: arr(下标)
变长数组:(创建变长数组,需要提前导入ArrayBuffer类, import scala.collection.mutable.ArrayBuffer)
语法 : 1. 创建空的ArrayBuffer变长数组,语法结构
val/var a = ArrayBuffer元素类型
2. 创建带有初始元素的ArrayBuffer
val/var a = ArrayBuffer(元素1,元素2,元素3…)
取值方式: arr(下标)
添加/修改/删除元素
使用 += 添加元素
使用 -= 删除元素
使用 ++= 追加一个数组到变长数组
遍历数组:
可以使用以下两种方式来遍历数组:
使用 for表达式 直接遍历数组中的元素
使用 索引 遍历数组中的元素
数组常用算法
scala中的数组封装了丰富的计算操作,将来在对数据处理的时候,不需要我们自己再重新实现。
以下为常用的几个算法:
求和——sum方法
求最大值——max方法
求最小值——min方法
排序——sorted方法 => sorted方法,可以对数组进行升序排序。而reverse方法,可以将数组进行反转,从而实现降序排序

十: 元组
元组可以用来包含一组不同类型的值,
1. 定义元组
val/var 元组名 = (元素1,元素2,元素3…)
当元组的元素只有两个时可以使用箭头来定义元素
val/var 元组名 = 元素1 -> 元素2
2. 元组取值方式 :_下标[元组的下标从1开始] 元组名._下标(从1开始取值)

十一: 列表
List是scala中最重要的、也是最常用的数据结构。List具备以下性质:
可以保存重复的值
有先后顺序
在scala中,也有两种列表,一种是不可变列表、另一种是可变列表

不可变列表: 	val/var 变量名 = List(元素1, 元素2, 元素3...)
				val/var 变量名 = Nil 	=> 使用Nil创建一个不可变的空列表
				val/var 变量名 = 元素1::元素2::元素3::Nil 	=> 使用 ::  方法创建一个不可变列表
可变列表: (使用前需要先导入 import scala.collection.mutable.ListBuffer)
	可变集合都在 mutable  包中
	不可变集合都在 immutable  包中(默认导入)
	定义语法:	val/var 变量名 = ListBuffer[Int]()
				val/var 变量名 = ListBuffer(元素1,元素2,元素3...)
2. 列表的操作
	1. 判断是否为空: isEmpty()
	2. 拼接两个列表: list1 ++ list2
	3. 获取第一个元素 与 除去第一个元素的其他元素
		1. head 取出第一个元素
		2. tail 取出除开第一个元素的其余元素
		3. last 取出最后一个元素
	4. 反转 : reserve
	5. 获取前几个元素与除开前几个元素的其余元素
			1. 获取前几个元素: take(数字)
			2. 获取除去前几个元素的其他元素: drop(数字)
	6、扁平化: flatten[只压平一次]
	例如1: val a = List(List(1,2), List(3), List(4,5))
			a.flatten	=> list(1,2,3,4,5)
	例如2: val list2 = ListBuffer("hadoop","sqoop","flume")
			list2.flatten 	=>  ListBuffer(h, a, d, o, o, p, s, q, o, o, p, f, l, u, m, e)
	7. 拉链(zip) 与 拉开(unzip)
		使用zip将两个列表,组合成一个元素为元组的列表(如果两个列表长度不一样,拉链后的长度为最短的列表的长度)
		例如1:  val a = List("张三", "李四", "王五")
				val b = List(19, 20, 21)
				a.zip(b) 	=> list((张三,19),(李四,20),(王五,21))
		例如2:  var list1 = List("张三","李四")
				val list2 = List(1,2,3,4,5,6)
				list1.zip(list2)		=> List[(String, Int)] = List((张三,1), (李四,2))
		使用unzip将一个包含元组的列表,解开成两个列表的元组 (只能针对包含是二元元组的列表)
	8、转字符串:toString
	9、mkString:将列表中的元素通过分隔符连接之后变成字符串展示
		例如:	 val a = List(1,2,3,4)
				a.mkString(",")		=> String = 1,2,3,4
				a.mkString("[", ":", "]")			=> String = [1:2:3:4]  // 可以调用mkString的另一种重载方式,加上前缀和后
	10. 并集: union [将两个集合的元素合并到一起,不会进行去重]
	11. 去重: distinct
	12. 交集: interset [取两个集合中都有的元素]
	13. 差集: diff  [取左边列表中存在,而右边列表中不存在的元素]

十二: set
1、特点:元素不重复,无顺序
2、可变set
1、创建:
import scala.collection.mutable.Set
val set = Set元素类型
2、取值:
set(角标)
3、不可变set
1、创建
val set = Set元素类型
2、取值:
set(角标)
十三: 映射
Map 可以称之为映射。它是由键值对组成的集合。在scala中,Map也分为不可变Map和可变Map。
不可变Map
val/var map = Map(键->值, 键->值, 键->值…) // 推荐,可读性更好
val/var map = Map((键, 值), (键, 值), (键, 值), (键, 值)…
可变Map
可变Map需要手动导入 import scala.collection.mutanle.Map
val/var map = Map(键->值, 键->值, 键->值…) // 推荐,可读性更好
val/var map = Map((键, 值), (键, 值), (键, 值), (键, 值)…
Map基本操作
获取值( map(key) )
获取所有key( map.keys )
获取所有value( map.values )
遍历map集合
getOrElse
增加key,value对
删除key

十四: 函数式编程
1、foreach
参数: A=>Unit
2、map
参数: A=>U
map方法参数是一个函数,函数有一个入参,有一个出参
map方法针对的列表的每一个元素
map应用场景: 一对一
3、flatMap
参数: A => 列表/集合
flatMap方法的参数是一个函数,函数有一个入参,出参是列表或者集合
flatMap针对的是列表的每一个元素。
flatMap的应用场景:一对多
4、filter:过滤
filter保留的函数返回值为true的数据
filter针对的也是列表的每一个元素
5、排序:
1、sorted:默认排序方法,默认升序
2、sortBy:按照指定的字段的进行排序
3、sortWith:指定排序规则

map与foreach的区别:
	map的函数是有返回值,foreach函数没有返回值
map与flatMap的区别:
	map针对列表的每一个元素,返回对应转换之后的数据,最终生成的列表的长度与原来的长度一样
	flatMap针对每一个元素,返回的是一个列表,最终生成列表的长度大于原来的列表长度
6、分组:groupBy ***
	1、参数: A => U
	2、通过groupBy生成的结果:[(key,List((key,value),(key,value)))]
7、聚合方法:reduce、fold
	reduce:
		参数: (上一次聚合结果,当前需要聚合的元素) => U
	fold(初始值)
		参数: (上一次聚合结果,当前需要聚合的元素) => U

十五: 类与对象
scala是支持面向对象的,也有类和对象的概念。
1. 创建类和对象
使用class来定义一个类
使用new来创建对象
用法
如果类是空的,没有任何成员,可以省略{}
如果构造器的参数为空,可以省略()
2. 定义成员变量:
在定义var类型的成员变量时,可以使用_来初始化成员变量
- String => null
- Int => 0
- Boolean => false
- Double => 0.0
val类型的成员变量,必须要自己手动初始化
3.定义成员方法、函数:
1、成员方法: def 方法名(参数名:参数类型,…)={方法体}
1、成员函数: val 函数名 = (参数名:参数类型,…) => {函数体}
4. 访问修饰符
在scala中,没有public关键字,任何没有被标为private或protected的成员都是公共的
private:private修饰的成员变量、方法、函数在类外面不可使用
5. 主构造器
语法:
class 类名(var/val 参数名:类型 = 默认值, var/val 参数名:类型 = 默认值){
// 构造代码块
}
注意事项:- 主构造器的参数列表是直接定义在类名后面,添加了val/var表示直接通过主构造器定义成员变量
- 构造器参数列表可以指定默认值
- 创建实例,调用构造器可以指定字段进行初始化
- 整个class中除了字段定义和方法定义的代码都是构造代码
6. 辅助构造器
语法:
定义辅助构造器与定义方法一样,也使用def关键字来定义
这个方法的名字为this
def this(参数名:类型, 参数名:类型) {
// 第一行需要调用主构造器或者其他构造器
// 构造器代码
}
7. object定义的为单例对象 ***
单例对象的成员变量或者方法可以通过 类名.成员变量/类名.方法 进行调用,类似java中static修饰的class
8. main的创建
object 单例对象名 extends App {
// 方法体
}
9. 伴生对象
一个class和object具有同样的名字。这个object称为伴生对象,这个class称为伴生类
注意事项:
- 伴生对象必须要和伴生类一样的名字
- 伴生对象和伴生类在同一个scala源文件中
- 伴生对象和伴生类可以互相访问private属性
private[this]访问权限
如果某个成员的权限设置为private[this],表示只能在当前类中访问。伴生对象也不可以访问
apply方法:
定义apply方法
object 伴生对象名 {
def apply(参数名:参数类型, 参数名:参数类型…) = new 类(…)
}
创建对象: 伴生对象名(参数1,参数2…) => 不需要new 可以直接使用
10. 继承
1. 使用extends关键字来实现继承
可以在子类中定义父类中没有的字段和方法,或者重写父类的方法
类和单例对象都可以从某个父类继承
语法:
class/object 子类 extends 父类 {

}
2.注意事项:
1. final 修饰的class不可以被继承
2. private修饰的成员不可以被继承
3. val 修饰的成员变量可以被继承,但是不可以被重写
4. final 修饰的成员变量可以被继承,但是不可以被重写
3、override与super
override用来重写成员变量或者方法
super用来子类调用父类的方法
4、isinstanceof与asInstanceOf
isInstanceOf:用来判断对象是否属于某种类型 【对象.isInstanceOf[类名]】
asInstanceOf:用来将对象转换为某一个类型 【对象.asInstanceOf[类名]】
5、getClass与ClassOf
getClass:获取对象所属某一个具体的类的class形式 【对象.getClass】
ClassOf:获取某一个具体类的class形式 【classOf[类名]】
6、抽象类
用abstract修饰的class为抽象类
抽象类中可以定义抽象方法【抽象方法如果有返回值必须定义返回值类型】与抽象字段【没有初始化的成员变量】
7、匿名内部类
匿名内部类是没有名称的子类,直接用来创建实例对象。Spark的源代码中有大量使用到匿名内部类。
scala中的匿名内部类使用与Java一致。
十六: 特质(trait) 相当于java中接口的概念
- 特质是scala中代码复用的基础单元
- 它可以将方法和字段定义封装起来,然后添加到类中
- 与类继承不一样的是,类继承要求每个类都只能继承一个超类,而一个类可以添加任意数量的特质。
- 特质的定义和抽象类的定义很像,但它是使用trait关键字
语法:
trait 名称 {
// 抽象字段
// 抽象方法
}
继承特质
class 类 extends 特质1 with 特质2 {
// 字段实现
// 方法实现
}
注意事项:
使用extends来继承trait(scala不论是类还是特质,都是使用extends关键字)
如果要继承多个trait,则使用with关键字
1、类似java的接口
2、trait中既可以定义抽象方法,也可以定义具体方法
3、trait中既可以定义抽象字段,也可以定义具体字段
4、trait的继承通过extends,多个trait的时候通过with关键字继承
5、对象混入:
让某一个对象或者某几个对象具有trait中定义的成员
语法: val/var 对象名 = new 类 with 特质
new ClassName with traitName
6、方法调用的链
根据继承关系,从右向左开始调用
7、构造器的初始化
根据继承关系,从左向右开始初始化父类的构造器,最后才初始化子类的构造器
- trait也有构造代码,但和类不一样,特质不能有构造器参数
- 一个类继承另一个类、以及多个trait,当创建该类的实例时,它的构造顺序如下:
1. 执行父类的构造器
2. 从左到右依次执行trait的构造器
3. 如果trait有父trait,先构造父trait,如果多个trait有同样的父trait,则只初始化一次
4. 执行子类构造器
8、trait可以继承class

十七:样例类
1. 语法格式:
case class 样例类名([var/val] 成员变量名1:类型1, 成员变量名2:类型2, 成员变量名3:类型3)
默认情况下,使用的val修饰的成员变量,val是可以省略的
2. 方法:
1. apply方法 可以让我们快速地使用类名来创建对
2. toString方法 返回 样例类名称(成员变量1, 成员变量2, 成员变量3…) 如 person(李四,21)
3. hashCode方法 如果所有成员变量的值相同,则hash值相同,只要有一个不一样,则hash值不一样。
4. copy方法 可以快速创建一个相同的实例对象,可以使用带名参数指定给成员进行重新赋值
5. equals方法 可以直接使用==比较两个样例类是否相等,即所有的成员变量是否相等
3. 样例对象
主要用在两个地方:
1. 定义枚举
2. 作为没有任何参数的消息传递
语法格式: case object 样例对象名

十八:模式匹配
1. 简单模式匹配(值匹配)
在Java中,有switch关键字,可以简化if条件判断语句。在scala中,可以使用match表达式替代。
语法格式
变量 match {
case “常量1” => 表达式1
case “常量2” => 表达式2
case “常量3” => 表达式3
case _ => 表达式4 // 默认匹配
}
2. 类型匹配
val str:Any = “hello”
str match{
case _:String => {…}
case :Int => {…}
case _ => {…}
}
3. 守卫
val a = stdIn.readInt() //键盘输入一个数字
a match {
case _ if a >=0 && a <=3 => println("[1-3]")
case _ if a >=4 && a <= 8 => println("[4-8]")
case _ => println(“未匹配”)
}
4. 样例类的匹配
val param:Any = Person(“zhangsan”,20)
param match{
case Person(name,age) => (name,age)
case Student(name) => {…}
case _ => {…}
}
5. 匹配数组
param match{
case Array(x,y,z) => {…} //表示数组元素值为x.y.z
case Array(x) =>{…} //表示数组只有一个元素为x
case Array(x,
*) =>{…} //表示以x开头的数组
}
6. 列表匹配
param match{
case x :: y :: z ::Nil =>{…}
case x::Nil =>{…}
case x :: tail =>{…}
}
7. 元组匹配
param match{
case (x,y,z) =>{…}
case (x:String,y:Int,z:Double)=>{…}
}

十九: Option类型
使用Option类型,可以用来有效避免空引用(null)异常.
scala中,Option类型来表示可选值。这种类型的数据有两种形式:
1、作用:避免空指针异常
2、子类: Some[有值]、None[没有值]
3、取值方式: option.getOrElse(默认值)

二十: 偏函数 ****
1、没有match关键字的模式匹配叫做偏函数
2、定义方式:
val 函数名:PartialFunction[A,B] = {
case xx => {…}
case yyy => {…}
}
A:代表入参类型
B:返回值的类型

正则
1、将字符串变成正则表达式: """正则字符串""".r
2、查找字符串中是否存在正则需要的字符串: regx.findAllMatchIn(字符串).size>0 
3、根据正则表达式获取表达式中的组: """正则字符串(组)""".r
	str match{
		case x @ regx(组名) => println(组的值)
	}

二十一: 异常 *****
异常处理的两种方式:
1、try{}catch{}finally{} //捕获异常
抛出异常
2、Try(表达式).getOrElse(默认值)

二十二: 提取器
1、作用: 将对象转成一个元组,可以将类用于模式匹配
2、unapply

二十四:泛型
1、定义方法的时候使用泛型:
def addT = x
2、定义类的时候使用泛型:
class 类名T{…}
3. 上下界
1. 上界: A<:B [A必须是B本身或者是B的子类]
2. 下界: A>:B [A必须是B本身或者是B的父类]
4. 协变,逆变,非变
非变 : class Pair[T]{}
- 默认泛型类是非变的
- 类型B是A的子类型,Pair[A]和Pair[B]没有任何从属关系
class MyClass[T]{}
val myclass1 = new MyClass[Super]
val myclass2 = new MyClass[Sub]
myclass1与myclass2没有任何关系
协变: class Pair[+T]
- 类型B是A的子类型,Pair[B]可以认为是Pair[A]的子类型
- 参数化类型的方向和类型的方向是一致的。
class MyClass[+T]{}
val myclass1 = new MyClass[Super]
val myclass2 = new MyClass[Sub]
myclass1是myclass2的父类
逆变: class Pair[-T]
- 类型B是A的子类型,Pair[A]反过来可以认为是Pair[B]的子类型
- 参数化类型的方向和类型的方向是相反的
class MyClass[-T]{}
val myclass1 = new MyClass[Super]
val myclass2 = new MyClass[Sub]
myclass2是myclass1的父类

二十五: actor编程
1. actor的编程模型
1. 创建class或者object继承Actor
2. 重写act方法
3. 通过loop+react实现消息的接收
4. 创建actor实例
5. 启动actor
6. 发送消息
2. actor的消息发送的方式
1. 异步无返回值 !
2. 同步有返回值 !?
3. 异步有返回值 !!
案例: 需求: 统计目录下多个文件下单词出现的次数
import java.io.File
import scala.actors.Actor
import scala.io.{BufferedSource, Source}
object Wordcounttest1 {

	  class wordCountActor() extends  Actor{
		override def act(): Unit = {
		  loop{
			react{
			  case filename : String =>
				//开始单词统计
				println(s"接收到的文件为: ${filename}")
				//读取要统计的文件
				val source:BufferedSource = Source.fromFile(filename)
				//获取文件的所有行
				val lines = source.getLines()
	//            println(lines.toList.foreach(println(_)))
			  val wordline = lines.toList
				//对获取到的每一行字符串进行切分,压平
			  val result: Map[String, Int] = wordline.flatMap(x => x.split(" ")).map(x => (x,1)).groupBy(x => x._1).map(x => (x._1,x._2.map(_._2).sum))
				//返回统计的结果
			  sender ! result
			}
		  }
		}
	  }
	  def main(args: Array[String]): Unit = {
		val DIR_NAME = "./data1"
		//1. 通过目录获取目录下所有的文件
		val file = new File(DIR_NAME)
		val fileList = file.list()
		//2. 构建目录+ 文件名
		val fileNameList = fileList.map(name => s"${DIR_NAME}/${name}")
		//3. 将文件发送到Actor进行每一文件的单词统计
		val futurelist =  fileNameList.map( filename => {
		  val actor = new wordCountActor
		  //1. 启动线程
		  actor.start()
		  //2. 发送文件名到wordcountactor中.进行单词统计
		  val result = actor !! filename
		  result
		})
		//判断所有的文件是否全部统计完成
		while(futurelist.filter(!_.isSet).size>0){}
	  //4. 对所有文件的统计结果进行汇总
		futurelist.flatMap(x => x.apply().asInstanceOf[Map[String,Int]])
		  .groupBy(_._1)
		  .map(x => (x._1,x._2.map(_._2).sum))
		  .foreach(println(_))
	  }
	}

二十六: 柯里化
定义: 是指将原先接受多个参数的方法转换为多个只有一个参数列表的过程
使用柯里化,让传递匿名函数作为参数的语法更为简洁

二十七:闭包
闭包就是一个函数,只不过这个函数的返回值依赖于声明在函数外部的变量

二十八: 隐式转换和隐式参数
隐式转换: 是指以implicit关键字声明的带有单个参数的方法。它是自动被调用的,自动将某种类型转换为另外一种类型。
隐式转换的使用步骤:
1. 在object中定义隐式转换方法(implicit)
2. 在需要隐式转换的地方,引入隐式转换(使用import)
3. 自动调用隐式转化后的方法
使用隐式转换的场景:
1. 当对象调用中不存在的方法时,编译器会自动将对象进行隐式转换
2. 当方法中的参数类型与目标类型不一致时
3.
二十九: akka
Akka *
1、akka编程模型
1、创建class/object继承Actor
2、重写Actor的生命周期方法[prestart、receive、poststop]
3、创建ActorSystem对象
4、实例化Actor
5、消息发送
2、akka定时调度
1、通过actorSystem直接创建
import scala.concurrent.duration._
import actorSystem.dispatcher
1、actorSystem.scheduler.schedule(什么时候开始第一次任务,每一次任务的间隔时间,发送到哪个actor,发送消息)
2、actorSystem.scheduler.schedule(什么时候开始第一次任务,每一次任务的间隔时间){自定义调度任务}
2、在actor中创建
import scala.concurrent.duration._
import context.dispatcher
1、context.system.scheduler.schedule(什么时候开始第一次任务,每一次任务的间隔时间,发送到哪个actor,发送消息)
2、context.system.scheduler.schedule(什么时候开始第一次任务,每一次任务的间隔时间){自定义调度任务}

发布了4 篇原创文章 · 获赞 0 · 访问量 706

猜你喜欢

转载自blog.csdn.net/fan_bigdata/article/details/103107854
今日推荐