1. Scala简介
- Scala是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性
- Scala运行在Java虚拟机上,并兼容现有的Java程序 (Scala是类Jvm言)
- Scala源代码被编译成Java字节码,所以它可以运行于JVM之上,并可以调用现有的Java类库
- Spark和flink等大数据框架都是使用Scala开发的
2. Scala的版本
- 2.10.x:(兼容spark2.x↓)
- 2.11.x:(兼容spark2.x↑)
- 因为spark2.x后,spark的是使用scala2.11.8编译的,所以在此我们一般企业开发都会选择一致的版本2.11.8
3. Scala的安装
3.1 windows的安装
一路下一步,安装完成之后,自动会把路径加入环境变量直接使用即可
3.2 linux的安装
解压,配置环境变量即可使用
4. Scala的REPL
REPL:Read Eval Print Loop交互式环境(交互式解释器)
jdk9.x也支持REPL(jshell)
5. Scala的第一个程序
5.1 编写Scala源代码文件
Ops1.scala
object Test1{
def main(args:Array[String]):Unit={
println("hello bigdata")
}
}
5.2 编译Scala源代码
scalac Ops1.scala
5.3 执行class文件
scala Test1
6. Scala与Java的区别
都是基于JVM虚拟机运行的
- Scala编译之后的文件也是.class,都要转换为字节码,然后运行在JVM虚拟机之上
Scala和Java相互调用
- 在Scala中可以直接调用Java的代码,同时在Java中也可以直接调用Scala的代码
Java 8 VS Scala
- Java 8(lambda)没有出来之前,Java只是面向对象的一门语言,但是Java 8出来以后,Java就是一个面向对象和面向函数的混合语言了。
- scala与java都支持面向对象和函数式编程(jdk1.8才开始支持)
- Scala设计的初衷是面向函数FP
- Java的设计初衷是面向对象OOP
7. Scala解释器和IDEA
Scala的解释器
- REPL
IDEA
- eclipse
- IntelliJ IDEA(推荐使用)
8. Repl的解释
res0: Int = 12
在我们输入解释器的每个语句后,它会输出一行信息,类似res0: Int = 12。输出的第一部分是REPL给表达式起的变量名。
9. Scala中的变量的定义
var 变量名称:变量类型 = 变量值
==在scala中除了定义变量还可以定义不可变的量”常量”==
val 变量名称:变量类型 = 变量值
在Scala编程中推荐使用val,要val不能解决问题才使用var
10. ==Scala中的类型推断==
var a1:Int = 10
var a2 = “hello”(在Scala中就会根据值的类型推断出变量的类型相当与 var a2:String = “hello” )
var a3 = 20
a3=”hello” ==错误的,因为a3虽然没有显示的声明类型,但是根据值已经推断出a3是Int类型==
11. Scala中的编程思想
能少敲一下键盘绝不多敲一下
12. Scala中的数据类型
基本数据类型(AnyVal)
- Boolean
- Byte
- Short
- Char
- Int
- Float
- Long
- Double
引用数据类型(AnyRef)
- String
……
- String
AnyVal和AnyRef都是Any的子类
在Scala中没有void,使用Unit来代替
13. Scala中的运算符重载
在Scala中是没有运算符的,所有的运算符都是方法
==ps: ==
- 在Scala中没有++ 和–
- 在Scala中没有三元运算符
14. Scala中的的流程控制
- 在Scala中的代码快都是有返回值的
- 在Scala中代码快的返回值是代码快的最后一条语句执行的结果
- 在Scala中不能使用return关键字
- Scala中没有switch语句(有更加强大的模式匹配)
14.1 Scala中的if语句
var ret = if(a>10) ">10" else 0
14.2 Scala中的for循环
//单循环
for(i <- 1 .to(10) ) {
println( i)
}
//普通的双循环
for(i <- 1 .to(10) ) {
for(j <- 1 .to(10) ) {
println("i=="+i+",j=="+j)
}
}
//改良(循环嵌套使用分号分隔)
for(i <- 1 .to(10);j <- 1 .to(10)) println("i=="+i+",j=="+j)
//带条件的双循环
for(i <- 1 .to(10);j <- 1 .to(10)) {
if(i!=j) println("i=="+i+",j=="+j)
}
//if守卫
for(i <- 1 .to(3);j <- 1 .to(3) if(i!=j)) println("i=="+i+",j=="+j)
//yield把循环的结果放到一个Vector中, yield后面不能出现其他语句
var ret = for (i <- 1.to(3); j <- 1.to(3) if (i != j)) yield j
//for循环的跳出循环(在scala中没有break和continue)
import scala.util.control.Breaks._
breakable {
for (i <- 1.to(10)) {
println(i)
if (i == 6) {
break();
}
}
}
14.3 while do…while
与Java中一致
15. 代码快的返回值
- 代码快的值是代码快最后一个语句的计算结果
- 如果代码快的最后一个语句为赋值语句或打印语句则代码快的值为Unit,表现形式为 ()
16. Scala中的输出
//scala风格的输出
print("hello");
println("hello,bigdata")
//C语言风格的输出
printf("Hello, %s! You are %d years old.\n", "Fred", 42)
17. Idea开发Scala(pom)
- 在Idea中安装开发Scala的插件
scala-intellij-bin-2017.2.7.zip
- POM依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.uplooking.bigdata</groupId>
<artifactId>scala</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<scala.version>2.11.8</scala.version>
</properties>
<dependencies>
<!-- 导入scala的依赖 -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<!-- 编译scala的插件 -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>scala-test-compile</id>
<phase>process-test-resources</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
18. Scala中的函数和方法
18.1 函数和方法的区别
- 方法操作的是对象,函数操作的不是对象
- 其实在实际开发过程中并不会对函数和方法做严格的区分
18.2 方法的定义
def 方法名称(参数名称:参数类型,....):方法的返回值类型 = {方法体}
//定义方法
def say1(str: String): Unit = {
println(str)
}
- 定义方法时形参必须要有类型
- 方法的返回值类型可以省略,一般如果不是递归调用则不会显示的声明返回值类型,返回值类型支持类型推断
18.3 过程
def 函数名称(参数名称:参数类型,....) {函数体}
- 在 过程 中,我们关注的是函数的副作用
18.4 单行函数
//单行函数就是写在一行的函数
def say01(str: String) = "hello" + str;
18.5 默认参数
def say01(name: String, age: Int = 12) = "name=" + name + ";age=" + age;
- 默认值参数一般放在参数的最后几个
18.6 可变参数
def say02(age: Int*) = age
- 传多个参数返回WrappedArray
19. Scala中的Lazy
- 当val被声明为lazy时,它的初始化将被推迟,直到我们首次对它取值。例如,
lazy val lines= scala.io.Source.fromFile(“D:/test/scala/wordcount.txt”).mkString
- 如果程序从不访问lines ,那么文件也不会被打开。但故意拼错文件名。在初始化语句被执行的时候并不会报错。不过,一旦你访问words,就将会得到一个错误提示:文件未找到。
lazy val lines= scala.io.Source.fromFile(“D:/test/scala/wordcount.txt”).mkString
- 懒值对于开销较大的初始化语句而言十分有用
20. Scala中的定长数组(Array)
20.1 数组的定义
//定义数组有两种方式
//直接初始化的定义
val ages = Array(12,14,15,10)
//创建一个长度为10 的数组
var ages = new Array(10);
20.2 数组的访问
println(ages(1))
ages(3) = 12
20.3 数组的遍历
for(age <- ages) println(age)
20.4 数组的长度
ages.length
21. Scala中的变长数组(ArrayBuffer)
21.1 ArrayBuffer的创建
var agess = ArrayBuffer(12,13,14,15)
21.2 ArrayBuffer的操作
mkString(","); 把Scala中ArrayBuffer的数组读取出来按指定的分隔符以字符串的形式打印
+=(element*);Scala中的追加一个元素
++=(arrayList)
22. 定长数组与边长数组的转换
val ages =Array(10,12,15)
val buf = ages.toBuffer//定长数组转为边长数组
val ary = buf.toArray//变长数组转为定长数组
23. Scala中的匿名函数
//在匿名函数中不能显示的声明匿名函数返回值的类型
val func1 = (name: String, age: Int) => name
//调用匿名函数
func1("admin",12)
- 如果单独定义匿名函数,则函数中的形参的类型和小括号都不可以省略
24. Scala中的方法调用
- 定义方法时如果没有参数但是有括号,则调用时可以写括号也可以不写括号
- 定义方法时如果没有参数而且无括号,则调用时一定不能有括号
25. Scala中的集合
25.1 集合的继承结构
|-Traversable
|-Iterable
|-Set(是一组没有重复元素的集合)
|-HashSet
|-TreeSet
|-Map(Map,是一组k-v对 )
|-HashMap
|-Seq(是一组有序的元素 )
|-Array
|-String
|-Vector
==|-List(列表)==
|-Queue(FIFO)
|-Stack(FIEO)
25.2 List的操作
//创建List
val ages1 = List(12, 14, 16);
val ages2 = List(10, 11, 19);
//把两个集合合并在一起,从冒号的一边开始计算
val ages3 = ages1 ::: (ages2);
25.3 Set的操作
//创建Set
val set1 = Set(12, 14, 12, 10)
for (elem <- set1) println(elem)
25.4 Map的操作
//定义map
val map = Map("name" -> "admin", "age" -> 12)
//遍历map
val map = Map("name" -> "admin", "age" -> 12)
for (elem <- map) {
println(elem._2)
}
25.5 Scala中的元组Tuple
元组: 一组数据的集合,最大有22个
val t =Tuple2(12,14)
var t = (12,14,15)//有几个元素就是Tuple几
println(t._1)//获取元组中的数组 t._1 t._2 t._3....
26. Scala中的闭包
- 函数的嵌套
- 外部函数的返回值是内部函数
- 内部函数使用了外部函数的参数
//定义具有闭包特性的两个函数(方法)
def A(i: Int) = {
def B(j: Int) = {
i + j
}
B _
}
//调用闭包函数
val b = A(10)
val ret = b(20)
println(ret)
//柯里化
val ret = A(10)(20)
println(ret)
27. Scala中的柯里化
把传递多个参数的函数编程传递一个一个参数的函数
//柯里化定义函数 def fun1(a: Int)(b: Int) = a + b //柯里化调用函数 val ret = fun1(12)(10) println(ret)
柯里化的过程
//var func = (a:Int,b:Int,c:Int)=>a+b+c val func = (a: Int) => (b: Int) => (c: Int) => a + b + c println(func(10)(20)(30))
28. 高阶函数
函数func1的参数是函数func2,把func1就称为高阶函数
//定义高阶函数
val func1 = (func2: (Int, Int) => Int) => func2(10, 20)
//定义匿名函数
val func2 = (a: Int, b: Int) => a + b
//调用高阶函数
val ret = func1(func2)
println(ret)
//使用def的方式来定义高阶函数
def func1(func2: (Int, Int) => Int) = {
func2(10, 20)
}
//定义匿名函数
val func2 = (a: Int, b: Int) => a + b
//调用高阶函数
val ret = func1(func2)
println(ret)
- 直接定义匿名函数,匿名参数的类型和括号不可以省略
- ==直接传递匿名函数给高阶函数类型可以省略(一般都会省略),如果匿名函数只有一个参数括号也可以省略==
29. 高阶函数常用的调用方式
不省略匿名函数的类型(不经常使用)
val func1 = (func2: (Int, Int) => Int) => func2(10, 20) func1((a: Int, b: Int) => a + b)
省略匿名函数的类型(比较常用)
val func1 = (func2: (Int, Int) => Int) => func2(10, 20) func1((a, b) => a + b)
使用通配符(比较常用)
val func1 = (func2: (Int, Int) => Int) => func2(10, 20) func1(_ + _)
30. 集合的常用高阶函数
foreach
val list1 = List(12, 14, 15, 10) //foreach内部循环遍历集合中的每个元素,每遍历一个元素,调用一次我们自己传递的匿名函数(计算法则) val ret = list1.foreach(i =>println(i))
map
//map内部循环遍历集合中的每个元素,每遍历一个元素,调用一次我们自己传递的匿名函数(计算法则),最后把每一次的计算法则的返回值放入一个新的List返回 val ret2 = list1.map(_ + 10) println(ret2)
count**
//count内部循环遍历集合中的每个元素,每遍历一个元素,调用一次我们自己传递的匿名函数(计算法则),最后如果每一次的计算法则的返回值为true则count会加1,最后返回count var ret = list1.count(i => { if (i > 13) { true } else { false } }) println(ret)
- union并集
- intersect交集
- diff差集
- groupBy分组
- sorted排序
- sortBy根据元素进行排序
- zip拉链操作
- **fold折叠操作
==31. Scala中的面向对象编程==
32. Scala中的类
32.1 Scala中类的定义
class Person {
var name: String = _;
val age: Int = 0;
}
- 定义在类中的属性全部为私有属性
- 使用val修饰的属性不能再赋值
- 类中的属性必须初始化
32.2 Scala对象的创建
val p = new Person();
32.3 属性的访问
在Class定义的内部声明的属性都被系统置为private,外部不能直接访问,但是我们一般是一下这样来访问属性的
println(p.name); //其实访问的不是属性,只是在调用getter方法 def name=this.name p.name="admin" //其实这个本质上就是调用setter方法, def name_=(name:String)={this.name=name}
32.4 Scala中的构造器
主构造器
- 在定义类时类名后面跟()就是主构造器
- 主构造器的属性用var修饰,则属性私有化 对外提供getter和setter方法
- 主构造器中的属性用val修饰,则属性私有化,对外提供getter方法
- 主构造器中的属性不用var和val修饰,则属性私有化,内部生成私有的getter和setter方法
- scala中可以定义多个辅助构造器,但是辅助构造器的第一行必须调用主构造器或者其他辅助构造器
33. Scala中的嵌套类
在Scala中,你几乎可以在任何语法结构中内嵌任何语法结构
class A { class B { def say() = { println("halou B.....") } }
//创建内部类的对象 object Ops1 { def main(args: Array[String]): Unit = { //创建外部类对象 val a: A = new A() val b = new a.B() b.say() } }
34. Scala中的object(单例对象)
- 在Scala中没有static的概念的
Scala中的Object中定义的成员都是静态的
//创建对象 object Obj2 { def aa(): Int = { 90 } }
//调用对象的方法 object Ops2 { def main(args: Array[String]): Unit = { println(Obj2.aa()) } }
35. Scala中的伴生对象
- 在同一个.scala文件中出现一个类名和Object名称相同的类和对象,那么把这个对象就称为类的伴生对象,把类称为对象的伴生类
- 伴生对象中可以访问私有的伴生类中的成员
一般使用伴生对象来创建伴生类的对象
object Ops3 { def main(args: Array[String]): Unit = { val p = Person("admin",12) println(p.age) } } //定义一个Person类 class Person { private var name: String = _; var age: Int = _; def say() = { println("my name is " + this.name + "my age is " + this.age) } } //定义一个Person对象 object Person { def apply(name: String, age: Int): Person = { val person = new Person() person.name = name; person.age = age; person; } }
36. Scala中的继承
36.1 继承父类
class Person() { var name: String = _; var age: Int = _; def say() = { println("my name is " + this.name + " ,my age is " + this.age) } } class Student extends Person
object Ops4 { def main(args: Array[String]): Unit = { val student = new Student() student.name = "admin" println(student.name) student.say() } }
/**
*/
class Student extends Person {
override def say() = {
println("hello,my name is " + this.name + " ,my age is " + this.age)
}
//对象也可以继承类,访问时直接使用对象方法
object Student extends Person {
override def say() = {
println("hello,my name is " + this.name + " ,my age is " + this.age)
}
}
//访问
Student.say();
# 36. 抽象类
- 抽象类在定义之前使用abstract修饰
- 抽象方法所在的类一定要声明为抽象类
- 抽象类中可以没有抽象方法
object Ops4 {
def main(args: Array[String]): Unit = {
val stu = new Student();
stu.see();
stu.say();
}
}
abstract class Person() {
var name: String = _;
var age: Int = _;
def say() = {
println("my name is " + this.name + " ,my age is " + this.age)
}
def see();
}
class Student extends Person {
override def see() = {
println("see.....")
}
}
# 37. Scala中的特质(接口)
- Scala中特质使用trait修饰
- Scala中的特质中可以有抽象方法,但是同时可以有普通方法
- 扩展特质使用extends或者with关键字
object Ops5 {
def main(args: Array[String]): Unit = {
val p9 = new HuaWei()
p9.gaming();
}
trait IGame {
def gaming();
def aa() = {
println("aaa.....")
}
}
trait IMp3 {
def mp3();
}
class P
class HuaWei extends P with IMp3 with IGame {
def gaming() = println("gaming......")
def mp3() = {
println("mp3.....")
}
}
# 38. extends和with区别
- extends可以扩展类,抽象类,特质
- with只能扩展特质
# 39. 扩展App的特性
object Ops6 extends App{
println("hello")
}
# 40. Scala中实现枚举
Scala并没有枚举类型。不过,标准类库提供了一个Enumeration助手类,可以用于产出枚举。定义一个扩展Enumeration类的对象并以Value方法调用初始化枚举中的所有可选值
object Ops6 extends App {
println(Color.Red)
}
object Color extends Enumeration {
val Red, Yellow, Green =Value
}
# 41. Scala中的样例类
- Scala中的一种特殊的类
- 样例类使用**case class**来修饰
- 样例类的主构造体必须有(必须有括号)
- 一个类被声明为样例类则自动创建其伴生对象,并且生成两个apply方法(一个无参数,另外一个的参数和样例类中构造器的参数一致)
- 样例类主构造体中的参数私有化,但是对外提供getter方法
- 除了生命样例类还可以生命样例对象 **case object**,样例对象不能主构造体
# 42. Scala模式匹配
- 因为Scala中没有Switch..case的语法,但是模式匹配就相当于switch..case的语法,而且比其更加强大
## 42.1 匹配值
val op = “+”
op match {
case "+" => println("加")
case "-" => println("减")
case "*" => println("乘")
case "/" => println("除")
case _ => println("......")//变量的匹配 变量名称就为 _
}
## 42.2 类型匹配
val s: Any = "hello"
s match {
case str: Int => println("Int")
case str: String => println("String类型")
case _ => println(".....")
}
## 42.3 集合的匹配
val ages = List(12, 13, 14, 15)
ages match {
case List(12, _*) => println("List(12, _*)")
case _ => println("_________")
}
## 42.4 样例类匹配
object Ops5 {
def main(args: Array[String]): Unit = {
val user = User("admin", 12)
user match {
case User(name, age) => println("..." + name)
}
}
}
case class User(name: String, age: Int)
## 42.3 样例对象匹配
object Ops5 {
def main(args: Array[String]): Unit = {
val user = User(“admin”, 12)
val p = Person
p match {
case Person => println(“…”)
}
}
}
case class User(name: String, age: Int)
case object Person
# 43. Scala中的Option
- Option类型用来表示可能存在也可能不存在的值
- Some(值) 存在值
- None不存在值
- getOrElse(默认值)
- isEmpty 判断是否为None
# 44. Scala中implicit
## 44.1 隐式参数
- 如果形参使用implicit修饰,要么直接传递隐式参数值,其实这种直接传递隐式参数值的方式就使得我们的隐式参数没有任何作用了(**不可取**)
- 不传递隐式参数的值,则不能写多余的括号(**一般不传递**)
- 隐私参数必须是柯里化方法的最后几个参数
- 隐式参数不传递,则在运行时就要到当前的上下文中去找与之匹配的隐式值
- 在当前上下文中一个类型只能对应一个隐式值
implicit val c1: Int = 30
implicit val s1 = “hello”
def sum(a: Int)(implicit b: Int, c: String) = a + b + c
val ret: String = sum(10)
println(ret)
## 44.2 隐式类型转换
### 44.2.1 隐式转换发生的时机
- **传递的实参的类型和形参的类型不一致时,会到当前的上下文中去查找有无可用的隐式转换函数(根据函数的签名查找)**
//定义隐式转换函数
implicit def string2Int(str: String) = str.toInt
def sum(a: Int, b: Int) = a + b
val ret: Int = sum(12, “13”)
println(ret)
- **把一个类型的值赋值给另外一个类型的变量时**
implicit def string2Int(str: String) = str.toInt
val a: Int = “23”//到当前上下文查找可用的隐式转换函数,根据函数的签名查找
println(a)
object Ops8 {
def main(args: Array[String]): Unit = {
implicit def string2Int(str: String) = str.toInt
implicit def person2Int(p: Person) = p.age
val p = new Person("admin", 23)
val a: Int = p
println(a)
}
}
class Person(name: String, val age: Int)
- **调用对象不存在的方法时**
package com.uplooking.scala
object Ops8 {
def main(args: Array[String]): Unit = {
implicit def phone2MyCustomPhone(phone: Phone) = new MyCustomPhone
val phone = new Phone
phone.say();
}
}
class MyCustomPhone() {
def say() = {
println(“Person…say()”)
}
}
class Phone() {
def call() = {
println(“Phone…call()”)
}
}
## 44.3 隐式类
/隐式类必须定义在object中/
implicit class MyCustomPhone(phone: Phone) {
def say() = {
println("MyCustomPhone...say()")
}
}
- 隐式类其实就是隐式转换函数的一种语法糖
- 隐式类的功能 = 普通类 + 隐式转换函数
# 45. 类型参数(泛型)
- 类型参数
相当于Java中的泛型
- 作用
限定类型
# 46. 泛型类
- 我们把带有一个或者多个类型参数的类,叫作泛型类
object Ops1 {
def main(args: Array[String]): Unit = {
val p = new Person[Int, Int]
p.say(12, 12)
}
}
class Person[T, S] {
def say(a: T, s: S) = {
println(a)
println(s)
}
}
# 47. 泛型函数
Scala会从调用该方法使用的实际类型来推断出类型
object Ops1 {
def main(args: Array[String]): Unit = {
val p = new Person
p.say(23, 34)
}
}
class Person {
def sayT, S = {
println(a)
println(s)
}
}
# 48. 类型限定
限定类型的**"类型"**
## 48.1 上界(重要)
**<:**
[T<: Person] T是Person的子类
## 48.2 下界
**>:**
[ T>:Person] T是Person的父类
## 48.3 视图界定(重要)
**<%**
[T <% Person] T能**隐式转换**成Person类型
**视图界定也包含上界**
## 48.4 上下文界定
**:**
[T :Person]
**视图界定的一种语法糖**
# 49. Scala中的Actor
**Java中多线程+Socket**
# 50. Scala中的Akka
**基于Actor的一个网络通信框架**
- 缺点
==版本不兼容==
# 51. Spark中的网络通信
- spark1.6之前内部通信使用的是Akka
- spark1.6的版本中引入了另外一种**默认**的实现Netty
- 其实在spark1.6的版本中有两种网络通信框架 akka+**netty(默认)**
- spark2.x的版本中akka被彻底的弃用了,纯使用netty