一、概述
1、介绍
Scala是由瑞士洛桑联邦理工学院(EPFL)的 Martin Odersky 于 2001 年设计,2004 年正式发布的多范式编程语言 。它无缝融合了面向对象编程(OOP) 和函数式编程(FP) 的核心特性,运行在 Java 虚拟机(JVM)上,同时也支持 JavaScript 和 Native 运行时,适用于从脚本到大型企业级应用的各种场景。
2、特点
Scala是一门以Java虚拟机(JVM)为运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言(静态语言需要提前编译的如:Java、c、c++等,动态语言如:js)。
1)Scala是一门多范式的编程语言,Scala支持面向对象和函数式编程。(多范式,就是多种编程方法的意思。有面向过程、面向对象、泛型、函数式四种程序设计方法。)
2)Scala源代码(.scala)会被编译成Java字节码(.class),然后运行于JVM之上,并可以调用现有的Java类库,实现两种语言的无缝对接。
3)Scala单作为一门语言来看,非常的简洁高效。Scala 代码通常比 Java 代码短30%-70%,更少的代码意味着更少的 bug 和更好的可读性
4)Scala在设计时,马丁·奥德斯基是参考了Java的设计思想,可以说Scala是源于Java,同时马丁·奥 德斯基也加入了自己的思想,将函数式编程语言的特点融合到JAVA中, 因此,搞清楚Scala和Java相同点和不同点,就可以快速的掌握Scala这门语言。
官网地址
https://www.scala-lang.org/中文网地址
https://scala-lang.org.cn/
二、环境搭建
1、安装Scala
(1)确保安装了JDK
(2)下载Scala安装文件
下载地址
https://scala-lang.org/download/(3)解压文件
解压路径不能有任何中文路径,最好不要有空格

(4)配置环境变量


(5)测试


2、安装插件
默认情况下IDEA不支持Scala的开发,需要安装Scala插件
(1)离线安装插件
1)下载插件
下载地址
https://plugins.jetbrains.com/plugin/1347-scala2)安装
打开IDEA,在左上角找到File->在下拉菜单中点击Setting... ->点击Plugins->点击 右 下 角 Install plugin from disk... , 找 到 插 件 存 储 路 径 scala-intellij-bin-2023.3.29.zip,最后点击 ok。


(2)在线安装
在搜索插件框里面输入Scala->点击Install->点击ok->点击apply。
重启IDEA,再次来到Scala插件页面,已经变成Uninstall。

三、HelloWord案例
1、创建工程
(1)创建project



(2)新建包
再src上右击,之后输入包名



(3)新建类
在包上右击新建,之后输入类名


(4)编写代码
在类中中输入main,然后回车可以快速生成main方法;
Scala
package com.hk.scalademo
object Demo1 {
def main(args: Array[String]): Unit = {
println("hello word")
}
}
(5)运行
方式一:点击
图标,然后点击Run '类名'或者DeBug '类名'

方式二:在方法上右击,点击Run '类名'或者DeBug '类名'

输出:

2、代码说明与源码
Scala
package demo1
/*
object: 关键字,声明一个单例对象(伴生对象)
*/
object HelloWorld {
/*
main 方法:从外部可以直接调用执行的方法
def 方法名称(参数名称: 参数类型): 返回值类型 = { 方法体 }
*/
def main(args: Array[String]): Unit = {
println("hello world")
System.out.println("hello scala from java")
}
}
(1)查看源码
例如查看Array源码。按住ctrl键->点击Array->右上角出现choose Soures...

(2)关联源码
1)将源码包scala-sources-2.13.14.tar.gz 拷贝到 D:\xxx\scala-2.13.14\lib 文件夹 下,并解压为scala-sources-2.13.14 文件夹
2)点击choose Sources...->选择 D:\xxx\scala-2.13.14\lib\scala-sources-2.13.14,这个文件夹,就可以看到源码,之后看到的就是下面这种格式

3、官方编程指南
(1)在线查看
在线地址
https://www.scala-lang.org/(2)离线查看
下载解压scala-docs-2.13.14.zip,可以获得Scala 的API操作。

四、变量与数据类型
1、注释
Scala 注释使用和Java完全一样。 注释是一个程序员必须要具有的良好编程习惯。将自己的思想通过注释先整理出来,再用代码去体现。
(1)基本语法
1)单行注释://
2)多行注释:/* */
3)文档注释:/** * */
(2)案例实操
Scala
object Demo1 {
/**
* 这种是文本注释,一般在方法和类上使用
* @param args
*/
def main(args: Array[String]): Unit = {
println("hello word")
// 这个是单行注释
/*
这种是多行注释
*/
}
}
(3) 代码规范
1)使用一次tab操作,实现缩进,默认整体向右边移动,用shift+tab整体向左移
2)或者使用ctrl + alt + L 来进行格式化
3)运算符两边习惯性各加一个空格。比如:2 + 4 * 5。
4)一行最长不超过80个字符,超过的请使用换行展示,尽量保持格式优雅
2、变量和常量
(1)基本语法
1)var 变量名 [: 变量类型] = 初始值
var a = 10
a = 20
2)val 常量名 [: 常量类型] = 初始值 注意:能用常量的地方不用变量
val b = 10
b = 20 // 会报错,常量的值不可以改变
(2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 1、声明变量时,类型可以省略,编译器自动推导,即类型推导
var a1 = 10
var a2: Int = 20
a1 = 30
// 2、类型确定后,就不能修改,说明Scala是强数据类型语言。
// a1 = "abc" // 会报错,应为a1的类型推导为Int
// 3、变量声明时,必须要有初始值
// var a3 如果不赋值会直接报错
// 4、在声明/定义一个变量时,可以使用var或者val来修饰,var修饰的变量可改变, val 修饰的变量不可改。
var a3 = 19
a3 = 20
val a4 = 22
// a4 = 33 会报错
// 5、var修饰的对象引用可以改变,val修饰的对象则不可改变,但对象的状态(值)却是可以改变的。(比如:自定义对象、数组、集合等等)
var student_1 = new Student("lisi", 10)
student_1 = new Student("wangwu", 20)
val student_2 = new Student("jak", 22)
// student_2 = new Student("jak", 33) // 会直接报错
// student_2.age = 30 // 报错,因为age是val修饰
student_2.name = "hello" // 因为name是var修饰,值可以改变
}
}
class Student (var name: String, val age: Int){
}
3、标识符的命名规范
(1)命名规则
Scala中的标识符声明,基本和Java是一致的,但是细节上会有所变化,有以下三种规 则:
1)以字母或者下划线开头,后接字母、数字、下划线
2)以操作符开头,且只包含操作符(+ - * / # !等)
3)用反引号`....`包括的任意字符串,即使是Scala关键字(39个)也可以
• package, import, class, object, trait, extends, with, type, for
• private, protected, abstract, sealed, final, implicit, lazy, override
• try, catch, finally, throw
• if, else, match, case, do, while, for, return, yield
• def, val, var
• this, super
• new
• true, false, null
(2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 1、以字母或者下划线开头,后接字母、数字、下划线
var hello: String = "" // ok
var Hello12: String = "" // ok
// var 1hello: String = "" // error 数字不能开头
// var h-b: String = "" // error 不能用-
// var x h: String = "" // error 不能有空格
var h_4: String = "" // ok
var _ab: String = "" // ok
var Int: String = "" // ok 因为在 Scala 中 Int是预定义的字符,不是关键字,但不推荐
var _: String = "hello" // ok 单独一个下划线不可以作为标识符,因为_被认为是一个方法
println(_)
// 2、以操作符开头,且只包含操作符(+ - * / # !等)
var +*-/#! : String = "" // ok
// var +*-/#!1 : String = "" // error 以操作符开头,必须都是操作符
// 3、用反引号`....`包括的任意字符串,即使是Scala关键字(39个)也可以
// var if : String = "" // error 不能用关键字
var `if` : String = "" // ok 用反引号`....`包括的任意字符串,包括关键字
}
}
4、字符串输出
(1)基本语法
1)字符串,通过+号连接
2)printf用法:字符串,通过%传值。
3)字符串模板(插值字符串):通过$获取变量值
(2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var name: String = "lisi"
var age: Int = 18
var score: Double = 89.55
// 1、字符串,通过+号连接
println(name + ":" + age)
// 2、printf用法字符串,通过%传值。
printf("name=%s age=%d\n", name, age)
// 3、字符串模板(插值字符串),通过$获取变量值
println(s"$name : $age")
println(s"$name : ${age + 10}")
println(f"分数为:${score}%2.1f")
println(raw"分数: ${score}%2.1f")
// 4、三引号表示字符串,保持多行字符串的原格式
var sql =
s"""
|select *
|from student
|where
| name = $name
| age = $age
|""".stripMargin
println(sql)
}
}

5、键盘输入
(1)基本语法
StdIn.readLine()、StdIn.readShort()、StdIn.readDouble()
(2)案例实操
需求:从控制台接收用户信息,【姓名,年龄,薪水】。
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
print("请输入您的姓名:")
val name = StdIn.readLine();
print("请输入您的年龄:")
var age = StdIn.readInt()
print("请输入您的薪资:")
var sal = StdIn.readDouble()
println(s"您的输入为: $name : $age : $sal")
}
}

6、文件的读取与写入
(1)基本语法
文件读取:Source.fromFile("文件路径")
文件写入:new PrintWriter(new File("文件路径"))
writer.writer("内容")
(2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 读取文件内容
Source.fromFile("resources/test.txt").foreach(print)
println()
// 写出
val writer = new PrintWriter(new File("resources/test2.txt"))
writer.write("lisi")
writer.close()
}
}


7、数据类型

1)Scala中一切数据都是对象,都是Any的子类。
2)Scala中数据类型分为两大类:数值类型(AnyVal)、 引用类型(AnyRef),不管是值类型还是引用类型都是对象。
3)Scala数据类型仍然遵守,低精度的值类型向高精度值类型自动转换(隐式转换)
4)Scala中的StringOps是对Java中的String增强
5)Unit:对应Java中的void,用于方法返回值的位置,表示方法没有返回值。Unit是一个数据类型,只有一个对象就是()。Void不是数据类型,只是一个关键字
6)Null是一个类型,只有一个对象就是null。它是所有引用类型(AnyRef)的子类。
7)Nothing,是所有数据类型的子类,主要用在一个函数没有明确返回值时使用,因为这样可以把抛出的返回值,返回给任何的变量或者函数。
8、整数类型
(1)分类
|-------------|----------------------------------------------------------------------------|
| 数据类型 | 描述 |
| Byte [1] | 8位有符号补码整数。数值区间为 -128 到 127 |
| Short [2] | 16位有符号补码整数。数值区间为 -32768 到 32767 |
| Int [4] | 32位有符号补码整数。数值区间为 -2147483648 到 2147483647 |
| Long [8] | 64位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807 = 2的(64-1)次方-1 |
(2)案例实操
Scala的整型,默认为Int型,声明Long型,须后加'l'或'L'
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
val a_1: Byte = -128
val a_2: Byte = 127
// val a_3: Byte = -129 // 错误
// val a_4: Byte = 128 // 错误
val a_5 = 9223372036854775807L
}
}
9、浮点类型
(1)分类
|--------------|--------------------------|
| 数据类型 | 描述 |
| Float [4] | 32 位, IEEE 754 标准的单精度浮点数 |
| Double [8] | 64 位 IEEE 754 标准的双精度浮点数 |
(2)案例实操
Scala 的浮点型常量默认为Double型,声明Float型常量,须后加'f'或'F'。
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var n7 = 2.2345678912f
var n8 = 2.2345678912
println("n7=" + n7)
println("n8=" + n8)
}
}
10、字符类型
(1)基本说明
字符类型可以表示单个字符,字符类型是Char。
1)字符常量是用单引号 ' ' 括起来的单个字符。
2)\t :一个制表位,实现对齐的功能
3)\n :换行符
4)\\ :表示\
5)\" :表示"
(2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 1、字符常量是用单引号 ' ' 括起来的单个字符。
var c1: Char = 'a'
println("c1=" + c1)
var c2:Char = 'a' + 1
println(c2)
// 2、\t :一个制表位,实现对齐的功能
println("hello\tword")
// 3、\n :换行符
println("hello\nword")
// 4、\\ :表示\
println("c:\\test\\avi")
// 5、\" :表示"
println("你好:\"世界\"")
}
}

11、布尔类型
(1)基本说明
1)布尔类型也叫Boolean类型,Booolean类型数据只允许取值true和false
2)boolean类型占1个字节。
(2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var isResult : Boolean = false
var isResult2 : Boolean = true
}
}
12、Unit、Null和Nothing类型
(1)基本说明
|---------|------------------------------------------------------------------------------------------------------|
| 数据类型 | 描述 |
| Unit | 表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。 |
| Null | null , Null 类型只有一个实例值null |
| Nothing | Nothing类型在Scala的类层级最低端;它是任何其他类型的子类型。 当一个函数确定没有正常的返回值,可以用Nothing来指定返回类型,这样可以把返回的值(异常)赋给其它的函数或者变量(兼容性) |
(2)案例实操
1)Unit类型用来标识过程,也就是没有明确返回值的函数。 由此可见,Unit类似于Java里的void。Unit只有一个实例 ( ),这个实例也没有实质意义
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
def test : Unit = { // unit表示没有返回值,即void
println("unit测试")
}
println(test)
}
}

2)Null类只有一个实例对象,Null类似于Java中的null引用。Null可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var cat = new Student("lisi", 20);
cat = null //正确
println(cat)
// var n1: Int = null//错误
// println("n1:" + n1)
}
}

3)Nothing,可以作为没有正常返回值的方法的返回类型,而且由于Nothing是其他任意类型的子类,他还能跟要求返回值的方法兼容。
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
def test() : Nothing={
throw new Exception()
}
test
}
}

