Scala 编程
文章目录
一. Scala 简介
Scala 是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。
Scala 运行在 Java 虚拟机上,并兼容现有的 Java 程序。
Scala 源代码被编译成 Java 字节码,所以它可以运行在 JVM 上,并可以调用现有的 Java 类库。
函数编程范式更适合用于 Map/Reduce 和大数据模型,它摒弃了数据与状态的计算模型,着眼于函数本身,而非执行的过程的数据和状态的处理。函数范式逻辑清晰、简单,非常适合用于处理基于不变数据的批量处理工作,这些工作基本都是通过 map 和 reduce 操作转换数据后,生成新的数据副本,然后再进行处理。像 Spark,Flink等都是采用 Scala 开发的。
二. Scala 的安装与验证
2.1 下载安装
下载地址 选择的版本为 2.11.8
2.2 验证
打开 dos 命令窗口,并输入 scala -version
2.3 IDEA 中使用 Scala
三. Scala 的特性
3.1 面向对象
Scala 是一种纯面向对象的语言,每个值都是对象。对象的数据类型一级行为由类和特质描述。
类抽象机制的客栈有两种途径:
- 子类继承
- 灵活的混入机制
3.2 函数式编程
Scala 也是一种函数式语言,其函数也能当成值来用。Scala 提供了轻量级的语法用以定义匿名函数,支持高阶函数,允许嵌套多层函数,并支持柯里化。Scala 的 case class 及其内置的模式匹配相当于函数式编程语言中常用的代数类型。更近一步,程序员可以利用 Scala 的模式匹配,编写类似正则表达式的代码处理 XML 数据。
3.3 静态类型
Scala 具备类型系统,通过编译时检查,保证代码的安全性和一致性,类型系统具体支持一下特性:
- 泛型类
- 协变和逆变
- 标注
- 类型参数的上下限约束
- 把类别和抽象类型作为对象成员
- 符合类型
- 引用自己时显示指定类型
- 视图
- 多态方法
3.4 可扩展性
Scala 的设计秉承一项事实,即在实践中,某个领域特定的应用程序开发往往需要特定于该领域的语言扩展,Scala 提供了许多独特的语言机制,可以以库的形式轻易无缝添加新的语言结构:
- 任何方法可用作前缀或后缀操作符
- 可以根据于其类型自动构造闭包
3.5 并发性
Scala 使用 Actor 作为其并发模型,Actor 是类似线程的实体,通过邮箱发收消息。Actor 可以复用线程,因此可以在程序中使用数百万个 Actor,而线程只能创建数千个。在 2.10 之后的版本中,使用 Akka 作为其默认 Actor 实现。
四. 基本语法
4.1 变量的声明与定义
Scala 通常使用关键字 val/var
进行变量的声明与定义,二者的区别在于:使用 val 定义的变量相当于 Java 中的常量,其值是不可以改变的,而由 var 定义的变量,其值是可以改变的
4.2 数据类型和操作符
4.2.1 数据类型
4.2.2 操作符
4.2.2.1 数学运算
Scala 中,你可以舍弃方法调用的空括号。例外就是如果方法带有副作用就加上括号,如 println()
,不过如果方法没有副作用九可以去掉括号,如 String 上调用的 toLowerCase;
你可以通过中缀操作符,加号(+),减号(-),乘号(*),除号(/)和余数(%),在任何数类型上调用数学方法, scala中的基础运算与java一致
4.2.2.2 关系与逻辑操作
你可以用关系方法:大于(>),小于(<),大于等于(>=)和小于等于(<=)比较数类型,像等号操作符那样,产生一个Boolean结果。另外,你可以使用一元操作符!(unary_!方法)改变Boolean值
4.2.2.3 对象相等性
其比较的是值,而不是 Java 概念中的地址值。
五. 控制结构与函数
5.1 if 表达式
Scala IF…ELSE 语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。可以通过下图来简单了解条件语句的执行过程:
5.1.1 if 语句的语法格式
if(布尔表达式)
{
// 如果布尔表达式为 true 则执行该语句块
}
如果布尔表达式为 true 则执行大括号内的语句块,否则跳过大括号内的语句块,执行大括号之后的语句块。
object IfDemo {
def main(args: Array[String]): Unit = {
var x = 10;
if (x < 20) {
println("x < 20")
}
}
}
5.1.2 if … else 语法格式
if(布尔表达式){
// 如果布尔表达式为 true 则执行该语句块
}else{
// 如果布尔表达式为 false 则执行该语句块
}
if 语句后可以紧跟 else 语句,else 内的语句块可以在布尔表达式为 false 的时候执行。
object IfElseDemo {
def main(args: Array[String]): Unit = {
var x = 30;
if (x < 20) {
println("x 小于 20")
} else {
println("x 大于 20")
}
}
}
5.1.3 if … else if … else 语法格式
if(布尔表达式 1){
// 如果布尔表达式 1 为 true 则执行该语句块
}else if(布尔表达式 2){
// 如果布尔表达式 2 为 true 则执行该语句块
}else if(布尔表达式 3){
// 如果布尔表达式 3 为 true 则执行该语句块
}else {
// 如果以上条件都为 false 执行该语句块
}
if 语句后可以紧跟 else if…else 语句,在多个条件判断语句的情况下很有用。
object Test {
def main(args: Array[String]) {
var x = 30;
if( x == 10 ){
println("X 的值为 10");
}else if( x == 20 ){
println("X 的值为 20");
}else if( x == 30 ){
println("X 的值为 30");
}else{
println("无法判断 X 的值");
}
}
}
5.1.4 if … else 嵌套语句格式
if(布尔表达式 1){
// 如果布尔表达式 1 为 true 则执行该语句块
if(布尔表达式 2){
// 如果布尔表达式 2 为 true 则执行该语句块
}
}
if…else 嵌套语句可以实现在 if 语句内嵌入一个或多个 if 语句。
object Test {
def main(args: Array[String]) {
var x = 30;
var y = 10;
if( x == 30 ){
if( y == 10 ){
println("X = 30 , Y = 10");
}
}
}
}
5.2 方法与函数
Scala 有方法与函数,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。
Scala 中的方法跟 Java 的类似,方法是组成类的一部分。
Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。
Scala 中使用 val
语句可以定义函数,def
语句定义方法。
5.2.1 方法的声明
声明格式:
def functionName ([参数列表]) : [return type]
如果你不写等于号和方法主体,那么方法会被隐式声明为**抽象(abstract)**,包含它的类型于是也是一个抽象类型。
5.2.2 方法的定义
方法定义由一个 def 关键字开始,紧接着是可选的参数列表,一个冒号 : 和方法的返回类型,一个等于号 = ,最后是方法的主体。
定义格式:
def functionName ([参数列表]) : [return type] = {
function body
return [expr]
}
object add{
def addInt( a:Int, b:Int ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
}
如果方法没有返回值,可以返回为 Unit,这个类似于 Java 的 void, 实例如下:
object Hello{
def printMe() : Unit = {
println("Hello, Scala!")
}
}
5.2.3 方法的调用
调用格式:
functionName( 参数列表 )
如果方法使用了实例的对象来调用,我们可以使用类似java的格式 (使用 . 号):
[instance.]functionName( 参数列表 )
object Test {
def main(args: Array[String]) {
println( "Returned Value : " + addInt(5,7) );
}
def addInt( a:Int, b:Int ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
}
5.3 控制结构
有的时候,我们可能需要多次执行同一块代码。一般情况下,语句是按顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。
编程语言提供了更为复杂执行路径的多种控制结构。
循环语句允许我们多次执行一个语句或语句组,下面是大多数编程语言中循环语句的流程图:
#### 5.3.1 循环类型
5.3.1.1 for 循环
用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。
语法格式:
for( var x <- Range ){
statement(s);
}
以上语法中,Range 可以是一个数字区间表示 i to j ,或者 i until j。左箭头 <- 用于为变量 x 赋值。i to j 为左右都为闭区间的范围,i until j 为左闭右开的范围区间
object ForDemo {
def main(args: Array[String]): Unit = {
for (i <- 1 to 10) {
println(s"i=$i")
}
for (i <- 1 until 10) {
println(s"i=$i")
}
println("=================双重for循环====================")
for (i <- 1 to 3; j <- 1 to 5) {
println(i * j)
}
println("====================守卫式 使用 if 条件=============================")
for (i <- 1 to 10; j <- 1 to 10 if i == j) {
println(s" i * j = $i * $j = ${i * j}")
}
println("====================推导式 使用 yield 接收返回结果=============================")
// 如果 for 循环的循环体中以 yield 开始,那么此循环会构造出一个集合,每次迭代生成集合中的一个值
// 可以使用变量接收产生的新集合
val result = for (i <- 1 to 10) yield i % 2
result.foreach(println(_))
println("===================九九乘法表=============================")
for (i <- 1 to 9; j <- 1 to i) {
print(s"$j * $i = ${i * j}\t")
if (i == j) {
println()
}
}
println("====================for 循环中使用 {} =============================")
for {
i <- 1 to 3
from = 4 - i
j <- from to 3
}
println(s"i=$i,j=$j")
println("======================================遍历字符串===============================")
val message = "dafsdfsdfadsfsd"
for (elem <- message) print(elem + " ")
}
5.3.1.2 while循环
运行一系列语句,如果条件为true,会重复运行,直到条件变为false。
- A :语法格式:
while(condition)
{
statement(s);
}
在这里,statement(s) 可以是一个单独的语句,也可以是几个语句组成的代码块。
condition 可以是任意的表达式,当为任意非零值时都为 true。当条件为 true 时执行循环。 当条件为 false 时,退出循环,程序流将继续执行紧接着循环的下一条语句。
- B:流程图
在这里,while 循环的关键点是循环可能一次都不会执行。当条件为 false 时,会跳过循环主体,直接执行紧接着 while 循环的下一条语句。
object Test {
def main(args: Array[String]) {
// 局部变量
var a = 10;
// while 循环执行
while( a < 20 ){
println( "Value of a: " + a );
a = a + 1;
}
}
}
5.3.1.1 do while循环
类似 while 语句区别在于判断循环条件之前,先执行一次循环的代码块。
- A:语法格式
do {
statement(s);
} while( condition );
- B:流程图
请注意,条件表达式出现在循环的尾部,所以循环中的 statement(s) 会在条件被测试之前至少执行一次。如果条件为 true,控制流会跳转回上面的 do,然后重新执行循环中的 statement(s)。这个过程会不断重复,直到给定条件变为 false 为止。
object Test {
def main(args: Array[String]) {
// 局部变量
var a = 10;
// do 循环
do{
println( "Value of a: " + a );
a = a + 1;
}while( a < 20 )
}
}
5.3.2 循环控制语句
循环控制语句改变你代码的执行顺序,通过它你可以实现代码的跳转。Scala 以下几种循环控制语句:Scala 不支持 break 或 continue 语句,但从 2.8 版本后提供了一种中断循环的方式:breakable 和 break
;语法格式如下:
// 导入以下包
import scala.util.control._
// 创建 Breaks 对象
val loop = new Breaks;
// 在 breakable 中循环
loop.breakable{
// 循环
for(...){
....
// 循环中断
loop.break;
}
}
流程图
import scala.util.control._
object Test {
def main(args: Array[String]) {
var a = 0;
val numList = List(1,2,3,4,5,6,7,8,9,10);
val loop = new Breaks;
loop.breakable {
for( a <- numList){
println( "Value of a: " + a );
if( a == 4 ){
loop.break;
}
}
}
println( "After the loop" );
}
}
中断嵌套循环:
import scala.util.control._
object Test {
def main(args: Array[String]) {
var a = 0;
var b = 0;
val numList1 = List(1,2,3,4,5);
val numList2 = List(11,12,13);
val outer = new Breaks;
val inner = new Breaks;
outer.breakable {
for( a <- numList1){
println( "Value of a: " + a );
inner.breakable {
for( b <- numList2){
println( "Value of b: " + b );
if( b == 12 ){
inner.break;
}
}
} // 内嵌循环中断
}
} // 外部循环中断
}
}
5.3.3 无限循环
如果条件永远为 true,则循环将变成无限循环。我们可以使用 while 语句来实现无限循环:
object Test {
def main(args: Array[String]) {
var a = 10;
// 无限循环
while( true ){
println( "a 的值为 : " + a );
}
}
}
六. 数组
Scala 语言中提供的数组是用来存储固定大小的同类型元素,数组对于每一门编辑应语言来说都是重要的数据结构之一。
声明数组变量并不是声明 number0、number1、…、number99 一个个单独的变量,而是声明一个就像 numbers 这样的变量,然后使用 numbers[0]、numbers[1]、…、numbers[99] 来表示一个个单独的变量。数组中某个指定的元素是通过索引来访问的。
数组的第一个元素索引为0,最后一个元素的索引为元素总数减1。
scala中,有两种数组,一种是定长数组,另一种是变长数组
6.1 定长数组
定长数组指的是数组的长度是不允许改变的,元素是可以改变的数组。
- 语法格式
// 通过指定长度定义数组
val / var 变量名 = new Array[元素类型](数组长度)
// 用元素直接初始化数组
val / var 变量名 = Array(元素1, 元素2, 元素3...)
Tips:
- 在 scala 中,数组的泛型使用
[ ]
来指定- 使用
( )
来获取元素
-
示例
-
定一个长度为10的整型数组
-
设置第1个元素为110
-
打印第1个元素
C:\Users\21349>scala Welcome to Scala 2.11.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_252). Type in expressions for evaluation. Or try :help. scala> val a = new Array[Int](10) a: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0) scala> a(0) = 110 scala> println(a(0)) 110 scala> println(a) [I@59e32960 scala> println(a.toBuffer) ArrayBuffer(110, 0, 0, 0, 0, 0, 0, 0, 0, 0)
-
6.2 变长数组
变长数组指的是数组的长度是可变的,可以往数组中添加、删除元素
6.2.1 定义变长数组
定义变长数组,需要提前导入 ArrayBuffer 类, import scala.collection.mutable.ArrayBuffer
- 语法格式
// 创建空的ArrayBuffer变长数组,语法结构:
val / var a = ArrayBuffer[元素类型]()
// 创建带有初始元素的ArrayBuffer
val / var a = ArrayBuffer(元素1,元素2,元素3....)
-
示例
定义一个长度为0的整型变长数组
scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer
scala> val a = new ArrayBuffer[Int]()
a: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()
scala>
6.2.2 添加/删除 元素
-
使用
+=
添加元素 -
使用
-=
添加元素 -
使用用
++=
追加一个数组到变长数组示例:
- 定义一个变长数组,包含以下元素: “hadoop”, “spark”, “flink”
- 往该变长数组添加一个"flume"元素
- 从该变长数组删除"hadoop"元素
- 再将一个数组,该数组包含"hive", "sqoop"追加到变长数组中
scala> // 定义变长数组
scala> val a = ArrayBuffer("hadoop","spark","scala","flink")
a: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(hadoop, spark, scala, flink)
scala> // 追加一个元素
scala> a += "flume"
res4: a.type = ArrayBuffer(hadoop, spark, scala, flink, flume)
scala> // 删除一个元素
scala> a -= "hadoop"
res5: a.type = ArrayBuffer(spark, scala, flink, flume)
scala> // 追加一个数组
scala> a ++= Array("hive","sqoop")
res6: a.type = ArrayBuffer(spark, scala, flink, flume, hive, sqoop)
scala>
6.2.3 遍历数组
可以使用以下两种方式来遍历数组:
-
使用
for表达式
直接遍历数组中的元素 -
使用
索引
遍历数组中的元素示例一:
- 定义一个数组,包含以下元素1,2,3,4,5,6
- 使用for表达式直接遍历,并打印数组的元素
scala> val a = Array(1,2,3,4,5,6) a: Array[Int] = Array(1, 2, 3, 4, 5, 6) scala> for(i <- a) println(i) 1 2 3 4 5 6 scala>
示例二:
- 定义一个数组,包含以下元素1,2,3,4,5
- 使用for表达式基于索引下标遍历,并打印数组的元素
scala> val a = Array(1,2,3,4,5) a: Array[Int] = Array(1, 2, 3, 4, 5) scala> for(i <- 0 to a.length <