Scala是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各类特性。Scala运行于Java平台(Java虚拟机),并兼容现有的Java程序。java
学习Scala编程语言,为后续学习Spark奠基基础。程序员
l 优雅:这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API是否优雅直接影响用户体验。算法
l 速度快:Scala语言表达能力强,一行代码抵得上Java多行,开发速度快;Scala是静态编译的,因此和JRuby,Groovy比起来速度会快不少。编程
l 能融合到Hadoop生态圈:Hadoop如今是大数据事实标准,Spark并非要取代Hadoop,而是要完善Hadoop生态。JVM语言大部分可能会想到Java,但Java作出来的API太丑,或者想实现一个优雅的API太费劲。 数组
l 德国的一位资深Java专家Adam Bien在参加JavaOne 2008大会(他在会上有一个技术讲座)期间,亲耳听到,有人问Java之父James Gosling“除了Java以外,你如今最但愿在JVM上使用什么语言?”的时候,他出人意料的回答:“Scala!”几乎是脱口而出。安全
由于Scala是运行在JVM平台上的,因此安装Scala以前要安装JDK,安装JDK1.8版本网络
① Windows安装Scala编译器数据结构
访问Scala官网http://www.scala-lang.org/下载Scala编译器安装包,目前最新版本是2.12.x,可是目前大多数的框架都是用2.11.x编写开发的,Spark2.x使用的就是2.11.x,因此这里推荐2.11.x版本,下载scala-2.11.12.msi后点击下一步就能够了多线程
1) 安装JDK闭包
2) 下载Scala:http://www.scala-lang.org/download/
3) 安装Scala:
设置环境变量:SCALA_HOME和PATH路径
4) 验证Scala
② Linux安装Scala编译器
下载Scala地址http://downloads.typesafe.com/scala/2.11.12/scala-2.11.12.tgz而后解压Scala到指定目录
tar -zxvf scala-2.11.12.tgz -C /usr/java
配置环境变量,将scala加入到PATH中
vi /etc/profile
export JAVA_HOME=/usr/java/jdk1.8.0_111
export PATH=$PATH:$JAVA_HOME/bin:/usr/java/scala-2.11.12/bin
l REPL(Read Evaluate Print Loop):命令行
l IDE:图形开发工具
n The Scala IDE (Based on Eclipse):http://scala-ide.org/
n IntelliJ IDEA with Scala plugin:http://www.jetbrains.com/idea/download/
n Netbeans IDE with the Scala plugin
因为IDEA的Scala插件更优秀,大多数Scala程序员都选择IDEA,能够到http://www.jetbrains.com/idea/download/下载社区免费版,点击下一步安装便可,安装时若是有网络能够选择在线安装Scala插件。这里咱们使用离线安装Scala插件:
l 安装IDEA,点击下一步便可。因为咱们离线安装插件,因此点击Skip All and Set Defaul
l 下载IEDA的scala插件,地址http://plugins.jetbrains.com/plugin/1347-scala
1.安装Scala插件:Configure -> Plugins -> Install plugin from disk -> 选择Scala插件 -> OK -> 重启IDEA
注意:在Scala中,任何数据都是对象。例如:
③ 数值类型:Byte,Short,Int,Long,Float,Double
l Byte: 8位有符号数字,从-128 到 127
l Short: 16位有符号数据,从-32768 到 32767
l Int: 32位有符号数据
l Long: 64位有符号数据
例如:
val a:Byte = 10
a+10
获得:res9: Int = 20
这里的res9是新生成变量的名字
val b:Short = 20
a+b
注意:在Scala中,定义变量能够不指定类型,由于Scala会进行类型的自动推导。
④ 字符类型和字符串类型:Char和String
对于字符串,在Scala中能够进行插值操做。
注意:前面有个s;至关于执行:"My Name is " + s1
⑤ Unit类型:至关于Java中的void类型
⑥ Nothing类型:通常表示在执行过程当中,产生了Exception
例如,咱们定义一个函数以下:
l 使用val和var申明变量
例如:scala> val answer = 8 * 3 + 2
能够在后续表达式中使用这些名称
l val:value 简写,表示的意思为值,不可变
要申明其值可变的变量:val
l var:variable 简写,表示的变量,能够改变值
要申明其值不可变的变量:var
l 例子
def main(args: Array[String]) {
//使用val定义的变量值是不可变的,至关于java里用final修饰的变量
//使用var定义的变量是可变得,在Scala中鼓励使用val
//Scala编译器会自动推断变量的类型,必要的时候能够指定类型
//
变量名在前,类型在后
} }
|
注意:能够不用显式指定变量的类型,Scala会进行自动的类型推到
Scala的if/else语法结构和Java或C++同样。
不过,在Scala中,if/else是表达式,有值,这个值就是跟在if或else以后的表达式的值。
def main(args: Array[String]) { val x = 1
//判断x的值,将结果赋给y
//打印y的值
println
(y)
//支持混合类型表达式
//打印z的值
println
(z)
//若是缺失else,至关于if (x > 2) 1 else ()
println
(m)
//在scala中每一个表达式都有值,scala中有个Unit类,写作(),至关于Java中的void
println
(n)
//if和else if
else if (x >= 1) 1 else -1
println
(k)} }
|
def main(args: Array[String]) { val x = 0
//在scala中{}中课包含一系列表达式,块中最后一个表达式的值就是块的值
//下面就是一个块表达式
if (x < 0){ -1 } else if(x >= 1) { 1 } else { "error" } }
//result的值就是块表达式的结果
println
(result)} }
|
Scala拥有与Java和C++相同的while和do循环
Scala中,可使用for和foreach进行迭代
注意:
(*) <- 表示Scala中的generator,即:提取符
(*)第三种写法是第二种写法的简写
在上面的案例中,咱们将list集合中的每一个元素转换成了大写,而且使用yield关键字生成了一个新的集合。
注意:在上面的例子中,foreach接收了另外一个函数(println)做为值
当val被申明为lazy时,它的初始化将被推迟,直到咱们首次对它取值。
一个更为复杂一点的例子:读取文件:
Scala异常的工做机制和Java或者C++同样。直接使用throw关键字抛出异常。
使用try...catch...finally来捕获和处理异常:
Scala数组的类型:
l 定长数组:使用关键字Array
l 变长数组:使用关键字ArrayBuffer
l 遍历数组
l Scala数组的经常使用操做
l Scala的多维数组
l 和Java同样,多维数组是经过数组的数组来实现的。
l 也能够建立不规则的数组,每一行的长度各不相同。
① 定长数组和变长数组
import scala.collection.mutable.ArrayBuffer def main(args: Array[String]) {
//初始化一个长度为8的定长数组,其全部元素均为0
//直接打印定长数组,内容为数组的hashcode值
println
(arr1)
//将数组转换成数组缓冲,就能够看到原数组中的内容了
//toBuffer会将数组转换长数组缓冲
println
(arr1.toBuffer)
//注意:若是new,至关于调用了数组的apply方法,直接为数组赋值
//初始化一个长度为1的定长数组
Array
[Int](10)
println
(arr2.toBuffer)
//定义一个长度为3的定长数组
Array
("hadoop", "storm", "spark")
//使用()来访问元素
println
(arr3(2))
//////////////////////////////////////////////////
//变长数组(数组缓冲)
//若是想使用数组缓冲,须要导入import scala.collection.mutable.ArrayBuffer包
//向数组缓冲的尾部追加一个元素
//+=尾部追加元素
//追加多个元素
//追加一个数组++=
Array
(6, 7)
//追加一个数组缓冲
//打印数组缓冲ab
//在数组某个位置插入元素用insert
//删除数组某个位置的元素用remove
println
(ab)} }
|
② 遍历数组
object ForArrayTest {
//初始化一个数组
Array
(1,2,3,4,5,6,7,8)
//加强for循环
println
(i)
//好用的until会生成一个Range
//reverse是将前面生成的Range反转
println
(arr(i))} }
|
③ 数组转换
yield关键字将原始的数组进行转换会产生一个新的数组,原始的数组不变
def main(args: Array[String]) {
//定义一个数组
Array
(1, 2, 3, 4, 5, 6, 7, 8, 9)
//将偶数取出乘以10后再生成一个新的数组
println
(res.toBuffer)
//更高级的写法,用着更爽
//filter是过滤,接收一个返回值为boolean的函数
//map至关于将数组中的每个元素取出来,应用传进去的函数
println
(r.toBuffer)} }
|
④ 数组经常使用算法
在Scala中,数组上的某些方法对数组进行相应的操做很是方便!
在Scala中,把哈希表这种数据结构叫作映射
① 构建映射
② 获取和修改映射中的值
好用的getOrElse
注意:在Scala中,有两种Map,一个是immutable包下的Map,该Map中的内容不可变;另外一个是mutable包下的Map,该Map中的内容可变
例子:
注意:一般咱们在建立一个集合是会用val这个关键字修饰一个变量(至关于java中的final),那么就意味着该变量的引用不可变,该引用中的内容是否是可变,取决于这个引用指向的集合的类型
例子:以下这段超简单的循环便可遍历映射中全部的键/值对偶
映射是K/V对偶的集合,对偶是元组的最简单形式,元组能够装着多个不一样类型的值。
① 建立元组
② 获取元组中的值
③ 将对偶的集合转换成映射
④ 拉链操做
zip命令能够将多个值绑定在一块儿
注意:若是两个数组的元素个数不一致,拉链操做后生成的数组的长度为较小的那个数组的元素个数
Scala中的+ - * / %等操做符的做用与Java同样,位操做符 & | ^ >> <<也同样。只是有
一点特别的:这些操做符其实是方法。例如:
a + b
是以下方法调用的简写:
a.+(b)
a 方法 b能够写成 a.方法(b)
① 定义方法
方法的返回值类型能够不写,编译器能够自动推断出来,可是对于递归方法,必须指定返回类型
② 定义函数
③ 方法和函数的区别
在函数式编程语言中,函数是“头等公民”,它能够像任何其余数据类型同样被传递和操做
案例:首先定义一个方法,再定义一个函数,而后将函数传递到方法里面
//定义一个方法
//方法m2参数要求是一个函数,函数的参数必须是两个Int类型
//
返回值类型也是Int类型
f(2, 6) }
//定义一个函数f1,参数是两个Int类型,返回值是一个Int类型
f1
= (x: Int, y: Int) => x + y
//再定义一个函数f2
f2
= (m: Int, n: Int) => m * n
//main方法
//调用m1方法,并传入f1函数
m1
(
f1
)
println
(r1)
//调用m1方法,并传入f2函数
m1
(
f2
)
println
(r2)} }
|
④ 将方法转换成函数(神奇的下划线)
l 可使用Scala的预约义函数
例如:求两个值的最大值
l 也可使用def关键字自定义函数
语法:
示例:
l Call By Value:对函数实参求值,且仅求一次
l Call By Name:函数实参每次在函数体内被用到时都会求值
咱们来分析一下,上面两个调用执行的过程:
一份复杂一点的例子:
l 默认参数
l 代名参数
l 可变参数
在Scala中,函数是“头等公民”,就和数字同样。能够在变量中存放函数,即:将函数做为变量的值(值函数)。
(*)首先,定义一个最普通的函数
(*)再定义一个高阶函数
(*)分析这个高阶函数调用的过程
在这个例子中,首先定义了一个普通的函数mytest,而后定义了一个高阶函数myFunction;myFunction接收三个参数:第一个f是一个函数参数,第二个是x,第三个是y。而f是一个函数参数,自己接收两个Int的参数,返回一个Int的值。
就是函数的嵌套,即:在一个函数定义中,包含另一个函数的定义;而且在内函数中能够访问外函数中的变量。
测试上面的函数:
柯里化函数(Curried Function)是把具备多个参数的函数转换为一条函数链,每一个节点上是单一参数。
一个简单的例子:
在这个例子中,能够被2整除的被分到一个分区;不能被2整除的被分到另外一个分区。
在这个例子中,分为两步:
(1)将(1,2,3)和(4,5,6)这两个集合合并成一个集合
(2)再对每一个元素乘以2
把数据及对数据的操做方法放在一块儿,做为一个相互依存的总体——对象
面向对象的三大特征:
封装
继承
多态
简单类和无参方法:
案例:注意没有class前面没有public关键字修饰。
若是要开发main方法,须要将main方法定义在该类的伴生对象中,即:object对象中,(后续作详细的讨论)。
l 当定义属性是private时候,scala会自动为其生成对应的get和set方法
private var stuName:String = "Tom"
l 定义属性:private var money:Int = 1000 但愿money只有get方法,没有set方法??
l private[this]的用法:该属性只属于该对象私有,就不会生成对应的set和get方法。若是这样,就不能直接调用,例如:s1.stuName ---> 错误
咱们能够在一个类的内部在定义一个类,以下:咱们在Student类中,再定义了一个Course类用于保存学生选修的课程。
开发一个测试程序进行测试:
类的构造器分为:主构造器、辅助构造器
l 主构造器:和类的声明结合在一块儿,只能有一个主构造器
Student4(val stuName:String,val stuAge:Int)
(1) 定义类的主构造器:两个参数
(2) 声明了两个属性:stuName和stuAge 和对应的get和set方法
l 辅助构造器:能够有多个辅助构造器,经过关键字this来实现
Scala没有静态的修饰符,但Object对象下的成员都是静态的 ,如有同名的class,这其做为它的伴生类。在Object中通常能够为伴生类作一些初始化等操做。
下面是Java中的静态块的例子。在这个例子中,咱们对JDBC进行了初始化。
而Scala中的Object就至关于Java中静态块。
Object对象的应用
单例对象
使用应用程序对象:能够省略main方法;须要从父类App继承。
遇到以下形式的表达式时,apply方法就会被调用:
Object(参数1,参数2,......,参数N)
一般,这样一个apply方法返回的是伴生类的对象;其做用是为了省略new 关键字
Object的apply方法举例:
Scala和Java同样,使用extends关键字扩展类。
l 案例一:Employee类继承Person类
l 案例二:在子类中重写父类的方法
l 案例三:使用匿名子类
l 案例四:使用抽象类。抽象类中包含抽象方法,抽象类只能用来继承。
l 案例五:使用抽象字段。抽象字段就是一个没有初始值的字段
trait就是抽象类。trait跟抽象类最大的区别:trait支持多重继承
Scala的包和Java中的包或者C++中的命名空间的目的是相同的:管理大型程序中的名称。
Scala中包的定义和使用:
包的定义
包的引入:Scala中依然使用import做为引用包的关键字,例如
并且Scala中的import能够写在任意地方
包能够包含类、对象和特质,但不能包含函数或者变量的定义。很不幸,这是Java虚拟机的局限。
把工具函数或者常量添加到包而不是某个Utils对象,这是更加合理的作法。Scala中,包对象的出现正是为了解决这个局限。
Scala中的包对象:常量,变量,方法,类,对象,trait(特质)
l
l
其实这里的source就指向了这个文件中的每一个字符。
l
l
l
Scala的集合有三大类:序列Seq、集Set、映射Map,全部的集合都扩展自Iterable特质
在Scala中集合有可变(mutable)和不可变(immutable)两种类型,immutable类型的集合初始化后就不能改变了(注意与val修饰的变量进行区别)
l 可变集合
l 不可变集合:
集合从不改变,所以能够安全地共享其引用。
甚至是在一个多线程的应用程序当中也没问题。
l 不可变列表(List)
不可变列表的相关操做:
l 可变列表(LinkedList):scala.collection.mutable
经常使用的序列有:Vector和Range
Vector是ArrayBuffer的不可变版本,是一个带下标的序列
Range表示一个整数序列
l 集Set是不重复元素的集合
l 和列表不一样,集并不保留元素插入的顺序。默认以Hash集实现
Scala有一个强大的模式匹配机制,能够应用在不少场合:
switch语句
类型检查
Scala还提供了样本类(case class),对模式匹配进行了优化
模式匹配示例:
l 更好的switch
l Scala的守卫
l 模式匹配中的变量
l 类型模式
l 匹配数组和列表
简单的来讲,Scala的case class就是在普通的类定义前加case这个关键字,而后你能够对这些类来模式匹配。
case class带来的最大的好处是它们支持模式识别。
首先,回顾一下前面的模式匹配:
其次,若是咱们想判断一个对象是不是某个类的对象,跟Java同样可使用isInstanceOf
最后,在Scala中有一种更简单的方式来判断,就是case class
注意:须要在class前面使用case关键字。
和Java或者C++同样,类和特质能够带类型参数。在Scala中,使用方括号来定义类型参数
测试程序:
函数和方法也能够带类型参数。和泛型类同样,咱们须要把类型参数放在方法名以后。
注意:这里的ClassTag是必须的,表示运行时的一些信息,好比类型。
类型的上界和下界,是用来定义类型变量的范围。它们的含义以下:
S <: T
这是类型上界的定义。也就是S必须是类型T的子类(或自己,本身也能够认为是本身的子类。
U >: T
这是类型下界的定义。也就是U必须是类型T的父类(或自己,本身也能够认为是本身的父类)。
l 一个简单的例子:
l 一个复杂一点的例子(上界):
l 再来看一个例子:
它比 <: 适用的范围更广,除了全部的子类型,还容许隐式转换过去的类型。用 <% 表示。尽可能使用视图界定,来取代泛型的上界,由于适用的范围更加普遍。
示例:
l 上面写过的一个列子。这里因为T的上界是String,当咱们传递100和200的时候,就会出现类型不匹配。
l 可是100和200是能够转成字符串的,因此咱们可使用视图界定让addTwoString方法接收更普遍的数据类型,即:字符串及其子类、能够转换成字符串的类型。
注意:使用的是 <%
l 但实际运行的时候,会出现错误:
这是由于:Scala并无定义如何将Int转换成String的规则,因此要使用视图界定,咱们就必须建立转换的规则。
l 建立转换规则
l 运行成功
l 协变:
Scala的类或特征的范型定义中,若是在类型参数前面加入+符号,就可使类或特征变为协变了。
逆变:
在类或特征的定义中,在类型参数以前加上一个-符号,就可定义逆变范型类和特征了。
总结一下:Scala的协变:泛型变量的值能够是自己类型或者其子类的类型
Scala的逆变:泛型变量的值能够是自己类型或者其父类的类型
所谓隐式转换函数指的是以implicit关键字申明的带有单个参数的函数。
l 前面讲视图界定时候的一个例子:
l 再举一个例子:咱们把Fruit对象转换成了Monkey对象
使用implicit申明的函数参数叫作隐式参数。咱们也可使用隐式参数实现隐式的转换
所谓隐式类: 就是对类增长implicit 限定的类,其做用主要是对类的功能增强!