13、类型转换
(1)数值类型自动转换
当Scala 程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数值类型,这个就是自动类型转换(隐式转换)。数据类型按精度(容量)大小排序为:

1)基本说明
a、自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那个数据类型,然后再进行计算。
b、把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
c、(byte,short)和char之间不会相互自动转换。
d、byte,short,char他们三者可以计算,在计算时首先转换为int类型。
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 1、自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数值类型,然后再进行计算。
var a: Byte = 1
var a2: Short = 11
var c = a + a2
println(c)
// 2、把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
var n2 : Double= 1.0
//var n3 : Int = n2 //错误,原因不能把高精度的数据直接赋值和低精度。
// 3、(byte,short)和char之间不会相互自动转换。
var n4 : Byte = 1
//var c1 : Char = n4 //错误
var n5:Int = n4
// 4、byte,short,char他们三者可以计算,在计算时首先转换为int类型。
var n6 : Byte = 1
var c2 : Char = 1
// var n : Short = n6 + c2 //当n6 + c2 结果类型就是int
// var n7 : Short = 10 + 90 //错误
}
}
(2)强制类型转换
1)基本说明
自动类型转换的逆过程,将精度大的数值类型转换为精度小的数值类型。使用时要加上强制转函数,但可能造成精度降低或溢出,格外要注意
2)案例实操
a、将数据由高精度转换为低精度,就需要使用到强制转换
b、强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 1、将数据由高精度转换为低精度,就需要使用到强制转换
var a: Double = 3.5
var b: Int = a.toInt
println("b: " + b)
// 2、强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
var c: Int = 1.5.toInt + 2.5.toInt
var d: Int = (1.5 + 2.5).toInt
println(s"c: $c, d: $d")
}
}

(3)数值类型和String 类型间转换
1)基本说明
在程序开发中,经常需要将基本数值类型转成String类型。或者将String类型转成基本数值类型。
2)案例实操
a、基本类型转String类型(语法:将基本类型的值+"" 即可)
b、String 类型转基本数值类型(语法:s1.toInt、s1.toFloat、s1.toDouble、s1.toByte、s1.toLong、s1.toShort)
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 1、基本类型转String类型(语法:将基本类型的值+"" 即可)
var a: String = 2 + ""
var b: String = 2.5.toString
println(s"a = $a, b = $b")
// 2、String类型转基本数值类型
var s = "21"
var c = s.toInt
var d =s.toDouble
println(s"c = $c, d = $d")
}
}

3)注意事项
在将String 类型转成基本数值类型时,要确保String类型能够转成有效的数据,比如可以把"123",转成一个整数,但是不能把"hello"转成一个整数。
var n5:Int = "12.6".toInt 会出现 NumberFormatException 异常。
五、运算符
1、算术运算符
(1)基本语法
|-----|------------|-------------------------|
| 运算符 | 运算 | 范例 |
| + | 正号、加、字符串相加 | +3 3+4 "hello" + "word" |
| - | 负号、减 | -3 4-3 |
| * | 乘 | 3*4 |
| / | 除 | 6/2 |
| % | 取模(取余) | 10%3 |
对于除号"/",它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。
对一个数取模a%b,和Java的取模规则一样。
(2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var a: Int = 10/3
var b = 10.0/3
println(a)
println(b)
println(b.formatted("%.2f"))
var c = 10%3
println(c)
}
}

2、比较运算符
(1)基本语法
|-----|------|---------|-------|
| 运算符 | 运算 | 范例 | 结果 |
| == | 相等于 | 3==4 | false |
| != | 不等于 | 3 != 4 | true |
| > | 大于 | 3 > 4 | false |
| >= | 大于等于 | 3 >= 4 | false |
| < | 小于 | 3 < 4 | true |
| <= | 小于等于 | 3 <= 4 | true |
(2)案例实操
1)基本比较
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var a: Int = 2
var b: Int = 1
println(a > b) // true
println(a >= b) // true
println(a <= b) // false
println(a < b) // false
println("a==b" + (a == b)) // false
println(a != b) // true
}
}

2)==的区别
Java: ==比较两个变量本身的值,即两个对象在内存中的首地址;
equals比较字符串中所包含的内容是否相同。
Scala:==更加类似于Java中的equals
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var a: String = "hello"
var b: String = new String("hello")
println(a==b) // 在Java中是false,但是在Scala是true比较的是值
println(a.equals(b))
// 如果想要像Java一样比较地址,则使用eq函数
println(a.eq(b))
}
}

3、逻辑运算符
(1)基本语法
用于连接多个条件(关系表达式),最终的结果也是一个Boolean值。
假定:变量A为true,B为false
|------|-----|------------------------|
| 运算符 | 描述 | 实例 |
| && | 逻辑与 | (A && B) 运算结果为 false |
| || | 逻辑或 | (A || B) 运算结果为 true |
| ! | 逻辑非 | !(A && B) 运算结果为 true |
注意:当多个表达式连接判断时,例如:(A && B && C),当A表达式不成立 时,B和C表达式将不在运行,(A || B || C)当A表达式成立时,B和C表达式将不在运行
(2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var a: Boolean = true
def p_str(): Boolean = {
println("我是prin_str 方法")
true
}
// 避免逻辑与空指针异常
def isNotEmpty(s: String): Boolean = {
//如果按位与,s为空,会发生空指针
s!=null && !"".equals(s.trim())
}
println("a && b : " + (a && p_str))
println("===============")
println("a || b : " + (a || p_str))
println("===============")
println(isNotEmpty(null))
}
}

4、赋值运算符
(1)基本语法
赋值运算符就是将某个运算后的值,赋给指定的变量
|-------|-------------------------|-------------------------------|
| 运算符 | 描述 | 实例 |
| = | 简单的赋值运算符,将一个表达式的值赋给一个左值 | C = A + B 将 A + B 表达式结果赋 值给 C |
| += | 相加后再赋值 | C += A 等于 C = C + A |
| -= | 相减后再赋值 | C -= A 等于 C = C - A |
| *= | 相乘后再赋值 | C *= A 等于 C = C * A |
| /= | 相除后再赋值 | C /= A 等于 C = C / A |
| %= | 求余后再赋值 | C %= A 等于 C = C % A |
| <<= | 左移后赋值 | C <<= 2等于 C = C << 2 |
| >>= | 右移后赋值 | C >>= 2 等于 C = C >> 2 |
| &= | 按位与后赋值 | C &= 2 等于 C = C & 2 |
| ^= | 按位异或后赋值 | C ^= 2 等于 C = C ^ 2 |
| |= | 按位或后赋值 | C |= 2 等于 C = C | 2 |
(2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var a: Int = 3
a += 3
println(a)
a >>= 2
println(a)
/*
* 1的二进制为: 0001
* 2的二进制为: 0010
* &位数两个值都为1则为1 否者为0
* 结果为: 0000
* */
var b = 1
b &= 2
println(b)
/*
* 1的二进制为: 0001
* 2的二进制为: 0010
* | 位数有一个值为1则为1
* 结果为: 0011
* */
var c = 1
c |= 2
println(c)
/*
* 3的二进制为: 0011
* 2的二进制为: 0010
* ^ 位数值相同则为0,位数值不同则为1
* 结果为: 0001
* */
var d = 3
d ^= 2
println(d)
}
}

5、位运算符
(1)基本语法
下表中变量 a 为 3,b 为 13。
|--------|---------|----------------------------------------------------|
| 运算符 | 描述 | 实例 |
| & | 按位与运算符 | (a & b) 输出结果 1 ,二进制解释:0000 0001 |
| | | 按位或运算符 | (a | b) 输出结果 15 ,二进制解释:0000 1111 |
| ^ | 按位异或运算符 | (a ^ b) 输出结果 14 ,二进制解释:0000 1110 |
| ~ | 按位取反运算符 | (~a ) 输出结果 -4 ,二进制解释:1111 1100, 在 一个有符号二进制数的补码形式。 |
| << | 左移动运算符 | a << 2 输出结果 12 ,二进制解释: 0000 1100 |
| >> | 右移动运算符 | a >> 2 输出结果 0 ,二进制解释: 0000 0000 |
| >>> | 无符号右移 | b >>>2 输出结果 3, 二进制解释: 0000 0011 |
(2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var a = 3
var b = 13
/**
* a: 0000 0011
* b: 0000 1101
* 0000 0001
*/
println(a&b)
/**
* a: 0000 0011
* b: 0000 1101
* 0000 1111
*/
println(a|b)
/**
* a: 0000 0011
* b: 0000 1101
* 0000 1110
*/
println(a^b)
/**
* a: 0000 0011
* 1111 1100
*/
println(~a)
/**
* a: 0000 0011
* 0000 1100
*/
println(a<<2)
/**
* a: 0000 0011
* 0000 0000
*/
println(a>>2)
/**
* b: 0000 1101
* 0000 0011
*/
println(b>>>2)
}
}

6、运算符本质
在Scala 中其实是没有运算符的,所有运算符都是方法。
(1)当调用对象的方法时,点**.**可以省略
(2)如果函数参数只有一个,或者没有参数,()可以省略
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 1.1 标准的加法运算
val i:Int = 1.+(1)
// 1.2 当调用对象的方法时,.可以省略
val j:Int = 1 +(1)
// 1.3 如果函数参数只有一个,或者没有参数,()可以省略
val k:Int = 1 + 1
println(1.toString())
println(1.toString )
}
}
六、流程控制
1、分支控制if-else
(1)单分支
1)基本语法
if (条件表达式) {
执行代码块
}
说明:当条件表达式为ture时,就会执行{ }的代码。
2)案例实操
需求:输入年龄,如果该年龄小于18岁,则输出"童年"
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
print("请输入年龄:")
val age = StdIn.readInt()
if (age < 18) {
println("童年")
}
}
}


(2)双分支
1)基本语法
if (条件表达式) {
执行代码块1
} else {
执行代码块2
}
2)案例实操
需求:输入年龄,如果年龄小于18岁,则输出"童年"。否则,输出"成年"。
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
print("请输入年龄:")
val age = StdIn.readInt()
if (age < 18) {
println("童年")
} else {
println("成年")
}
}
}

(3)多分支
1)基本语法
if (条件表达式1) {
执行代码块1
} else if (条件表达式2) {
执行代码块2
} ...... else {
执行代码块n
}
2)案例实操
a、需求1:输入年龄,如果年龄小于18岁,则输出"童年"。如果年龄大于等于18且小于等于30,则输出"中年",否则,输出"老年"。
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
print("请输入年龄:")
val age = StdIn.readInt()
if (age < 18) {
println("童年")
} else if(age < 30){
println("中年")
} else {
println("老年")
}
}
}

b、需求2:Scala中if else表达式其实是有返回值的,具体返回值取决于满足条件的代码体的最后一行内容。
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
print("请输入年龄:")
val age = StdIn.readInt()
var res: String = if (age < 18) {
println("童年")
"童年"
} else if(age < 30){
println("中年")
"中年"
} else {
println("老年")
"老年"
}
println("res = " + res)
}
}

c、需求3:Scala中返回值类型不一致,取它们共同的祖先类型。
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
print("请输入年龄:")
val age = StdIn.readInt()
var res: Any = if (age < 18) {
println("童年")
"童年"
} else if (age < 30) {
println("中年")
"中年"
} else {
println("老年")
age
}
println("res = " + res)
}
}

d、需求4:Java中的三元运算符可以用if else实现
如果大括号{}内的逻辑代码只有一行,大括号可以省略。如果省略大括号,if只对最近的一行逻辑代码起作用。
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
print("请输入年龄:")
val age = StdIn.readInt()
var res: String = if (age < 18) "童年" else "老年"
println("res = " + res)
}
}

(4)嵌套分支
在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构称为内层。外面的分支结构称为外层分支。嵌套分支不要超过3层。
1)基本语法
if (条件表达式1) {
if (条件表达式1-1) {
执行代码1
} else {
执行代码2
}
} else {
if (条件表达式1-2) {
执行代码3
} else {
执行代码4
}
}
2)案例实操
需求:如果输入的年龄小于18,返回"童年"。如果输入的年龄大于等于18,需要再判断:如果年龄大于等于18且小于30,返回"中年";如果其他,返回"老年"。
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
print("请输入年龄:")
val age = StdIn.readInt()
if (age < 18) {
println("同年")
} else {
if (age < 30) {
println("中年")
} else {
println("老年")
}
}
}
}

2、循环控制for
Scala 也为 for 循环提供了非常多的特性,这些for循环的特性被称 为for 推导式或for表达式。
(1)范围数据循环(To)
1)基本语法
for ( 变量 <- 起始值 to 终止值 ) {
循环执行的代码
}
注意:为前后闭合,包括起始值和终止值
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
for (i <- 1 to 5) {
println(i + " hello word")
}
}
}

(2)范围数据循环(Until)
1)基本语法
for ( 变量 <- 起始值 until 终止值 ) {
循环执行的代码
}
注意:为前闭后开,包括起始值不包含终止值
或者
for ( 变量 <- Range(起始值, 终止值) ) {
循环执行的代码
}
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
for (i <- Range(1, 5)) {
println(i + " hello word")
}
println("="*5)
for (i <- 1 until 5) {
println(i + " hello word")
}
}
}

3)集合遍历
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// for (i <- Array(10, 20, 30, 40)) {
// println(i + " hello word")
// }
//
// for (i <- List(10, 20, 30, 40)) {
// println(i + " hello word")
// }
for (i <- Set(10, 20, 30, 30)) {
println(i + " hello word")
}
}

