scala 环境搭建&基本语法

Scala


0x00开始安装

1. JVM

Scala是运行在JVM平台上的,所以你需要有JDK环境

2.安装Scala

Windows安装Scala编译器
  1. 点此下载

    访问Scala官网http://www.scala-lang.org/下载Scala编译器安装包,目前最新版本是2.12.x

    下载scala-2.11.8.msi后点击下一步就可以了(一般会自动配置上环境变量)。也可以下载scala-2.11.8.zip解压即可

  2. 检查环境变量是否成功,打开CMD(也可以去path里看有没有$SCALA_HONE$)输入scala

    cmd

Linux安装Scala编译器

下载Scala地址https://www.scala-lang.org/download/2.11.8.html

然后解压Scala到指定目录

tar -zxvf scala-2.11.8.tgz -C /opt/soft

配置环境变量,将scala加入到PATH中

  • vim /etc/profile

export JAVA_HOME=/opt/soft/jdk1.8

export PATH=$PATH:$JAVA_HOME/bin:/opt/soft/scala-2.11.8/bin

Idea安装

目前Scala的开发工具主要有两种:Eclipse和IDEA,这两个开发工具都有相应的Scala插件,如果使用Eclipse,直接到Scala官网下载即可http://scala-ide.org/download/sdk.html。

由于IDEA的Scala插件更优秀,大多数Scala程序员都选择IDEA,可以到http://www.jetbrains.com/idea/download/下载,点击下一步安装即可,安装时如果有网络可以选择在线安装Scala插件。

离线安装Scala插件:

1.安装IDEA,点击下一步即可。

2.下载IEDA的scala插件

插件地址: https://plugins.jetbrains.com/plugin/1347-scala

3.安装Scala插件:Configure -> Plugins -> Install plugin from disk -> 选择Scala插件 -> OK -> 重启IDEA

在线安装Scala插件:

idea打开: Settings -> Plugins -> 搜索Scala -> lnstall ->重启idea

记得设置补全变量类型:

idea


0x01基本语法

  • Scala代码
1
2
3
4
5
6
object HelloScala {
def main(args: Array[String]): Unit = {
println("hello scala")
System.out.println("hello java")
}
}
  • 对比Java代码
1
2
3
4
5
6
public class HelloJava {
public static void main(String[] args){
System.out.println("hello java");
Console.print("hello scala");
}
}

Scala易百教程

Scala菜鸟教程


0x01变量、类型、操作符

1.变量声明

Scala声明变量有两种方式,一个用val,一个用var。

val/var 变量名 : 变量类型 = 变量值。

val和var声明变量时都必须初始化。

val定义的值是不可变的,它不是一个常量,是不可变量,或称之为只读变量。相当于java里用final修饰的变量

val定义的变量虽然不能改变其引用的内存地址,但是可以改变其引用的对象的内部的其他属性值。

为了减少可变性引起的bug,应该尽可能地使用不可变变量。

变量类型可以省略,解析器会根据值进行推断。

关键字 是否可以更改
var 声明变量 可以
val 声明常量 不可以

Scala 语言鼓励程序员使用 val 来声明,因为Scala语言设计哲学中,认为流程中修改变量值,会造成程序逻辑复杂,不易维护,不易并行计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
object VariableDemo {
def main(args: Array[String]) {
//使用val定义的变量值是不可变的,相当于java里用final修饰的变量
val i:Int = 1
//使用var定义的变量是可变得,在Scala中鼓励使用val
var s1 = "hello"
var s2 = "hello"
//Scala编译器会自动推断变量的类型,必要的时候可以指定类型
//变量名在前,类型在后
//import java.lang._
val s3:String = "bigdata"
val s4:java.lang.String = "bigdata"
}
}

2.常用数据类型

Scala和Java一样,有多种数值类型Byte、Char、Short、Int、Long、Float、Double类型和1个Boolean类型。

Boolean true 或者 false
Byte 8位, 有符号
Short 16位, 有符号
Int 32位, 有符号
Long 64位, 有符号
Char 16位, 无符号
Float 32位, 单精度浮点数
Double 64位, 双精度浮点数
String 其实就是由Char数组组成
  • Scala继承层级

