1. Scala的介绍
-
Scala的官网https://www.scala-lang.org
-
Scala是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程(OOP)和函数式编程(FP)的各种特性
-
Scala运行在Java虚拟机上,并兼容现有的Java程序(Scala和Java之间可以互相调用)
-
Scala源代码被编译成Java字节码,所以它可以运行于JVM之上,并可以调用现有的Java类库
2. 类JVM语言
-
Kotlin
-
Scala
-
Clojure
-
Groovy
-
Jython
-
JRuby
-
…
3. Java与Scala的编程优势
Scala的优点
Scala的复杂功能促进更好的编码,并提高性能。函数,宏和元组只是Scala提供的一些改进。Scala将功能性编程和面向对象的编程融入了强大的语言。
Scala和C ++或Go这样的语言进行比较是很困难的,但是与Java比较就相对容易一点。我们将Scala与Java进行比较的原因是因为该语言是在Java虚拟机环境中运行的,该语言是为了消除Java的限制性,为程序员提供了一个组织良好,语言清晰的语言。
Java兼容性和互操作性
Scala和Java是不同的语言,但这并不意味着开发人员需要重新造轮子。Scala支持与Java的兼容性和互操作性,允许开发人员利用JVM的优势、保持Java库。
Scala中的单例对象使用,而不是Java或c++中的类静态使用,允许进行更清晰的编码。
4. Scala的应用领域
- kafka spark flink都是使用scala来编写的
- web开发
5. Scala的版本
- scala2.10.5 Spark1.6就是基于scala2.10.5编写的
- scala2.11.8 Spark2.0就是基于scala2.11.8编写的
6. 什么要学Scala
- 优雅
- 这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API是否优雅直接影响用户体验
- 速度快
- Scala语言表达能力强,一行代码抵得上Java多行,开发速度快;Scala是静态编译的,所以和JRuby,Groovy比起来速度会快很多
- 能融合到Hadoop生态圈
- Hadoop现在是大数据事实标准,Spark并不是要取代Hadoop,而是要完善Hadoop生态。JVM语言大部分可能会想到Java,但Java做出来的API太丑,或者想实现一个优雅的API太费劲
7. Scala能代替Java吗?
- 洗洗睡吧,Scala只是对Java不足的地方进行增强的一个编程语言
8. Windows的Scala安装
8.1 安装jdk
jdk要求1.8及其1.8+
8.2 安装scala
-
准备安装包
scala-2.11.8.msi -
一直下一步安装即可
9. Linux的Scala的安装
-
准备安装包
scala-2.11.8.tgz -
解压
tar -zxvf scala-2.11.8.tgz -C /opt -
配置环境变量
export SCALA_HOME=/opt/scala export PATH=$PATH:$SCALA_HOME/bin
10. Scala的REPL
- R(Read)E(evaluate)P(print)L(loop)
11. Scala的HelloWord
#在scala的repl中执行
println("hello word")
12. 使用Scala编译器编译Scala代码
-
创建一个Scala源代码文件
//Hello.scala object Hello{def main(agrs:Array[String]){println("hello word")} } -
使用scala编译器进行编译
scalac Hello.scala -
编译之后出现了class字节码文件,可以使用scala的执行器执行
scala Hello
13. Scala开发工具
- eclipse: 需要安装插件
- sbt: 直接可以使用
- idea:推荐使用
14. Scala的开发工具插件的安装配置
-
准备与idea版本对应的scala插件
scala-intellij-bin-2017.2.7.zip -
安装插件
-
安装完成之后重启idea即可
15. Scala声明变量
#使用val声明变量,相当于java中的final修饰,不能在指向其他的数据了val a = 10
#使用var声明变量,可以指向其他数据var b = 20
#scala中的变量的类型可以显式的声明,也可以不声明,如果不显式的声明这会根据变量的值来推断出来变量的类型(scala支持类型推断)val c:Int = 20
ps: scala中的设计原则是能少敲一次键盘绝不多敲一次,而且scala中推荐使用val来定义变量
16. Scala中的常用类型
Byte、Char、Short、Int、Long、Float、Double、Boolean、Any
17. Scala中的标识符的规则
- 数字 字母 下划线 美元符$组成
- 不能以数字开头
- 不能使用scala和java的关键字
- 做到"见名知意"
18. Scala中的流程控制
18.1 if语句
val a = 20
if (a > 30) {println(">30")
}else{println("<30")
}
18.2 while语句
var i = 0
var sum = 0
while (i < 100) {sum = sum + ii = i + 1
}
print(sum)
18.3 for循环
val name = "hello"
for (n <- name) {println(n)
}
val ages = Array(12, 14, 15, 16, 10)
for (age <- ages) {println(age)
}
19. Scala中的操作符
同java一致
20. Scala中的操作符的重载
//其实这些运算符都是方法
var a = 20
val b = 10
val c = a.+(b)
println(c)
21. Scala中的条件表达式
val x = 1
//判断x的值,将结果赋给y
val y = if (x > 0) 1 else -1
//打印y的值
println(y)//支持混合类型表达式
val z = if (x > 1) 1 else "error"
//打印z的值
println(z)//如果缺失else,相当于if (x > 2) 1 else ()
val m = if (x > 2) 1
println(m)//在scala中每个表达式都有值,scala中有个Unit类,写做(),相当于Java中的void
val n = if (x > 2) 1 else ()
println(n)//if和else if
val k = if (x < 0) 0
else if (x >= 1) 1 else -1
println(k)
22. Scala中的快表达式
var x=0
val result = {if (x < 0) {-1} else if (x >= 1) {1} else {"error""error1"//代码快的最后一条语句就是代码快的值,如果代码快的最后一条语句是一个表达式,这代码快的值为 () println("...") }
}
23. Scala中的循环的嵌套
//原始的循环嵌套
for (a <- 1 to 10) {for (b <- 1 to 10) {if (a == b) {println("a=" + a + " b=" + b)}}
}
//scala中的循环的嵌套的新写法
for (a <- 1 to 10; b <- 1 to 10 if a == b) {println("a=" + a + " b=" + b)
}
24. Scala中的for循环的推导式
//原生的写法
val arry = Array[Int](1, 2, 3, 4, 5)
val arry1 = new Array[Int](arry.length)
for (index <- 0 until arry.length) {arry1(index) = arry(index) + 10
}
println(arry1.toBuffer)
//scala中的yield推导式写法,yield推导式中不能出现循环体
val arry = Array[Int](1, 2, 3, 4, 5)
val ret = for (a <- arry) yield a + 10
println(ret.toBuffer)
25. Scala中的方法和函数的定义
25.1 Scala中的方法的定义
//方法的执行结果类型自动推断
def m1(a: Int, b: Int) = {println("hello method " + (a + b))23
}
//显式的指定方法的返回值类型
def m1(a: Int, b: Int):Unit = {println("hello method " + (a + b))23
}
//相当于方法的返回值这样声明 :Unit =
def m1(a: Int, b: Int) {println("hello method " + (a + b))23
}
25.2 Scala中的函数的定义
//scala中的函数的定义不需要声明函数的返回值类型
val f1 = (a: Int, b: Int) => a + b
println(f1(10, 20))
25.3 Scala中的方法的调用
//定义方法时如果没有参数但是却写了形参的括号,调用时可以加括号也可以省略括号
def m1(): Unit = println("m1...")
m1
//定义方法时如果没有参数并且没有写形参的括号,调用时一定不能加括号
def m1: Unit = println("m1...")
m1
25.4 Scala中的匿名函数
//把没有名称的函数称为匿名函数,单独定义函数时,必须声明形参的类型,如果给方法传递函数对象时可以省略形参的类型
(x:Int,y:Int)=>x+y
26. 方法和函数的区别
-
在函数式编程语言中,函数是“头等公民”,它可以像任何其他数据类型一样被传递和操作
-
在scala的方法中可以把函数当成方法的形参来使用
//定义一个方法,接收两个Int类型的普通参数,还有一个函数参数(计算法则) def m1(a: Int, b: Int, f: (Int, Int) => Int) = {f(a, b) } val f1 = (a: Int, b: Int) => a + b val ret = m1(10, 20, f1) println(ret)
27. 方法和函数之间的转换
//方法名称 _ 就能把一个方法转换成函数对象
def m1(a: Int, b: Int) = a+b
(m1 _) (10,20)
28. Scala中的数组
28.1 数组的定义
#定义数组并且给定初始值
val arry1 = Array(1,2,3,4)
#定义固定长度的数组,数组中的元素使用默认值来填充
val arry2 = new Array[Int](5)
28.2 数组的遍历
val arry = Array(10, 20, 30, 40, 50)
for (a <- arry) {println(a)
}
val arry = Array(10, 20, 30, 40, 50)
for(index <- 0 until arry.length){println(arry(index))
}
28.3 数组的常用操作
28.3.1 不可变数组Array
val arry = Array(40, 10, 20, 30, 50)//按升序进行排序
val ret1 = arry.sorted//反转数组
val ret2 = arry.sorted.reverse//更新数组中的元素
arry(2)=12//在数组的末尾添加元素,并且生成一个新的数组返回,原来的数组并没有改变
arry :+ 12//在数组的首部添加元素,并且生成一个新的数组返回,原来的数组并没有改变
arry.+:(12)
val arry = Array(40, 12, 21, 33, 50)//filter操作
val func1 = (x: Int) => {if (x % 2 == 0) true else false
}
println(arry.filter(func1).toBuffer)//map操作
val arry = Array(10, 20, 30, 1)
val ret: Array[Int] = arry.map(_ + 10)//flatten 压平 压扁
val arry = Array(Array(10, 20, 30), Array(10, 20, 30), Array(10, 20, 30))
arry.flatten//flatMap 相当于先map 然后再flatten
val arry = Array(Array(10, 20, 30), Array(10, 20, 30), Array(10, 20, 30))
val arry1 = arry.flatMap(ary => ary.map(_ + 100))//reduce聚合操作
val arry1 = Array(10, 20, 30, 40)
println(arry1.reduce(_+_))//foreach遍历操作
val arry1 = Array(10, 20, 30, 40)
arry1.foreach(println)//fold聚合操作
val ret = Array(10, 20, 30, 40).foldLeft(0)(_ + _)
val ret = Array(10, 20, 30, 40).fold(0)(_ + _)
val ret = Array(10, 20, 30, 40).foldRight(0)(_ + _)//aggregate聚合操作,aggregate里面调用的就是foldLeft
val ret: Int = arr1.aggregate(0)(_ + _, null)//sorted排序
val ret1 = arr1.sorted
val ret2 = arr1.sortBy(e => e)//分组操作
val arr1 = Array(90, 10, 20, 30, 40,50,60)
val ret = arr1.grouped(2)
ret.foreach(ary=>println(ary.toBuffer))//topn
val arr1 = Array(10, 20, 30, 40)
val ret = arr1.take(2)//从左开始取前两个
val ret = arr1.takeRight(2)//从右开始取两个,按从左到右的顺序放入新的数组并且返回
28.3.1 可变数组ArrayBuffer
val arr = ArrayBuffer(12, 13, 14)
val arr2 = ArrayBuffer(12, 13, 14)
//给ArrayBuffer作家元素
arr.append(20, 30, 40)
//移除指定位置的元素
arr.remove(2)
//在末尾追加元素
arr += 20
//从ArrayBuffer中移除12 这个元素
arr -= 12
//在ArrayBuffer的末尾追加一个ArrayBuffer
arr ++= arr2
//修改ArrayBuffer中的元素
arr(0)=130
println(arr)#############其他的操作都和Array一致(map filter fold...)#################
29. Scala中的集合
29.1 Scala中的List
29.1.1 不可变的List
//使用了List伴生类的伴生对象创建的
val list = List(10, 11, 12, 13)//在列表的末尾添加元素,返回一个新的List
val list = List(10, 11, 12, 13)
list.:+(12)//在列表的开始添加元素,返回一个新的List
list.+:(12)//把两个List合并到一起,返回一个新的List
val list1= List(10, 11, 12, 13)
val list2 = List(10, 11, 12, 13)
val ints: List[Int] = list1:::list2//其他的操作同Array一致
29.1.2 可变的List
import scala.collection.mutable._
//创建可变的List
val list = ListBuffer(10, 12)
//追加元素
list.append(20)
//追加元素
list += 12
//移除元素
list -= 12
//其他的操作同Array一致
29.2 Scala中的Set
29.2.1 不可变的Set
//创建不可变的Set
val set = Set(5, 1, 2, 3, 4, 1)
val set2 = Set(10, 20, 30)
//给Set添加元素,返回新的Set
println(set + (23))
//移除Set中的元素,返回新的Set
println(set - (23))
//合并两个Set,返回一个新的Set
println(set ++ (set2))
//其他操作和Array一致
29.2.2 可变的Set
import scala.collection.mutable._
val set = Set(10, 20, 30)
//给Set的末尾添加元素
set += (40)
//移除Set中的元素
set -=(10)
println(set)
//其他操作和Array一致
29.3 Scala集合中的Map
29.3.1 不可变的Map
val map = Map("name" -> "xiaoming", "age" -> 20, "sex" -> "mr")
//获取map中的值
println(map("name"))
//给map中添加k-v返回新的map
println(map.+("love" -> "java"))
println(map.-("name"))
//其他操作和Array一致
29.3.2 可变的Map
import scala.collection.mutable._
val map = Map[String, String]()
//给map中添加元素
map.put("name", "tom")
map += ("age" -> "20")
//移除map中的key所对应的元素
map -= ("name")
map.remove("age")
println(map)
//其他操作和Array一致
30. Scala中的元组Tuple
//定义元组,元组中的最大个数可以存放22个
val t3 = (10, "name", "other")
//获取元组中的数据
println(t3)
//获取元组的第一个元素
println(t3._1)
//scala中Map的一个k-v键值对其实就是一个二元元组
val map = Map(("name", "xiaohua"), ("age", 13),("sex","nan"))
println(map("name"))
31. Scala中的异常的处理
try {//可能出现异常的代码val a = 10val b = 0val c = a / b;println(c)
} catch {//如果出现异常则从上而下匹配异常类型的匹配case e: ArithmeticException => println("算术异常" + e)case e: Exception => println("exception异常" + e)case _ => println("最大的异常范围")
} finally {//无论如何都会执行的代码快println("finally")
}
32. Scala中的懒加载
//如果文件名称错误则会立刻报错
val content = Source.fromFile("F:\\bigdata03\\day35[scala]\\笔记\\books1.txt").mkString//如果使用lazy来修饰则不会立刻报错,只有当第一次使用时才报错
lazy val content = Source.fromFile("F:\\bigdata03\\day35[scala]\\笔记\\books1.txt").mkString
println(centent)//报错
33. Scala的面向对象编程
33.1 Scala中的类的创建
class Person{}
33.2 定义类中的成员
object Ops3 {def main(args: Array[String]): Unit = {val p = new Person()p.say()println(p.age) //可以访问println(p.name) //可以访问//p.age_=(12)//调用setter方法p.age = 20 //这样调用的底层也是调用_=的setter方法println(p.age)//println(p.sex)//不能调用其私有的成员变量}
}class Person {//类中的成员属性,Scala中的成员属性都是私有的val name: String = "xiaohua"//如果使用val修饰,则会自动生成公有的getter方法var age: Int = 23//如果使用var修饰,则会自动生成公有的getter和setter方法private var sex: String = "nane" //使用private var修饰,则会生成私有的getter和setter方法//类中的成员方法private[this] var addr: String = "甘肃省"def say() = {println("say...")}
}
总结:
- private private[this] 和 var val 没有直接的联系
- scala中定义的成员变量肯定是私有的
- 使用var修饰则会生成公有的getter和setter方法
- 使用val修饰则会生成公有的getter方法
- 使用private val修饰则会生成私有的getter方法,可以在伴生对象中访问
- 使用private var修饰则会生成私有的getter和setter方法,可以在伴生对象中访问
- 使用private[this] var或者val修饰则都不会生成getter和setter方法,只能在本类中使用,不能在伴生对象中访问
33.3 类的构造体(构造器)
//构造体中的修饰符private 与var val的规则同上面一致
//在类定义的后面的构造体称为主构造体,当然我们还可以定义辅助构造体
class Student(val name: String, var age: Int, private[this] val sex: String) {def say(): Unit = {}
}//定义辅助构造器,辅助构造器可以定义多个,但是辅助构造器的第一行必须调用主构造器
def this(name: String, age: Int) {this(name)
}
33.4 类的嵌套
object Ops5 {def main(args: Array[String]): Unit = {val i = new Outter()//创建内部类的对象val inner = new i.Inner()}
}class Outter {class Inner {def say(): Unit = {println("say...")}}}
33.5 单例对象
-
在scala中并没有static关键字,要实现类似java中的static的功能就必须定义object
object DbUtils {def add(): Unit = {println("add....")} }
33.6 Scala中的伴生类和伴生对象
/*** 在同一个文件中类名和单例对象名称如果相同则* 这个类被称为单例对象的伴生类* 这个单例对象被称为类的伴生对象*/
class Phone {private var name: String = _
}object Phone {def say(): Unit = {println(new Phone().name)}def apply(name:String,age:Int,love:String*): Phone = new Phone()
}
33.7 Scala中的类的扩展
class Person {def say(): Unit = {println("Person...say")}
}class Student extends Person {override def say() {println("Student..say")}
}
33.8 Scala中的抽象类
class Student extends AA {override def aa(): Int = {12}
}abstract class AA {//普通的方法def a() {}//抽象方法,只有定义,没有实现def aa(): Int
}
33.9 Scala中的特质Trait
- java中的接口中的方法不能有实现,但是新版本中可以有方法的实现
- scala中trait就相当于java新版本中的接口
class Student extends Igame1 with Igame2{override def mm1(): Unit = {}override def mm2(): Unit = {}
}trait Igame1 {def m1(): Unit = {println("m1...")}//只有定义没有实现的抽象方法def mm1()
}trait Igame2 {def m2(): Unit = {println("m2...")}//只有定义没有实现的抽象方法def mm2()
}
34. Scala中的类型检查和类型转换
//判断一个实例是否属于某个类型
val bool: Boolean = stu.isInstanceOf[Person]
//类型转换
val igame = stu.asInstanceOf[Person]
35.Scala中的样例类
/*** 样例类的定义* 1. 样例类自动实现序列化接口* 2. 样例类自动生成伴生对象* 3. 样例类一般用来网络传输和模式匹配*/case class Person1(name: String, age: Int)
/*** 样例对象* 一般用作模式匹配中匹配唯一对象*/
case object Person2
36. Scala中的模式匹配
val a = "hello"
a match {case x:String=>println(x)//类型的匹配case "hello" => println("hello...") //值得匹配case _ => println("_______")//通用匹配
}//匹配List中的元素
val list = List(10, 20, 30, 40)
list match {case List(10, 20, x, y) => println(x + "......")case List(10, 20, 30, y) => println(y + "......")
}//scala中的样例类的匹配
object Ops5 {def main(args: Array[String]): Unit = {val arr = Array(CheckTimeOutTask, HeartBeat(12333), SubmitTask("0001", "task-0001"))arr(Random.nextInt(arr.length)) match {case SubmitTask(id, name) => {println(s"$id, $name")}case HeartBeat(time) => {println(time)}case CheckTimeOutTask => {println("check")}}}
}case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
case object CheckTimeOutTask
37. Scala中的Option类型
-
在Scala中Option类型样例类用来表示可能存在或也可能不存在的值(Option的子类有Some和None)。Some包装了某个值,None表示没有值
val map = Map("name" -> "xiaohua", "age" -> 20, "sex" -> "man") //对偶 二元元组 val option: Option[Any] = map.get("name1") option match {case Some(v)=>println(v)case None=>println("没有找到kei所对应的值") }
38. Scala中的偏函数
- 被包在花括号内没有match的一组case语句是一个偏函数
- 它是PartialFunction[A, B]的一个实例,A代表参数类型,B代表返回类型,常用作输入模式匹配
//偏函数
def func1: PartialFunction[String, Int] = {case "one" => 1case "two" => 2case _ => -1
}//普通的模式匹配
def func2(num: String): Int = num match {case "one" => 1case "two" => 2case _ => -1
}def main(args: Array[String]): Unit = {val ret = func1("one")println(ret)
}
39. Scala中的闭包
-
函数或者方法的嵌套
-
内部函数|方法 使用了外部函数|方法的形参
-
外部方法的返回值是内部方法所对应的函数对象
package com.it.bigdata.scalaobject Ops1 {def main(args: Array[String]): Unit = {val ret = func(10)(20)//柯里化println(ret)}val func = (x: Int) => (y: Int) => x + ydef m1(x: Int) = {def m2(y: Int) = x + y//把方法转换成函数m2 _} }
40. Scala中的柯里化
柯里化就是把接受多个参数的一个函数|方法 转换成接受一个参数一个参数的函数|方法的过程
41. Scala中的高阶函数
object Ops2 {def main(args: Array[String]): Unit = {val ret = func(10, 20, (x, y) => x + y)println(ret)}//func函数就可以称为 高阶函数 (函数的形参可以是函数对象,把这个接受函数对象作为形参的函数称为高阶函数)val func = (x: Int, y: Int, op: (Int, Int) => Int) => op(x, y)
}
42. Scala中的隐式转换
42.1 隐式转换的介绍
- 隐式转换和隐式参数是Scala中两个非常强大的功能,利用隐式转换和隐式参数,你可以提供优雅的类库,对类库的使用者隐匿掉那些枯燥乏味的细节
42.2 隐式转换的作用
- 隐式的对类的方法进行增强,丰富现有类库的功能
42.3 隐式转换函数
- 是指那种以implicit关键字声明的带有单个参数的函数
- 同一个类中多个隐式转换方法|函数,形参的类型都是唯一的
/*** 隐式转换函数*/
object Ops3 {def main(args: Array[String]): Unit = {//隐式转换方法,把File对象转换成RichFileimplicit def file2RichFile(file: File) = new RichFile(file)//调用一个对象不存在的方法,则自动调用隐式转换方法|函数val ret = new File("f://a.txt").read()println(ret)}
}/*** 定义增强的类** @param file*/
class RichFile(file: File) {def read() = Source.fromFile(file).mkString
}
42.4 隐式转换函数调用的时机
- 调用对象不存在的方法时,调用隐式转换方法|函数
- 变量的值和类型不一致时会调用隐式转换方法|函数
- 调用函数|方法时形参的类型和实参的类型不一致时调用隐式转换方法|函数
43. Scala中的泛型和类型界定
43.1 scala中的泛型的定义
object Ops5 {def main(args: Array[String]): Unit = {val u = new User[Int]()u.say(13)}
}/**
*在类上定义泛型
*/
class User [T]{def say(a: T) = {println(a)}
}
object Ops5 {def main(args: Array[String]): Unit = {val u = new User()u.say[Int](13)}
}/**
*在方法上定义泛型
*/
class User {def say[T](a: T) = {println(a)}
}
43.2 scala中的类型界定(泛型的限制)
上界 <:
object Ops5 {def main(args: Array[String]): Unit = {val u = new User()u.say(new Stu)}
}class User {def say[T <: Person](a: T) = {println(a)}
}class Person
class Stu extends Person
下界 >: 规定下界
视图界定 <%
object Ops5 {def main(args: Array[String]): Unit = {val u = new Userimport MyImplicit.phone2Personu.say(new Phone)}
}class User {def say[T <% Person](a: T) = {println(a)}
}class Personclass Stu extends Personclass Phone {def talk() = println("tal")
}
上下文界定 : 隐式转换函数+柯里化+implicitly
逆变 -
协变 +
44. Scala中的Actor与Akka
- actor===>java中的多线程+socket
- akka===>基于actor的高效的网络通信框架(spark1.6之前内部通信使用akka)
- 在spark1.6中已经加入了另外一种通信框架netty,并且把netty做为默认的通信框架
- 在spark2.0及其以后彻底移除了akka,完全使用netty来实现spark进程通信
- akka的弊端: 各个版本之间的兼容性不好