(3)循环守卫
1)基本语法
for ( 变量 <- 起始值 to 终止值 if 变量 != 值 ) {
循环执行的代码
}
循环守卫,即循环保护式(也称条件判断式,守卫)。保护式为true则进入循环 体内部,为false则跳过,类似于continue。
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
for (i <-1 to 5 if i != 3) {
println(i + " hello word")
}
// 等价于
// for (i <-1 to 5 ) {
// if (i != 3) {
// println(i + " hello word")
// }
// }
}
}

(4)循环步长
1)基本语法
for ( 变量 <- 起始值 to 终止值 by 步长) {
循环执行的代码
}
2)案例实现
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
for (i <-1 to 5 by 3) {
println(i + " hello word")
}
println("*" * 10)
for (i <- 10 to 1 by -2) {
println(i + " hello word")
}
}
}

(5)嵌套循环
1)基本语法
for ( 变量1 <- 起始值1 to 终止值1; 变量2 <- 起始值2 to 终止值2) {
循环执行的代码
}
相等于
for ( 变量1 <- 起始值1 to 终止值1) {
for (变量2 <- 起始值2 to 终止值2) {
循环执行的代码
}
}
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 输出九九乘法表
for (i <-1 to 9; j <- 1 to i) {
print(s"$j * $i = ${j*i} \t")
if(i == j) {
println()
}
}
}
}

(6)引入变量
1)基本语法
for ( 变量1 <- 起始值 to 终止值; 变量2) {
循环执行的代码
}
说明:
for推导式一行中有多个表达式时,所以要加 ; 来隔断逻辑
for 推导式有一个不成文的约定:当 for 推导式仅包含单一表达式时使用圆括号, 当包含多个表达式时,一般每行一个表达式,并用花括号代替圆括号,如下
for {
i <- 1 to 3
j = 4 - i
} {
println("i=" + i + " j=" + j)
}
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
for (i <-1 to 5; j = i * 2 ) {
println(s"$i == $j")
}
}
}

(7)循环返回值
1)基本语法
var 变量 = for ( 变量 <- 起始值 to 终止值) yield 变量
说明:将遍历过程中处理的结果返回到一个新Vector集合中,使用yield关键字。
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var res = for(i <-1 to 10) yield {
i * 2
}
println(res)
}
}

3、循环控制While
(1)While 循环控制
1)基本语法
循环变量初始化
while (循环条件) {
循环体(语句)
循环变量迭代
}
- 循环条件是返回一个布尔值的表达式
- while 循环是先判断再执行语句 (
- 与for语句不同,while语句没有返回值,即整个while语句的结果是Unit类型()
- 因为 while 中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量,而变量需要声明在while循环的外部,那么就等同于循环的内部对外部的变量造成了影响,所以不推荐使用,而是推荐使用for循环。
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var i: Int = 5
while (i > 0) {
println("=== " + i)
i -= 1
}
}
}

(2)do..while 循环控制
1)基本语法
do{
循环体(语句)
循环变量迭代
} while(循环条件)
- 循环条件是返回一个布尔值的表达式
- do..while 循环是先执行,再判断
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var i: Int = 5
do {
println("=== " + i)
i -= 1
} while (i > 0)
}
}

4、循环中断
Scala 内置控制结构特地去掉了break 和continue,是为了更好的适应函数式编程,推荐使用函数式的风格解决break和continue的功能,而不是一个关键字。Scala中使用breakable 控制结构来实现break和continue功能。
(1)异常退出循环
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
try {
for (i <- 1 to 5) {
if (i == 3) throw new RuntimeException()
println("==:" + i)
}
} catch {
case e =>
}
println("正常结束循环")
}
}

(2)break函数退出循环
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
Breaks.breakable(
for (i <- 1 to 5) {
if (i == 3) Breaks.break()
println("==:" + i)
}
)
println("正常结束循环")
}
}
(3)break函数简化
Scala
import scala.util.control.Breaks._
object Demo1 {
def main(args: Array[String]): Unit = {
breakable(
for (i <- 1 to 5) {
if (i == 3) break()
println("==:" + i)
}
)
println("正常结束循环")
}
}
七、函数式编程
Scala 语言是一个完全函数式编程语言。万物皆函数。 函数可以当做一个值进行传递
1、函数基础
(1)基本语法
1)基本语法
关键字 函数名(参数名: 参数类型,参数名:参数类型......): 返回值类型 ={
函数体
}
例如:
def sum(a: Int, b: Int): Unit = {
a + b
}
2)案例实操
定义一个函数,实现将传入的名称打印出来。
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 定义函数
def f(a: Int): Unit = {
println("a: ==" + a)
}
// 调用函数
f(1)
}
}
(2)函数与方法的区别
1)为完成某一功能的程序语句的集合,称为函数。
2)类中的函数称之方法。
1)Scala语言可以在任何的语法结构中声明任何的语法
2)函数没有重载和重写的概念;方法可以进行重载和重写
3)Scala中函数可以嵌套定义
(3)函数定义
1)类型
函数1:无参,无返回值
函数2:无参,有返回值
函数3:有参,无返回值
函数4:有参,有返回值
函数5:多参,无返回值
函数6:多参,有返回值
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 函数1:无参,无返回值
def f1(): Unit = {
println("无参,无返回值")
}
//函数2:无参,有返回值
def f2(): Int = {
println("无参,有返回值")
1
}
//函数3:有参,无返回值
def f3(a: Int): Unit = {
println("有参,无返回值 : " + a)
}
//函数4:有参,有返回值
def f4(b: Int): Int ={
println("有参,有返回值 :" + b)
b
}
//函数5:多参,无返回值
def f5(a: Int, b: Int): Unit ={
println("多参,无返回值: " + a + " " + b)
}
//函数6:多参,有返回值
def f6(a: Int, b: Int): Int = {
println("多参,有返回值: " + a + " " + b)
a + b
}
f1()
f2()
f3(2)
f4(2)
f5(2,3)
f6(2,3)
}
}

(4)函数参数
1)说明
- 可变参数,如果参数列表中存在多个参数,那么可变参数一般放置在最后
- 参数默认值,一般将有默认值的参数放置在参数列表的后面
- 带名参数
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 可变参数
def f1(a: Int*): Unit ={
println("可变参数: " + a)
}
// 如果参数列表中存在多个参数,那么可变参数一般放置在最后
def f2(a: Int, b: String*): Unit = {
println(a + " 可变参数: " + b)
}
// 参数默认值,一般将有默认值的参数放置在参数列表的后面
def f3(name: String = "lisi"): Unit = {
println("参数默认值: " + name)
}
// 带名参数
def f4(name: String = "lisi", age: Int): Unit = {
println(name + " 参数默认值: " + age)
}
f1(1,2,3,4)
f2(1,"lisi","wangwu")
println("================")
f3()
f3("wangwu")
println("================")
f4(age = 24)
f4("wangwu", 20)
f4(age = 30, name = "haha")
}
}

(5)函数至简原则
1)原则
- return 可以省略,Scala会使用函数体的最后一行代码作为返回值
- 如果函数体只有一行代码,可以省略花括号
- 返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
- 如果有return,则不能省略返回值类型,必须指定
- 如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
- Scala 如果期望是无返回值类型,可以省略等号
- 如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
- 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
- 如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
def f(name: String): String ={
println("===" + name)
return name
}
f("lisi")
println("*"*9)
// return 可以省略,Scala会使用函数体的最后一行代码作为返回值
def f1(name: String): String ={
println("===" + name)
name
}
f1("wangwu")
println("*"*9)
//如果函数体只有一行代码,可以省略花括号
def f2(name: String): String = "===" + name
println(f2("lisi2"))
println("*"*9)
//返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
def f3(name: String) = "===" + name
println(f3("lisi3"))
println("*"*9)
//如果有return,则不能省略返回值类型,必须指定
/*def f4(name: String) ={
println("===" + name)
return name // 报错
}*/
//如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
def f5(name: String): Unit ={
println("===" + name)
return name
}
println(f5("lisi4"))
println("*"*9)
//Scala 如果期望是无返回值类型,可以省略等号
def f6(name: String) {
println(name)
}
f6("lisi5")
println("*"*9)
//如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
def f7() {
println("f7")
}
f7
f7()
println("*"*9)
//如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
def f8: Unit = println("f8")
f8
// f8() // 报错
println("*"*9)
//如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
(name: String) => println(name)
}
}

2、函数高级
(1)高阶函数
1)函数可以作为值进行传递
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
def addOne(num: Int): Int = num + 1
def test(): Int = 5
// 调用test函数,把返回值给变量f
var f = test
println(f)
// 函数可以作为值进行传递
// 在被调用函数addOne后面加上 _,相当于把函数addOne当成一个整体,传递给变量f2
var f1 = addOne _
println(f1(5))
// 如果明确变量类型,那么不使用下划线也可以将函数作为整体传递给变量
var f2: Int=>Int = addOne
println(f2(3))
}
}

2)函数可以作为参数进行传递
定义一个函数,函数参数还是一个函数 ;f 表示函数名称; (Int,Int) 表示输入两个Int参数;Int表示函数返回值
def f1 (f: (Int, Int) => Int): Int = {
f(2, 4)
}
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 定义一个函数,函数参数还是一个函数签名;f表示函数名称;(Int,Int) 表示输入两个Int参数;Int表示函数返回值
def f(a: Int, b: Int, f1: (Int, Int) => Int): Int ={
f1(a, b)
}
// 定义一个函数,参数和返回值类型和f的输入参数一致
def add(a: Int, b: Int): Int = a+b
// 将add函数作为参数传递给f函数,如果能够推断出来不是调用,_可以省略
println(f(3, 5, add _))
println(f(1, 2, add))
}
}

3)函数可以作为函数返回值返回
将函数f2作为函数f1的返回值
def f1() = {
def f2() = { }
f2 _
}
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
def f1() = {
def f2() = {
}
f2 _
}
val f = f1()
// 因为f1函数的返回值依然为函数,所以可以变量f可以作为函数继续调用
f()
// 上面的代码可以简化为
f1()()
}
}
(2)匿名函数
1)说明
没有名字的函数就是匿名函数。
(x:Int)=>{函数体}
- x:表示输入参数类型;
- Int:表示输入参数类型;
- 函数体:表示具体代码逻辑
2)至简原则
- 参数的类型可以省略,会根据形参进行自动的推导
- 类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号。
- 匿名函数如果只有一行,则大括号也可以省略
- 如果参数只出现一次,则参数省略且后面参数可以用_代替
3)案例实操
需求1:传递的函数有一个参数
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 定义一个函数:参数包含数据和逻辑函数
def f(arr: Array[Int], op: Int => Int) ={
for (item <- arr) yield op(item)
}
// 定义逻辑函数
def op(a: Int): Int = a+1
// 标准函数调用
var _arr = f(Array(1,3,5,7,9),op)
println(_arr.mkString(","))
println("==============================")
// 采用匿名函数
var _arr2 = f(Array(1,3,5,7,9), (value: Int) =>{value + 1})
println(_arr2.mkString(","))
println("==============================")
// 参数的类型可以省略,会根据形参进行自动的推导;
var _arr3 = f(Array(1,3,5,7,9), (value) =>{value + 2})
println(_arr3.mkString(","))
println("==============================")
// 只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号。
var _arr4 = f(Array(1,3,5,7,9), value =>{value + 1})
println(_arr4.mkString(","))
println("==============================")
// 匿名函数如果只有一行,则大括号也可以省略
var _arr5 = f(Array(1,3,5,7,9), value => value + 1)
println(_arr5.mkString(","))
println("==============================")
// 如果参数只出现一次,则参数省略且后面参数可以用_代替
var _arr6 = f(Array(1,3,5,7,9), _ + 1)
println(_arr6.mkString(","))
println("==============================")
}
}

需求2:传递的函数有两个参数
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 定义一个函数:参数包含数据和逻辑函数
def f(a: Int, b: Int, op: (Int, Int) => Int) ={
op(a,b)
}
// 定义逻辑函数
def op(a: Int, b:Int): Int = a+b
// 标准函数调用
var _arr = f(3,6,op)
println(_arr)
println("==============================")
// 采用匿名函数
var _arr2 = f(3,4, (a: Int, b: Int) =>{a + b})
println(_arr2)
println("==============================")
// 如果只有一行,则大括号也可以省略
var _arr3 = f(3,4, (a: Int, b: Int) => a + b)
println(_arr3)
println("==============================")
// 参数的类型可以省略,会根据形参进行自动的推导;
var _arr4 = f(3,4, (a, b) => a + b)
println(_arr4)
println("==============================")
// 如果参数只出现一次,则参数省略且后面参数可以用_代替
var _arr5 = f(1,2, _ + _ )
println(_arr5)
println("==============================")
}
}

(3)综合案例
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// Map
def map(arr: Array[Int], op: Int => Int) ={
for (value <- arr) yield op(value)
}
var arr_1 = map(Array(1,2,3,4), x => x*x)
println(arr_1.mkString(","))
// filter过滤。有参数,且参数再后面只使用一次,则参数省略且后面参数用_表示
def filter(arr: Array[Int], op: Int => Boolean) ={
var a:ArrayBuffer[Int] = ArrayBuffer[Int]()
for (value <- arr if op(value)) {
a.append(value)
}
a.toArray
}
var arr_2 = filter(Array(1,2,3,4,5,6), _%2==1)
println(arr_2.mkString(","))
// reduce聚合。有多个参数,且每个参数再后面只使用一次,则参数省略且后面参数用_表示,第n个_代表第n个参数
def reduce(arr: Array[Int], op: (Int, Int) => Int) ={
var a:Int = 1
for (value <- arr) {
a = op(a,value)
}
a
}
var arr_3 = reduce(Array(2,3,4,5), _*_)
println(arr_3)
}
}

