快速上手Scala语言

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/

image.png

image.png

进入相应的版本链接后,根据自己的电脑系统选择相应的安装版本,这里选择Mac OX版本

www.scala-lang.org/download/2.…

image.png

配置环境变量 (配置SCALA_HOME,PATH)

image.png

刷新source /etc/profile

验证,在终端输入scala验证

image.png

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声明的变量,值不能被修改,否则会报错

image.png

注意:在实际工作中,针对一些不需要改变值的变量,通常建议使用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函数

image.png

注意,to函数还可以这样写

image.png

使用基本数据类型,直接就可以调用RichInt中对应的函数

image.png

操作符

Scala的算术操作符与Java的算术操作符没有什么区别
比如 +、-、*、/、% 等,以及 &、|、^、>>、<< 等
注意:Scala中没有提供++、–操作符
我们只能使用+和-,比如count = 1,count++是错误的,必须写做count += 1

image.png

if 表达式

在Scala中,if表达式是有返回值的,就是if或者else中最后一行语句返回的值,这一点和java中的if是不一
样的,java中的if表达式是没有返回值的

例如: val age = 20; if (age > 18) 1 else 0

image.png

在这因为if表达式是有返回值的,所以可以将if表达式赋予一个变量

image.png

由于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的公共父类型

image.png

如果if后面没有跟else,则默认else的值是Unit,也可以用()表示,类似于java中的void或者null
例如,val age = 12; if(age > 18) “old”。此时就相当于if(age > 18) “old” else ()。 此时表达式的值是Any

image.png

如果想在scala REPL中执行多行代码,该如何操作? 使用 :paste 和 ctrl+D 的方式
:paste 表示代码块的开始
ctrl+D 表示代码块的结束

image.png

语句终结符

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在语 法层面的区别

image.png

这里的to可以换成until

image.png

对比两次执行的结果发现
1 to 10 可以获取1~10之间的所有数字
1 until 10可以获取1~9之间的所有数字
所以在这需要注意了,to 和 until 其实都是函数,一个是闭区间,一个是开区间 具体用哪个就要看你的需求了

for循环针对字符串还可以用

image.png

注意:在这里我在for循环后面没有使用花括号,都省略了,主要是因为for循环的循环体代码就只 有一行,如果有多行,就需要使用花括号了,否则,最终执行的结果就不是我们想要的

image.png

image.png

while循环

while循环,它的用法和java中的while也是很像的,主要看一下语法层面的区别

image.png

高级for循环

if守卫

if守卫模式,假设我们想要获取1~10之间的所有偶数,使用普通的for循环,需要把每一个数字都循 环出来,然后判断是否是偶数
如果在for循环里面使用if守卫,可以在循环的时候就执行一定的逻辑,判断数值是否是偶数

image.png

for推导式

for推导式,一个典型例子是构造集合

我们在使用for循环迭代数字的时候,可以使用yield指定一个规则,对迭代出来的数字进行处理,并 且创建一个新的集合

image.png

Scala的集合体系

image.png

集合的顶层接口是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关键字,来看一下

image.png

这是不是很奇怪,本来Set是一个接口,但是却可以创建对象,更神奇的是竟然还不需要使用new关键 字,这就有点颠覆我们的认知了

注意了,大家在学习Scala的时候,可以拿Java来进行对比,加深理解,但是不要全部拿Java里面 的知识点来硬套,因为它们两个有些地方还是不一样的。

来看一下Scala的文档,你会发现这个Set不仅仅是一个接口,它还是一个Object,具体这个Object类型我 们在后面会详细分析,在这大家先知道这个东西就行了。

image.png

注意:默认情况下直接创建的set集合是一个不可变集合,在这可以看到是在immutable包里面 的,不可变集合中的元素一经初始化,就不能改变了,所以初始化后再向里面添加元素就报错了。

但是注意,我使用s + 4 这种操作是可以的

这样是不是和我们刚才说的自相矛盾?
不是的,因为 s + 4 返回的是一个新的集合了,相当于在之前的集合的基础上,创建一个新的集合,新的 集合包含之前集合的元素和我们新增的4这个元素
这个大家需要能够区分开
如果想要创建一个可变的set集合,可以使用mutable包下面的set集合,显式指定包名

image.png

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代表一个不可变的列表

image.png

针对List有 head 、 tail 以及 :: 这几个操作

先演示一下 head、tail 操作

image.png

  • head:表示获取List中的第一个元素
  • tail:表示获取List中第一个元素之后的所有元素

那其实head和tail就可以获取list中的所有元素了
通过 :: 操作符,可以将head和tail的结果合并成一个List

:: 这种操作符要清楚,在spark源码中是有体现的,一定要能够看懂

在这里List是不可变的列表,在实际工作中使用的时候会很不方便,因为我们很多场景下都是需要向列表 中动态添加元素?

Scala还提供的有一个ListBuffer
ListBuffer: 可以支持动态增加或者移除元素

image.png

image.png

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

image.png
也可以直接使用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
复制代码

完整写法

image.png
其中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
}
复制代码

注意:如果在定义方法的时候指定了(),那么在调用的时候()可写可不写,如果在定义方法的时候 没指定(),则调用方法时肯定不能带()

image.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享