1 下载地址
2 安装部署
- 下载
[root@zhangyu software]# wget https://downloads.lightbend.com/scala/2.11.8/scala-2.11.8.zip
- 解压
[root@zhangyu software]# unzip scala-2.11.8.zip
- 配置环境变量
[root@zhangyu ~]# vi /etc/profile
export SCALA_HOME=/opt/software/scala-2.11.8
export PATH=$SCALA_HOME/bin:$PATH
生效配置文件:
[root@zhangyu ~]# source /etc/profile
- 启动
[root@zhangyu ~]# scala -version
Scala code runner version 2.11.8 -- Copyright 2002-2016, LAMP/EPFL
[root@zhangyu ~]# scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_45).
Type in expressions for evaluation. Or try :help.
scala>
3 基本语法
- 学习前我们先提出一个问题,为什么要学习Scala语言呢?
- 第一肯定是为了学习后面的Spark,因为Spark底层是用Scala实现;
- 对于作者来说是有一定java的基础的,刚开始接触Scala给我的感觉就是语言很简洁,优雅,开发速度快,给我的感觉就是很强大,有种让人不可思议的感觉;
- 能够融合到大数据生态圈。
- Scala是运行在JVM上的,所以Scala中也可以写java代码,所以很多java的工具类,也可以使用。
- 简单了解Scala
- Scala是基于Java虚拟机(JVM)的一门编程语言,所有Scala的代码需经过编译为字节码,然后交由Java虚拟机来运行,所以Scala可以任意调用Java的代码和引用Jar包。
- 本文为大家介绍了 Scala 的基本语法,相比 Java,Scala 的语法更加简洁,比如 Scala 的类型推断可以省略程序中绝大多数的类型声明,短小精悍的匿名函数可以方便的在函数之间传递,还有各种在 Scala 社区约定俗成的习惯,比如省略的分号以及函数体只有一条表达式时的花括号,这一切都帮助程序员写出更简洁,更优雅的程序,下面我们一起进行简单的学习。
3数据类型
Scala有8种数据类型:
Boolean:true/false
Byte:8位,有符号
Short:16位,有符号
Int:32位,有符号
Long:64位,有符号
Char:16位,无符号
Float:32位,单精度浮点数
Double:64位,双精度浮点数
Scala 中所有值都是类对象,所有的类都继承统一的根类 Any:
Null 是所有引用类型的子类型 , Nothing是所有类型的子类型.
3.1内置变量
scala> 1+2
res0: Int = 3
scala> val array = Array(1,2,3,4)
array: Array[Int] = Array(1, 2, 3, 4)
scala> array(0) = 5
scala> array
res1: Array[Int] = Array(5, 2, 3, 4)
scala> res0+5
res1: Int = 8
上面res0就是内存变量,可用res0变量参与公式计算或字符串拼接。如输入res0+5或"hello:res0"
3.2变量声明
Scala具有用于声明变量的不同语法。它们可以被定义为值,即常数或变量。
- val(常量声明):不可变
scala> val x:Int=0
x: Int = 0
- var(变量声明):可变
scala> var y:String="Hello Scala"
y: String = Hello Scala
现在我们尝试改变x,y的值,会有什么不同:
scala> x=1;
<console>:12: error: reassignment to val
x=1;
^
给常量赋值会出现编译错误
scala> y="haha"
y: String = haha
- 下面我们再试试如果不给常量或者变量声明类型会报错吗?
可变类型推断
scala> val x1=1
x1: Int = 1
scala> var y1="hello"
y1: String = hell0
默认情况下,x1将会被计算推断为Int类型,y1将计算推断为String类型变量。
- 定义变量时没有指定变量类型。这是否意味着 Scala 是和 Python 或者 Ruby 一样的动态类型语言呢?恰恰相反,Scala 是严格意义上的静态类型语言,由于其采用了先进的类型推断(Type Inference)技术,程序员不需要在写程序时显式指定类型,编译器会根据上下文推断出类型信息。比如变量 x被赋值为 0,0 是一个整型,所以 x的类型被推断出为整型。当然,Scala 语言也允许显示指定类型,如变量 x1,y1的定义。一般情况下,我们应尽量使用 Scala 提供的类型推断系统使代码看上去更加简洁。
- 另一个发现是程序语句结尾没有分号,这也是 Scala 中约定俗成的编程习惯。大多数情况下分号都是可省的,如果你需要将两条语句写在同一行,则需要用分号分开它们。
val的再次理解
scala> val array = Array(1,2,3,4)
array: Array[Int] = Array(1, 2, 3, 4)
scala> array(0) = 5
scala> array
res1: Array[Int] = Array(5, 2, 3, 4)
上面我们说了val是申明不可变变量的,但在这里我们声明了一个array但是我们又修改了值,并没有报错.是不是很奇怪呢?
val 和var关键字只标识引用本身是否可以指向另外一个不同的对象,他们并没有表明所引用的对象是否可变. 为了减少可变性引起的bug,应该尽量使用不可变变量
val 和var在声明变量时都必须初始化
3.3多个赋值
- Scala支持多个赋值。如果代码块或方法返回一个元素数组(Tuple - 保持不同类型的对象的集合),则可以将元素数组分配给一个val变量。
注 :我们将在随后的章节学习元组。
scala> val (myVar1:Int,myVar2:String)=Pair(22,"Hello")
warning: there was one deprecation warning; re-run with -deprecation for details
myVar1: Int = 22
myVar2: String = Hello
类型推断得到正确的类型
scala> val (myVar11,myVar22)=Pair(22,"Hello")
warning: there was one deprecation warning; re-run with -deprecation for details
myVar11: Int = 22
myVar22: String = Hello
3.4 强制类型转换
Scala中不像java可以进行强制类型转化,需要通过方法进行:
to toChar toFloat toLong toShort
toBinaryString toDegrees toHexString toOctalString toString
toByte toDouble toInt toRadians
- to…
定义一个常量:
scala> val c=10
c: Int = 10 (自动推测为Int类型)
强转:
scala> 10.to 这是按Tab键查看如下函数
to toChar toFloat toLong toShort
toBinaryString toDegrees toHexString toOctalString toString
toByte
scala> 10.toDouble
res1: Double = 10.0
- asInstanceOf
scala> val a=2
a: Int = 2
scala> a.asInstanceOf[Double]
res4: Double = 2.0
- isInstanceOf
scala> a.isInstanceOf[Int]
res5: Boolean = true
scala> a.isInstanceOf[Double]
res6: Boolean = false
3.5 定义函数
- 语法:
def functionName ([list of parameters]) : [return type] = {
function body
return [expr]
}
这里,返回类型可以是任何有效的Scala数据类型,参数列表将是由逗号分隔的变量列表,参数列表和返回类型是可选的。
1.带参数没有返回值
scala> def test1(x:Int) : Unit={println(x+x)}
test1: (x: Int)Unit
scala> test1(2)
4
2.带参数带返回值
scala> def add(x:Int):Int={x+x}
add: (x: Int)Int
scala> add(2)
res4: Int = 4
3.无参无返回值
scala> def sayHello():Unit=println("hello word")
sayHello: ()Unit
scala> sayHello()
hello word
scala> sayHello 如果没有入参括号都可以省略,是不是很强大,很神奇。
hello word
总结:首先,函数体没有像 Java 那样放在 {}里。Scala 中的一条语句其实是一个表达式,函数的执行过程就是对函数体内的表达式的求值过程,最后一条表达式的值就是函数的返回值。如果函数体只包含一条表达式,则可以省略 {}。其次,没有显示的 return语句,最后一条表达式的值会自动返回给函数的调用者。
3.6 HelloWorld.scala
- 在java中我们对于一个java文件需要使用javac编译成class文件,那么scala中其实和java也是一样的,下面来学习我们经典的Hello World例子。
[hadoop@zhangyu ~]$ vi Hello.scala
object HelloWorld {
def main(args: Array[String]) {
println("Hello World")
}
}
编译:
[hadoop@zhangyu ~]$ scalac ~/Hello.scala
查看生成的class文件:
-rw-rw-r--. 1 hadoop hadoop 88 Feb 3 23:03 Hello.scala
-rw-rw-r--. 1 hadoop hadoop 581 Feb 3 23:05 HelloWorld.class
-rw-rw-r--. 1 hadoop hadoop 630 Feb 3 23:05 HelloWorld$.class
可以看到生成了class文件
结果:
[hadoop@zhangyu ~]$ scala HelloWorld
Hello World
- Scala 还提供了一个更简便的方式,直接继承另一个对象 App,无需定义 main方法,编译即可运行。
object HelloScala extends App {
println("Hello Scala!")
}
3.7 lazy关键字
Scala中使用关键字lazy来定义惰性变量,实现延迟加载(懒加载)。 惰性变量只能是不可变变量,并且只有在调用惰性变量时,才会去实例化这个变量。
java中也有懒加载,java中的单例模式大家应该在熟悉不过来(懒汉模式,饿汉模式),作者以前的博客中也有所解释,有兴趣的同学可以看一看。这里我们主要看看scala中懒加载。
注意: lazy只能修饰常量,不能修饰变量
- 不使用lazy关键字对常量修饰
object LazyOptions {
def init(): Unit ={
println("你好,我是init")
}
def main(args: Array[String]): Unit = {
//定义一个常量
val property1 = init() //没有使用lazy修饰
println("哈哈")
}
}
上面的property1没有使用lazy关键字进行修饰,所以property是立即实例化的,观察程序的输出:
你好,我是init
哈哈
- 使用lazy关键字进行修饰:
object LazyOptions {
def init(): Unit ={
println("你好,我是init")
}
def main(args: Array[String]): Unit = {
//定义一个常量
// val property1 = init() //没有使用lazy修饰
// println("哈哈")
lazy val property2= init()
println("哈哈")
println(property2)
}
}
这时property2使用了lazy关键字修饰,我们来看看输出结果:
哈哈
你好,我是init
()
在声明property2时,并没有立即调用实例化方法intit(),而是在使用property2时,才会调用实例化方法,并且无论缩少次调用,实例化方法只会执行一次。
Scala同样使用了Java中常用的懒加载的方式自动帮助我们实现了延迟加载,并且还加锁避免多个线程同时调用初始化方法可能导致的不一致问题。
3.8 迭代
- to(左闭右闭)
scala> 1 to 5
res9: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)
scala> 1.to(5)
res10: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)
- Range(左闭右开)
scala> Range(1,10)
res16: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> Range(1,10,1)
res17: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> Range(1,10,2)
res18: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
scala> Range(10,1,-1)
res20: scala.collection.immutable.Range = Range(10, 9, 8, 7, 6, 5, 4, 3, 2)
- until(左闭右开)
scala> 1 until 10
res23: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
和Range是一样的
3.9 流程控制语句
- 使用 if else 表达式
def abs(n: Int): Int =
if (n > 0) n else -n
scala> val x = 5
x: Int = 5
scala> val s = if(x>0) 1 else -1
s: Int = 1
scala> val s = if(x>1) "a" else ()
s: Any = a
scala> val x = -1
x: Int = -1
scala> val s = if(x>1) "a" else ()
s: Any = ()
- 使用 while 为列表求和
def sum(xs: List[Int]) = {
var total = 0
var index = 0
while (index < xs.size) {
total += xs(index)
index += 1
}
total
}
- for循环(重点)
语法结构for (i <- 表达式)
让变量i遍历右边的表达式的所有值。至于这个遍历具体如何执行,则取决于表达式的类型。对于Scala集合比如Range而言,这个循环会让i依次取得区间中的每个值。
说明:在for循环的变量之前并没有val或var的指定。该变量的类型是集合的元素类型。循环变量的作用域一直持续到循环结束。
- 最简单的语法
object For extends App{
for (i <- 1 to 5){
println(i)
}
}
结果:
1
2
3
4
5
- 您可以在for循环中使用由分号(;)分隔的多个范围,在这种情况下,循环将遍历给定范围所有可能的计算。以下是仅使用两个范围的示例,也可以使用两个以上的范围。
object For extends App{
for (i <- 1 to 3;j <- 1 to 3){
println("i"+":"+i)
println("j"+":"+j)
}
}
结果:
i:1
j:1
i:1
j:2
i:1
j:3
i:2
j:1
i:2
j:2
i:2
j:3
i:3
j:1
i:3
j:2
i:3
j:3
- 遍历集合中的元素
object For extends App{
var sum=0
val numList = List(1, 2, 3, 4, 5, 6)
for (i <- numList){
sum+=i
println(i)
}
println(sum);
}
结果:
1
2
3
4
5
6
21
- for循环允许过滤出使用一个或多个某些元素if语句(多个)。
var a = 0;
val numList = List(1,2,3,4,5,6,7,8,9,10);
// for loop execution with multiple filters
for( a <- numList
if a != 3; if a < 8 ){
println( "Value of a: " + a );
}
结果:
Value of a: 1
Value of a: 2
Value of a: 4
Value of a: 5
Value of a: 6
Value of a: 7
scala> for(i <- 1 to 5; x = 10-i) print(x + " ")
9 8 7 6 5
- for循环与yield
需要返回值怎么办呢?
这时候就可以使用yield,使用yield关键值能在for循环中生成新的集合,集合中的元素会根据类型推到而出.
var a = 0;
val numList = List(1,2,3,4,5,6,7,8,9,10);
// for loop execution with a yield
var retVal = for{ a <- numList if a != 3; if a < 8 }
yield a
// Now print returned values using another loop.
for( a <- retVal){
println( "Value of a: " + a );
}
}
结果:
Value of a: 1
Value of a: 2
Value of a: 4
Value of a: 5
Value of a: 6
Value of a: 7
3.10 默认参数/命令参数/变长参数
- 默认参数
object Params {
// 默认参数
def sayName(name: String = "zhangsan"): Unit = {
println(name)
}
def main(args: Array[String]): Unit = {
sayName("zhangsan")
sayName("lisi")
sayName()
}
}
结果:
zhangsan
lisi
zhangsan
- 命令参数
object Params {
// 命令参数
def speed (distance:Float, time:Float) = distance/time
def main(args: Array[String]): Unit = {
println(speed(100, 3))
println(speed(distance = 100, time = 3))
println(speed(time = 3, distance = 100))
}
}
结果:
33.333332
33.333332
33.333332
- 变长参数
object Params {
// 变长参数
def sum(nums: Int*) = {
var result = 0
for(num <- nums) {
result += num
}
result
}
def main(args: Array[String]): Unit = {
println(sum(2,3))
println(sum(2,3,4))
println(sum(2,3,4,5))
// :_* 强转符号
println(sum(1 to 5 : _*))
}
}
结果:
5
9
14
15
3.11 option类型
Scala为单个值提供了对象的包装器,表示可能存在也可能不存在的值, Some 表示某个值,None表示空,避免了使用null 和空字符串.
scala> val map = Map("zhangsan" -> 1, "lisi" -> 2)
map: scala.collection.immutable.Map[String,Int] = Map(zhangsan -> 1, lisi -> 2)
scala> map.get("zhangsan")
res8: Option[Int] = Some(1)
scala> val value = map.get("zhangsan")
value: Option[Int] = Some(1)
scala> value
res9: Option[Int] = Some(1)
scala> println(value)
Some(1)
scala> val value1 = map.get("wangwu")
value1: Option[Int] = None
scala> println(value1)
None
//使用getOrElse 可以给定一个默认值,如果可以查不出来就只指定的默认值
scala> val value2 = map.getOrElse("wangwu","aaa")
value2: Any = aaa