(4)函数柯里化&闭包
1)概念
闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包
函数柯里化:把一个参数列表的多个参数,变成多个参数列表。
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 闭包
def f1(a: Int): Int => Int ={
def f2(b: Int): Int ={
a + b
}
f2
}
// 在调用时,f1函数执行完毕后,局部变量a应该随着栈空间释放掉
// var t = f1(3)
// 但是在此处,变量a其实并没有释放,而是包含在了f2函数的内部,形成了闭合的效果
// println(t(4))
println(f1(4)(5))
// 简写形式
def f3(a: Int): Int => Int ={
(b: Int) => a + b
}
def f4(a: Int): Int => Int ={
b => a + b
}
def f5(a: Int): Int => Int = a + _
println(f5(5)(1))
// 函数柯里化,其实就是将复杂的参数逻辑变得简单化,函数柯里化一定存在闭包
def f6(a: Int)(b: Int): Int = a+b
println(f6(1)(2))
}
}

3)练习案例
定义一个函数func,它接收一个Int类型的参数,返回一个函数(记作f1)。 它返回的函数f1,接收一个String类型的参数,同样返回一个函数(记作f2)。函数f2接 收一个Char类型的参数,返回一个Boolean的值。
要求调用函数func(0) ("") ('0')得到返回值为false,其它情况均返回true。
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
def func(a: Int): String => (Char => Boolean) ={
def f1(b: String): Char => Boolean ={
def f2(c: Char): Boolean ={
if(a==0 && b == "" && c == '0') false else true
}
f2
}
f1
}
println(func(0)("")('0'))
println(func(0)("1")('1'))
// 简写
def func1(a: Int): String => (Char => Boolean) ={
def f1(b: String): Char => Boolean ={
(c: Char) => if(a==0 && b == "" && c == '0') false else true
}
f1
}
def func2(a: Int): String => (Char => Boolean) ={
def f1(b: String): Char => Boolean ={
c => if(a==0 && b == "" && c == '0') false else true
}
f1
}
def func3(a: Int): String => (Char => Boolean) ={
(b: String) => c => if(a==0 && b == "" && c == '0') false else true
}
// 最终简写
def func4(a: Int): String => (Char => Boolean) ={
b => c => if(a==0 && b == "" && c == '0') false else true
}
// 柯里化
def func5(a: Int)(b:String)(c:Char): Boolean = if(a==0 && b == "" && c == '0') false else true
println(func4(1)("1")('1'))
println(func5(0)("")('0'))
}
}

(5)递归
1)说明
一个函数在函数体内又调用了本身,称之为递归调用
2)案例实操
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
// 阶乘
// 递归算法
// 1) 方法调用自身
// 2) 方法必须要有跳出的逻辑
// 3) 方法调用自身时,传递的参数应该有规律
// 4) scala中的递归必须声明函数返回值类型
// 计算1到100相加
def add(a: Int): Int ={
if (a == 1) return 1
add(a-1) + a
}
println(add(100))
// 尾递归
def f1(n: Int): Int ={
@tailrec
def loop(a: Int, b: Int): Int ={
if (a == 1) return b
loop(a-1, a+b)
}
loop(n, 1)
}
println(f1(100))
}
}

(6)控制抽象
1)值调用:把计算后的值传递过去,简单说就是把A函数返回的值传递给B函数
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
def f1 = () => {
println("我是f1函数......")
10
}
def f2(a: Int): Unit ={
println("我是f2函数......")
println(a)
println(a)
}
f2(f1())
}
}

2)名调用:把代码传递过去
// (1)传递代码块
foo({
println("aaa")
})
// (2)小括号可以省略
foo{
println("aaa")
}
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
def f1 = () => {
println("我是f1函数......")
10
}
// def f2(a: Int): Unit ={
def f2(a: =>Int): Unit ={
println("我是f2函数......")
println(a)
println(a)
}
f2(f1())
}
}
只要使用了a,那么a的函数就会执行一遍,a不在是一个值,而是代码

3)案例实操
自定义一个While循环
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var n: Int = 5
/* myWhile(n > 0)({
println(n)
n -= 1
})*/
myWhile3(n > 0){
println(n)
n -= 1
}
}
def myWhile(condition: =>Boolean): ( => Unit) => Unit ={
def doLoop(op: => Unit): Unit ={
if (condition) {
op
myWhile(condition)(op)
}
}
doLoop _
}
def myWhile2(condition: =>Boolean): ( => Unit) => Unit ={
op =>{
if (condition) {
op
myWhile2(condition)(op)
}
}
}
def myWhile3(condition: =>Boolean)(op: => Unit): Unit ={
if (condition) {
op
myWhile2(condition)(op)
}
}
}

(7)惰性加载
1)说明
当函数返回值被声明为lazy时,函数的执行将被推迟,直到首次取值,该函数才会执行。这种函数称之为惰性函数。
2)案例实操
不加lazy查看结果
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
var a = sum()
println("main函数执行")
println(a)
}
def sum(): Int ={
println("sum函数被执行")
2
}
}

添加lazy查看结果
注意:lazy不能修饰var类型的变量
Scala
object Demo1 {
def main(args: Array[String]): Unit = {
lazy val a = sum()
println("main函数执行")
println(a)
}
def sum(): Int ={
println("sum函数被执行")
2
}
}
八、面向对象
1、Scala 包
基本语法
package 包名
Scala 包的三大作用(和Java一样)
1)区分相同名字的类
2)当类很多时,可以很好的管理类
3)控制访问范围
(1)包的命名
1)命名规则
只能包含数字、字母、下划线、小圆点.,但不能用数字开头,也不要使用关键字。
2)命名规范
一般是小写字母+小圆点
com.公司名.项目名.业务模块名
3)案例实操
Scala
package com.hk.scalademo
object Demo1 {
def main(args: Array[String]): Unit = {
}
}
(2)包说明
1)说明
Scala有两种包的管理风格,一种方式和Java的包管理风格相同,每个源文件一个包(包名和源文件所在路径不要求必须一致),包名用"."进行分隔以表示包的层级关系,另一种风格,通过嵌套的风格表示层级关系,
第一种:
package com.hk.scalademo
第二种:
package com{
package hk{
package scalademo{
}
}
}
第二种风格有以下特点:
1)一个源文件中可以声明多个package
2)子包中的类可以直接访问父包中的内容,而无需导包
2)案例实操
Scala
package com{
package hk{
// 父包访问子包需要导包
import com.hk.scalademo.Inner
object Outer{
var a: Int = 10
def main(args: Array[String]): Unit = {
println("访问子包元素:b=" + Inner.b)
}
}
package scalademo{
object Inner{
var b: Int = 20
def main(args: Array[String]): Unit = {
// 子包访问父包无需导包
println("访问上级元素:a=" + Outer.a)
}
}
}
}
}
(3)包对象
在Scala中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有class和object的共享变量,可以被直接访问。
1)定义
package object com{
val shareValue="share"
def shareMethod()={}
}
2) 说明
方式一:包对象一般定义在其对应包下的package.scala 文件中,包对象名与包名保持一致。



方式二:采用嵌套方式管理包,则包对象可与包定义在同一文件中,但是要保证包对象与包声明在同一作用域中。
Scala
package com{
package hk{
package scalademo{
object Test {
def main(args: Array[String]): Unit = {
println(student)
println(student2)
}
}
}
}
}
package object com {
val student: String = "hello"
}
package com{
package object hk{
val student2: String = "hello2"
}
}
(4)导包说明
1)使用import导入,在这个文件中的所有类都可以使用。
2)局部导入:什么时候使用,什么时候导入。在其作用范围内都可以使用
3)通配符导入:import java.util._
4)给类起名:import java.util.{ArrayList=>JL}
5)导入相同包的多个类:import java.util.{HashSet, ArrayList}
6)屏蔽类:import java.util.{ArrayList =>,}
7)导入包的绝对路径:new root.java.util.HashMap
例如:
|-----------------------------------|------------------------------------|
| import com.hk.Fruit | 引入com.hk包下Fruit(class和object) |
| import com.hk._ | 引入com.hk下的所有成员 |
| import com.hk.Fruit._ | 引入Fruit(object)的所有成员 |
| import com.hk.{Fruit,Vegetable} | 引入com.hk下的Fruit和Vegetable |
| import com.hk.{Fruit=>Shuiguo} | 引入com.hk包下的Fruit并更名为Shuiguo |
| import com.hk.{Fruit=>Shuiguo,} | 引入com.hk包下的所有成员,并将Fruit更名 为Shuiguo |
| import com.hk.{Fruit=>,_} | 引入com.hk包下所有类,但屏蔽Fruit类 |
| new root.java.util.HashMap | 引入的Java的绝对路径 |
注意 Scala 中的三个默认导入,分别是
- import java.lang._
- import scala._
- import scala.Predef._
(5)访问权限
1)说明
在Java 中,访问权限分为:public,private,protected 和默认。在 Scala 中,可以通过类似的修饰符达到同样的效果。但是使用上有区别。
(1)Scala 中属性和方法的默认访问权限为public,但Scala中无public关键字。
(2)private 为私有权限,只在类的内部和伴生对象中可用。
(3)protected 为受保护权限,Scala 中受保护权限比 Java 中更严格,同类、子类可以访问,同包无法访问。
(4)private[包名]增加包访问权限,包名下的其他类也可以使用
2)案例实操
Scala
object test01 {
def main(args: Array[String]): Unit = {
val person = new Person()
// println(person.id) // 错误,无法访问私有属性
// println(person.name) // 错误,受保护属性,子类可以访问
println("person " + person.age)
person.info()
val student1 = new Student()
println(student1.age)
println(student1.sex)
student1.info()
}
}
// 父类
class Person {
// 属性
private var id: String = "id_0001"
protected var name: String = "lisi"
var age: Int = 18
def info(): Unit = {
println(s"Person == $id $name $age")
}
}
// 子类
class Student extends Person {
var sex: String = "f"
override def info(): Unit = {
// id = "id_0002" // 父类的私有属性无法修改
name = "wangwu"
age = 15
println(s"Student == $name $age $sex")
}
}

2、类和对象
(1)定义类
1)基本语法
修饰符\] class 类名 { 类体 } 说明 * Scala 语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是 public) * 一个Scala源文件可以包含多个类
2)案例实操
Scala
object test01 {
def main(args: Array[String]): Unit = {
val student1 = new Student()
println(student1.name)
// println(student1.age) // 错误,无法访问私有属性
println(student1.sex)
student1.sex = "m"
println(student1.sex)
}
}
class Student {
// 属性
var name: String = _
private var age: Int = _
var sex: String = "f"
}

(2)属性
1)基本语法
[修饰符] var|val 属性名称 [:类型] = 属性值
注:Bean属性(@BeanPropetry),可以自动生成规范的setXxx/getXxx方法
_ 表示给属性一个默认值
2)案例实操
Scala
object test01 {
def main(args: Array[String]): Unit = {
val student1 = new Student()
println(student1.name)
// println(student1.age) // 错误,无法访问私有属性
println(student1.sex)
// student1.setSex("m")
student1.sex = "m"
println(student1.getSex)
}
}
class Student {
// 属性 _表示给属性一个默认值
var name: String = _
private var age: Int = _
@BeanProperty
var sex: String = "f"
}
(3)方法
1)基本语法
def 方法名(参数列表) [:返回值类型] = {
方法体
}
2)案例实操
Scala
object Test02 {
def main(args: Array[String]): Unit = {
val student = new Student2()
val i = student.addOne(3)
println(i)
}
}
class Student2 {
def addOne(a: Int): Int ={
a + 1
}
}

(4)创建对象
1)基本语法
val | var 对象名 [:类型] = new 类型()
1)val修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。
2)var修饰对象,可以修改对象的引用和修改对象的属性值
3)自动推导变量类型不能多态,所以多态需要显示声明
2)案例实操
Scala
object Test02 {
def main(args: Array[String]): Unit = {
val person = new Person()
//val修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属 性的值。
// person = new Person() // 错误
person.name = "wangwu"
}
}
class Person {
var name: String = "lisi"
}
(5)构造器
Scala 类的构造器包括:主构造器和辅助构造器
1)基本语法
class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个...
}
}
说明:
1)辅助构造器,函数的名称 this,可以有多个,编译器通过参数的个数及类型来区分。
2)辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。
3)构造器调用其他另外的构造器,要求被调用构造器必须提前声明。
4)如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可 以省略。
2)案例实操
Scala
object Test02 {
def main(args: Array[String]): Unit = {
val person = new Person2("hello", 20)
}
}
// 如果主构造器无参数,小括号可省略
//class Person2() {
class Person2 {
var name: String = "lisi"
var age: Int = _
println("我是主构造器...")
def this(name: String){
this()
this.name = name
println("我是辅助构造器1...")
}
def this(name: String, age: Int){
this(name)
this.age = age
println("我是辅助构造器2...")
}
}

(6)构造器参数
1)说明
Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰
1)未用任何修饰符修饰,这个参数就是一个局部变量
2)var修饰参数,作为类的成员属性使用,可以修改
3)val修饰参数,作为类只读属性使用,不能修改
2)案例实操
Scala
object Test02 {
def main(args: Array[String]): Unit = {
val person = new Person2("hello", 20, "f")
// 未用任何修饰符修饰,这个参数就是一个局部变量
// println(person.name) // 错误, 只能在类内部使用
// var修饰参数,作为类的成员属性使用,可以修改
person.age = 10
println(person.age)
// val修饰参数,作为类的只读属性使用,不能修改
// person.sex = "m" // 错误,不能修改
println(person.sex)
person.info()
}
}
class Person2(name: String, var age: Int, val sex: String) {
def info(): Unit = {
println(s"$name $age $sex")
}
}