统一类型,是Scala的又一大特点。

  • Any

在scala中,所有的类,包括值类型,都最终继承自一个统一的根类型Any,Any类是跟节点

Any中定义了isInstanceOf、asInstanceOf方法,以及哈希方法等。AnyVal和AnyRef都扩展自Any类。

  • AnyVal

所有的值都是类类型都是AnyVal的子类,

  • AnyRef

所有其他类都是AnyRef的子类,类似Java的Object。

更特别的是,Scala中还定义了几个底层类(Bottom Class),比如Null和Nothing。

  • Null

是所有引用类型的子类型,Null类只有一个实例对象,null,类似于Java中的null引用。null可以赋值给任意引用类型,但是不能赋值给值类型。

  • Nothing

是所有类型的子类型。Nothing类型没有实例。它对于泛型结构是有用处的,举例:

空列表Nil的类型是List[Nothing],它是List[T]的子类型,T可以是任何类。

Nothing可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于Nothing是其他任意类型的子类,他还能跟要求返回值的方法兼容。

  • Unit

用来标识过程,也就是没有明确返回值的函数。由此可见,Unit类似于Java里的void。Unit只有一个

对象实例,(),这个实例也没有实质的意义。

继承关系

  • Any

在scala中,Any类是所有类的超类,Any有两个子类:AnyVal和AnyRef

  • AnyVal- 所有值类型的基类, 它描述的是值,而不是代表一个对象。

它包括 9个AnyVal 子类型:

- scala.Double

- scala.Float

- scala.Long

- scala.Int

- scala.Char

- scala.Short

- scala.Byte

上面是数字类型。

还包括scala.Unit 和 scala.Boolean 是非数字类型。

  • AnyRef- 是所有引用类型的基类。除了值类型,所有类型都继承自AnyRef 。
  • 注意:

与Java中的数据类型不同,Scala并不刻意区分基本类型和引用类型,所以这些类型都是对象,可以调用相对应的方法。

String直接使用的是java.lang.String. 不过,由于String实际是一系列Char的不可变的集合,Scala中大部分针对集合的操作,都可以用于String.

具体来说,String的这些方法存在于类scala.collection.immutable.StringOps中。

由于String在需要时能隐式转换为StringOps,因此不需要任何额外的转换,String就可以使用这些方法。

每一种数据类型都有对应的Rich* 类型,如RichInt、RichChar等,为基本类型提供了更多的有用操作。


3.操作符重载

Scala中的+ - * / %等操作符的作用与Java一样,位操作符 & | ^ >> <<也一样。但是有一点区别,这些操作符实际上是方法。

那么既然+ - * / %是方法,那么就可以进行操作符重载,完成特殊的运算

你几乎可以用任何符号来为方法命名。

注意:Scala中没有++、–操作符,需要通过+=、-=来实现同样的效果。

  • 举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.bigdata.basic

object OperationDemo {
def main(args: Array[String]): Unit = {
var a = 1 + 2
var b = 1.+(2)
println(a)//3
println(b)//3.0

//var c = a++ //错误
a += 1
a.+=(1)
println(a)//5
}
}
  • 运算符重载 伪代码

scala

  • 操作符总结

1) 如果想在变量名、类名等定义中使用语法关键字(保留字),可以配合反引号反引号:

1
val `val` = 123

2) 中置操作符,A操作符B等同于A.操作符(B) ==> a + b ==> a.+(b)

3) 后置操作符,A操作符等同于A.操作符,如果操作符定义的时候不带()则调用时不能加括号

4) 前置操作符,+、-、!、~等操作符A等同于A.unary_操作符。

5) 赋值操作符,A操作符=B等同于A=A操作符B


0x02判断和循环

1.条件表达式

Scala的的条件表达式比较简洁,且Scala中if else表达式是有返回值的,如果if或者else返回的类型不一样,就返回Any类型(所有类型的公共超类型)。

如果缺少一个判断,什么都没有返回,但是Scala认为任何表达式都会有值,对于没有返回值的,使用Unit类,写做(),叫做无用占位符,相当于java中的void

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.bigdata.basic

