**
Scala
一、概述
Scala是一门多范式的编程语言,一种类似java的编程语言 [1] ,设计初衷是实现可伸缩的语言 [2] 、并集成面向对象编程和函数式编程的各种特性。
二、安装和使用
安装配置JDK(略)
安装配置Scala
下载地址:
https://downloads.lightbend.com/scala/2.13.1/scala-2.13.1.msi
配置环境变量
使用
-命令行(REPL)
Scala解释器读到一个表达式,对它进行求值,将它打印出来,接着再继续读下一个表达式。这个过程被称做读取–求值–打印–循环,即:REPL。
C:\Users\PC>scala
Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_211).
Type in expressions for evaluation. Or try :help.
scala>
三、变量和类型
常用类型
和Java一样,Scala中也有7种数值类型:Byte、Short、Int、Long、Double、Float、Char,以及1个Boolean类型。跟Java不同的是,这些类型都是类,归属于scala.*包。还有一种常用类型为java.lang.String用以声明字符串
表达式
表达式是可计算的语句。如: 1+1
你可以使用println来输出表达式。
println(1) // 1
println(1 + 1) // 2
println("Hello!") // Hello!
println("Hello," + " world!") // Hello, world!
声明变量
除了可以重新赋值,变量和常量类似。你可以使用"var"关键字来定义一个变量。
var x = 1 + 1
x = 3 // This compiles because "x" is declared with the "var" keyword.
println(x * x) // 9
注意:scala类型可以被推断,或者你也可以显示地声明类型,语法var x : Int = 10
声明常量
你可以使用"val"关键字来给表达式的结果命名。
var x = 1 + 1
println(x) //2
x = 3 // This does not compile.
类型转换
值类型可以按照下面的方向进行转换:
例如:
val x: Long = 987654321
val y: Float = x // 9.8765434E8 (note that some precision is lost in this case)
val face: Char = '☺'
val number: Int = face // 9786
转换是单向,下面这样写将不会通过编译。
val x: Long = 987654321
val y: Float = x // 9.8765434E8
val z: Long = y // Does not conform
你可以将一个类型转换为子类型
运算符
- 算术运算符:+、-、*、/、%
- 关系运算符:==、!=、>、<、>=、<=
- 逻辑运算符:&&、||、!
- 位运算符:&(按 位与)、|(按位或)、^(异或)、~(取反)、<<、>>、>>>(无符号)
- 赋值运算符:=
注意:在Scala中,运算符即是方法。
10 + 1可以写成10.+(1)
Scala类型层次结构
在Scala中,所有的值都有类型,包括数值和函数。下图阐述了类型层次结构的一个子集。
Any
是所有类型的超类型,也称为顶级类 型。它定义了一些通用的方法如equals、hashCode和toString。Any有两个直接子类:AnyVal和AnyRef。AnyVal
代表值类型。有9个预定义的非空的值类型分别是:Double、Float、Long、Int、Short、Byte、Char、Unit和Boolean。Unit(类似于java中的void)是不带任何意义的值类型,它仅有一个实例可以像这样声明:()。所有的函数必须有返回,所以说有时候Unit也是有用的返回类型。AnyRef
代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义的类型都是AnyRef的子类型。如果Scala被应用在Java的运行环境中,AnyRef相当于java.lang.Object。Nothing
是所有类型的子类型,也称为底部类型。没有一个值是Nothing类型的。它的用途之一是给出非正常终止的信号,如抛出异常、程序退出或者一个无限循环(可以理解为它是一个不对值进行定义的表达式的类型,或者是一个不能正常返回的方法)。Null
是所有引用类型的子类型(即AnyRef的任意子类型)。它有一个单例值由关键字null所定义。Null主要是使得Scala满足和其他JVM语言的互操作性,但是几乎不应该在Scala代码中使用。我们将在后面的章节中介绍null的替代方案。
这里有一个例子,说明了字符串、整型、布尔值和函数都是对象,这一点和其他对象一样:
val list: List[Any] = List(
"a string",
732, // an integer
'c', // a character
true, // a boolean value
() => "an anonymous function returning a string"
)
list.foreach(element => println(element))
这里定义了一个类型List的变量list。这个列表里由多种类型进行初始化,但是它们都是scala.Any的实例,所以可以把它们加入到列表中。
下面是程序的输出:
a string
732
c
true
<function>
四、条件分支
if(布尔表达式 1){
// 如果布尔表达式 1 为 true 则执行该语句块
}else if(布尔表达式 2){
// 如果布尔表达式 2 为 true 则执行该语句块
}else if(布尔表达式 3){
// 如果布尔表达式 3 为 true 则执行该语句块
}else {
// 如果以上条件都为 false 执行该语句块
}
if条件分支
if ( n1>0) true
if…else条件分支
如:
val result = if ( n1 > 0) true else false
if…else if … else条件分支
val result = if (n1 > 0) 1
else if(n1 == 0) 0
else -1
注意:
- 代码块中只有一行内容,可以省略{},如有多行代码需用{}
- 代码块的最后一个表达式的值就是块的值
- scala中没有switch语句,不过它有一个强大很多的模式匹配,后续介绍
五、循环
Scala的循环类似于Java的循环
while循环
while(boolean条件){
// 循环体
}
如:求1到100数值之和
// 控制台输入求和值
val input : String = readLine("请输入:")
var num = input.toInt
var sum = 0
while(num > 0){
sum = sum + num
num = num - 1
}
print(sum)
do … while…循环
do{
//循环体
}while(boolean条件)
如:求1到100数值之和
do {
sum = sum.+(num)
num = num - 1
} while (num > 0)
print(sum)
for循环
Scala没有与Java for循环直接对应的结构,语法如下:
// m to n ,返回m到n(包含m)的Range
for (n <- 1 to 100) {
sum = sum + n
}
print(sum)
// m until n ,返回m到n(不包含m)的Range
for (n <- 1 until 100) {
sum = sum + n
}
print(sum)
嵌套for循环
如:九九乘法表
// 传统方式
for (m <- 1 to 9) {
for (n <- 1 to m) {
print(s"$m*$n=" + m * n + "\t")
if (m == n) println
}
}
// 推荐方式:多层循环用”;“分隔
for (m <- 1 to 9; n <- 1 to m) {
print(s"$m*$n=" + m * n + "\t")
if (m == n) println
}
for遍历集合
val list = List[String]("Hello World", "Hello Hadoop")
for (str <- list) {
println(str)
}
for循环过滤(if守卫)
if作为for守卫语句能实现过滤的效果,显著地降低循环次数,并且更易读,如下:
for (n <- 1 to 10 if n % 2 == 0 if n > 5) {
println(s"$n")
}
// 6 8 10
yield
for 循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合
- 针对每一次 for 循环的迭代, yield 会产生一个值,被循环记录下来 (内部实现上,像是一个缓冲区).
- 当循环结束后, 会返回所有 yield 的值组成的集合.
- 返回集合的类型与被遍历的集合类型是一致的.
val result = for (n <- 1 to 10 if n % 2 == 1 if n != 5) yield n * 2
println(result)
//Vector(2, 6, 14, 18)
// 如果您愿意:也可以将生成器、守卫和定义包含在花括号中,并以换行的方式分隔
val result = for {n <- 1 to 10
if n % 2 == 1
if n != 5}yield n * 2
println(result)
Break
Scala中没有提供break和continue关键词来退出循环,可以使用Breaks对象中的break方法终止循环
import scala.util.control.Breaks._
breakable {
for (n <- 1 to 10) {
if (n == 5) break
println(n)
}
}
六、函数
函数三要素:函数名、参数、函数体
声明函数语法
def functionName ([参数列表]) : [return type] = { 函数体}
示例
// 无返回值
def sayHi(str: String): Unit = {
println("Hello:" + str)
}
// 可省略返回值类型,支持自动推导
def sum(x: Int, y: Int) = x + y
def multi(x: Double, y: Double): Double = x * y
变长参数
object Function01 {
def main(args: Array[String]): Unit = {
sum(1,2,3,4,5)
}
// 可变长参数
def sum(nums: Int*):Unit = {
for (num <- nums){
println(num)
}
}
}
参数默认值
m1()
def m1(arg1: String = "default value1", arg2: Int = 10): Unit ={
println (arg1 + "\t" + arg2)
}
// default value1 <---> 10
带名参数
m1(arg2 = 100, arg1 = "new value")
def m1(arg1: String = "default value1", arg2: Int = 10): Unit ={
println (arg1 + "\t" + arg2)
}
//new value <---> 100
Lazy值
当val被声明为lazy时,它的初始化将被推迟,直到我们首次对它取值。
import scala.io.Source._
/**
* lazy值 使用时初始化 节省资源
*/
object function03 {
def main(args: Array[String]): Unit = {
val file1 = fromFile("").mkString
println(file1)
// lazy val file2 = fromFile("").mkString
// println(file2)
}
}
递归函数
递归求1到n之和
def sum(n: Int): Int = {
var total = 0
if (n >= 1) {
total = n + sum(n - 1)
}
total
}
递归求1到n的阶乘
def factorial(n: Int): Int = {
if (n > 1) {
n * factorial(n - 1)
} else {
1
}
}
匿名函数
Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。使用匿名函数后,我们的代码变得更简洁了。
val plus = (x: Int, y: Int) => x + y
println(plus(1, 2))
val call = () => "土豆土豆,我是地瓜!"
println(call())
柯里化(Currying)
柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。
// 传统写法
def sum1(x: Int,y: Int) = x + y
// 柯里化写法
def sum2(x: Int) = (y: Int) => x + y
// 柯里化简化写法
def sum3(x: Int)(y: Int)(z: Int) = x + y + z
七、数组操作
- 概念上类似于Java中的数组,一个特定类型的元素集合
数组的初始化
object Array01 {
def main(args: Array[String]): Unit = {
// 数组初始化
val a1 = new Array[Int](10)
val a2 = new Array[String](5)
val a3 = Array[Int](1,2,3,4,5)
// 数组的使用
println(a1(0)) //默认值 0
println(a2(0)) //默认值 null
println(a3(0)) //初始值 1
}
}
数组的使用
- 长度 length
- 下标 index
val a1 = Array(1, 2, 3, 4, 5)
// 传统方式
for (n <- 0 until (a1.length)) {
println(a1(n))
}
// 推荐方式
for (n <- a1.indices) {
println(a1(n))
}
// 跳跃遍历(步长2)
for (n <- a1.indices by 2) {
print(a1(n) +"\t")
}
排序
/**
* 冒泡排序
* 2 1 5 0 3
* 第一轮:
* 1 2 5 0 3
* 1 2 5 0 3
* 0 2 2 1 3
*
*
*/
object Array02 {
def main(args: Array[String]): Unit = {
val a1 = Array(2, 11, 1, 13, 5, 0, 3)
for (m <- 0 until a1.length) {
for (n <- m + 1 until a1.length) {
val tmp = a1(m)
if (a1(m) > a1(n)) {
a1(m) = a1(n)
a1(n) = tmp
}
}
}
for (i <- a1) {
print(i + "\t")
}
}
}
ArrayBuffer
可变数组
- 类似于ArrayList集合
import scala.collection.mutable.ArrayBuffer
object ArrayBuffer01 {
def main(args: Array[String]): Unit = {
val a1 = new ArrayBuffer[Byte]()
// 注意: += 语法是给ArrayBuffer添加元素
a1 += 1
a1 += 2
// 一次性添加多个元素
a1 += (3,4,5)
// 注意: ++= 将数组中的所有元素添加到ArrayBuffer中
a1 ++= Array[Byte](6,7,8)
// 从末尾截断指定个数的元素
a1.trimEnd(3)
// 从下标5 添加元素 6,7,8
a1.insert(5,6,7,8)
// 跳跃遍历
for ( i <- 0 until(a3.length,2) ) {
print(a3(i) +"\t")
}
}
}
Array和ArrayBuffer相互转换
val a1 = new ArrayBuffer[Byte]()
// 转为数组
val a2 = a1.toArray
a2(2) = 0
// 转为ArrayBuffer
val a3 = a2.toBuffer
println(a1)
println(a3)
其它操作
import scala.util.Sorting
object Array04 {
def main(args: Array[String]): Unit = {
val a1 = Array(5,4,3,2,1)
println(a1.sum) //15
println(a1.max) //5
println(a1.min) //1
println(a1.mkString) //54321
println(a1.mkString(",")) //5,4,3,2,1
println(a1.mkString("<",",",">")) //<5,4,3,2,1>
//println(a1.sorted.mkString) // 排序
Sorting.quickSort(a1) //快速排序
println(a1.mkString)
}
}
多维数组
定义
// 二维数组 有三个一维数组,每个一维中包含四个元素
val arr = Array.ofDim(3,4)
使用
package com.baizhi.array
/**
* 多维数组
*/
object MultiplyBuffer01 {
def main(args: Array[String]): Unit = {
val arr = Array.ofDim[Int](3, 4)
for (m <- arr) {
print(m.mkString(","))
println()
}
// 二维数组赋值
arr(2)(1) = 100
for (m <- arr) {
print(m.mkString(","))
println()
}
}
}
练习:算法案列:移除第一个负数后的所有负数
八、类
类定义
class User
val user1 = new User
关键字new 被用于创建类的实例,类默认提供一个无参的构造器
简单类
Scala中的类,类似于java,由属性和方法构成
package classes
class Student {
var name: String = "" // 必须初始化
var age: Int = 0
def sayHello(): String = {
"Hello:" + name + ",I am " + age + "years old"
}
override def toString: String = this.name + "\t" + this.age
}
object StudentDemo {
def main(args: Array[String]): Unit = {
val s1 = new Student()
s1.name = "小红"
s1.age = 18
val s2 = new Student
s2.name = "小青"
s2.age = 20
println(s1) // 小红 18
println(s2) //小青 20
}
}
getter/setter
自动生成
class Person {
var name: String = null
val age : Int = 0
private var sex: Boolean = false
}
注意:
- 在scala类中,声明的属性都是私有成员
- 用var定义的属性,自动提供getter/setter方法
- 用val定义的属性,自动提供getter方法
- scala中的getter/setter,命名不同于java,语法是 name 和 name_
- 在scala中属性一旦设置为private, 它的getter/setter方法均为私有,仅限本类使用。
手动生成
class Person2 {
private var privateName: String = null
def name = privateName // 类似于java的getter
def name_(newName: String) { // setter
privateName = newName
}
}
@BeanProperty
class Person3{
@BeanProperty var name:String = null
}
注意:scala的字段被标注为@BeanProperty时,会自动生成Java和Scala的getter/setter
反编译后的结果如下:
public class Person3
{
private String name = null;
public String name() { return this.name; }
public void name_$eq(String x$1) { this.name = x$1; }
public String getName() { return name(); }
public void setName(String x$1) { name_$eq(x$1); }
}
辅助构造器
和Java一样,Scala的类有一个主构造器(Primary Constructor)和任意多个辅助构造器(Auxiliary Constructor)
- 辅助构造器的名称为this
- 每一个辅助构造器必须以一个对先前已定义的其它辅助构造器或主构造器的调用开始
如:
package classes
class Fish {
var kind: String = ""
var name: String = ""
def this(name: String) {
this() // 调用主构造
this.name = name
}
def this(name: String, kind: String) {
this(name) // 调用前一个辅助构造器
this.kind = kind
}
}
object FishDemo{
def main(args: Array[String]): Unit = {
val f1 = new Fish("鲫鱼")
val f2 = new Fish("金鱼","xdd")
val f3 = new Fish()
}
}
主构造器
在scala中,每个类都有主构造器。主构造器并不以this方法定义,而是与类定义交织在一起
// 主构造器的参数写在类名之后 color
class Tiger(val color: String){
}
主构造的参数被编译成字段,其值被初始化成构造传入的参数。注意:主构造函数看上去和类的定义已经完全融合在了一起!它的参数列表放到了类名的后面(我们也可以直接叫它类参数列表),它的方法体就是整个类体,实例化一个类时,类体(主构造函数)中所有可行的部分都会被执行,不管是函数调用还是表达式等等,只是对于类的字段和方法声明而言是没有什么可执行的,它们只是声明而已。
// 主构造器的参数写在类名之后 color
class Tiger(val color: String) {
println("构造开始")
def sayHi(name: String): String = {
"Hello:" + name
}
println("构造结束")
}
object TigerDemo {
def main(args: Array[String]): Unit = {
val t1 = new Tiger("红色")
println("-------------------------------")
val t2 = new Tiger("白色")
}
}
/*
构造开始
构造结束
-------------------------------
构造开始
构造结束
*/
主构造的其它变化
// 对于var修饰的参数:外部可读/可改写 (实际上是:编译器为该类参数(字段)自动生成了getter和setter)
class Tiger2(var name: String, var age: Int) {
var sex: String = ""
}
// 同上
class Tiger2(var name: String, var age: Int) {
var sex: String = ""
}
// 对于val修饰的参数:外部可读/不可改写(实际上是:编译器为该类参数(字段)只生成了getter没有生成setter)
class Tiger3(var name: String, var age: Int, val sex: Boolean) {
}
// 对于private var修饰的参数:内部可读/可改写 (编译器不会为私有类参数(字段)自动生成getter和setter)
// 对于private val修饰的参数:内部可读/不可改写 (编译器不会为该类参数(字段)自动生成getter和setter)
class Tiger4(private var name: String) {
}
// 不带val或者var的参数,不会作为类的成员,只有被一个方法所使用,它将被升格为字段
class Tiger5(name: String, age: Int) {
def sayHi() = "Hello:" + name
}
八、对象
单例对象
Scala中没有静态方法或者静态属性,可以使用object这样的语法达到相同目的。
package objects
object IdFactory {
private var id: Int = 0
def getId(): Int = {
id += 1
id
}
}
object TestIdFactory {
def main(args: Array[String]): Unit = {
println(IdFactory.getId()) // 1
println(IdFactory.getId()) // 2
println(IdFactory == IdFactory) // true
}
}
伴生对象
当类名和单例类名字一样的时候,我们把单例类称为伴生对象
package objects
// 伴生对象
object Person {
private var id: Int = 0
def getPersonNum(): Int = {
id += 1
id
}
def main(args: Array[String]): Unit = {
val p1 = new Person()
val p2 = new Person()
val p3 = Person
val p4 = Person
println(p1 == p2) // false
println(p3 == p4) // true
println(p1.id) // 1
println(p2.id) // 2
}
}
// 伴生类
class Person {
private var id: Int = Person.getPersonNum()
private var address: String = ""
}
注意:
- object声明的为伴生对象,class声明的为伴生类
- 伴生类可以访问伴生对象中的私有成员,前提是在同一源文件中,语法:伴生对象.私有成员/方法
apply & unapply
apply 方法用在object中一般作为工厂方法用于产生Class对象
class Tiger(var kind: String) {}
object Tiger {
def apply(kind: String): Tiger = new Tiger( kind)
def main(args: Array[String]): Unit = {
val t1= Tiger.apply("东北虎")
val t2 = Tiger("东北虎")
}
}
apply方法的最佳实践方式之一就是用来做工厂。比如在Scala的标准库中,许多集合类给我们提供了apply方法来创建集合:
val a1 = Array(1,2,3)
val b1 = List("Hello Hadoop","Hello Scala")
可以认为unapply方法是apply方法的反向操作,apply方法接受构造参数变成对象,而unapply方法接受一个对象,从中提取值
class Tiger(var kind: String) {}
object Tiger {
def apply(kind: String): Tiger = new Tiger(kind)
def unapply(arg: Tiger): Option[String] = {
if (arg == null) None
else Some(arg.kind)
}
def main(args: Array[String]): Unit = {
val t1 = Tiger.apply("东北虎")
val t2 = Tiger("东北虎")
val Tiger(kind) = t2
println(kind) // 东北虎
}
}
九、继承
scala继承使用extends关键字
package classes
class Animals {
var name: String = ""
}
class Bear extends Animals {
def run = name + "在愉快的奔跑!"
}
object AnimalsDemo {
def main(args: Array[String]): Unit = {
val bear = new Bear
bear.name = "北极熊"
println(bear.run) // 北极熊在愉快的奔跑!
}
}
方法覆盖
覆盖父类中的方法,使用override关键字
如:
class Animals {
var name: String = ""
}
class Bear extends Animals {
def run = name + "在愉快的奔跑!"
// 覆盖toString方法
override def toString: String = name+"愉快的玩耍!"
}
object AnimalsDemo {
def main(args: Array[String]): Unit = {
val bear = new Bear
bear.name = "北极熊"
println(bear.run)
println(bear)
}
}
注意:
- final修饰的方法不能覆盖,类不能继承
- 调用父类中的方法,使用super.方法名
类型检查和转换
判断类型是否兼容,可以使用isInstanceOf
类型强转,可以使用asInstanceOf
val bear = new Bear
bear.name = "北极熊"
// println(bear.run)
// println(bear)
println(bear.isInstanceOf[Animals]) //true
println(bear.asInstanceOf[Animals]) //classes.Bear@7d417077
println(bear.getClass == classOf[Bear]) //true
调用父类有参构造
类有一个主构器和任意数量的辅助构造器,而每个辅助构造器都必须以对先前定义的辅助构造器或主构造器的调用开始。子类的辅助构造器最终都会调用主构造器,只有主构造器可以调用超类的构造器。辅助构造器永远都不可能直接调用超类的构造器。在Scala的构造器中,你不能调用super(params)
class Animals(val name: String) {
}
class Dog(name: String) extends Animals(name: String) {
}
重写字段
package classes.constructor
class Animals3 {
var name: String = ""
var count: Int = 0
val sex: Boolean = false
}
class Dog3 extends Animals3 {
name = "xh"
count = 1
override val sex: Boolean = true
}
object Animals3Demo {
def main(args: Array[String]): Unit = {
val a1: Animals3 = new Dog3()
println(a1.name) // "xh"
println(a1.count) // "1"
println(a1.sex) // "true"
}
}
覆写具体val 字段时,override 关键字是必不可少的,不过对于count 这个var 字段而言,则不然。这是因为修改val 字段意味着我们正在修改某一常量(val 字段)的初始化过程,这类“特殊”操作需要使用override 关键字。
抽象类
和Java一样,使用abstract关键字来标记不能被实例化的类
package classes.constructor
abstract class Student {
var name: String // 抽象字段,没有初始化
var sex: Boolean = false // 普通字段
def playGame(): String // 抽象方法 只有方法的声明,没有实现
}
class SmallStudent extends Student {
var name: String = "小王"
def playGame(): String = s"$name play 王者荣耀"
}
object StudentDemo {
def main(args: Array[String]): Unit = {
val s:Student = new SmallStudent
s.name="小刘"
println(s.playGame())
}
}
注意:
- 子类重写超类的抽象方法或者抽象字段时,可以省略override关键字
- 抽象类中的抽象字段被标记为var,生成的JAVA类并不带字段,自动产生抽象的getter和setter方法
- 抽象类中的抽象字段被标记为val,生成的JAVA类并不带字段,自动产生抽象的getter
匿名子类
类似Java的匿名内部类,可以通过包含带有定义或者重写代码块的方式创建一个匿名的子类
val s2 = new Student {
override def playGame(): String = s"$name play LOL"
override var name: String = "小张"
}
println(s2.playGame()) // 小张 play LOL
包可见性(访问修饰符)
在Scala里面就出现下面这几种类型:这里的省缺修饰符default,就是public。
- Class:类
- Companion:(伴生对象(相当于一个java里面的静态类),必须与所要陪伴的类放在同一个文件中)
- Subclass:子类
- Package:包
- World:同一文件
class Student {
// 缺省为public
var name: String = ""
val sex: Boolean = false
private var age: Int = 0
protected var birthday = new Date()
}
private限定 private修饰的属性只能对本类可以使用,对于伴生对象可见
object Student {
def apply(): Student = new Student()
def main(args: Array[String]): Unit = {
val s1 = Student()
// 伴生对象访问伴生类中的公开成员 ok
println(s1.sex)
// 伴生对象访问伴生类中的私有成员 ok
println(s1.age)
// 伴生对象访问伴生类中的受保护成员 ok
println(s1.birthday)
}
}
protected限定 protect修饰的属性对本类/伴生对象以及子类/子类的伴生对象可见
class SmallStudent extends Student {
}
object SmallStudent {
def apply: SmallStudent = new SmallStudent()
def main(args: Array[String]): Unit = {
val s1 = Student()
println(s1.birthday) //ok
println(s1.sex) //ok
println(s1.age) //error
}
}
==private[this]==限定如果严格限定属性可以操作范围仅仅限制在本类,去除伴生对象的可见性,可以添加this限定
class Student {
private[this] var id: Int = 1
}
object Student {
def apply(): Student = new Student()
def main(args: Array[String]): Unit = {
val s1 = Student()
// private[this] 修饰符仅限在伴生类中使用
println(s1.id) //error
}
}
==protected[this]==限定如果严格限定属性可以操作范围仅仅限制在本类及其子类,去除伴生对象的可见性,可以添加protected限定
class SmallStudent extends Student {
override def toString: String = super.address // ok
}
object SmallStudent {
def apply: SmallStudent = new SmallStudent()
def main(args: Array[String]): Unit = {
val s1 = Student()
println(s1.birthday) //ok
println(s1.sex) //ok
println(s1.age) //error
println(s1) //error
}
}
十、特质trait
类似于Java中的接口
语法
- 特质关键词trait
- 特质可以同时用于抽象方法和具体方法
- 无父类 实现特质extends trait1 with trait2 with trait3 …
- 有父类 实现特质extends 父类 with 特质1 with 特质2 …
当做接口的特质
- Scala可以完全像Java接口一样工作, 你不需要将抽象方法声明为 abstract, 特质中未被实现的方法默认就是抽象方法;
- 类可以通过 extends 关键字继承特质, 如果需要的特质不止一个, 通过 with 关键字添加额外特质
- 重写特质的抽象方法时, 不需要 override 关键字
- 所有 Java 接口都可以当做 Scala 特质使用
trait Animals {
def eat(food: String): String // 抽象方法
}
class Dog extends Animals {
def this(name: String, food: String) {
this()
}
override def eat(food: String): String = {
s"dog eat ---> $food"
}
}
带有具体实现的特质
trait Animals {
def eat(food: String): String
def sleep():Unit = println("animals sleep~~~") // 特质带有方法实现的方法
}
注意:让特质混有具体行为有一个弊端. 当特质改变时, 所有混入该特质的类都必须重新编译
带有特质的对象 动态混入(mixin)
在构造单个对象时, 你可以为它添加特质
- 特质可以将对象原本没有的方法与字段加入对象中, 功能扩展
- 无需修改类声明定义,扩展类的功能,灵活简便
- 如果特质和对象改写了同一超类的方法, 则排在右边的先被执行
package traits.test1.mixin
trait Animals {
def eat(): Unit
}
trait Sheep extends Animals {
// 实现特质声明的方法
override def eat() = {
println("吃草")
}
def run = println("在跑...")
def sleep
}
class Person extends Animals {
override def eat(): Unit = {
println("吃食物")
}
}
object Person {
def main(args: Array[String]): Unit = {
val person = new Person with Sheep {
override def sleep: Unit = println("睡觉觉!!!")
} // 动态混入
person.eat // Person 和 Sheep都有eat方法,调用时从右往左调用 输出 吃草
person.run // Person 中没有run方法,Sheep特质将自己的方法混入Person
person.sleep // 睡觉觉!!!
}
}
this别名
看scala的源码的话很发现很多源码开头都有一句:self => 这句相当于给this起了一个别名为self。self不是关键字,可以用除了this外的任何名字命名(除关键字)。就上面的代码,在Student内部,可以用this指代当前对象,也可以用self指代,两者是等价的。
class Student {
self => // this起别名
val x = 2
def foo = self.x + this.x
}
object Student {
def main(args: Array[String]): Unit = {
val s1 = new Student
println(s1.foo) //4
}
}
它的一个场景是用在有内部类的情况下:
class Outer {
outer =>
val v1 = "here"
class Inner {
println(outer.v1) // 用outer表示外部类,相当于Outer.this
}
}
self-type
自类型用于声明一个特质必须混入其他特质,尽管该特质没有直接扩展其他特质。 这使得所依赖的成员可以在没有导入的情况下使用。
自类型是一种细化 this 或 this 别名之类型的方法。 语法看起来像普通函数语法,但是意义完全不一样。
要在特质中使用自类型,写一个标识符,跟上要混入的另一个特质,以及 =>(例如 someIdentifier: SomeOtherTrait =>)
package traits
trait A {
val name: String
}
trait B {
this: A => // this:混入类型 => [self type] 在特质中混入特质,所依赖的成员可以再没有导入的情况下使用
def sayHi = s"Hello:$name"
}
class C(val name: String) extends B with A {
}
object C {
def main(args: Array[String]): Unit = {
val c = new C("zs")
println(c.sayHi)
}
}
十一、高阶函数
scala的函数对象是一种特殊的class,即可担当函数计算职责又可以作为变量传递
作为值的函数
object Function04 {
def main(args: Array[String]): Unit = {
val result = sum _ // _ 匹配函数参数列表
println(result)
}
def sum(x: Int, y: Int): Int = x + y
}
作用:
- 调用
- 作为参数传递
匿名函数(略)
参考之前
柯里化函数(略)
带函数参数的函数
object Function05 {
def main(args: Array[String]): Unit = {
val f1 = (x: Int, y: Int) => x + y // 声明匿名函数
val result = sum(1, 2, f1)
println(result) // 3
}
def sum(x: Int, y: Int, f: (Int, Int) => Int) = { // 带函数参数的函数 函数作为值传递
f(x, y)
}
}
参数(类型)推断
// 方式1
val a1 = Array(1, 2, 3, 4)
val f2 = (x: Int) => println(x)
// foreach 遍历方法 参数需要一个函数对象
a1.foreach(f2)
// 方式2
a1.foreach((x) => println(x)) // 函数参数的类型自动推导
// 方式3
a1.foreach(x => println(x)) // 对于只有一个参数的函数,省略参数外围的()
// 方式4
a1.foreach(println(_)) // 参数在 => 右侧出现一次,可以用 _ 替换掉
// 方式5
a1.foreach(println) // 更为简化的写法
一些有用的高阶函数
- map 映射方法
Array(1, 2, 3, 4, 5).map("*" * _).foreach(println _) - filter 过滤方法
(1 to 10).filter(_ % 2 == 0).foreach(println) - reduceLeft 从左计算
(1 to 10).filter(_ % 2 == 0).reduceLeft((x,y) => x+y) // 30 - WordCount(单词计数)
val a1 = Array(“Hello Scala”, “Hello Hadoop”, “Hello Hello Hello”)
a1.flatMap(.split(" ")).map((, 1)).groupBy(_._1).map(x => (x._1, x._2.length)).foreach(println)
函数闭包
闭包指在函数体内,可以访问相应作用于内的任何变量,因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。
语法:函数 + 外部变量 = 闭包
如:
var num = 1 // 外部变量
val result = (x: Int) => num + x // 匿名函数引用外部变量,闭包后 多次调用 共享相同的外部变量
println(result(1)) // 1
num += 10
println(result(1)) // 12
十二、异常处理
异常
- Scala 的异常处理和其它语言比如 Java 类似。
- Scala 的方法可以通过抛出异常的方法的方式来终止相关代码的运行,不必通过返回值。
/**
* 异常处理
*/
object Exception01 {
def main(args: Array[String]): Unit = {
try {
//throw new RuntimeException("runtime exception")
throw new IllegalAccessException
} catch {
case e1: RuntimeException => println("runtime exception")
case e2: Exception => println("exception")
} finally {
println("release resource")
}
}
}
十三、隐式转换
隐式转换函数(implicit conversion function)指的是以implicit关键字声明的带有带个参数的函数。这样的函数将被自动应用,将值从一种类型转换为另一种类型。隐式转换函数叫什么名字是无所谓的,因为通常不会由用户手动调用,而是由Scala进行调用。但是如果要使用隐式转换,则需要对隐式转换函数进行导入。因此通常建议将隐式转换函数的名称命名为“one2one”的形式。
Scala会考虑如下位置的隐式转换函数:
- 位于源或目标类型的伴生对象中的隐式函数
- 位于当前作用域可以以单个标识符指代的隐式函数
隐式转换在如下三种不同情况下会被考虑:
- 当表达式类型与预期类型不同时
- 当对象访问一个不存在成员时
- 当对象调用某个方法,而这个方法的参数声明与传入参数不匹配时
有三种情况编译器不会尝试使用隐式转换
- 如果代码能够在不使用隐式转换的前提下通过编译,则不会使用隐式转换
- 编译器不会尝试同时执行多个转换
- 存在二义性的转换是错误
隐式值
implicit val x = 2 // 声明隐式值
println(implicitly[Int]) // 获取隐式值
隐式参数(传值)
方法或者函数的参数,在声明时有implicit。要求参数之中只能有一个implicit类型匹配。
def sum(x: Int)(implicit y: Int): Int = { // y 为隐式参数
x + y
}
implicit val x = 2 // 声明隐式值
println(sum(10)) // 12
println(sum(10)(30)) // 40
参数隐式转换
class Student(var name: String)
object Implicit1 {
def main(args: Array[String]): Unit = {
sayHi("zs") // 传入的类型和方法的参数类型 不匹配,触发隐式转换 str--->student
}
implicit def strToStudent(str: String): Student = {
new Student(str)
}
def sayHi(student: Student): Unit = {
println("Hello:" + student.name)
}
}
隐式转换增强现有类型
object Implicit2 {
implicit def manToSuperMan(man: Man): SuperMan = new SuperMan(man.name)
def main(args: Array[String]): Unit = {
val man = new Man("小张")
man.fly()
}
}
class Man(val name: String)
class SuperMan(val name: String) {
def fly(): Unit = {
println("超人会飞...")
}
}
隐式类
在上面的例子中为了让人也能够飞,需要在SuperMan中定义fly方法,再写一个隐式转换函数,将Man隐式转换为SuperMan。这种写法过于啰嗦,可以使用隐式类实现等价功能
class Man2(val name: String)
object Implicit5 {
def main(args: Array[String]): Unit = {
implicit class SuperMan2(man: Man2) {
var name: String = _
def fly = {
this.name = man.name
println(s"$name ---> 超人会飞")
}
}
val man = new Man2("zs")
man.fly
}
}
隐式类就是在类定义前加一个implicit关键字,这表示它的构造函数是一个隐式转换函数,能够将参数的类型转换成自己的类型,在这里就是构造函数SuperMan2(man: Man2)定义了Man2到SuperMan2的隐式转换。
注意:使用隐式类时需要注意以下限制条件
- 只能在别的trait/类/对象内部定义
- 构造函数只能携带一个非隐式参数
- implict关键字不能用于case类
引入隐式转换
隐式值
- 定义在伴生对象或者作用域
隐式参数
- 定义在伴生对象或者作用域或import
参数隐式转换
- 定义在伴生对象或者作用域
- 其它类,需要import
import xxx.隐式转换类._
十四、泛型
泛型
- 安全: 将运行期可能出现类型错误转换到了编译期
- 避免类型转换
List list = new ArrayList();
list.add("aa")
list.add(10)
Object obj = list.get(0);
Java泛型(回顾)
// 1. 泛型类
// 2. 泛型方法
// 3. 上边界
// 4. 下边界
// 5. 无固定边界
package generic.java;
import scala.actors.threadpool.Arrays;
import java.util.ArrayList;
import java.util.List;
/**
* java 泛型学习
*/
public class GenericTest {
public static void main(String[] args) {
List list = new ArrayList();
list.add("aa");
list.add(10);
String aa = (String) list.get(0);
System.out.println(aa);
List<String> list2 = new ArrayList<String>();
list2.add("aa");
String aa2 = list2.get(0);
System.out.println(aa2);
A<String, Integer, Double> a1 = new A<String, Integer, Double>();
a1.setT("zs");
String name = a1.getT();
System.out.println(name);
a1.m1(10.0);
a1.m1(10L);
a1.m1(3.14F);
ArrayList<C> list3 = new ArrayList<C>();
ArrayList<B> list4 = new ArrayList<B>();
ArrayList<String> list5 = new ArrayList<String>();
a1.m2(list3);
a1.m2(list4);
// a1.m2(list5); //error
ArrayList<C> list6 = new ArrayList<C>();
ArrayList<B> list7 = new ArrayList<B>();
ArrayList<String> list8 = new ArrayList<String>();
a1.m3(list6);
a1.m3(list7);
ArrayList<C> list9 = new ArrayList<C>();
ArrayList<B> list10 = new ArrayList<B>();
ArrayList<String> list11 = new ArrayList<String>();
a1.m4(list9);
a1.m4(list10);
a1.m4(list11);
}
}
// 泛型类
class A<T, S, K> {
private T t;
private S s;
private K k;
// 泛型方法 M类型必须是Number类型的子类型
public <M extends Number> void m1(M m) {
System.out.println(m);
}
// 上边界
public void m2(List<? extends B> list) {
list.forEach(n -> System.out.println(n));
}
// 下边界 ? 类型必须是C类型的父类型
public void m3(List<? super C> list){
list.forEach(n-> System.out.println(n));
}
// 无固定边界
public void m4(List<?> list){
list.forEach(n-> System.out.println(n));
}
public T getT() {
return this.t;
}
public void setT(T t) {
this.t = t;
}
}
class B {
}
class C extends B {
}
泛型类
语法: 类名[泛型类型,…]
package generic.scala
object GenericClass {
def main(args: Array[String]): Unit = {
val a1 = new A1[String]
a1.t = "Hello"
println(a1.t)
val b1 = new B1[String, Int, Boolean]("zs", 18, true)
println(b1)
}
}
// 泛型类
class A1[T] {
var t: T = _
}
class B1[T, S, K](t: T, s: S, k: K) {
override def toString: String = t + "\t" + s + "\t" + k
}
泛型方法
package generic.scala
import scala.collection.mutable
object GenericMethod {
def main(args: Array[String]): Unit = {
print[String](Array("abc", "efg", "xyz"))
print(Array("abc", "efg", "xyz"))
print(Array[Any]("abc", "efg", "xyz", 10)) //OK
// print(Array[String]("abc", "efg", "xyz",10)) //ERROR
ret[String](Array("abc", "efg", "xyz"))
}
/**
* 泛型方法
*
* @param arr
* @tparam K
*/
def print[K](arr: Array[K]): Unit = {
arr.foreach(println)
}
def ret[M](arr: Array[M]): mutable.Buffer[M] = {
arr.toBuffer
}
}
上边界 &下边界
上边界: 语法:[T <: S] ,T类型必须是S类型的子类型,包含S类型本身
下边界: 语法:[T >: S] ,T类型必须是S类型的父类型,包含S类型本身
package generic.scala
object GenericUpperAndLowerBound {
def main(args: Array[String]): Unit = {
val a1 = new A2[B2]
val a2 = new A2[C2]
// val a3 = new A2[Int] // ERROR
val a3 = new A3[B2]
val a4 = new A3[C2]
// val a5 = new A3[String] //ERROR
}
}
// 泛型类
// 上边界
class A2[T <: B2]
// 下边界
class A3[T >: C2]
class B2
class C2 extends B2
视图边界(View Bound)
语法:[T <% S], 运行时尝试将T类型隐式转换为S类型,要求当前类或者作用域存在隐式转换;
package generic.scala
import java.text.SimpleDateFormat
object ViewBound {
def main(args: Array[String]): Unit = {
val a4 = new A4[String]("2018-08-01 12:00:01")
a4.printDate()
}
// 隐式函数
implicit def str2Date(strDate: String): java.util.Date = {
val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
sdf.parse(strDate)
}
}
// 视图边界泛型
// T String --> Date
class A4[T <% java.util.Date](var t: T) {
def printDate(): Unit = {
// java.util.Date中方法
println(t.getYear + "-" + t.getMonth)
}
}
多重限定
多重上界
语法:[T <: S with K] 表示T类型必须同时是S类型和K类型的子类型
package generic.scala.muti
/**
* 多重上界
*/
object MutiUpperBound {
def main(args: Array[String]): Unit = {
val d1 = new D[C] // OK
val d2 = new D[B] // Ok
// val d3 = new D[A] //ERROR
}
}
class A
class B extends A
class C extends B
class D[T <: B with A]
多重下界
语法:[T >: S with K] 表示T类型必须S类型或者K类型的父类型
package generic.scala.muti
/**
* 多重下界
*/
object MutiUpperBound {
def main(args: Array[String]): Unit = {
val e1 = new E[A] // OK
val e2 = new E[B] // OK
val e3 = new E[C] // OK
//val e4 = new E[F] // ERROR
}
}
class A
class B extends A
class C extends B
// T类型必须是B类型或者C类型的父类型
class E[T >: B with C]
class F
既有上界又有下界
语法:[T >: S <: K] 表示T类型的上边界为K类型,下边界为S类型
package generic.scala.muti
/**
* 既有上界又有下界
*/
object MutiUpperBound {
def main(args: Array[String]): Unit = {
val g1 = new G[B] //ok
val g2 = new G[A] //ok
val g3 = new G[C] //ok
// val g4 = new G[F] //ok
}
}
class A
class B extends A
class C extends B
class F
// T类型必须是C类型父类型 A类型子类型 包含上界和下界本身
class G[T >: C <: A]
协变
语法:[+T]
如果T类型是T1类型的父类型,则 C[T]类型是C[T1]类型的父类
package generic.scala.xb
object GenericXB {
def main(args: Array[String]): Unit = {
val c1 = new C[B]
val c2: C[A] = c1
}
}
class A
class B extends A
// 协变 C[B] extends C[A]
class C[+T]
逆变
语法:[-T]
如果T类型是T1类型的父类型,则C[T]类型是C[T1]类型的子类型
package generic.scala.nb
object GenericNB {
def main(args: Array[String]): Unit = {
val c1 = new C[A]
// 逆变后:C[B] 是 C[A] 父类型
val c2: C[B] = c1
}
}
class A
class B extends A
class C[-T]
不变
语法:[T]
上下文限定(拓展)
语法:[T:M] 上下文界定的形式为T:M,其中M是另一个泛型类,它要求必须存在一个类型为M[T]的隐式值。
implicit val ordering:Ordering[Person] = new Ordering(){}
[T:Ordering]
implicitly[Ordering[T]]
package generic.scala.contextbound
// 语法:`[T:M]`
//
// 上下文界定的形式为T:M,其中M是另一个泛型类,它要求必须存在一个类型为M[T]的隐式值。
class Person(var name: String, var age: Int)
object Person {
// 声明ordering[Person]的隐式值【对象】
implicit val ordering: Ordering[Person] = new Ordering[Person] {
override def compare(x: Person, y: Person): Int = if (x.age > y.age) 1 else -1
}
def main(args: Array[String]): Unit = {
val p1 = new Person("zs", 20)
val p2 = new Person("ls", 30)
val tools = new CompareUtils[Person](p1, p2)
if(tools.compare == 1){
println("大于")
}else{
println("小于")
}
}
}
/**
* T: 泛型类(Ordering)
*
* T:M M必须是泛型类,并且必须提供同类型的隐式值
*
* @tparam T
*/
class CompareUtils[T: Ordering](p1: T, p2: T) {
def compare: Int = {
val ordering = implicitly[Ordering[T]] // 获取一个Ordering[T]隐式值
ordering.compare(p1,p2)
}
}
十七、集合
元组类型(重要)
Tuple: 可以存放任意类型的特殊类型,类似于ArrayList
内置:Tuple1~Tuple22
使用Tuple2模拟K\V键值对
创建元组
# 创建2个元素元组
scala> val t2 = ("zs",18)
t2: (String, Int) = (zs,18)
scala> t2.getClass
res0: Class[_ <: (String, Int)] = class scala.Tuple2
# 创建4个元组元组
scala> val t4 = ("zs",18,true,10.0)
t4: (String, Int, Boolean, Double) = (zs,18,true,10.0)
scala> t4.getClass
res1: Class[_ <: (String, Int, Boolean, Double)] = class scala.Tuple4
使用元组
获取元素的语法:元组引用名._值位置
scala> t4._1
res2: String = zs
scala> t4._2
res3: Int = 18
scala> t4._3
res4: Boolean = true
scala> t4._4
res5: Double = 10.0
scala> val t2 =(1,("zs",true,("bj","朝阳")))
t2: (Int, (String, Boolean, (String, String))) = (1,(zs,true,(bj,朝阳)))
scala> t2.getClass
res6: Class[_ <: (Int, (String, Boolean, (String, String)))] = class scala.Tuple2
scala> t2._1
res7: Int = 1
scala> t2._2._1
res8: String = zs
scala> t2._2._2
res9: Boolean = true
scala> t2._2._3._1
res10: String = bj
scala> t2._2._3._2
res11: String = 朝阳
元组的遍历
val t2 = (1, ("zs", true, ("bj", "朝阳")))
val iterator = t2.productIterator
while (iterator.hasNext) {
println(iterator.next())
}
集合
Scala提供了一套很好的集合实现,提供了一些集合类型的抽象。
Scala 集合分为可变的和不可变的集合:
- 可变集合(mutable) 可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。
- 不可变集合(默认 immutable)类永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。
Seq(序列集合)
Range
数值区间范围对象,不可变集合
scala> 1 until 10
res14: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> 1 to 10 by 3
res15: scala.collection.immutable.Range = Range(1, 4, 7, 10)
scala> new Range(1,9,2) // 等价于 1 until 9 by 2
Vector
向量集合,树形结构实现,效率高
scala> Vector(1,2,3,4)
res21: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3, 4)
scala> Vector[String]("a","b","c")
res22: scala.collection.immutable.Vector[String] = Vector(a, b, c)
scala> val v1 = for(n <- 1 to 9) yield n*2
v1: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10, 12, 14,16, 18)
List
列表集合,元素有下标、可重复、有序; 不可变集合
// 声明
scala> List(1,2,3,4,5)
res23: List[Int] = List(1, 2, 3, 4, 5)
// 获取元素
scala> res23(2)
res24: Int = 3
// 不可变集合 不允许修改元素
scala> res23(2) = 10
<console>:13: error: value update is not a member of List[Int]
res23(2) = 10
^
// 不可变集合的模拟添加元素
// :+ 元素 在集合的末尾添加元素
scala> res0 :+ 6
res2: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> res0.:+(7)
res6: List[Int] = List(1, 2, 3, 4, 5, 7)
// +: 元素 在集合的头添加元素
scala> 0 +: res0
res8: List[Int] = List(0, 1, 2, 3, 4, 5)
// drop == dropLeft 删除前N个元素
scala> res0.drop(3)
res2: List[Int] = List(4, 5)
Nil
关键字,空集合等价于List()
scala> val l1 = Nil
l1: scala.collection.immutable.Nil.type = List()
:: 和 :::
:: 如:A :: List 将A作为整体添加到List集合的头部
::: 如:A ::: List 将A中的各个元素添加到List集合的头部
scala> 1 :: Nil
res0: List[Int] = List(1)
scala> Nil
res1: scala.collection.immutable.Nil.type = List()
scala> 1 :: 2 :: 3:: Nil
res2: List[Int] = List(1, 2, 3)
scala> List(1,2,3) :: 4 :: 5 :: Nil
res3: List[Any] = List(List(1, 2, 3), 4, 5)
scala> List(1,2,3) ::: 4:: 5::Nil
res4: List[Int] = List(1, 2, 3, 4, 5)
ListBuffer
可变列表:List列表集合的可变形式;
scala.collection.mutable.ListBuffer
# 声明
scala> scala.collection.mutable.ListBuffer(1,2,3,4,5)
res11: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4, 5)
# 获取元素
scala> res11(2)
res12: Int = 3
# 修改元素内容
scala> res11(2) = 12
scala> res11
res14: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 12, 4, 5)
# 添加元素
scala> res11 += 6
res15: res11.type = ListBuffer(1, 2, 12, 4, 5, 6)
# 移除元素
scala> res11
res24: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 4, 5, 6)
# 删除元素
scala> res11 -= 4
res25: res11.type = ListBuffer(1, 5, 6)
scala> res11 == res25
res26: Boolean = true
scala> res11
res27: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 5, 6)
# 删除元素 新建集合
scala> res11 - 6
res28: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 5)
scala> res11
res29: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 5, 6)
# 添加元素到集合的头部
scala> -4 +=: res34
res37: res34.type = ListBuffer(-4, 1, 5, 6, 7, 8, 9, -1, -2, -3)
scala> res34
res38: scala.collection.mutable.ListBuffer[Int] = ListBuffer(-4, 1, 5, 6, 7, 8,
9, -1, -2, -3)
注意:
- 操作符中不包含=号,通常会返回新集合
- 操作符中包含=号,通常是在原集合上的操作
遍历
.foreach
for循环
do...while
while
Set集合
特点: 无序、无下标、不可重复
Set(不可变)
scala> Set(1,1,2,3,3,3,4)
res41: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)
# 添加元素 返回新集合
scala> res41 + 5
res42: scala.collection.immutable.Set[Int] = Set(5, 1, 2, 3, 4)
scala> res41 == res42
res43: Boolean = false
# 移除元素 返回新集合
scala> res41 - 3
res44: scala.collection.immutable.Set[Int] = Set(1, 2, 4)
Set(可变集合)
# 声明
scala> scala.collection.mutable.Set[Int](1,2,2,3,4,4)
res45: scala.collection.mutable.Set[Int] = Set(1, 2, 3, 4)
# 添加元素
scala> res45 += 5
res46: res45.type = Set(1, 5, 2, 3, 4)
scala> res46 == res45
res47: Boolean = true
# 移除元素
scala> res45 -= 3
res48: res45.type = Set(1, 5, 2, 4)
# 交集
scala> res48 & res49
res50: scala.collection.mutable.Set[Int] = Set(1, 2, 4)
# 并集
scala> res48 | res49
res51: scala.collection.mutable.Set[Int] = Set(1, 5, 2, 6, 3, 4)
# 差集
scala> res48 &~ res49
res52: scala.collection.mutable.Set[Int] = Set(5)
# 差集
scala> res49 &~ res48
res53: scala.collection.mutable.Set[Int] = Set(6, 3)
Map集合
K V 对
# 声明(可变)
scala> scala.collection.mutable.Map("k1" -> "v1","k2" -> "v2")
res56: scala.collection.mutable.Map[String,String] = Map(k2 -> v2, k1 -> v1)
scala> scala.collection.mutable.Map(("k1","v1"),("k2","v2"))
res57: scala.collection.mutable.Map[String,String] = Map(k2 -> v2, k1 -> v1)
# 声明(不可变)
scala> Map("k1"->"v1","k2"->"v2")
res58: scala.collection.immutable.Map[String,String] = Map(k1 -> v1, k2 -> v2)
scala> Map(("k1","v1"),("k2","v2"))
res59: scala.collection.immutable.Map[String,String] = Map(k1 -> v1, k2 -> v2)
# 操作
# 获取指定K的V
scala> res56("k1")
res60: String = v1
# 修改指定K的V
scala> res56("k1") = "vv1"
scala> res56
res62: scala.collection.mutable.Map[String,String] = Map(k2 -> v2, k1 -> vv1)
# 添加KV[可变]
scala> res56 +=(("k3","v3"))
res63: res56.type = Map(k2 -> v2, k1 -> vv1, k3 -> v3)
scala> res56 +=("k4" -> "v4")
res64: res56.type = Map(k2 -> v2, k4 -> v4, k1 -> vv1, k3 -> v3)
# 添加KV【不可变】
scala> res59 + ("k4" -> "v4")
res65: scala.collection.immutable.Map[String,String] = Map(k1 -> v1, k2 -> v2, k4 -> v4)
# 移除KV
scala> res56 -= ("k2")
res67: res56.type = Map(k4 -> v4, k1 -> vv1, k3 -> v3)
遍历
# 1. foreach
# 2. 键遍历
# 3. 值遍历
val map = scala.collection.mutable.Map(("k1", "v1"), ("k2", "v2"))
map.foreach((t2: (String, String)) => println(t2._1 + "\t" + t2._2))
val ks = map.keySet
for (k <- ks) {
println(k + "\t" + map(k))
}
val values = map.values
for (v <- values) {
println(v)
}
十八、模式匹配、样例类、密封类
match pattern
模式匹配是Scala中非常有特色,非常强大的一种功能。模式匹配,其实类似于Java中的swich case语法,即对一个值进行条件判断,然后针对不同的条件,进行不同的处理。
但是Scala的模式匹配的功能比Java的swich case语法的功能要强大地多,Java的swich case语法只能对值进行匹配。但是Scala的模式匹配除了可以对值进行匹配之外,还可以对类型进行匹配、对Array和List的元素情况进行匹配、对case class进行匹配、甚至对有值或没值(Option)进行匹配。
值匹配
// 值匹配
val keyword = readLine("请输入您的内容:")
keyword match {
case "zs" => println("张三")
case "ls" => println("李四")
case _ => println("其他人")
}
if守卫
package matchpattern
object MatchPattern02 {
def main(args: Array[String]): Unit = {
// if守卫
// "18" --> 18
val age = readLine("请输入您的内容:").toInt
age match {
case _ if (age > 60) => println("老年人")
case _ if (age > 40) => println("中年人")
case _ if (age > 18) => println("青年人")
case _ => println("未成年人")
}
}
}
模式匹配中的变量
package matchpattern
object MatchPattern03 {
def main(args: Array[String]): Unit = {
// 模式匹配中的变量【可以在if守卫和case语句的代码块中使用局部变量】
// "18" --> 18
val age = readLine("请输入您的内容:").toInt
age match {
case a1 if (a1 > 60) => println("老年人:"+a1)
case a2 if (a2 > 40) => println("中年人:"+a2)
case a3 if (a3 > 18) => println("青年人:"+a3)
// default起一个变量名
case _name => println("未成年人:"+_name)
}
}
}
类型匹配
对表达式的类型进行匹配,Scala推荐使用这样的方式进行类型匹配,而不是使用isInstanceOf操作
package matchpattern
object MatchPattern04 {
def main(args: Array[String]): Unit = {
// val n: Any = 10.0D
// val n: Any = "abc"
val n: Any = new java.util.Date()
// 类型的模式匹配
n match {
case n1: String => println("string ...")
case n2: Boolean => println("boolean ....")
case n3: Double => println("double ...")
case _ => println("other type...")
}
}
}
集合模式匹配
个数、元素类型(只适用于数组)、内容
package matchpattern
object MatchPattern05 {
def main(args: Array[String]): Unit = {
// 数组
/*
val arr = Array("a", "b", "c", "d")
// 元素的个数
arr match {
case Array(n1, n2) => println("2个元素")
case Array(n1, n2, n3, _*) => println("至少3个元素")
case _ => println("除以上两种规则外的其它数据")
}
*/
/*
val arr = Array("a", "b", "x")
// 元素的内容
arr match {
case Array("a", "b") => println("匹配元素是a、b的数组")
case Array("a", "b", "e") => println("匹配元素是a、b、e的数组")
case Array("a", "b", "c", "d", _*) => println("匹配元素是a、b、c、d、*的数组")
case _ => println("其他内容的数组")
}
*/
// 将运行期错误转换到编译期
// 元素类型的匹配【Array】
val arr1 = Array[Int](1, 2, 3, 4)
val arr2 = Array[String]("a", "b", "c", "d")
arr2 match {
//case a1: Array[Int] => println("int array")
case a2:Array[String] => println("string array")
case _ => println("other")
}
}
}
样例类(案例类或者模板类)
Case Class
- Scala中提供了一种特殊的类,用case class进行声明,中文也可以称作样例类。case class其实有点类似于Java中的JavaBean的概念。即只定义field,并且由Scala编译时自动提供getter方法,但是没有method。
- case class的主构造函数接收的参数通常不需要使用var或val修饰,Scala自动就会使用val修饰(但是如果你自己使用var修饰,那么还是会按照var来)
- Scala自动为case class定义了伴生对象,也就是object,并且定义了apply()和unapply()方法,该方法接收主构造函数中相同的参数,并返回case class对象
- case class常用于模式匹配
package matchpattern
object MatchPattern07 {
def main(args: Array[String]): Unit = {
// 样例类应用模式匹配中
val s: Person = Student("zs", 18, "A101")
val t: Person = Teacher("ls", 35, "B101")
val c: Person = Child("ww", 2)
val p: Person = new Person
p match {
// 判断s类型是否是Student类型 是:将s类的内容调用伴生对象的unapply解析
case Student(name, age, stuNum) => println(name + "\t" + age + "\t" + stuNum)
case Teacher(name, age, teaNum) => println(name + "\t" + age + "\t" + teaNum)
case Child(name, age) => println(name + "\t" + age)
case _ => println("other...")
}
}
}
class Person
case class Student(name: String, age: Int, stuNum: String) extends Person
case class Teacher(name: String, age: Int, teaNum: String) extends Person
case class Child(name: String, age: Int) extends Person
密封类(Sealed)
B类和A类在相同的源文件中(OK)
sealed class A
class B extends A
十九、线程
进程和线程
线程状态: 初始化状态—> start()–>等待状态—>CPU分配时间片—> 运行状态 --> sleep|wait —> 阻塞状态 —> 死亡状态
创建线程
package mutlithread
import scala.actors.threadpool.Executors
object ThreadExample {
def main(args: Array[String]): Unit = {
val a = new A
val b = new B
a.start()
b.start()
// 线程池 固定大小的线程池
val threadpool = Executors.newFixedThreadPool(10)
threadpool.submit(new Runnable {
override def run(): Unit = {
for (n <- 100 to 200) {
println(n)
}
}
})
// 基于缓存线程池(线程60s得到复用,重复利用)
val threadpool2 = Executors.newCachedThreadPool()
}
}
class A extends Thread {
override def run() {
for (n <- 1 to 50) {
println(n)
}
}
}
class B extends Thread {
override def run() {
for (n <- 50 to 100) {
println(n)
}
}
}
二十、IO
I: Input
O:Output
package io
import java.io.PrintWriter
import scala.io.Source
object IOExample {
def main(args: Array[String]): Unit = {
/*
// 获取本地磁盘中一个文本文件的内容
val source = Source.fromFile("D:\\HelloWorld.scala")
// 字符串结构
val content = source.mkString
println(content)
*/
val sink = new PrintWriter("D:\\abc.txt")
sink.write("Hello World")
sink.close()
}
}
爬虫
package io
import scala.io.Source
object IOExample2 {
def main(args: Array[String]): Unit = {
//val city = readLine("请输入您的城市别名:")
//val area = readLine("请输入您的区域:")
val source = Source.fromURL("https://www.danke.com/room/bj?from=hot_keywords&search_text=西二旗")
val content = source.mkString
// xml解析工具 Jsoup
import org.jsoup.Jsoup
val document = Jsoup.parse(content)
// select方法 jq的选择器
val elements = document.select(".r_lbx")
val xiaoqu = elements.select(".r_lbx a").attr("xiaoqu")
val link = elements.select(".r_lbx .r_lbx_cen a").attr("href")
val title = elements.select(".r_lbx .r_lbx_cen a").text()
val desc = elements.select(".r_lbx .r_lbx_cenb").text()
println(xiaoqu + "\t" + link +"\t"+title +"\t"+desc)
}
}