3、封装
封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
Scala 中的 public 属性,底层实际为 private,并通过 get 方法(obj.field())和 set 方法 (obj.field_=(value))对其进行操作。所以 Scala 并不推荐将属性设为 private,再为其设置 public 的 get 和 set 方法的做法。但由于很多Java框架都利用反射调用getXXX和setXXX方 法,有时候为了和这些框架兼容,也会为Scala的属性设置getXXX和setXXX方法**(通过 @BeanProperty 注解实现)**。
Scala
object Test02 {
def main(args: Array[String]): Unit = {
val person = new Person2
person.setName("lisi")
println(person.getName)
}
}
class Person2 {
@BeanProperty
var name: String = _
}
4、继承和多态
1)基本语法
class 子类名 extends 父类名 {
类体
}
(1)子类继承父类的属性和方法
(2)scala是单继承
2)案例实操
Scala
object Test02 {
def main(args: Array[String]): Unit = {
val lisi = new Student2("lisi", 20, "m")
lisi.info()
val wangwu = new Teacher("wangwu")
println("=========================")
personInfo(lisi)
personInfo(wangwu)
// 这里体现的就是多态
def personInfo(person: Person2): Unit = {
person.info()
}
}
}
// 父类
class Person2(name: String) {
var age: Int = _
println("父类主构造器调用......")
def this(name: String, age: Int) {
this(name)
this.age = age
println("父类辅助构造器调用......")
}
def info(): Unit = {
println(s"父类info调用......$name $age")
}
}
// 子类
class Student2(name: String, age: Int) extends Person2(name, age) {
var sex: String = _
println("子类主构造器调用......")
def this(name: String, age: Int, sex: String){
this(name, age)
this.sex = sex
println("子类辅助构造器调用......")
}
override def info(): Unit = {
println(s"Student2子类info调用......$name $age $sex")
}
}
// 子类
class Teacher(name: String) extends Person2(name) {
override def info(): Unit = {
println(s"Teacher子类info调用......$name")
}
}

3)动态绑定
Scala中属性和方法都是动态绑定
Scala
object Test01 {
def main(args: Array[String]): Unit = {
val student: Person3 = new Student3
student.info()
}
}
class Person3{
val name: String = "lisi"
def info(): Unit = {
println(s"Person3的info方法 $name")
}
}
class Student3 extends Person3 {
override val name: String = "wangwu"
override def info(): Unit = {
println(s"Student3的info方法 $name")
}
}

5、抽象类
(1) 抽象属性和抽象方法
1)基本语法
(1)定义抽象类:abstract class Person{} //通过 abstract 关键字标记抽象类
(2)定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性
(3)定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法
Scala
object Test01 {
def main(args: Array[String]): Unit = {
}
}
abstract class Person3{
// 非抽象属性
var name: String = "lisi"
// 抽象属性
var age: Int
// 非抽象方法
def eat(): Unit = {
println("Person3的eat方法")
}
// 抽象方法
def sleep(): Unit
}
2)继承&重写
(1)如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类
(2)重写非抽象方法需要用override修饰,重写抽象方法则可以不加override。
(3)子类中调用父类的方法使用super关键字
(4)子类对抽象属性进行实现,父类抽象属性可以用var修饰; 子类对非抽象属性重写,父类非抽象属性只支持val类型,而不支持var。 因为var修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写
Scala
object Test01 {
def main(args: Array[String]): Unit = {
val student = new Student3
student.eat()
student.sleep()
}
}
abstract class Person3{
// 非抽象属性
var name: String = "lisi"
// 抽象属性
var age: Int
// 非抽象方法
def eat(): Unit = {
println("Person3的eat方法")
}
// 抽象方法
def sleep(): Unit
}
class Student3 extends Person3{
// 实现抽象属性和方法
var age: Int = 13
def sleep(): Unit = {
println("Student3 sleep")
}
// 重写非抽象属性和方法
// 父类非抽象属性只支持val类型,而不支持var
// override val name: String = "wangwu" // 错误,
name = "wangwu"
override def eat(): Unit = {
super.eat()
println("Student3的eat方法")
}
}

(2)匿名子类
1)说明
通过包含带有定义或重写的代码块的方式创建一个匿名的子类。
2)案例实操
Scala
object Test01 {
def main(args: Array[String]): Unit = {
val student = new SStudent4 {
override var name: String = "lisi"
override def eat(): Unit = {
println("SStudent4 eat")
}
}
println(student.name)
student.eat()
}
}
abstract class SStudent4 {
var name: String
def eat(): Unit
}

6、单例对象(伴生对象)
(1)单例对象语法
1)基本语法
object 对象名称{
// 属性方法
}
2)说明
(1)单例对象采用object关键字声明
(2)单例对象对应的类称之为伴生类,伴生对象的名称和伴生类名一致。
(3)单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
3)案例实操
Scala
object Test01 {
def main(args: Array[String]): Unit = {
val student3 = Student3.newStudent("lisi", 30)
student3.info()
}
}
// 伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
class Student3 private(val name: String, val age: Int){
def info(): Unit = {
println(s"Student3 $name $age")
}
}
// 伴生对象采用object关键字声明
object Student3{
val sex: String = "f"
// 定义一个类的对象实例的创建方法
def newStudent(name: String, age: Int): Student3 = {
new Student3(name, age)
}
}

对象实例化模式:懒汉模式和饿汉模式
Scala
object Test01 {
def main(args: Array[String]): Unit = {
val student3 = Student3.getInstance()
student3.info()
}
}
// 伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
class Student3 private(val name: String, val age: Int){
def info(): Unit = {
println(s"Student3 $name $age")
}
}
/*// 饿汉式
object Student3{
private val student: Student3 = new Student3("lisi", 20)
// 定义一个类的对象实例的创建方法
def getInstance(): Student3 = student
}*/
// 懒汉式
object Student3{
private var student: Student3 = _
// 定义一个类的对象实例的创建方法
def getInstance(): Student3 = {
if (student == null) {
student = new Student3("lisi", 10)
}
student
}
}
(2)apply 方法
1)说明
(1)通过伴生对象的apply方法,实现不使用new方法创建对象。
(2)如果想让主构造器变成私有的,可以在()之前加上private。
(3)apply 方法可以重载。
(4)Scala 中obj(arg)的语句实际是在调用该对象的apply方法,即obj.apply(arg)。用以统一面向对象编程和函数式编程的风格。
(5)当使用new关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构建对象时,调用的其实时伴生对象的apply方法。
2)案例实操
Scala
object Test01 {
def main(args: Array[String]): Unit = {
// 通过伴生对象的apply方法,实现不使用new关键字创建对象。
val student1 = Student3()
student1.info()
println("================")
val student2 = Student3("wangwu")
student2.info()
}
}
// 如果想让主构造器变成私有的,可以在()之前加上private
class Student3 private(val name: String){
def info(): Unit = {
println(s"Student3 $name ")
}
}
object Student3{
// 定义一个类的对象实例的创建方法
def apply(): Student3 = {
println("apply 空参被调用")
new Student3("lisi")
}
def apply(name: String): Student3 = {
println("apply 有参被调用")
new Student3(name)
}
}

7、特质(Trait)
Scala 语言中,采用特质 trait(特征)来代替接口的概念,也就是说,多个类具有相同的特质(特征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明。
Scala 中的trait 中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质。类似于Java中的抽象类。 Scala 引入 trait 特征,第一可以替代Java 的接口,第二个也是对单继承机制的一种补充。
1.优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
2.如果需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数, 而特质不行(有无参构造)。
(1)特质声明
1)基本语法
trait 特质名 {
trait 主体
}
2)案例实操
Scala
trait PersonTrait {
// 属性
var id: String = _
// 方法
def eat(): Unit = {
println(s"PersonTrait eat $id")
}
// 抽象属性
var name: String
// 抽象方法
def play():Unit
}
(2)特质基本语法
一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素, 所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with 关键字连接。
1)基本语法:
没有父类:
class 类名 extends 特质1 with 特质2 with 特质3 ...
有父类:
class 类名 extends 父类 with 特质1 with 特质2 with 特质3...
2)说明
(1)类和特质的关系:使用继承的关系。
(2)当一个类去继承特质时,第一个连接词是extends,后面是with。
(3)如果一个类在同时继承特质和父类时,应当把父类写在extends后。
(4)特质可以同时拥有抽象方法和具体方法
(5)一个类可以混入(mixin)多个特质
(6)所有的Java接口都可以当做Scala特质使用
(7)动态混入:可灵活的扩展类的功能
1)动态混入:创建对象时混入trait,而无需使类混入该trait
2)如果混入的trait中有未实现的方法,则需要实现
3)案例实操
Scala
object Test01{
def main(args: Array[String]): Unit = {
val student = new Student4
student.play()
student.dating()
println("=============")
// 动态混入
val student1 = new Student5 with SexTrait {
override var sex: String = "f"
}
student1.play()
}
}
trait PersonTrait {
// 抽象属性
val name: String = "lisi-1"
// 抽象方法
def play():Unit ={
println(s"PersonTrait $name")
}
def dating(): Unit
}
trait SexTrait {
// 抽象属性
var sex: String
}
class Person4{
val name: String = "lisi-2"
def play(): Unit = {
println(s"Person4 $name")
}
}
class Student4 extends Person4 with PersonTrait with SexTrait {
var sex: String = "f"
def dating(): Unit = println(s"Student4 $name $sex")
override val name: String = "lisi-3"
override def play(): Unit = {
super.play()
println(s"Student4 play $name")
}
}
class Student5 extends Person4 with PersonTrait {
def dating(): Unit = println(s"Student5 $name ")
override val name: String = "lisi-4"
override def play(): Unit = {
super.play()
println(s"Student5 play $name")
}
}

(3)特质叠加
由于一个类可以混入(mixin)多个trait,且trait中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。 冲突分为以下两种:
第一种:一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait 之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法。

Scala
object Test01{
def main(args: Array[String]): Unit = {
val student = new Student4
student.singing()
student.amount += 1
student.increase()
}
}
trait PersonTraitOne {
var amount: Int = _
def increase():Unit ={
println(s"PersonTraitOne increase $amount")
}
}
trait PersonTraitTwo {
def singing(): Unit
def increase():Unit ={
println(s"PersonTraitTwo increase")
}
}
class Student4 extends PersonTraitOne with PersonTraitTwo {
override def singing(): Unit = {
println("Student4 singing")
}
override def increase(): Unit = {
// 指定使用哪个特质里里面的方法,或者重写这个方法
super[PersonTraitOne].increase()
}
}

如果不重写会出现如下错误

第二种:一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait 继承自相同的trait(TraitC),及所谓的"钻石问题",解决这类冲突问题,Scala 采用了特质叠加的策略。所谓的特质叠加,就是将混入的多个trait中的冲突方法叠加起来。

Scala
object Test01{
def main(args: Array[String]): Unit = {
val myClothes = new MyClothes
println(myClothes.Shirt())
}
}
// 衣服特质
trait Clothes {
def Shirt(): String = "Shirt"
}
// 颜色
trait ColorClothes extends Clothes {
var color: String = "red"
override def Shirt(): String ={
color + " - " + super.Shirt()
}
}
// 大小
trait SizeClothes extends Clothes {
var size: String = "M"
override def Shirt(): String ={
size + " - " + super.Shirt()
}
}
class MyClothes extends ColorClothes with SizeClothes {
override def Shirt(): String = {
"我的衣服是: " + super.Shirt() // 输出:我的衣服是: M - red - Shirt
// "我的衣服是: " + super[SizeClothes].Shirt() // 输出:我的衣服是: M - red - Shirt
// "我的衣服是: " + super[ColorClothes].Shirt() // 输出:我的衣服是: red - Shirt
}
}

(4)执行顺序
当一个类混入多个特质的时候,scala 会对所有的特质及其父特质按照一定的顺序进行 排序,而此案例中的 super.Shirt()调用的实际上是排好序后的下一个特质中的 Shirt() 方法。排序规则如下:

案例中的super,不是表示其父特质对象,而是表示上述叠加顺序中的下一个特质, 即,MyClothes中的super指代Size,Size中的super指代Color,Color中的super 指代Clothes。
如果想要调用某个指定的混入特质中的方法,可以增加约束:super[],例如super[ColorClothes].Shirt()
(5)特质自身类型
1)说明
自身类型可实现依赖注入的功能。
2)案例实操
Scala
object Test01{
def main(args: Array[String]): Unit = {
val user = new ProductUser("lisi", "123456")
user.insertUser()
}
}
// 用户类
class User(val name: String, val password: String)
// 特质
trait UserDao{
_: User =>
// 新增数据
def insertUser()={
println(s"insert into user: ${this.name}")
}
}
class ProductUser(name: String, password: String) extends User(name, password) with UserDao

8、扩展
(1)类型检查和转换
1)说明
(1)obj.isInstanceOf[T]:判断 obj 是不是 T 类型。
(2)obj.asInstanceOf[T]:将 obj 强转成 T 类型。
(3)classOf获取对象的类名。
2)案例实操
Scala
object Test01{
def main(args: Array[String]): Unit = {
val student4 = new Student4("lisi", 20)
// 判断对象是否为某个类型的实例
println("student4 is Student4 : " + student4.isInstanceOf[Student4])
println("student4 is Person4 : " + student4.isInstanceOf[Person4])
println("=====================")
val person4: Person4 = new Student4("lisi", 20)
if(person4.isInstanceOf[Student4]) {
// 将对象转换为某个类型的实例
val student = person4.asInstanceOf[Student4]
println(student.name)
// 获取类的信息
println(classOf[Student4])
}
}
}
// 用户类
class Person4(val name: String, val age: Int)
class Student4(name: String, age: Int) extends Person4(name, age)

