Scala学习笔记
java语言的脚本化。
【菜鸟教程】https://www.runoob.com/scala/scala-tutorial.html
一、安装
安装【推荐下载2.11.12,后面spark2.4.3匹配这个版本】
配置环境变量(菜鸟上有教程)
CMD中运行
Code>scala
Welcome to Scala 2.13.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_251).
Type in expressions for evaluation. Or try :help.
scala> println("Hello World!")
Hello World!
注意 JDK版本,可能会报错
>java -version |
二、IDEA中的Scala插件
安装插件
创建项目/模块
创建一个对象
编写代码
scalaobject Hello {
def main(args: Array[String]): Unit = {
print("Hello World!")
}
}运行
三、变量
3.1 分类
- val:常量,不可变
- var:变量
scala> var a = 1 |
3.2 类型
Scala 与 Java有着相同的数据类型,下表列出了 Scala 支持的数据类型:
数据类型 | 描述 |
---|---|
Byte | 8位有符号补码整数。数值区间为 -128 到 127 |
Short | 16位有符号补码整数。数值区间为 -32768 到 32767 |
Int | 32位有符号补码整数。数值区间为 -2147483648 到 2147483647 |
Long | 64位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807 |
Float | 32 位, IEEE 754 标准的单精度浮点数 |
Double | 64 位 IEEE 754 标准的双精度浮点数 |
Char | 16位无符号Unicode字符, 区间值为 U+0000 到 U+FFFF |
String | 字符序列 |
Boolean | true或false |
Unit | 表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。 |
Null | null 或空引用 |
Nothing | Nothing类型在Scala的类层级的最底端;它是任何其他类型的子类型。 |
Any | Any是所有其他类的超类 |
AnyRef | AnyRef类是Scala里所有引用类(reference class)的基类 |
上表中列出的数据类型都是对象,也就是说scala没有java中的原生类型。在scala是可以对数字等基础类型调用方法的。
3.3 类型转换
scala> var a = 111 |
自定义类型检查和转换
$scala>class Animal{} |
3.4 lazy 延迟计算
有时候可以按需加载,即需要的时候再进行计算。
scala> lazy val x = scala.io.Source.fromFile("d:/tmp") |
3.5 表达式
任何一条表达式都有返回值。如果一个{}中,有多个表达式,其值为最后一个表达式的值。
如果只有一个表达式可以不加分号(;),多个就要用分号隔开。
四、条件判断
4.1 if-else
scala> if (1 > 2) { |
注意:如果要换行,要用{}包起来,不然会报错!
五、循环语句
5.1 while
scala> var a = 1 |
还有 do … while
5.2 for
闭区间
scala> for (i <- 1 to 5) println(i) |
左闭右开区间
scala> for (i <- 1 until 5) println(i) |
5.3 循环中断
scala中没有break和continue来中止循环。但是提供了一个break语句来实现。
scala> var a = 1 |
5.4 for 循环过滤
可以在for中对不满足条件的进行过滤操作。
打印出偶数:
scala> for (i <- 1 to 10 if i % 2 == 0) println(i) |
5.4 for 双重循环
scala> for (i <- 1 to 3; j <- 1 to 2) { |
5.5 for 使用 yield
你可以将 for 循环的返回值作为一个变量存储。语法格式如下:
scala> for (x <- 1 to 10) yield x |
循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合。
六、访问修饰符
Scala 访问修饰符基本和Java的一样,分别有:private,protected,public。
如果没有指定访问修饰符,默认情况下,Scala 对象的访问级别都是 public。
Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。
作用域保护
Scala中,访问修饰符可以通过使用限定词强调。格式为:
private[x] |
这里的x指代某个所属的包、类或单例对象。如果写成private[x],读作”这个成员除了对[…]中的类或[…]中的包中的类及它们的伴生对像可见外,对其它所有类都是private。
这种技巧在横跨了若干包的大型项目中非常有用,它允许你定义一些在你项目的若干子包中可见但对于项目外部的客户却始终不可见的东西。
七、方法与函数
Scala 有方法与函数,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。
Scala 中的方法跟 Java 的类似,方法是组成类的一部分。
Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。
Scala 中使用 val 语句可以定义函数,def 语句定义方法。
class Test{ |
7.1 方法定义
方法定义由一个 def 关键字开始,紧接着是可选的参数列表,一个冒号 : 和方法的返回类型,一个等于号 = ,最后是方法的主体。
Scala 方法定义格式如下:
def functionName ([参数列表]) : [return type] = { |
可以不用写return,每个表达式都有返回值 {}最后会有一个值给函数返回;
没有参数的话,可以不加();
例子:
def addInt( a:Int, b:Int ) : Int = { |
如果返回类型是空(即JAVA中的void),那么返回类型Unit可以省略;如果参数为空,那么()可以省略:
def printMe( ) : Unit = { |
等价于:
scala> def printMe = println("Hello, Scala!") |
7.2 递归函数
递归函数必须显式定义返回类型!
打印阶乘:
scala> def fac(n:Int):Int = if(n == 1) 1 else n * fac(n - 1); |
7.3 函数参数默认值
以下是定义了3个参数,其中1.3位带默认参数。的各种情况。
scala> def decorate(prefix:String = "[[",str:String,suffix:String = "]]") = { |
7.4 可变参数
可变参数带个星号(*):
scala> def sum(a:Int*) = { |
7.5 apply() 和 update()
Array(100) //Array.apply(100);
Array(100) = 200 //Array.update(200)
7.6 闭包
可以简单的理解为:一个函数中,使用了函数外部的一个变量。
var factor = 3 |
七、异常
Scala 的异常处理和其它语言比如 Java 类似。
Scala 的方法可以通过抛出异常的方法的方式来终止相关代码的运行,不必通过返回值。
7.1 抛出异常
Scala 抛出异常的方法和 Java一样,使用 throw 方法,例如,抛出一个新的参数异常:
throw new IllegalArgumentException |
7.2 try | catch | finally
异常捕捉的机制与其他语言中一样,如果有异常发生,catch字句是按次序捕捉的。因此,在catch字句中,越具体的异常越要靠前,越普遍的异常越靠后。 如果抛出的异常不在catch字句中,该异常则无法处理,会被升级到调用者处。
捕捉异常的catch子句,语法与其他语言中不太一样。在Scala里,借用了模式匹配的思想来做异常的匹配,因此,在catch的代码里,是一系列case字句。
finally 语句用于执行不管是正常处理还是有异常发生时都需要执行的步骤。
例子:
import java.io.FileReader |
执行以上代码,输出结果为:
$ scalac Test.scala |
八、字符串
在 Scala 中,字符串的类型实际上是 Java String,它本身没有 String 类。
在 Scala 中,String 是一个不可变的对象,所以该对象不可被修改。这就意味着你如果修改字符串就会产生一个新的字符串对象。
8.1 创建字符串
不可变
scala> var str = "Hello World!" |
scala> var str:String = "Hello World!" |
可变
我们前面提到过 String 对象是不可变的,如果你需要创建一个可以修改的字符串,可以使用 String Builder 类,如下实例:
object Test { |
执行以上代码,输出结果为:
$ scalac Test.scala |
8.2 字符串长度
scala> str.length() |
8.3 字符串连接
scala> str.concat("I'm Tom.") |
scala> "Hello," + "Scala" |
8.4 String 其他方法
可以像使用JAVA中的String方法到Scala中来使用!
【菜鸟示例】https://www.runoob.com/scala/scala-strings.html
八、数组
8.1 定长数组
可以理解[]相当于JAVA中<>的泛型:
var z = new Array[String](3) |
以上语法中,z 声明一个字符串类型的数组,数组长度为 3 ,可存储 3 个元素。我们可以为每个元素设置值,并通过索引来访问每个元素,如下所示:
scala> var z = new Array[String](3) |
注意:
scala> var z = Array(1,2,3) |
这里同样可以创建数组!但是这里代表的意思是:创建一个数组,并赋予3个初始值!而上面那个是创建一个长度为3的数组!
8.2 变长数组
类似C++中的Vector。
创建数组缓冲区对象
scala> import scala.collection.mutable.ArrayBuffer |
末尾追加一个元素
scala> bufArray += 1 |
末尾追加一个数组
scala> bufArray ++= Array(2,3,4) |
删除末尾N个元素
scala> bufArray.trimEnd(2) |
在索引处插入元素
scala> bufArray.insert(0,0) |
删除索引处元素
scala> bufArray.remove(1) |
删除索引处开始N个元素
scala> bufArray.remove(0, 2) |
转为普通数组
bufArray.toArray |
8.3 多维数组
多维数组一个数组中的值可以是另一个数组,另一个数组的值也可以是一个数组。矩阵与表格是我们常见的二维数组。
创建矩阵数组
scala> var a = Array.ofDim[Int](3,3) |
8.4 数组常用方法
排序
scala> var a = Array(1,5,3,2,4) |
求和
scala> a.sum |
最大(小)值
scala> a.min |
格式输出
以什么字符串开头,什么作为分隔符,什么作为结束。
scala> a.mkString("[",",","]") |
数组合并
scala> var b = Array(6,7,8) |
创建区间数组
第三个参数是步长
scala> var c = Array.range(1, 10, 2) |
Scala数组所有方法
下表中为 Scala 语言中处理数组的重要方法,使用它前我们需要使用 import Array._ 引入包。
序号 | 方法和描述 |
---|---|
1 | def apply( x: T, xs: T* ): Array[T]创建指定对象 T 的数组, T 的值可以是 Unit, Double, Float, Long, Int, Char, Short, Byte, Boolean。 |
2 | def concat[T]( xss: Array[T]* ): Array[T]合并数组 |
3 | def copy( src: AnyRef, srcPos: Int, dest: AnyRef, destPos: Int, length: Int ): Unit复制一个数组到另一个数组上。相等于 Java’s System.arraycopy(src, srcPos, dest, destPos, length)。 |
4 | def empty[T]: Array[T]返回长度为 0 的数组 |
5 | def iterate[T]( start: T, len: Int )( f: (T) => T ): Array[T]返回指定长度数组,每个数组元素为指定函数的返回值。以上实例数组初始值为 0,长度为 3,计算函数为a=>a+1:scala> Array.iterate(0,3)(a=>a+1) res1: Array[Int] = Array(0, 1, 2) |
6 | def fill[T]( n: Int )(elem: => T): Array[T]返回数组,长度为第一个参数指定,同时每个元素使用第二个参数进行填充。 |
7 | def fill[T]( n1: Int, n2: Int )( elem: => T ): Array[Array[T]]返回二数组,长度为第一个参数指定,同时每个元素使用第二个参数进行填充。 |
8 | def ofDim[T]( n1: Int ): Array[T]创建指定长度的数组 |
9 | def ofDim[T]( n1: Int, n2: Int ): Array[Array[T]]创建二维数组 |
10 | def ofDim[T]( n1: Int, n2: Int, n3: Int ): Array[Array[Array[T]]]创建三维数组 |
11 | def range( start: Int, end: Int, step: Int ): Array[Int]创建指定区间内的数组,step 为每个元素间的步长 |
12 | def range( start: Int, end: Int ): Array[Int]创建指定区间内的数组 |
13 | def tabulate[T]( n: Int )(f: (Int)=> T): Array[T]返回指定长度数组,每个数组元素为指定函数的返回值,默认从 0 开始。以上实例返回 3 个元素:scala> Array.tabulate(3)(a => a + 5) res0: Array[Int] = Array(5, 6, 7) |
14 | def tabulate[T]( n1: Int, n2: Int )( f: (Int, Int ) => T): Array[Array[T]]返回指定长度的二维数组,每个数组元素为指定函数的返回值,默认从 0 开始。 |
九、集合
【菜鸟集合文档】https://www.runoob.com/scala/scala-collections.html
9.1 Map 映射
9.1.1 创建
可变 Map
// 空哈希表,键为字符串,值为整型 |
不可变 Map
scala.collection.immutable.Map[Int,String] |
9.1.2 添加与删除
添加
scala> map + (3->"Scala") |
scala> map += (3->"Scala") |
合并
注意!重复的Key会被后面覆盖
scala> var map1 = Map(1->"one",2->"two") |
删除
scala> map -= 1 |
9.1.3 Key-Value 基本操作
获取元素
scala> map(2) |
返回所有Key
scala> map.keys |
返回所有Value
scala> map.values |
判断空
scala> map.isEmpty |
查看Map中是否存在指定Key
scala> map.contains(3) |
9.1.4 遍历
scala> map1 |
scala> for((k, v) <- map1) printf("Key: %d, Value: %s\n", k, v) |
9.2 Tuple 元组
元组:一组数据的集合
9.2.1 创建
以一个()返回
scala> var tuple = (1,"2",true) |
9.2.2 获取元素
scala> tuple._1 |
注意:下标是1开始
9.2.3 zip 拉链操作
数组进行ZIP,两两组合成一个元组,多余无法匹配的被丢弃。
scala> var array1 = Array(1, 2, 3, 4) |
9.3 List 列表
Scala 列表类似于数组,它们所有元素的类型都相同,但是它们也有所不同:列表是不可变的,值一旦被定义了就不能改变,其次列表 具有递归的结构(也就是链接表结构)而数组不是。。
9.3.1 创建
// 字符串列表 |
构造列表的两个基本单位是 Nil 和 :: (:: 是右结合)
Nil 也可以表示为一个空列表。
以上实例我们可以写成如下所示:
// 字符串列表 |
fill
我们可以使用 List.fill() 方法来创建一个指定重复数量的元素列表:
object Test { |
9.3.2 基本操作
head
返回列表第一个元素tail
返回一个列表,包含除了第一元素之外的其他元素isEmpty
在列表为空时返回truereverse
用于将列表的顺序反转
9.3.3 连接列表
你可以使用 ::: 运算符或 List.:::() 方法或 List.concat() 方法或ListA ++ ListB来连接两个或多个列表。
object Test { |
9.4 Set 集合
Scala Set(集合)是没有重复的对象集合,所有的元素都是唯一的。
Scala 集合分为可变的和不可变的集合。
默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包。
9.4.1 创建、添加、删除
默认引用 scala.collection.immutable.Set,不可变集合实例如下:
val set = Set(1,2,3) |
如果需要使用可变集合需要引入 scala.collection.mutable.Set:
import scala.collection.mutable.Set // 可以在任何地方引入 可变集合 |
注意: 虽然可变Set和不可变Set都有添加或删除元素的操作,但是有一个非常大的差别。对不可变Set进行操作,会产生一个新的set,原来的set并没有改变,这与List一样。 而对可变Set进行操作,改变的是该Set本身,与ListBuffer类似。
9.4.2 连接集合
你可以使用 ++ 运算符或 Set.++() 方法来连接两个集合。如果元素有重复的就会移除重复的元素。实例如下:
object Test { |
执行以上代码,输出结果为:
$ vim Test.scala |
9.4.3 交集
你可以使用 Set.& 方法或 Set.intersect 方法来查看两个集合的交集元素。实例如下:
object Test { |
执行以上代码,输出结果为:
$ vim Test.scala |
十、迭代器
Scala Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法。
迭代器 it 的两个基本操作是 next 和 hasNext。
调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
调用 it.hasNext() 用于检测集合中是否还有元素。
让迭代器 it 逐个返回所有元素最简单的方法是使用 while 循环:
object Test { |
执行以上代码,输出结果为:
$ scalac Test.scala |
最大最小值
你可以使用 it.min 和 it.max 方法从迭代器中查找最大与最小元素,实例如下:
object Test { |
执行以上代码,输出结果为:
$ scalac Test.scala |
获取迭代器的长度
你可以使用 it.size 或 it.length 方法来查看迭代器中的元素个数。实例如下:
object Test { |
执行以上代码,输出结果为:
$ scalac Test.scala |
【其他常用方法】https://www.runoob.com/scala/scala-iterators.html
十一、类 与 对象
类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。
如果不什声明修饰符,那么默认是public。
11.1 定义类
示例
object Hello { |
运行结果
1 |
11.2 BeanProperty 注解
为了能更好的配合JAVA的一些工具类,提供了注解的方式,来生成JAVA风格的getter和setter。
import scala.beans.BeanProperty |
11.3 构造函数
11.3.1 主构造
class Person(val name:String,var age:Int , id :Int){ |
编译 scalac test.scala、 javap Person.class
public class Person { |
可以看到如果在类参数中加上var\val或者不加,也会生成相应的属性。
val:只读,即只有get
var:有get和set
无类型:虽然编译后没有该属性,但如果有用到该属性的时候,也会创建一个。
scalavar p = new Person("tom", 15, 5)
p.hello()
p.id // 错误!无法调用输出结果:
Code5
11.3.2 辅助构造
注意:使用辅助构造的时候,第一行必须有this()或this(…)
class Person{ |
11.4 object 对象
11.4.1 单例对象
说明:
scala没有静态的概念,如果需要定义静态成员,可以通过object实现。object对象不能带参数。
编译完成后,会生成对应的类,方法都是静态方法,非静态成员对应到单例类中
单例类以Util$作为类名称。
object Util{ |
def main(args: Array[String]): Unit = { |
运行结果:
hello world |
11.4.2 伴生对象
当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。
class Car{ |
输出结果
run... |
11.5 抽象类 与 继承
一个简单的例子
//抽象类 |
Scala继承一个基类跟Java很相似, 但我们需要注意以下几点:
- 1、重写一个非抽象方法必须使用override修饰符。
- 2、只有主构造函数才可以往基类的构造函数里写参数。
- 3、在子类中重写超类的抽象方法时,你不需要使用override关键字。
接下来让我们来看个实例:
class Point(xc: Int, yc: Int) { |
Scala 使用 extends 关键字来继承一个类。实例中 Location 类继承了 Point 类。Point 称为父类(基类),Location 称为子类。
override val xc 为重写了父类的字段。
继承会继承父类的所有属性和方法,Scala 只允许继承一个父类(单继承)。
实例如下:
import java.io._ |
执行以上代码,输出结果为:
$ scalac Test.scala |
Scala重写一个非抽象方法,必须用override修饰符。
class Person { |
执行以上代码,输出结果为:
$ scalac Test.scala |
十二、包
包对象,编译完之后生成以xxx为package,下面含有类package.class + package.class
package object xxxx{ |
限定符
private[package|this] |
导包
import java.io.Exception |
十三、文件IO
13.1 写文件
Scala 进行文件写操作,直接用的都是 java中 的 I/O 类 (java.io.File):
import java.io._ |
执行以上代码,会在你的当前目录下生产一个 test.txt 文件,文件内容为”菜鸟教程”:
$ scalac Test.scala |
13.2 读文件
从文件读取内容非常简单。我们可以使用 Scala 的 Source 类及伴生对象来读取文件。以下实例演示了从 “test.txt”(之前已创建过) 文件中读取内容:
import scala.io.Source |
执行以上代码,输出结果为:
$ scalac Test.scala |
13.3 从控制台读取
import scala.io.StdIn |
十四、正则*
14.1 查找
14.2 替换
14.3 正则表达式
十五、Trait 特征
Scala Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。
与接口不同的是,它还可以定义属性和方法的实现。
一般情况下Scala的类只能够继承单一父类,但是如果是 Trait(特征) 的话就可以继承多个,从结果来看就是实现了多重继承。
如果只有一个trait使用extends进行扩展,如果多个,使用with对剩余的trait进行扩展。
Trait(特征) 定义的方式与类类似,但它使用的关键字是 trait,如下所示:
trait Equal { |
以上Trait(特征)由两个方法组成:isEqual 和 isNotEqual。isEqual 方法没有定义方法的实现,isNotEqual定义了方法的实现。子类继承特征可以实现未被实现的方法。所以其实 Scala Trait(特征)更像 Java 的抽象类。
以下演示了特征的完整实例:
/* 文件名:Test.scala |
执行以上代码,输出结果为:
$ scalac Test.scala |
特征构造顺序
特征也可以有构造器,由字段的初始化和其他特征体中的语句构成。这些语句在任何混入该特征的对象在构造时都会被执行。
构造器的执行顺序:
- 调用超类的构造器;
- 特征构造器在超类构造器之后、类构造器之前执行;
- 特征由左到右被构造;
- 每个特征当中,父特征先被构造;
- 如果多个特征共有一个父特征,父特征不会被重复构造
- 所有特征被构造完毕,子类被构造。
构造器的顺序是类的线性化的反向。线性化是描述某个类型的所有超类型的一种技术规格。
十六、高级函数
16.1 函数变量
变量类型是函数类型。就有点C++的函数指针的意思。
// 定义一个函数 |
16.2 匿名函数
没有函数名的函数。一般配合一些参数是函数的函数。
scala> val f = (n: Double) => 3 * n |
有点C++中的lambda表达式的意思。
scala> Array(1,2,3,4).map(x => x * 2) |
16.3 参数是函数的函数
f: (参数类型1[, 参数类型N])=>(结果类型)
scala> def fun(a: Int, b: Int, f: (Int, Int) => Int) = f(a, b) |
16.4 返回值是函数的函数
可以在返回值后加一个()调用
scala> def fun(a: Int, b: Int) = (x: Int) => (a + b) * x |
其中fun(1, 2)返回了一个新的函数,后面的(3)是新的函数的参数。
十七、控制抽象(分线程)
return挺有用的有时候,可以中途返回。
object Thread { |
输出结果:
1 |
这里提供一种更简洁的写法:
object Thread { |
十八、模式匹配
18.1 模式匹配
值匹配
Scala 提供了强大的模式匹配机制,应用也非常广泛。
一个模式匹配包含了一系列备选项,每个都开始于关键字 case。每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。
val ch = '9' |
输出结果:
数字 |
有点类似switch语句,但是不同的是,执行完case后的语句会直接结束,而不用使用break
类型匹配
val x:Any = "123"; |
输出结果:
is String |
18.2 样例类
使用了case关键字的类定义就是就是样例类(case classes),样例类是种特殊的类,经过优化以用于模式匹配。
在声明样例类时,下面的过程自动发生了:
- 构造器的每个参数都成为val,除非显式被声明为var,但是并不推荐这么做;
- 在伴生对象中提供了apply方法,所以可以不使用new关键字就可构建对象;
- 提供unapply方法使模式匹配可以工作;
- 生成toString、equals、hashCode和copy方法,除非显示给出这些方法的定义。
object Test { |
执行以上代码,输出结果为:
$ scalac Test.scala |
sealed 密封样例类
子类和父类必须定义在同一文件中。
sealed abstract class Dog{} |
18.3 PartialFunction 偏函数
val f:PartialFunction[Char,Int] = { |
十九、泛型
19.1 类泛型
class Pair[T,S](one:T,second:S); //定义泛型类 |
19.2 方法泛型
def getMiddle[T](arr:Array[T]) = arr(arr.length / 2); |
19.3 泛型的界限
def run[T <: Dog](d:T) = println("hello") |