Scala环境安装配置
注意:由于Scala是基于Java虚拟机的,所以使用 Scala 之前必须先安装 Java,Java我们已经安装过了。 那在这里我们先到官网下载Scala安装包
Scala现在有三个主要在使用的版本, 2.11,2.12,2.13 目前的话2.12使用的比较多,所以我们就使用这个版本
Scala官网链接:www.scala-lang.org/ ,下载链接:www.scala-lang.org/download/
进入相应的版本链接后,根据自己的电脑系统选择相应的安装版本,这里选择Mac OX版本
www.scala-lang.org/download/2.…
配置环境变量 (配置SCALA_HOME,PATH)
刷新source /etc/profile
验证,在终端输入scala验证
Scala命令行
咱们刚才进入的就是Scala命令行 Scala命令行也称为Scala解释器(REPL),它会快速编译Scala代码为字节码,然后交给JVM来执行 这里的REPL表示:Read(取值)-> Evaluation(求值)-> Print(打印)-> Loop(循环)
在Scala命令行内,输入Scala代码,解释器会直接返回结果 如果你没有指定变量来存放计算的值,那么值默认的名称会显示为res开头的变量,而且会显示结果的数
例如:
scala> 1+1
res0: Int = 2
复制代码
在后面可以继续使用res0这个变量,以及它里面存放的值
例如:
scala> 5 * res0
res1: Int = 10
复制代码
scala的命令行也有自动补全功能,使用起来还是比较方便的 输入res,按键盘上的tab键,下面就会列出目前以res开头的变量名称
scala> res
res0 res1
复制代码
Scala的基本使用
变量
Scala中的变量分为两种:可变 var 和 不可变 val
可变var:可以随时修改var声明的变量的值
不可变val:val声明的变量,值不能被修改,否则会报错
注意:在实际工作中,针对一些不需要改变值的变量,通常建议使用val,这样可以不用担心值被 错误的修改(等于java中的final类型)。这样可以提高系统的稳定性和健壮性!
无论声明val变量,还是声明var变量,都可以手动指定变量的类型 如果不指定,Scala会自动根据值,进行类型推断
val c = 1 等价于 val c:Int = 1
数据类型
Scala中的数据类型可以分为两种,基本数据类型和增强版数据类型
基本数据类型有: Byte、Char、Short、Int、Long、Float、Double、Boolean
增强版数据类型有: StringOps、RichInt、RichDouble、RichChar 等
scala使用这些增强版数据类给基本数据类型增加了上百种增强的功能 例如:RichInt提供的有一个to函数,1.to(10) ,此处Int会先隐式转换为RichInt,然后再调用其to函数
注意,to函数还可以这样写
使用基本数据类型,直接就可以调用RichInt中对应的函数
操作符
Scala的算术操作符与Java的算术操作符没有什么区别
比如 +、-、*、/、% 等,以及 &、|、^、>>、<< 等
注意:Scala中没有提供++、–操作符
我们只能使用+和-,比如count = 1,count++是错误的,必须写做count += 1
if 表达式
在Scala中,if表达式是有返回值的,就是if或者else中最后一行语句返回的值,这一点和java中的if是不一
样的,java中的if表达式是没有返回值的
例如: val age = 20; if (age > 18) 1 else 0
在这因为if表达式是有返回值的,所以可以将if表达式赋予一个变量
由于if表达式是有值的,而if和else子句的值的类型可能还不一样,此时if表达式的值是什么类型呢?
注意:Scala会自动进行推断,取两个类型的公共父类型
例如,if(age > 18) 1 else 0,表达式的类型是Int,因为1和0都是Int
例如,if(age > 18) “old” else 0,此时if和else的值分别是String和Int,则表达式的值是Any类型,Any是 String和Int的公共父类型
如果if后面没有跟else,则默认else的值是Unit,也可以用()表示,类似于java中的void或者null
例如,val age = 12; if(age > 18) “old”。此时就相当于if(age > 18) “old” else ()。 此时表达式的值是Any
如果想在scala REPL中执行多行代码,该如何操作? 使用 :paste 和 ctrl+D 的方式
:paste 表示代码块的开始
ctrl+D 表示代码块的结束
语句终结符
Scala默认不需要语句终结符,它将每一行作为一个语句
如果一行要放多条语句,则前面的语句必须使用语句终结符
语句终结符和Java中的一样,就是我们平时使用的分号
scala> val age = 20; if(age > 18) 1 else 0
age: Int = 20
res0: Int = 1
复制代码
循环
-
print和println
在讲循环之前,先来看一下打印命令print和println print打印时不会加换行符,而println打印时会加一个换行符,这个特性和Java中的打印语句的特性 是一样的 -
for循环
for循环本身的特性就没什么好说的了,直接上案例,主要注意一下scala中的for和java中的for在语 法层面的区别
这里的to可以换成until
对比两次执行的结果发现
1 to 10 可以获取1~10
之间的所有数字
1 until 10可以获取1~9
之间的所有数字
所以在这需要注意了,to 和 until 其实都是函数,一个是闭区间,一个是开区间 具体用哪个就要看你的需求了
for循环针对字符串还可以用
注意:在这里我在for循环后面没有使用花括号,都省略了,主要是因为for循环的循环体代码就只 有一行,如果有多行,就需要使用花括号了,否则,最终执行的结果就不是我们想要的
while循环
while循环,它的用法和java中的while也是很像的,主要看一下语法层面的区别
高级for循环
if守卫
if守卫模式,假设我们想要获取1~10之间的所有偶数,使用普通的for循环,需要把每一个数字都循 环出来,然后判断是否是偶数
如果在for循环里面使用if守卫,可以在循环的时候就执行一定的逻辑,判断数值是否是偶数
for推导式
for推导式,一个典型例子是构造集合
我们在使用for循环迭代数字的时候,可以使用yield指定一个规则,对迭代出来的数字进行处理,并 且创建一个新的集合
Scala的集合体系
集合的顶层接口是Iterable,Iterable接口下面还有一些子接口, Set、Seq、Map 这几个子接口下面有具体的实现类
- set下面有HashSet、LinkedHashSet、SortedSet等等
- seq下面有List、Buffer、Range等等
- Map下面有HashMap、SortedMap、LinkedHashMap等等
- 其中Buffer下面还有两个常用的,ArrayBuffer、ListBuffer
集合
Scala中的集合是分成可变和不可变两类集合的
- 其中可变集合就是说,集合的元素可以动态修改
- 而不可变集合就是说,集合的元素在初始化之后,就无法修改了
可变集合:在 scala.collection.mutable 这个包下面
不可变集合:在scala.collection.immutable这个包下面
我们在创建集合的时候,如果不指定具体的包名,默认会使用不可变集合
Set
先来看一下Set,Set代表一个没有重复元素的集合
这个集合的特性和Java中Set集合的特性基本一样
Set集合分为可变的和不可变的集合,默认情况下使用的是不可变集合
Set可以直接使用,并且不需要使用new关键字,来看一下
这是不是很奇怪,本来Set是一个接口,但是却可以创建对象,更神奇的是竟然还不需要使用new关键 字,这就有点颠覆我们的认知了
注意了,大家在学习Scala的时候,可以拿Java来进行对比,加深理解,但是不要全部拿Java里面 的知识点来硬套,因为它们两个有些地方还是不一样的。
来看一下Scala的文档,你会发现这个Set不仅仅是一个接口,它还是一个Object,具体这个Object类型我 们在后面会详细分析,在这大家先知道这个东西就行了。
注意:默认情况下直接创建的set集合是一个不可变集合,在这可以看到是在immutable包里面 的,不可变集合中的元素一经初始化,就不能改变了,所以初始化后再向里面添加元素就报错了。
但是注意,我使用s + 4 这种操作是可以的
这样是不是和我们刚才说的自相矛盾?
不是的,因为 s + 4 返回的是一个新的集合了,相当于在之前的集合的基础上,创建一个新的集合,新的 集合包含之前集合的元素和我们新增的4这个元素
这个大家需要能够区分开
如果想要创建一个可变的set集合,可以使用mutable包下面的set集合,显式指定包名
Set常用子类有: HashSet、LinkedHashSet、SortedSet
- HashSet:这个集合的特点是:集合中的元素不重复无序
- LinkedHashSet:这个集合的特点是:集合中的元素不重复、有序,它会用一个链表维护插入顺序, 可以保证集合 中元素是有序的
- SortedSet:这个集合的特点是:集合中的元素不重复、有序,它会自动根据元素来进行排序
scala> val s = new scala.collection.mutable.HashSet[Int]()
s: scala.collection.mutable.HashSet[Int] = Set()
scala> s +=1
res35: s.type = Set(1)
复制代码
List
接下来看一下List,List属于Seq接口的子接口 List代表一个不可变的列表
针对List有 head 、 tail 以及 :: 这几个操作
先演示一下 head、tail 操作
- head:表示获取List中的第一个元素
- tail:表示获取List中第一个元素之后的所有元素
那其实head和tail就可以获取list中的所有元素了
通过 :: 操作符,可以将head和tail的结果合并成一个List
:: 这种操作符要清楚,在spark源码中是有体现的,一定要能够看懂
在这里List是不可变的列表,在实际工作中使用的时候会很不方便,因为我们很多场景下都是需要向列表 中动态添加元素?
Scala还提供的有一个ListBuffer
ListBuffer: 可以支持动态增加或者移除元素
Map
创建一个不可变的Map
scala> val ages = Map("jack"->30,"tom"->25,"jessic"->23)
ages: scala.collection.immutable.Map[String,Int] = Map(jack -> 30, tom -> 25,
scala>
ages("jack")
res100: Int = 30
复制代码
创建一个可变的Map
scala> val ages = scala.collection.mutable.Map("jack"->30,"tom"->25,"jessic"- ages: scala.collection.mutable.Map[String,Int] = Map(jessic -> 23, jack -> 30
scala> ages("jack")
res101: Int = 30
复制代码
还有一种创建Map的简易方式,这种方式创建的是不可变Map
scala> val ages = Map(("jack",30),("tom",25),("jessic"->23))
ages: scala.collection.immutable.Map[String,Int] = Map(jack -> 30, tom -> 25,……
复制代码
- 查询操作
获取指定key对应的value,如果key不存在,会报错
scala> val ages = scala.collection.mutable.Map(("jack",30),("tom",25),("jessic",23))
scala> val age = ages("jack")
age: Int = 30
scala> val age = ages("jack1")
java.util.NoSuchElementException: key not found: jack1
复制代码
所以在实际工作中这样直接获取不太好,如果遇到了不存在的key程序会报错,导致程序异常退出。
那是不是可以考虑在获取key的值之前,先判断key是否存在 可以使用contains函数检查key是否存在、
使用if-else语句,如果指定的key不存在,则返回一个默认值
scala> val age = if (ages.contains("jack1")) ages("jack1") else 0
age: Int = 0
复制代码
这样是没问题的,就是写起来有点麻烦了,有没有方便一点的用法呢? map中还有一个getOrElse函数
scala> val age = ages.getOrElse("jack1", 0)
age: Int = 0
复制代码
建议后期从map中获取数据都使用这个 getOrElse 函数
- 修改
更新map中的元素
scala> ages("jack") = 31
复制代码
增加多个元素
scala> ages += ("hehe" -> 35, "haha" -> 40)
复制代码
移除元素
scala> ages -= "hehe"
复制代码
- 遍历
遍历map的entrySet
scala> for ((key, value) <- ages) println(key + " " + value)
jessic 23
jack 31
tom 25
haha 40
复制代码
遍历map的key
scala> for (key <- ages.keySet) println(key) jessic
jack
tom
haha
复制代码
遍历map的value
scala> for (value <- ages.values) println(value)
23
31
25
40
复制代码
最后看一下Map的几个子类 HashMap、SortedMap和LinkedHashMap
- HashMap:是一个按照key的hash值进行排列存储的map
- SortedMap:可以自动对Map中的key进行排序【有序的map】
- LinkedHashMap:可以记住插入的key-value的顺序
Array
也可以直接使用Array()创建数组,元素类型自动推断
scala> val a = Array("hello", "world")
scala> a(0)
res68: String = hello
scala> val a1 = Array("hello", 30)
a1: Array[Any] = Array(hello, 30)
复制代码
如果想使用一个长度可变的数组,就需要使用到ArrayBuffer了
ArrayBuffer
Scala中ArrayBuffer与Java中的ArrayList类似,长度可变
ArrayBuffer:添加元素、移除元素
如果不想每次都使用全限定名,则可以预先导入ArrayBuffer类
scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer
复制代码
- 初始化
使用ArrayBuffer()的方式可以创建一个空的ArrayBuffer
注意:也支持直接创建并且初始化ArrayBuffer(1,2,3,4)
scala> val b = new ArrayBuffer[Int]()
b: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()
复制代码
- 添加元素
使用+=操作符,可以添加一个元素,或者多个元素
b += 1 或者 b += (2, 3, 4, 5)
scala> b += 1
res69: b.type = ArrayBuffer(1)
scala> b += (2, 3, 4, 5)
res70: b.type = ArrayBuffer(1, 2, 3, 4, 5)
复制代码
使用insert()函数可以在指定位置插入元素,但是这种操作效率很低,因为需要移动指定位置后的所有元素
向3号角标的位置添加一个元素 30
scala> b.insert(3,30)
复制代码
- 移除元素
使用 remove() 函数可以移除指定位置的元素
b.remove(0)
复制代码
注意:Array与ArrayBuffer可以互相进行转换
b.toArray:ArrayBuffer转Array
a.toBuffer:Array转ArrayBuffer
Tuple
Tuple:称之为元组,它与Array类似,都是不可变的,但与数组不同的是元组可以包含不同类型的元素 Tuple中的元素角标从 1 开始
注意:目前 Scala 支持的元组最大长度为 22 ,对于更大长度可以使用集合或数组
scala> val t = (1, 3.14, "hehe")
t: (Int, Double, String) = (1,3.14,hehe)
scala> t._1
res117: Int = 1
scala> t._3
res118: String = hehe
复制代码
完整写法
其中Tuple3, 3是指元素的个数
总结
前面讲了很多集合体系中的数据结构,有的是可变的,有的是不可变的,有的是既是可变的又是不可变 的,听起来有点乱,在这里我们总结一下
可变集合: LinkedHashSet、ListBuffer、ArrayBuffer、LinkedHashMap
不可变集合: List、SortedMap
可变+不可变集合: Set、HashSet、SortedSet、Map、HashMap
还有两个编外人员:
Array、Tuple
Array:长度不可变,里面的元素可变
Tuple:长度不可变,里面的元素也不可变
Scala中函数的使用
函数的定义
先来看一下函数的定义
在Scala中定义函数需要使用 def 关键字,函数包括函数名、参数、函数体 Scala要求必须给出函数所有参数的类型,但是函数返回值的类型不是必须的,因为Scala可以自己根据函 数体中的表达式推断出返回值类型。
函数中最后一行代码的返回值就是整个函数的返回值,不需要使用return,这一点与Java不同,java中函 数的返回值是必须要使用return的
下面来实现一个单行函数和多行函数
- 单行函数
scala> def sayHello(name: String) = print("Hello, " + name)
sayHello: (name: String)Unit
scala> sayHello("Scala") Hello, Scala
复制代码
- 多行函数
scala> :paste
// Entering paste mode (ctrl-D to finish)
def sayHello(name: String, age: Int) = { println("My name is "+name+",age is "+age)
age }
// Exiting paste mode, now interpreting. sayHello: (name: String, age: Int)Int
scala> sayHello("Scala",18)
My name is Scala,age is 18 res120: Int = 18
复制代码
Scala面向对象编程
在这里我们主要学习Scala中的类、对象和接口
注意:
Scala中类和java中的类基本是类似的
Scala中的对象时需要定义的,而java中的对象是通过class new出来的 Scala中的接口是trait,java中的接口是interface
类-class
首先看一下类
Scala中定义类和Java一样,都是使用 class 关键字 和Java一样,使用new关键字创建对象
那下面来看一个具体案例
class Person{
var name = "scala"
def sayHello(){
println("hello," +name)
}
def getName= name
}
复制代码
注意:如果在定义方法的时候指定了(),那么在调用的时候()可写可不写,如果在定义方法的时候 没指定(),则调用方法时肯定不能带()