(2)枚举类和应用类
1)说明
枚举类:需要继承Enumeration
应用类:需要继承App
2)案例实操
枚举类
Scala
object Test01{
def main(args: Array[String]): Unit = {
println(Color.BLUE)
}
}
// 定义枚举类对象
object Color extends Enumeration {
val RED = Value(1, "red")
val YELLOW = Value(2, "yellow")
val BLUE = Value(3, "blue")
}
应用类可以直接运行
Scala
object TestApp extends App {
println("app testApp")
}

(3)Type 定义新类型
1)说明
使用type关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名
2)案例实操
Scala
object Test01{
def main(args: Array[String]): Unit = {
type S = String
val name: S = "lisi"
println(name)
}
}
九、集合
1、介绍
(1)简介
1)Scala 的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable 特质。
2)对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本,分别位于以下两个包:不可变集合:scala.collection.immutable 可变集合: scala.collection.mutable
3)Scala 不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而不会对原对象进行修改。
4)可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象。
建议:在操作集合的时候,不可变用符号,可变用方法
(2)不可变集合继承图

(3)可变集合继承图

2、数组
(1)不可变数组
1)第一种方式定义数组
定义:val arr1 = new Array[Int](10)
(1)new是关键字
(2)[Int]是指定可以存放的数据类型,如果希望存放任意数据类型,则指定Any
(3)(10),表示数组的大小,确定后就不可以变化
2)第二种方式定义数组
val arr1 = Array(1, 2)
(1)在定义数组时,直接赋初始值
(2)使用apply方法创建数组对象
3)数组操作
访问/修改元素:
var a = arr(角标)
arr(角标) = 值
遍历数组
- 普通for遍历
for (i <- 0 until arr.length) {
arr(i)
}
或者
for (i <- arr.indices) {
arr(i)
}
- 增强for循环
for (item <- arr) {
item
}
- 迭代器
val iter = arr.iterator
while (iter.hasNext) {
iter.next()
}
- foreach方法
arr.foreach(item => {
item
})
添加元素(会生成新的数组)
- 尾部增加
val newArr = arr.:+(值)
或者
val newArr = arr :+ 值
- 头部增加
val newArr = arr.+:(值)
或者
val newArr = 值 +: arr
- 连续增加
val newArr = 值 +: 值 +: arr :+ 值 :+ 值
4)案例实操
Scala
object Test01 {
def main(args: Array[String]): Unit = {
// 创建数组
val array = new Array[Int](5)
val array2 = Array(5, 9, 2, 10, 15)
// 访问元素
println(array2(0))
println(array2(2))
println("===========================")
// 修改元素
array2(1) = 22
// 数组的遍历
// 1) 普通for循环
println("普通for循环")
for (i <- 0 until array2.length) {
println(array2(i))
}
println("普通for循环2")
for (i <- array2.indices) {
println(array2(i))
}
// 2) 增强for循环
println("增强for循环")
for (elem <- array2) {
println(elem)
}
// 3) 迭代器
println("迭代器")
val iterator = array2.iterator
while (iterator.hasNext) {
println(iterator.next())
}
// 4) foreach方法
println("foreach方法")
array2.foreach(item => {
println(item)
})
// 添加元素
// 1) 尾部添加
val array_1 = array2.:+(8)
println(array_1.mkString("-"))
val array_2 = array2 :+ 3
println(array_2.mkString(","))
// 2) 头部加
val array_3 = array2.+:(30)
println(array_3.mkString(","))
val array_4 = 11 +: array2
// 3) 可以连续加
val array_5 = 12 +: 14 +: array2 :+ 15 :+ 20
println(array_5.mkString("-"))
}
}
(2)可变数组
1)定义变长数组
val arr01 = ArrayBuffer[Any](3, 2, 5)
(1)[Any]存放任意数据类型
(2)(3, 2, 5)初始化好的三个元素
(3)ArrayBuffer需要引入scala.collection.mutable.ArrayBuffer
2)数组操作
访问/修改元素:
var a = arr(角标)
arr(角标) = 值
添加元素
- 尾部添加
arr += 值
或者
arr.append(值)
// 将一个数组加到另一个数组尾部
arr.appendAll(arr2)
- 头部添加
值 +=: arr
或者
arr.prepend(值)
// 将一个数组加到另一个数组头部
arr.prependAll(arr2)
- 指定位置添加
arr.insert(位置,值)
// 将一个数组加到另一个数组指定位置
arr.insertAll(位置,arr2)
删除元素
- 删除指定位置的值:arr.remove(位置)
- 删除指定位置,指定长度的值:arr.remove(位置,长度)
- 删除指定的值:arr -= 值
3)案例实操
Scala
object Test02 {
def main(args: Array[String]): Unit = {
// 创建可变数组
val arr_1 = new ArrayBuffer[Int]()
val arr_2 = ArrayBuffer(1, 2, 3, 4, 5)
// 访问元素
println(arr_2(0))
arr_2(0) = 11
// 添加元素
// 1) 尾部添加
println("尾部添加")
val array1 = arr_2 += 6
println(arr_2.mkString(","))
println(array1.mkString(","))
println(array1 == arr_2)
println("=============")
arr_2.append(7)
println(arr_2.mkString(","))
// 2) 头部添加
println("头部添加")
0 +=: arr_2
println(arr_2.mkString("-"))
arr_2.prepend(12)
println(arr_2.mkString(","))
// 3) 指定位置添加
println("指定位置添加")
arr_2.insert(1, 13)
println(arr_2.mkString(","))
// 删除元素
println("删除元素")
arr_2.remove(0)
println(arr_2.mkString(","))
println("======")
arr_2.remove(0, 2)
println(arr_2.mkString(","))
println("======")
arr_2 -= 4
println(arr_2.mkString(","))
}
}
(3)不可变与可变数组转换
1)说明
arr1.toBuffer //不可变数组转可变数组
arr2.toArray //可变数组转不可变数组
(1)arr2.toArray返回结果才是一个不可变数组,arr2本身没有变化
(2)arr1.toBuffer返回结果才是一个可变数组,arr1本身没有变化
2)案例实操
Scala
object Test03 {
def main(args: Array[String]): Unit = {
// 可变数组转换为不可变数组
val arr: ArrayBuffer[Int] = ArrayBuffer(23, 56, 98)
val newArr: Array[Int] = arr.toArray
println(newArr.mkString(", "))
println(arr)
// 不可变数组转换为可变数组
val buffer: mutable.Buffer[Int] = newArr.toBuffer
println(buffer)
println(newArr)
}
}
(4)多维数组
1)多维数组定义
val arr = Array.ofDim[Double](3,4)
说明:二维数组中有三个一维数组,每个一维数组中有四个元素(3行4列)
2)案例实操
Scala
object Test04 {
def main(args: Array[String]): Unit = {
val array = Array.ofDim[Int](2, 3)
array(0)(0) = 1
array(0)(1) = 2
array(0)(2) = 3
array(1)(0) = 4
array(1)(1) = 5
array(1)(2) = 6
for (i <- 0 until array.length; j <- 0 until array(i).length) {
print(s"${array(i)(j)} ")
}
println("=======")
for (i <- array.indices; j <- array(i).indices) {
print(s"${array(i)(j)} ")
}
println("=======")
array.foreach(item => item.foreach(println))
println("=======")
array.foreach(_.foreach(println))
}
}
3、列表List
(1)不可变List
1)创建
val list = List(值,值,...)
2)集合操作
尾部添加元素
val newList = list :+ 值
头部添加元素
val newList = 值 +:list
::的运算规则从右向左
val newList = 值 :: 值 :: list
val newList = list .::(值)
空集合Nil
val newList =Nil
集合合并
val newList = arr1 ::: arr2
或者
val newList = arr1 ++ arr2
2)案例实操
Scala
object Test05 {
def main(args: Array[String]): Unit = {
val list = List[Int](10, 20, 30)
// 访问元素
println(list(0))
// 遍历元素
list.foreach(println)
// 添加元素
// 尾部添加元素
val list_1 = list :+ 11
list_1.foreach(item => {
print(s"$item ")
})
println()
// 头部添加元素
val list_2 = 12 +: list
list_2.foreach(item => {
print(s"$item ")
})
println()
val list_3 = list.::(11).::(12)
list_3.foreach(item => {
print(s"$item ")
})
println()
//
val list_5 = 33 :: 44 :: list
list_5.foreach(item => {
print(s"$item ")
})
println("===============")
// 空集合
val list_6 = Nil .::(10)
// 集合合并
val list1 = List(1,2,3)
val list2 = List(4,5,6)
val newList_1 = list1 ::: list2
println(newList_1)
val newList_2 = list1 ++ list2
println(newList_2)
}
}
(2)可变ListBuffer
1)创建
val list = new ListBuffer[any]()
或者
val list = ListBuffer(值,值,...)
2)集合操作
添加元素
- 尾部添加
list.append(值,值,...)
list += 值
- 头部添加
list.prepend(值,值,...)
值 +=: list
- 指定位置添加
list.insert(位置,值,值,......)
修改元素
list(位置) = 值
list.update(位置,值)
删除元素
- 删除指定位置值
list.remove(位置)
- 删除指定位置指定长度的值
list.remove(位置,长度)
- 删除指定的值
list -= 值
集合合并
list ++=: list2 list合并进list2
list ++= list2 list2合并进list
val list3 = list ++ list2 list和list2合并得到新的list3
3)案例实操
Scala
object Test06 {
def main(args: Array[String]): Unit = {
// 创建
var list1 = new ListBuffer[Int]()
val list2 = ListBuffer(1, 2, 3, 4)
// 尾部添加元素
list2.append(12, 13)
println(list2)
list2 += 14 += 15
println(list2)
// 头部添加
list2.prepend(22, 23)
println(list2)
33 +=: 34 +=: list2
println(list2)
// 指定位置添加
list2.insert(1, 0)
println(list2)
// 修改元素
list2(0) = -10
list2.update(1, -1)
println(list2)
// 删除元素
list2.remove(0)
list2.remove(0, 2)
list2 -= 1
println(list2)
// 集合合并
val list_1 = ListBuffer(1,2,3)
val list_2 = ListBuffer(4,5,6)
val list_3 = list_1 ++ list_2
println(list_3)
list_1 ++= list_2
println(list_1)
list_1 ++=: list_2
println(list_2)
}
}

4、Set 集合
默认情况下,Scala 使用的是不可变集合,使用可变集合,要引用 scala.collection.mutable.Set包
(1)不可变Set
1)创建
Set默认是不可变集合,数据无序且不可重复
val set = Set(值,值, ...)
2)集合操作
添加元素
val newSet = set + 值
删除元素
val newSet = set - 值
合并
val newSet = set1 + set2
3)案例实操
Scala
object Test07 {
def main(args: Array[String]): Unit = {
// 创建set
val set1 = Set(1,2,3,4)
// 添加元素
val set_1 = set1 + 5
println(set_1)
// 合并set
val set2 = Set(5,6,7)
val set_2 = set1 ++ set2
println(set_2)
// 删除元素
val set_3 = set1 - 1
println(set_3)
}
}

(2)可变mutable.Set
1)创建
val set = mutable.Set(值,值,...)
2)集合操作
添加元素
set += 值
或者
set.add(值)
删除元素
set -= 值
或者
set.remove(值)
3)案例实操
Scala
object Test08 {
def main(args: Array[String]): Unit = {
// 创建set
val set1 = mutable.Set(1, 2, 3, 4)
// 添加元素
set1 += 5
println(set1)
set1.add(6)
println(set1)
// 合并两个Set
val set2 = mutable.Set(1,2,13)
val set3 = set1 ++ set2
println(set3)
// 删除元素
set3.remove(1)
println(set3)
set3 -= 3
println(set3)
}
}

5、Map集合
(1)不可变Map
1)创建
val map = Map(键 -> 值,键 -> 值,...)
2)集合操作
根据键获取值
val value = map.get(键).get 如果键不存在会抛异常
val value = map(键) 如果键不存在会抛异常
val value = map.getOrElse(键, 默认值) 如果键不存在返回默认值
获取所有key和value
val set = map.keySet // 返回key的set集合
val iterableKeys = map.keys // 返回key的迭代器
val iterableValues = map.values // 返回value的迭代器
3)案例实操
Scala
object Test09 {
def main(args: Array[String]): Unit = {
// 创建map
val map1 = Map("a" -> 4, "b" -> 10, "c" -> 7)
// 访问某一个key的value
val value1 = map1.get("a").get
println(value1)
val value2 = map1("b")
println(value2)
val value3 = map1.getOrElse("c", 0)
println(value3)
println("-----------")
// 所有的key 或者 value
val keySet = map1.keySet
println(keySet)
val values = map1.values
println(values)
// 遍历元素
map1.foreach(println)
println("=========")
map1.foreach((item: (String, Int)) => {
println(item._1 + " " + item._2)
})
}
}

(2)可变Map
1)创建
val map = mutable.Map(键 ->值, 键 ->值,...)
2)集合操作
添加元素
map.put(键, 值)
或者
map += ((键, 值))
修改元素
map.update(键, 值) // 如果存在则修改,不存在则新增
删除元素
map.remove(键)
map -= 键
合并两个map
val newMap = map1 ++ map2 // map2向map1合并,存在则覆盖,不存在则新增
3)案例实操
Scala
object Test10 {
def main(args: Array[String]): Unit = {
// 创建map
val map1 = mutable.Map[String, Int]()
// 添加元素
map1.put("a", 3)
println(map1)
map1 += (("b", 5))
println(map1)
// 修改元素
map1.update("a", 10)
map1.update("c", 1)
println(map1)
// 删除元素
map1.remove("a")
println(map1)
map1 -= "b"
println(map1)
// 合并两个Map
val map2 = mutable.Map("c" -> 3, "d" -> 6)
val map3 = map1 ++ map2
println(map3)
}
}