object ConditionDemo {
def main(args: Array[String]) {
val x = 1
//判断x的值,将结果赋给y
val y = if (x > 0) 1 else -1
//打印y的值
println(y)

//支持混合类型表达式
val z: Any = if (x > 1) 1 else "error"
//打印z的值
println(z)

//如果缺失else,相当于if (x > 2) 1 else ()
val m = if (x > 2) 1
println(m)

//在scala中每个表达式都有值,scala中有个Unit类,用作不返回任何结果的方法的结果类型,相当于Java中的void,Unit只有一个实例值,写成()。
val n = if (x > 2) 1 else ()
println(n)

//if和else if
val k = if (x < 0) 0
else if (x >= 1) 1 else -1
println(k)
}
}

2.块表达式

定义变量时用{} 包含一系列表达式,其中块的最后一个表达式的值就是块的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.bigdata.basic

object BlockExpressionDemo {
def main(args: Array[String]) {
val x = 0
//在scala中{}中包含一系列表达式,块中最后一个表达式的值就是块的值
val result : Any= {
if (x < 0){
-1
} else if(x >= 1) {
1
} else {
"error"
}
}
//result的值就是块表达式的结果
println(result)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.bigdata.basic

object BlockExpressionDemo2 {
def main(args: Array[String]): Unit = {
val a = 1
val b = 2
val result = {
val c = a + b
val d = c + a
d
}
println(result)
}
}

3.循环

在scala中有for循环和while循环,用for循环比较多

  • while
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.bigdata.basic

object WhileDemo {
def main(args: Array[String]): Unit = {
var n = 1;
val while1 = while(n <= 10){
n += 1
}
println(while1)
println(n)

println("===============")

import scala.util.control.Breaks
val loop = new Breaks
loop.breakable{
while(n <= 20){
n += 1;
if(n == 18){
loop.break()
}
}
}
println(n)
}
}

注意:

与If语句不同,While语句本身没有值,即整个While语句的结果是Unit类型的()

scala并没有提供break和continue语句来退出循环,如果需要break,可以通过几种方法来做

1、使用Boolean型的控制变量

2、使用嵌套函数,从函数中return

3、使用Breaks对象的break方法。

  • for

for循环语法结构:

for (变量 <- 表达式/数组/集合)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.bigdata.basic

object ForDemo {
def main(args: Array[String]) {
//1.for(i <- 表达式),表达式1 to 10返回一个Range []
//to为[] until 为 [)
//每次循环将区间中的一个值赋给i
for (i <- 1 to 10)//[1,10]
println(i)

println("==========")
for (i <- 1 to 10 by 2)//[1,10]步长为2
println(i)

println("==========")

for (i <- 1 until 10)//[1,10)
println(i)

println("==========")


//2.for(i <- 数组)
val arr = Array("a", "b", "c")
for (i <- arr)
println(i)

//总结:
/*for(变量名 <- 集合){
循环体
}*/

//3.高级for循环
//每个生成器都可以带一个条件,注意:if前面没有分号
for(i <- 1 to 3; j <- 1 to 3 if i != j)
print((10 * i + j) + " ")
println()


//4.for推导式:如果for循环的循环体以yield开始,则该循环会构建出一个集合
//即将遍历过程中处理的结果返回到一个变量
//每次迭代生成集合中的一个值
val v = for (i <- 1 to 10) yield i * 10
println(v) // 10,20,30..100
//也就是说:如果for循环的循环体以yield开始,for循环会返回一个集合


//5.{}和 ()都可以
//当for 推导式仅包含单一表达式时使用()括号,当其包含多个表达式时使用{}括号
for(
i <- 1 to 3; //[1,3]
j = 4 - i//[3,1]
)
print(i * j + " ")
println()

for {
i <- 1 to 3;
j = 4 - i
}
print(i * j + " ")
println()
}
}

0x03 方法和函数

1.调用方法和函数

在scala中,函数与方法是不同的东西。后面我们再详细探讨。首先我们要学会使用scala来调用函数与方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
object MethodAndFunctionDemo {
def main(args: Array[String]): Unit = {
//1.静态方法(scala中没有静态方法这个概念,需要通过伴生类对象来实现)
val a: Double = math.sqrt(100)//对象名.方法名
println(a)

//2.非静态方法,使用对象调用
val s = "HelloWorld"
val len: Int = s.length
println(len)

//3.apply方法是调用时可以省略方法名的方法。用于构造和获取元素:
//Array(1,2,3) 等同于 Array.apply(1,2,3)
val arr1 = Array(1,2,3)
println(arr1.mkString(","))
val arr2 = Array.apply(1,2,3)
println(arr2.mkString(","))

//"Hello"(1) 等同于 "Hello".apply(1) //输出e
val c1: Char = "Hello"(1)
println(c1)
val c2 = "Hello".apply(1)
println(c2)

//4.update方法也是调用时可以省略方法名的方法,用于元素的更新
//arr1(1) = 5 等同于 arr2.update(1,5)
arr1(1) = 5
println(arr1.mkString(","))
arr2.update(1,5)
println(arr2.mkString(","))

//5.定义并调用函数
val fun = (x:Int) => x * 10
val arr3: Array[Int] = arr1.map(fun)
println(arr3.mkString(","))
}
}

2.定义方法

def 方法名(参数名1: 参数类型1, 参数名2: 参数类型2) : 返回类型 = {方法体}

scala

scala

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
object MethodAndFunctionDemo2 {
def main(args: Array[String]): Unit = {
val a = m1(2,3)
println(a)
val b = factorial(4)
println(b)//4*3*2*1==24
shout4("abc") //"abc4"
shout4(content="abc")//"abc4"
shout4(content="abc",leg=3)//"abc3"
shout4(leg=3,content="abc")//"abc3"
val s1: Int = sum(1,2)
println(s1)
val s2: Int = sum(1,2,3)
println(s2)
}
//1.方法的返回值类型和return可以不写,编译器可以自动推断出来
//def 方法名(参数名:参数类型,参数名:参数类型):返回值类型={方法体}
def m1(x:Int,y:Int):Int = {
x*y
}

//2.对于递归方法,必须指定返回类型
def factorial(n: Int): Int = {
if(n <= 0)
1
else
n * factorial(n - 1)
}
//f(3) --> 3*f(2) -->3*2*f(1) -->3*2*1*f(0) -->3*2*1*1 ==> 6

//3.如果方法没有返回值,返回Unit类型(返回值为Unit的方法也叫过程)
def shout1(content: String) : Unit = {
println(content)
}

//4.返回Unit类型,类型也可以省略
def shout2(content: String) = {
println(content)
}

//5.返回值类型有多种可能,此时为Any
def shout3(content: String):Any = {
if(content.length >= 3)
content + "喵喵喵~"
else
3
}

//6.带有默认值参数的方法,调用时,可以只给无默认值的参数传递值,也可以都传递,新值会覆盖默认值;
//传递参数时如果不按照定义顺序,则可以通过参数名来指定
def shout4(content: String, leg: Int = 4) = {
println(content + "," + leg)
}

//7.可变参使用 变量名:类型* (类似Java的...)
def sum(args: Int*) = {
var result = 0
for(arg <- args)
result += arg
result
}
}

3.定义函数

val函数名称 :(参数类型)=>函数返回值类型 = (参数名称:参数类型)=>函数体

或者简写

val函数名名称 = (参数名称:参数类型) => 函数体

带有一个参数的函数的类型是function1,带有两个是function2,以此类推

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
object FunctionDemo {
def main(args: Array[String]): Unit = {
//定义函数方式1:
//函数名称= (参数名:参数类型) => 函数体
val f1 = (x:Int,y:Int) => x + y
val i1 = f1(1,2)//3
println(i1)

//定义函数方式2:(了解)
//函数名称:(参数类型) =>函数返回值类型= (参数名称:参数类型) =>函数体
val f2:(Int,Int) => Int = (x,y) => x + y
val i2 = f2(1,2);
println(i2)
}
}

4.方法和函数的区别

方法:和之前理解的方法一样,是封装了完成某些功能的代码块,所属于某一个类或对象

函数:在Scala中,函数一个对象,那么既然是对象的话,函数就可以当作参数被传递,还可以使用函数打点调用方法

在函数式编程语言中,函数是“头等公民”,函数是一个对象,继承自FuctionN。它可以像任何其他数据类型一样被传递和操作

1
2
3
4
5
6
7
8
def main(args: Array[String]): Unit = {
val f1 = (x:Int) => x
val f2 = (x:Int,y:Int) => x + y
val f3 = (x:Int,y:Int,z:Int) => x + y + z
println(f1)
println(f2)
println(f3)
}

scala

函数对象有applycurriedtoStringtupled这些方法。而方法不具有这些特性。

  • 案例:首先定义一个方法,再定义一个函数,然后将函数传递到方法里面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
object MethodAndFunctionDemo3  {
def main(args: Array[String]): Unit = {
//定义了一个函数,参数是2个Int,返回值是
val f1 = (x:Int,y:Int) => x + y
//或者这样定义:
//函数名称:(参数类型) =>函数返回值类型=(参数引用)=>函数体
//val f1:(Int,Int)=>Int=(x,y)=>x+y
val f2 = (x:Int,y:Int) => x * y

//调用方法,并把函数作为参数传递给方法
val r1 = m1(f1)
val r2 = m1(f2)

println(r1)//8
println(r2)//12
}

//定义了一个方法,参数是函数类型,方法的返回值是Int,而函数的参数是2个Int,返回值是Int
def m1 (f:(Int,Int)=>Int):Int={
//方法体中调用了函数,并传递了2个参数给函数,
//并把函数的返回值,当作方法的返回值给方法的调用者返回
f(2,6)
}
}

5.将方法转换成函数(神奇的下划线)

将方法转换成函数,只需要在方法的后面加上一个下划线

1
2
3
4
5
6
7
8
def main(args: Array[String]): Unit = {
val f1 = m1 _
println(f1)
}

def m1 (f:(Int,Int)=>Int):Int={
f(2,6)
}

scala

6.总结

scala

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
object FunctionDemo {
def main(args: Array[String]): Unit = {
//定义函数方式1:
//函数名称=(参数名称:类型)=>函数体
val f1 = (x:Int,y:Int) => x + y
//定义函数方式2:(了解)
//函数名称:(参数类型)=>返回值类型 = (参数列表)=>函数体
val f2:(Int,Int)=>Int=(x,y)=>x - y

val r1 = m1(f1);
println(r1)//4

val r2 = m1(f2);
println(r2)//2

val r3 = m1((x:Int,y:Int)=>x*y)
println(r3)//3

}

//定义一个方法,方法的参数接收一个函数
def m1(f:(Int,Int)=>Int): Int ={
f(3,1)
}
}

0x04其他

1.懒值

当val被声明为lazy时,初始化将被推迟,直到我们首次对此取值,适用于初始化开销较大的场景。

1
2
3
4
5
6
7
8
9
10
11
12
object LazyDemo {
def main(args: Array[String]): Unit = {
//使用lazy修饰变量,只有当这个变量被使用到的时候,变量的赋值代码才会真正的执行
lazy val msg = init()
println("init方法调用了,但是没有执行")
println(msg)
}
def init(): String = {
println("init方法执行")
"嘿嘿嘿,我来了~"
}
}

2.异常处理

Scala的异常的工作机制和Java一样,但是Scala没有“checked”受检异常,你不需要声明函数或者方法可能会抛出某种异常。

(受检异常在编译器被检查,java必须声明方法所会抛出的异常类型)

抛出异常:用throw关键字,抛出一个异常对象。所有异常都是Throwable的子类型。throw表达式是有类型的,就是Nothing,因为Nothing是所有类型的子类型,所以throw表达式可以用在需要类型的地方。

捕捉异常:在catch的代码里,使用一系列case子句(借用了模式匹配的思想来做异常的匹配)

异常捕捉的机制与其他语言中一样,如果有异常发生,catch字句是按次序捕捉的。因此,在catch字句中,越具体的异常越要靠前,越普遍的异常越靠后。

如果抛出的异常不在catch字句中,该异常则无法处理,会被升级到调用者处。

finally字句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
object ExceptionDemo {
def main(args: Array[String]): Unit = {
try {
println(divider(10, 0))
} catch {
case ex: Exception => println("捕获了异常:" + ex)
} finally {
println("释放资源")
}
}

def divider(x: Int, y: Int): Float = {
if (y == 0) throw new Exception("0作为了除数")
else x / y
}
}
# Scala
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×