6、元组
1)说明
元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组。
注意:元组中最大只能有22个元素。
2)元组操作
创建元组
val tuple = (值, 值, 值,...)
访问数据
tuple._位置 // 位置从1开始
tuple.productElement(角标) // 角标从0开始
3)案例实操
Scala
object Test11 {
def main(args: Array[String]): Unit = {
// 创建元组
val tuple = ("scala", 10, 'a', true)
// 访问数据
println(tuple._1)
println(tuple._2)
println(tuple.productElement(0))
println("===============")
// 遍历元组数据
for (item <- tuple.productIterator) {
println(item)
}
println("----------")
// 嵌套元组
val tuple2 = (1, 2, 3, ("hello", "scala", 22), 33)
println(tuple2._4._2)
}
}

7、集合常用函数
(1) 常用操作
1)说明
- 获取集合长度
- 获取集合大小
- 循环遍历
- 迭代器
- 生成字符串
- 是否包含
2)案例实操
Scala
object Test12 {
def main(args: Array[String]): Unit = {
val list1 = List(1, 4, 7, 10, 3, 5)
// 获取集合长度
println(list1.length)
// 获取集合大小
println(list1.size)
// 循环遍历
for (elem <- list1) {
print(elem + " ")
}
println()
// 迭代器
for (elem <- list1.iterator) {
print(elem + " ")
}
println()
// 生成字符串
println(list1.mkString("-"))
// 是否包含
println(list1.contains(22))
println(list1.contains(3))
}
}

(2)衍生集合
1)说明
- 获取集合的头
- 获取集合的尾(不是头的就是尾,去除开头第一个数据,剩下的就是尾)
- 集合最后一个数据
- 集合初始数据(不包含最后一个)
- 反转
- 取前(后)n个元素
- 去掉前(后)n个元素
- 并集
- 交集
- 差集
- 拉链 注:如果两个集合的元素个数不相等,那么会将同等数量的数据进 行拉链,多余的数据省略不用
- 滑窗
2)案例实操
Scala
object Test13 {
def main(args: Array[String]): Unit = {
val list1 = List(1, 4, 7, 10, 3, 5, 13, 9, 15)
// 获取集合的头
println("获取集合的头: " + list1.head)
// 获取集合的尾(不是头的就是尾)
println("获取集合的尾: " + list1.tail)
// 集合最后一个数据
println("集合最后一个数据: " + list1.last)
// 集合初始数据(不包含最后一个)
println("集合初始数据: " + list1.init)
// 反转
println("反转: " + list1.reverse)
// 取前(后)n个元素
println("取前3个元素: " + list1.take(3))
println("取后3个元素: " + list1.takeRight(3))
// 去掉前(后)n个元素
println("去掉前2个元素: " + list1.drop(2))
println("去掉后2个元素: " + list1.dropRight(2))
val list2 = List(7, 10, 15, 19, 2, 16)
// 并集
println("并集: " + list1.union(list2))
// 交集
println("交集: " + list1.intersect(list2))
// 差集
println("list1 差集: " + list1.diff(list2))
println("list2 差集: " + list2.diff(list1))
// 拉链
println("list1 拉链: " + list1.zip(list2))
println("list2 拉链: " + list2.zip(list1))
// 滑窗
println("===============")
for (elem <- list1.sliding(3)) {
println(elem)
}
println("===============")
for (elem <- list1.sliding(4, 3)) {
println(elem)
}
println("===============")
for (elem <- list1.sliding(3, 3)) {
println(elem)
}
}
}

(3)简单计算函数
1)说明
(1)求和 (2)求乘积 (3)最大值 (4)最小值
(5)排序
1)sorted 对一个集合进行自然排序,通过传递隐式的Ordering
2)sortBy 对一个属性或多个属性进行排序,通过它的类型。
3)sortWith 基于函数的排序,通过一个comparator函数,实现自定义排序的逻辑。
2)实操
Scala
object Test14 {
def main(args: Array[String]): Unit = {
val list1 = List(3, 6, 1, 8, -3, 0, -6, 9)
val list2 = List(("a", 3), ("d", 9), ("f", 5), ("b", 1), ("e", 7))
// 求和
println(list1.sum)
// 求乘积
println(list1.product)
// 最大值
println("list1 最大值: " + list1.max)
println("list2 最大值: " + list2.maxBy((tuple: (String, Int)) => tuple._2))
println("list2 最大值: " + list2.maxBy(_._2))
// 最小值
println("list1 最小值: " + list1.min)
println("list2 最小值: " + list2.minBy(_._2))
// 排序
// sorted排序
println("list1 sorted排序 : " + list1.sorted)
println("list2 sorted排序 : " + list2.sorted)
// sorted排序降序
println("list1 sorted排序降序 : " + list1.sorted.reverse)
println("list2 sorted排序降序 : " + list2.sorted(Ordering[(String, Int)]).reverse)
// sortBy
println("list1 sortBy排序 : " + list1.sortBy(a => a ))
println("list2 sortBy排序 : " + list2.sortBy(_._2 ))
println("list2 sortBy排序降序 : " + list2.sortBy(_._2)(Ordering[Int]).reverse)
// sortWith
println("list1 sortWith排序 : " + list1.sortWith((a, b) => a < b ))
println("list2 sortBy排序 : " + list2.sortWith((a, b) => a._2 < b._2 ))
println("list2 sortBy排序降序 : " + list2.sortWith((a, b) => a._2 > b._2))
}
}

(4)计算高级函数
1)说明
(1)过滤 遍历一个集合并从中获取满足指定条件的元素组成一个新的集合
(2)转化/映射(map),将集合中的每一个元素映射到某一个函数
(3)扁平化
(4)扁平化+映射 注:flatMap相当于先进行map操作,在进行flatten操作 集合中的每个元素的子元素映射到某个函数并返回新集合
(5)分组(group) 按照指定的规则对集合的元素进行分组
(6)简化(归约)
(7)折叠
2)实操
Scala
object Test15 {
def main(args: Array[String]): Unit = {
val list = List(1, 2, 3, 4, 5, 6, 7)
// 过滤
val list_1 = list.filter(item => item % 2 == 0)
println("过滤: " + list_1)
// 映射map 把集合中每个数乘2
val list_2 = list.map(item => item * 2)
println("映射map: " + list_2)
// 扁平化
val list2 = List(List(1,2,3), List(4,5,6,7), List(8,9))
println("扁平化: " + list2.flatten)
// 扁平映射
val list3 = List("hello word", "hello java", "hello scala", "bob study")
val stringList = list3.flatMap(item => item.split(" "))
println("扁平映射: " + stringList)
// 分组groupBy
val value = list.groupBy(item => item % 2)
val value2 = list.groupBy(item => if (item % 2 == 0) "偶数" else "奇数")
println(value)
println(value2)
// 按照单词的首字母进行分组
val value1 = stringList.groupBy(item => item.charAt(0))
println(value1)
}
}

3)Reduce方法
Reduce简化(归约) :通过指定的逻辑将集合中的数据进行聚合,从而减少数据,最终获取结果。
Scala
object Test16 {
def main(args: Array[String]): Unit = {
val list = List(1, 2, 3, 4)
// 将数据两两结合,实现运算规则
println(list.reduce((a, b) => a + b))
println(list.reduceLeft((a, b) => a + b))
println("=============================")
println(list.reduceLeft(_ - _))
println(list.reduceRight(_ - _)) // 1 - (2 - (3 - 4))
}
}

4)Fold方法
Fold折叠:化简的一种特殊情况。
案例一
Scala
object Test17 {
def main(args: Array[String]): Unit = {
val list = List(1,2,3,4)
println(list.fold(5)((a, b) => a + b))
println(list.foldLeft(5)(_ - _))
println(list.foldRight(5)(_ - _)) // 1 - (2 - (3 - (4 - 5)))
}
}

案例二
Scala
object Test17 {
def main(args: Array[String]): Unit = {
// 两个Map的数据合并
val map1 = mutable.Map("a" -> 2, "b" -> 5, "c" -> 3)
val map2 = mutable.Map("a" -> 4, "c" -> 1, "d" -> 7, "e" -> 3)
val newMap = map1.foldLeft(map2)(
(m2, item) => {
val key = item._1
m2(key) = m2.getOrElse(key, 0) + item._2
m2
}
)
println(newMap)
}
}

(5)简单WordCount 案例
单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果
Scala
object Test18 {
def main(args: Array[String]): Unit = {
val list = List(
"hello",
"hello world",
"hello scala",
"hello spark from scala",
"hello flink from scala"
)
println(list)
// 对字符串进行切分,得到一个打散所有单词的列表
val wordList: List[String] = list.flatMap(_.split(" "))
println(wordList)
// 相同的单词进行分组
val groupMap: Map[String, List[String]] = wordList.groupBy(item => item)
println(groupMap)
// 对分组之后的list取长度,得到每个单词的个数
val countMap = groupMap.map(item => (item._1, item._2.length))
println(countMap)
// 将map转换为list,并排序取前3
val sortList = countMap.toList
.sortWith((a, b) => a._2 > b._2)
.take(3)
println(sortList)
}
}

(6)复杂WordCount案例
方式一
Scala
object Test18 {
def main(args: Array[String]): Unit = {
val list = List(
("hello", 1),
("hello world", 2),
("hello scala", 2),
("hello spark from scala", 4),
("hello flink from scala", 4)
)
println(list)
// 展开
val newList: List[String] = list.map(item => {
(item._1.trim + " ") * item._2
})
val wordCountList = newList.flatMap(_.split(" ")) // 空格分词
.groupBy(item => item) // 按照单词分组
.map(item => (item._1, item._2.size)) // 统计出每个单词的个数
.toList
.sortBy(item => item._2)(Ordering[Int].reverse)
.take(3)
println(wordCountList)
}
}

方式二
Scala
object Test18 {
def main(args: Array[String]): Unit = {
val list = List(
("hello", 1),
("hello world", 2),
("hello scala", 2),
("hello spark from scala", 4),
("hello flink from scala", 4)
)
println(list)
// 将字符串切割为单词,并结合对应的个数包装成二元组
val newListMap: List[(String, Int)] = list.flatMap(item => {
val strings: Array[String] = item._1.split(" ")
strings.map(word => (word, item._2))
})
// 对二元组按照单词进行分组
val preMap = newListMap.groupBy(item => item._1)
// 叠加每个单词预统计的个数值
val countMap: MapView[String, Int] = preMap.mapValues(item => {
item.map(a => a._2).sum
})
// 转换成list,排序取前3
val countList = countMap.toList
.sortWith((a, b) => a._2 > b._2)
.take(3)
println(countList)
}
}

8、队列
Scala 也提供了队列(Queue)的数据结构,队列的特点就是先进先出。进队和出队的方法分别为enqueue和dequeue。
(1)不可变队列
1)创建
val queue = Queue(值,值,....)
2)案例实操
Scala
object Test19 {
def main(args: Array[String]): Unit = {
// 不可变队列
val queue = Queue(1, 2, 4, 5)
// 加入数据会得到一个新的queue
val newQueue = queue.enqueue(10)
println(queue)
println(newQueue)
println("==========")
// 取出数据
val dequeue = queue.dequeue
println(dequeue)
}
}

(2)可变队列
1)创建
val queue = new mutable.Queue()
2)案例实操
Scala
object Test19 {
def main(args: Array[String]): Unit = {
// 可变队列
val queue = new mutable.Queue[Int]()
// 加入数据
queue.enqueue(1, 3, 5, 2)
// 取出数据
println(queue.dequeue())
println(queue)
}
}

十、模式匹配
Scala中的模式匹配类似于Java中的switch语法
int i = 10
switch (i) {
case 10 :
System.out.println("10");
break;
case 20 :
System.out.println("20");
break;
default :
System.out.println("other number");
break;
}
1、基本语法
(1)说明
模式匹配语法中,采用match关键字声明 ,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有case都不匹配,那么会执行case _分支, 类似于Java中default语句。
x match {
case 匹配 => 匹配成功执行的代码
case 匹配 => 匹配成功执行的代码
...
case _ => 如果都不匹配执行的代码
}
说明
1)如果所有case都不匹配,那么会执行case _ 分支,类似于Java中default语句, 若此时没有case _ 分支,那么会抛出MatchError。
2)每个case中,不需要使用break语句,自动中断case。
3)match case 语句可以匹配任何类型,而不只是字面量。
4)=> 后面的代码块,直到下一个case语句之前的代码是作为一个整体执行,可以使用{}括起来,也可以不括。
(2)案例
Scala
object Test01 {
def main(args: Array[String]): Unit = {
val a = 10
val b = 3
def matchDualOp(op: Char): Int = op match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case '%' => a % b
case _ => -1
}
println(matchDualOp('+'))
println(matchDualOp('/'))
println(matchDualOp('%'))
println(matchDualOp('a'))
}
}

2、模式守卫
1)说明
如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。
2)案例实操
Scala
object Test02 {
def main(args: Array[String]): Unit = {
def abs(num: Int): Int ={
num match {
case i if i >= 0 => num
case i if i < 0 => -num
}
}
println(abs(3))
println(abs(-5))
}
}

3、模式匹配类型
(1)匹配常量
1)说明
Scala 中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。
2)实操
Scala
object Test02 {
def main(args: Array[String]): Unit = {
def describeConst(value: Any): String ={
value match {
case 1 => "value: 1"
case "hello" => "value: hello"
case 'a' => "value: a"
case true => "value: true"
case _ => "value: other"
}
}
println(describeConst(1))
println(describeConst(true))
println(describeConst(123))
}
}

(2)匹配类型
1)说明
需要进行类型判断时,可以使用isInstanceOf[T]和asInstanceOf[T],也可使用模式匹配实现同样的功能。
2)案例实操
Scala
object Test02 {
def main(args: Array[String]): Unit = {
def describeType(value: Any): String ={
value match {
case i: Int => "Int: " + i
case i: String => "String: " + i
case i: List[String] => "List[String]: " + i
case i: Array[Int] => "Array[Int]: " + i.mkString("-")
case i => "other: " + i
}
}
println(describeType(1))
println(describeType("hello"))
println(describeType(List(1, 2, 3)))
println(describeType(List("a", "b", "c")))
println(describeType(Array("a", "b")))
println(describeType(Array(1, 2, 3)))
println(describeType(true))
}
}

注意:数组可保留泛型外,其他集合的泛型都会擦除(也就添加类型是无效的)
(3)匹配数组
1)说明
scala模式匹配可以对数组进行精确的匹配,例如匹配只有两个元素的、且第一个元素 为0的数组。
2)案例实操
Scala
object Test02 {
def main(args: Array[String]): Unit = {
val list = List(
Array(0),
Array(1, 0),
Array(0, 1, 0),
Array(1, 1, 0),
Array(2, 3, 7, 15),
Array("hello", 1, 30)
)
for (elem <- list) {
val str = elem match {
// 匹配Array(0) 这个数组
case Array(0) => "0"
// 匹配有两个元素的数组,然后将将元素值赋给对应的x,y
case Array(x, y) => "Array: x=" + x + " y=" + y
// 匹配以0开头和数组
case Array(0, _*) => "以0开头的数组: " + elem.mkString(" ")
// 中间为1的三元素数组
case Array(x, 1, y) => "中间为1的三元素数组: " + elem.mkString(" ")
case _ => "otherArray: " + elem.mkString(" ")
}
println(str)
}
}
}

(4)匹配列表
1)方式一
Scala
object Test02 {
def main(args: Array[String]): Unit = {
val list = List(
List(0),
List(1, 0),
List(0, 0, 0),
List(1, 1, 0),
List(88),
List("hello")
)
for (elem <- list) {
val str = elem match {
// 匹配List(0) 这个数组
case List(0) => "0"
// 匹配有两个元素的List,然后将将元素值赋给对应的x,y
case List(x, y) => "List: x=" + x + " y=" + y
// 匹配以0开头List
case List(0, _*) => "以0开头的List: " + elem.mkString(" ")
// 只有一个值的list
case List(x) => "只有一个值的list: " + elem.mkString(" ")
case _ => "otherArray: " + elem.mkString(" ")
}
println(str)
}
}
}

2)方式二
Scala
object Test02 {
def main(args: Array[String]): Unit = {
val list = List(1,2,3,4,5)
list match {
case a :: b :: rest => println(s"a: $a b: $b rest: $rest")
case _ => println("other list")
}
println("===============")
val list2 = List(3)
list2 match {
case a :: b :: rest => println(s"a: $a b: $b rest: $rest") // list长度必须大于等于3
case _ => println("other list")
}
}
}

(5)匹配元组
基本匹配
Scala
object Test02 {
def main(args: Array[String]): Unit = {
val list = List(
(0, 1),
(1, 0),
(0, 1, 0),
(0, 1, 1),
(1, 23, 56),
("hello", true, 0.5)
)
for (elem <- list) {
val str = elem match {
// 以0开头的元组
case (0, _) => "以0开头的元组: " + elem
// 任意两个元素的元组
case (a, b) => "任意两个元素的元组: " + a + " " + b
// 第二个值是1的元组
case (a, 1, _) => "第二个值是1的元组: " + elem
case _ => "other 元组: " + elem
}
println(str)
}
}
}

高级匹配
Scala
object Test03 {
def main(args: Array[String]): Unit = {
// 打印元组第一个元素
val list = List(("a", 1), ("b", 2), ("c", 3))
for (elem <- list) {
print(elem._1 + " ")
}
println()
for ((word, count) <- list) {
print(word + " ")
}
println()
for ((word, _) <- list) {
print(word + " ")
}
println()
for (("a", count) <- list) {
print(count + " ")
}
println()
println("--------------")
// 给元组元素命名 在变量声明时匹配
val (x, y) = (10, "hello")
println(s"x: $x y: $y")
val List(x1, y1, _*) = List(1,2,3,4,5)
println(s"x: $x1 y: $y1")
val a :: b :: res = List(1,2,3,4,5)
println(s"a= $a b= $b res= $res")
println("--------------")
// 遍历集合中的元组,给count * 2
var list2: List[(String, Int)] = List(("a", 1), ("b", 2), ("c", 3))
println(
list2.map{
case (word,count)=>(word,count*2)
}
)
var list3 = List(("a", ("a", 1)), ("b", ("b", 2)), ("c", ("c", 3)))
println(
list3.map{
case (groupkey,(word,count))=>(word,count*2)
}
)
}
}

(6)匹配对象
Scala
object Test04 {
def main(args: Array[String]): Unit = {
// 针对对象实例的内容进行匹配
val student = new Student("lisi", 30)
val str = student match {
case Student("lisi", 30) => "匹配成功: lisi 30"
case _ => "other ..."
}
println(str)
}
}
// 定义类
class Student(val name: String, val age: Int)
// 定义伴生对象
object Student {
def apply(name: String, age: Int): Student = new Student(name, age)
// 必须实现一个unapply方法,用来对对象属性进行拆解
def unapply(student: Student): Option[(String, Int)] = {
if (student == null)
None
else
Some((student.name, student.age))
}
}
说明:
➢ val user = User("lisi",30),该语句在执行时,实际调用的是User伴生对象中的 apply 方法,因此不用new关键字就能构造出相应的对象。
➢ 当将User("lisi", 30)写在 case 后时[case User("lisi", 30) => "yes"],会默认调用unapply 方法(对象提取器),user 作为 unapply方法的参数,unapply 方法将user对象的name和age属性提取出来,与User("lisi", 30)中的属性值进行匹配
➢ case中对象的unapply方法(提取器)返回Some,且所有属性均一致,才算匹配成功, 属性不一致,或返回None,则匹配失败。
➢ 若只提取对象的一个属性,则提取器为unapply(obj:Obj):Option[T]
若提取对象的多个属性,则提取器为unapply(obj:Obj):Option[(T1,T2,T3...)]
若提取对象的可变个属性,则提取器为unapplySeq(obj:Obj):Option[Seq[T]]
(7)样例类
1)语法:
case class Person (name: String, age: Int)
2)说明
1、样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如apply、unapply、toString、equals、hashCode和copy。
2、样例类是为模式匹配而优化的类,因为其默认提供了unapply方法,因此,样例类可以直接使用模式匹配,而无需自己实现unapply方法。
3、构造器中的每一个参数都成为val,除非它被显式地声明为var(不建议这样做)
3)实操
Scala
object Test05 {
def main(args: Array[String]): Unit = {
// 针对对象实例的内容进行匹配
val student = Student2("lisi", 30)
val str = student match {
case Student2("lisi", 30) => "匹配成功: lisi 30"
case _ => "other ..."
}
println(str)
}
}
// 定义样例类
case class Student2(name: String, age: Int)

4、变量声明模式匹配
Scala
object Test05 {
def main(args: Array[String]): Unit = {
val (x, y) = (1, 2)
println(s"x=$x,y=$y")
val Array(first, second, _*) = Array(1, 7, 2, 9)
println(s"first=$first,second=$second")
val Student(name, age) = Student("zhangsan", 16)
println(s"name=$name,age=$age")
}
}
// 定义类
case class Student(name: String, age: Int)
5、for 中的模式匹配
Scala
object Test05 {
def main(args: Array[String]): Unit = {
val map = Map("A" -> 1, "B" -> 0, "C" -> 3)
//直接将map中的k-v遍历出来
for ((k, v) <- map) {
println(k + " -> " + v) //3个
}
println("----------------------")
//遍历value=0的 k-v ,如果v不是0,过滤
for ((k, 0) <- map) {
println(k + " --> " + 0) // B->0
}
println("----------------------")
//if v == 0 是一个过滤的条件
for ((k, v) <- map if v >= 1) {
println(k + " ---> " + v) // A->1 和 c->33
}
}
}
6、偏函数模式匹配
1) 偏函数定义
val second: PartialFunction[List[Int], Option[Int]] = {
case x :: y :: _ => Some(y)
}
注:该偏函数的功能是返回输入的List集合的第二个元素

2)实操
Scala
object Test06 {
def main(args: Array[String]): Unit = {
val list: List[(String, Int)] = List(("a", 12), ("b", 35), ("c", 27), ("a", 13))
// 将值乘以2
val newList1 = list.map(item => (item._1, item._2 * 2))
// 模式匹配
val newList2 = list.map(
item => {
item match {
case (key, value) => (key, value * 2)
}
}
)
// 进行简化
val newList3 = list.map { case (k, v) => (k, v * 2) }
println(newList1)
println(newList2)
println(newList3)
// 偏函数的应用,求绝对值
// 对输入数据分为不同的情形:正、负、0
val positiveAbs: PartialFunction[Int, Int] = {
case x if x > 0 => x
}
val negativeAbs: PartialFunction[Int, Int] = {
case x if x < 0 => -x
}
val zeroAbs: PartialFunction[Int, Int] = {
case 0 => 0
}
def abs(x: Int): Int = (positiveAbs orElse negativeAbs orElse zeroAbs) (x)
println(abs(-67))
println(abs(35))
println(abs(0))
}
}

十一、异常
Scala
def main(args: Array[String]): Unit = {
try {
var n= 10 / 0
}catch {
case ex: ArithmeticException=>{
// 发生算术异常
println("发生算术异常")
}
case ex: Exception=>{
// 对异常处理
println("发生了异常")
}
}finally {
println("finally")
}
}
1)将可疑代码封装在try块中。在try块之后使用了一个catch处理捕获异常。如果发生任何异常,catch处理它,程序将不会异常终止。
2)Scala 的异常的工作机制和Java一样,但是Scala没有"checked(编译期)"异常, 即Scala 没有编译异常这个概念,异常都是在运行的时候捕获处理。
3)异常捕捉的机制与其他语言中一样,如果有异常发生,catch 子句是按次序捕捉的。 因此,在catch子句中,越具体的异常越要靠前,越普遍的异常越靠后。
4)finally 子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作,这点和Java一样。
5)用throw 关键字,抛出一个异常对象。所有异常都是Throwable的子类型。throw表达式是有类型的,就是Nothing,因为Nothing 是所有类型的子类型,所以throw 表达式可以用在需要类型的地方
def test():Nothing = {
throw new Exception("。。。")
}
6)在Scala中,可以使用throws注解来声明异常
def main(args: Array[String]): Unit = {
f11()
}
@throws(classOf[NumberFormatException])
def f11()={ "abc".toInt }
十二、隐式转换
1、隐式函数
1)说明
隐式转换可以在不需改任何代码的情况下,扩展某个类的功能。
2)案例实操 需求:通过隐式转化为Int类型增加方法。
Scala
object Test08 {
def main(args: Array[String]): Unit = {
// 隐式函数
// 使用implicit关键字声明的函数称之为隐式函数
implicit def conver(num: Int): MyRichInt = new MyRichInt(num)
println(1.myMax(10))
println(1.myMin(10))
}
}
// 自定义类
class MyRichInt(val self: Int) {
// 自定义比较大小的方法
def myMax(n: Int): Int = if (n < self) self else n
def myMin(n: Int): Int = if (n < self) n else self
}
2、隐式参数
普通方法或者函数中的参数可以通过implicit关键字声明为隐式参数,调用该方法时,就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。
1)说明
(1)同一个作用域中,相同类型的隐式值只能有一个
(2)编译器按照隐式参数的类型去寻找对应类型的
(3)隐式参数优先于默认参数
2)案例实操
Scala
object Test08 {
def main(args: Array[String]): Unit = {
// 隐式参数
implicit val name: String = "hello"
def test_1(implicit name: String = "hahaha"): Unit ={
println(name)
}
test_1
test_1("lisi")
// 简便写法
def hiName(): Unit = {
println("hi, " + implicitly[String])
}
hiName()
}
}

3、隐式类
在Scala2.10 后提供了隐式类,可以使用 implicit 声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用。
1)隐式类说明
(1)其所带的构造参数有且只能有一个
(2)隐式类必须被定义在"类"或"伴生对象"或"包对象"里,即隐式类不能是顶级的。
2)案例实操
Scala
object Test08 {
def main(args: Array[String]): Unit = {
// 隐式类
implicit class MyRichInt(self: Int) {
// 自定义比较大小的方法
def myMax(n: Int): Int = if ( n < self ) self else n
def myMin(n: Int): Int = if ( n < self ) n else self
}
println(12.myMin(15))
}
}
十三、泛型
1、协变和逆变
1)语法
class MyList[+T]{
//协变
}
class MyList[-T]{
//逆变
}
class MyList[T] //不变
2)说明
协变:Son是Father的子类,则MyList[Son] 也作为MyList[Father]的"子类"。
逆变:Son是Father的子类,则MyList[Son]作为MyList[Father]的"父类"。
不变:Son是Father的子类,则MyList[Father]与MyList[Son]"无父子关系"。
3)实操
Scala
object Test09 {
def main(args: Array[String]): Unit = {
val child: MyCollection[SubChild] = new MyCollection[Child]
val chil2: MyCollection2[Parent] = new MyCollection2[Child]
}
}
// 定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}
// 定义带泛型的集合类型
class MyCollection[-E] {}
class MyCollection2[+E] {}
2、泛型上下限
1)语法
Class PersonList[T <: Person]{ //泛型上限 }
Class PersonList[T >: Person]{ //泛型下限 }
2)说明
泛型的上下限的作用是对传入的泛型进行限定。
3)实操
Scala
object Test09 {
def main(args: Array[String]): Unit = {
def demo1[A <: Child](a: A): Unit ={
println(a.getClass.getName)
}
demo1[SubChild](new SubChild)
}
}
// 定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}
