第一节:概述
为什么学习Scala ?
Apache Spark 是专为大规模数据快速实时处理的计算引擎/内存级大数据计算框架。Apache Spark 是由Scala 语言编写。
Scala 与Java 关系
总结三点:
java 编译器与Scala 编译器可以相互使用。
Java SDK 的类库可以被Scala使用,Scala支持对Java 类库进行封装。Java 无法访问Scala特有类库
Scala 支持Java语法,JDK不支持Scala 特有语法。
Scala 语言特性
Scala 环境搭建
请参考文章:Windows 安装Scala 详情
IDEA 安装Scala 插件
默认情况下IDEA不支持Scala的开发,需要安装Scala插件。
我这里选择离线安装Scala 插件
a. IDEA的Scala插件地址: https://plugins.jetbrains.com/plugin/1347-scala/versions,找到自己的IDEA版本所对应的版本下载。
知识拓展:如何查看IDEA 是否支持Scala插件
b. 下载后是一个压缩包,将插件放于Scala安装目录下的plugin目录下
c. IDEA进入Settings,选择Plugins,选择从磁盘安装,然后选择Scala插件压缩包即可。
Scala 之Hello World
创建IDEA项目工程
1)打开IDEA->点击左侧的Flie->选择New->选择Project…
2)在src包下,创建包com.zzg.chapt01名和Hello类名,
右键点击src目录->New->Package->输入com.zzg.chapt01->点击OK。
右键点击com.zzg.chapt01->New->Scala Class->Kind项选择Object->Name项输入Hello
编写输出Hello Scala案例
在类中中输入main,然后回车可以快速生成main方法;
在main方法中输入println(“Hello World Scala”)
package com.zzg.chapt01
object Hello {
def main(args: Array[String]): Unit = {
println("Hello World Scala");
}
}
控制台结果输出:
Hello World 程序详细说明
//main 方法名、程序入口
//小括号表示参数列表
// Java 与scala 参数声明方式: java -> 类型 参数名
// scala -> 参数名 : 类型
// public修饰符: scala中没有public关键字,如果不声明访问权限,那么就是公共的
// static修饰符: scala中没有静态方法,所以没有static关键字
// void关键字: 表示返回值,但是不遵守面向对象语法,所以scala中没有,但是又Unit类型,表示没有返回值
// scala中: 方法名(参数列表):返回值类型
// scala中方法必须采用关键字def声明
// scala中方法实现赋值给方法声明,所以中间需要等号连接
// scala是一个完全面向对象的语言,所以没有静态语法,为了能调用静态语法(模仿静态语法)
// 采用伴生对象单例的方式调用方法
/*
* Scala 完全面向对象,故scala去掉了Java中非面向对象的元素,如static关键字,void类型
* 1) static
* scala无static关键字,由object实现类似静态方法的功能(类名.方法名)
* class关键字和Java中的class关键字作用相同,用来定义一个类
* 2) void
* 对于无返回值的函数,scala定义其返回值类型为Unit类型
*
* */
package com.zzg.chapt01
object Hello {
def main(args: Array[String]): Unit = {
println("Hello World Scala");
}
}
第二节:Scala 变量和支持数据类型
2.1 注释
Scala 注释与Java 注释完全一样。
基本语法:
(1)单行注释://
(2)多行注释:/* */
(3)文档注释:/**
*
*/
知识拓展:编码规范
(1)使用一次tab操作,实现缩进,默认整体向右边移动,用shift+tab整体向左移
(2)或者使用ctrl + alt + L来进行格式化
(3)运算符两边习惯性各加一个空格。比如:2 + 4 * 5
(4)一行最长不超过80个字符,超过的请使用换行展示,尽量保持格式优雅
2.2 变量和常量(重点)
Java 变量和常量语法声明:
变量类型 变量名称 = 初始值
eg:int a = 1
final 常量类型 常量名称 = 初始值
eg: final int b = 2
Scala 变量和常量语法声明:
var 变量名 [: 变量类型] = 初始值
eg:var i:Int = 10
val 常量名 [: 常量类型] = 初始值
eg:val j:Int = 20
知识拓展:能用常量就不要使用变量。
Scala 实例:
(1)声明变量时,类型可以省略,编译器自动推导,即类型推导
(2)类型确定后,就不能修改,说明Scala是强数据类型语言。
(3)变量声明时,必须要有初始值
(4)在声明/定义一个变量时,可以使用var或者val来修饰,var修饰变量可改变,val修饰常量不可改。
package com.zzg.chapt02
object VariableAndConstantDemo {
def main(args: Array[String]): Unit = {
// 定义Int 类型变量
var a:Int = 10;
// 类型推导
var b = 10;
// 定义Double 类型变量
var c:Double = 3.1415;
// 变量类型确定,不允许修改, 编译直接提示错误
// var d:Int = c;
// 定义变量, 不赋值, 编译器直接提示 错误
// var e;
// 定义常量
val f:Int = 10;
// 修改常量, 编译器直接提示错误
// f = 11;
}
}
(5)var修饰的对象引用可以改变,val修饰的对象则不可改变,但对象的状态(值)却是可以改变的。(比如:自定义对象、数组、集合等等)
package com.zzg.chapt02
/**
* 复杂对象
*/
object ComplexObjectDemo {
def main(args: Array[String]): Unit = {
// a对象使用var变量声明, 对象属性可以修改,对象状态也可以修改
var a = new Person();
// 对象属性修改
a.name = "Jane";
// 对象状态修改
a = null;
// b 对象使用val 常量声明,对象属性可以修改,对象状态不可以修改
val b = new Person();
// 对象属性修改
b.name = "Tom";
// 对象状态修改, 编译器直接提示错误。
// b = null;
}
/**
* 内部类定义
*/
class Person {
var name = "Jack";
}
}
2.3 标识符命名规范
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.4 字符串输出
基本语法:
(1)字符串,通过+号连接
(2)printf用法:字符串,通过%传值。
(3)字符串模板(插值字符串):通过$获取变量值
package com.zzg.chapt02
object StringInputDemo {
def main(args: Array[String]): Unit = {
var str1:String = "Hello ";
var str2:String = "Scala"
// 字符串输出:使用 +号 连接
println(str1 + str2)
// 字符串输出: 使用%号传值
printf("my name is =%s", str2);
/**
* 多行字符串输出:
* 1、 在Scala中,利用三个双引号包围多行字符串就可以实现。
* 2、 问题:输入的内容,带有空格、\t之类,导致每一行的开始位置不能整洁对齐。
* 3、 解决办法:使用Scala中stripMargin, 默认是“|”作为连接符, 要求:多行字符串的每一行的行头前面加一个“|”符号, 实现格式化
*/
val sql =
"""
|select
|name
|from
|base_user
|where
|id = 1
""".stripMargin
println(sql);
var id:Int =1;
// 字符串输出: 使用$号传值
val formateSql =
s"""
|select
|name
|from
|base_user
|where
|id = ${id}
""".stripMargin
println(formateSql);
}
}
2.5 键盘输入
基本语法:
StdIn.readLine() : 读取行记录
StdIn.readShort():读取Short
StdIn.readDouble(): 读取Double
package com.zzg.chapt02
import scala.io.StdIn
object KeyWordInouDemo {
def main(args: Array[String]): Unit = {
// 输入姓名
println("input name:")
var name = StdIn.readLine()
// 输入年龄
println("input age:")
var age = StdIn.readShort()
printf("my name is =%s", name);
println();
printf("my age is =%d", age);
}
}
2.6 数据类型
Java 数据类型
Java基本数据类型:char、byte、short、int、long、float、double、boolean
Java引用类型:(对象类型)
由于Java有基本类型,而且基本类型不是真正意义的对象,即使后面产生了基本类型的包装类,但是仍然存在基本数据类型,所以Java语言并不是真正意义上的面向对象
Java基本类型的包装类:Character、Byte、Short、Integer、Long、Float、Double、Boolean
温馨提示:Java中基本类型和引用类型没有共同的祖先。
Scala 数据类型
2.7 整数类型
整数分类
数据类型 |
描述 |
Byte [1] |
8位有符号补码整数。数值区间为 -128 到 127 |
Short [2] |
16位有符号补码整数。数值区间为 -32768 到 32767 |
Int [4] |
32位有符号补码整数。数值区间为 -2147483648 到 2147483647 |
Long [8] |
64位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807 = 2的(64-1)次方-1 |
Scala 实例:
(1)Scala各整数类型有固定的表示范围和字段长度,不受具体操作的影响,以保证Scala程序的可移植性。
(2)Scala的整型,默认为Int型,声明Long型,须后加‘l’或‘L’
package com.zzg.chapt02
object DataTypeDemo {
def main(args: Array[String]): Unit = {
// Scala各整数类型有固定的表示范围和字段长度
// 正确
var a: Byte = 127
var b: Byte = -128
// 错误
// var c:Byte = 128
// var d:Byte = -129
// Scala的整型,默认为Int型,声明Long型,然后加”i“ 或者 ”L“
var e = 10
println(e)
var f = 98989898989898989L
println(f)
}
}
2.8 浮点类型
浮点型分类
数据类型 |
描述 |
Float [4] |
32 位, IEEE 754标准的单精度浮点数 |
Double [8] |
64 位 IEEE 754标准的双精度浮点数 |
Scala 实例:
(1)Scala的浮点型常量默认为Double型,声明Float型常量,须后加‘f’或‘F’。
package com.zzg.chapt02
object DataTypeDemo {
def main(args: Array[String]): Unit = {
// Scala各整数类型有固定的表示范围和字段长度
// 正确
var a: Byte = 127
var b: Byte = -128
// 错误
// var c:Byte = 128
// var d:Byte = -129
// Scala的整型,默认为Int型,声明Long型,然后加”i“ 或者 ”L“
var e = 10
println(e)
var f = 98989898989898989L
println(f)
//Scala 声明Float类型,须后加‘f’或‘F’。
var g =1283732f
println(g)
}
}
2.9 字符类型
基本说明:
字符类型可以表示单个字符,字符类型是Char。
Scala 实例:
(1)字符常量是用单引号 ’ ’ 括起来的单个字符。
(2)\t :一个制表位,实现对齐的功能
(3)\n :换行符
(4)\ :表示\
(5)" :表示"
package com.zzg.chapt02
object CharTypeDemo {
def main(args: Array[String]): Unit = {
// 字符定义使用''括起来
var a:Char = 'a';
println(a);
//注意:这里涉及自动类型提升,其实编译器可以自定判断是否超出范围
// var b:Char = 'a' + 1
// \t :一个制表位,实现对齐的功能
println("姓名\t年龄")
// \n :换行符
println("西门庆\n潘金莲")
//(4)\\ :表示\
println("c:\\天黑了\\饿狼来了")
//(5)\" :表示"
println("你过来:\"看我一拳打死你\"")
}
}
控制台输出:
D:\Java\jdk1.8.0_121\bin\java.exe -Dvisualvm.id=756107015780000 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.1\lib\idea_rt.jar=52353:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.1\bin" -Dfile.encoding=UTF-8 -classpath D:\java\jdk1.8.0_121\jre\lib\charsets.jar;D:\java\jdk1.8.0_121\jre\lib\deploy.jar;D:\java\jdk1.8.0_121\jre\lib\ext\access-bridge-64.jar;D:\java\jdk1.8.0_121\jre\lib\ext\cldrdata.jar;D:\java\jdk1.8.0_121\jre\lib\ext\dnsns.jar;D:\java\jdk1.8.0_121\jre\lib\ext\jaccess.jar;D:\java\jdk1.8.0_121\jre\lib\ext\jfxrt.jar;D:\java\jdk1.8.0_121\jre\lib\ext\localedata.jar;D:\java\jdk1.8.0_121\jre\lib\ext\nashorn.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunec.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunjce_provider.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunmscapi.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunpkcs11.jar;D:\java\jdk1.8.0_121\jre\lib\ext\zipfs.jar;D:\java\jdk1.8.0_121\jre\lib\javaws.jar;D:\java\jdk1.8.0_121\jre\lib\jce.jar;D:\java\jdk1.8.0_121\jre\lib\jfr.jar;D:\java\jdk1.8.0_121\jre\lib\jfxswt.jar;D:\java\jdk1.8.0_121\jre\lib\jsse.jar;D:\java\jdk1.8.0_121\jre\lib\management-agent.jar;D:\java\jdk1.8.0_121\jre\lib\plugin.jar;D:\java\jdk1.8.0_121\jre\lib\resources.jar;D:\java\jdk1.8.0_121\jre\lib\rt.jar;E:\scala_workspace\ScalaDemo\out\production\ScalaDemo;D:\scala\lib\scala-library.jar;D:\scala\lib\scala-reflect.jar com.zzg.chapt02.CharTypeDemo
a
姓名 年龄
西门庆
潘金莲
c:\天黑了\饿狼来了
你过来:"看我一拳打死你"
2.10 布尔类型
基本说明:
(1)布尔类型也叫Boolean类型,Booolean类型数据只允许取值true和false
(2)boolean类型占1个字节。
Scala 实例:
package com.zzg.chapt02
class BooleanTypeDemo {
def main(args: Array[String]): Unit = {
var isTrue:Boolean =true;
var isFalse:Boolean = false;
}
}
2.11 Unit类型、Null类型和Nothing类型(重点)
基本说明:
Scala 实例:
(1)、Unit 修饰没有返回值函数。
(2)、Null类只有一个实例对象,Null类似于Java中的null引用。Null可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
(3)、Nothing,可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于Nothing是其他任意类型的子类,他还能跟要求返回值的方法兼容。
package com.zzg.chapt02
object NullAndNullAndNothingDemo {
def main(args: Array[String]): Unit = {
// 调用Unit 函数
println(num)
// null可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
var person = new Person()
person = null
//var a: Int = null // IDEA编译错误:an expression of type Null is ineligible for implicit conversion
//println("a is:" + a)
// 调用过期函数, 抛出异常信息。
calculate();
}
/**
* 定义Unit 函数
*/
def num:Unit ={
println("Unit 函数被调用")
}
/**
* 定义引用类型(AnyRef)
*/
class Person{
}
/**
* 定义calculate函数,会抛出异常
* @return
*/
def calculate():Nothing={
throw new Exception;
}
}
2.12 类型转换
当Scala程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数值类型,这个就是自动类型转换(隐式转换)。数据类型按精度(容量)大小排序为:
基本说明
(1)自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数据类型,然后再进行计算。
(2)把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
(3)(byte,short)和char之间不会相互自动转换。
(4)byte,short,char他们三者可以计算,在计算时首先转换为int类型。
Scala 实例:
package com.zzg.chapt02
object ValueTransDemo {
def main(args: Array[String]): Unit = {
//(1)自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数值类型,然后再进行计算。
var a = 1 + 2.2222
println(a) //Double
//(2)把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
var b: Double = 2.2222
// var c: Int = b //报错
//(3)(byte,short)和char之间不会相互自动转换。
var d: Byte = 44
// var e : Char = d//报错
var f: Int = d
//(4)byte,short,char他们三者可以计算,在计算时首先转换为int类型。
var g: Byte = 66
var h: Char = 22
// var l : Short = g + b //报错,结果是Int类型
// var m : Short = 10 + 20 //报错,byte,short,char他们三者可以计算,在计算时首先转换为int类型。
}
}
强制类型转换
基本说明:
自动类型转换的逆过程,将精度大的数值类型转换为精度小的数值类型。使用时要加上强制转函数,但可能造成精度降低或溢出,格外要注意。
语法说明:
Java : int num = (int)2.1
Scala : var num : Int = 2.1.toInt
Scala 实例:
(1)将数据由高精度转换为低精度,就需要使用到强制转换
(2)强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
def main(args: Array[String]): Unit = {
//(1)将数据由高精度转换为低精度,就需要使用到强制转换
var n1: Int = 2.5.toInt // 这个存在精度损失
//(2)强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
var r1: Int = 10 * 3.5.toInt + 6 * 1.5.toInt // 10 *3 + 6*1 = 36
var r2: Int = (10 * 3.5 + 6 * 1.5).toInt // 44.0.toInt = 44
println("r1=" + r1 + " r2=" + r2)
}
数值类型和String类型间转换
基本说明:
在程序开发中,我们经常需要将基本数值类型转成String类型。或者将String类型转成基本数值类型。
Scala 实例:
(1)基本类型转String类型(语法:将基本类型的值+“” 即可)
(2)String类型转基本数值类型(语法:s1.toInt、s1.toFloat、s1.toDouble、s1.toByte、s1.toLong、s1.toShort)
def main(args: Array[String]): Unit = {
//(1)基本类型转String类型(语法:将基本类型的值+"" 即可)
var str1: String = true + "你kin"
println(str1)
var str2: String = 4.444 + "拜拜"
println(str2)
var str3: String = 4444 + ""
println(str3)
//(2)String类型转基本数值类型(语法:调用相关API)
var s1: String = "11"
var n1: Byte = s1.toByte
var n2: Short = s1.toShort
var n3: Int = s1.toInt
var n4: Long = s1.toLong
println(s1)
println(n1)
println(n2)
println(n3)
println(n4)
}
第三节: Scala 运算符
Scala运算符的使用和Java运算符的使用基本相同,只有个别细节上不同。
算术运算符
(1)对于除号“/”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。小数之间做除法时,保留整数和小数部分
(2)对一个数取模a%b,和Java的取模规则一样。
Scala 实例:
package com.zzg.chapt02
object ArithmeticDemo {
def main(args: Array[String]): Unit = {
// 1)对于除号“/”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。
var a: Int = 10 / 3
println(a) //3
var b: Double = 10 / 3
println(b) //3.0
var c: Double = 10.0 / 3
println(c) //3.3333333333333335
println(c.formatted("%.2f")) //3.33 含义:保留小数点2位,使用四舍五入
//(2)对一个数取模a%b,和Java的取模规则一样。
var d = 10 % 3
println(d) //1
}
}
关系运算符(比较运算符)
Scala 实例:
package com.zzg.chapt02
object RelationDemo {
def main(args: Array[String]): Unit = {
var a:Int = 1
var b:Int = 2
println(a > b) // false
println(a >= b) // false
println(a < b) // true
println(a <= b) // true
println(a == b) // false
println(a != b) // true
}
}
知识拓展:Java和Scala中关于==的区别
Java:==比较两个对象在内存地址;equals比较两个对象toString生成内容是否相同。
Scala: == 等价于Java 的equals 方法。
逻辑运算符
Scala 实例:
package com.zzg.chapt02
object LogicDemo
{
def main(args: Array[String]): Unit = {
var a: Boolean = true;
var b: Boolean = false;
println(a && b) // false
println(a || b) // true
println(!a || !b) // true
}
}
赋值运算符
温馨提示:Scala中没有++、– –操作符,可以通过+=、-=来实现同样的效果;
Scala 实例:
package com.zzg.chapt02
object AssignmentDemo {
def main(args: Array[String]): Unit = {
var a:Int =10;
a += 1 // 11
println(a);
a -= 1 // 10
println(a);
}
}
位运算符
表中变量 a 为 60,b 为 13
Scala运算符本质
在Scala中其实是没有运算符的,所有运算符都是方法。
1)当调用对象的方法时,点.可以省略
2)如果函数参数只有一个,或者没有参数,()可以省略
package com.zzg.chapt02
object OperateDemo {
def main(args: Array[String]): Unit = {
// 标准的加法运算
val i:Int = 1.+(1)
// (1)当调用对象的方法时,.可以省略
val j:Int = 1 + (1)
// (2)如果函数参数只有一个,或者没有参数,()可以省略
val k:Int = 1 + 1
println(1.toString())
println(1.toString)
}
}
第四节: Scala 流程控制
if语句
if单分支语句
基本语法:
if (条件表达式) {
执行代码块
}
温馨提示:当条件表达式为ture时,就会执行{ }的代码。
if 双分支语句
基本语法:
if (条件表达式) {
执行代码块1
} else {
执行代码块2
}
温馨提示:当条件表达式为true 时,执行代码块1,否则执行代码块2。
if 多分支语句
基本语法:
if (条件表达式1) {
执行代码块1
}
else if (条件表达式2) {
执行代码块2
}
……
else {
执行代码块n
}
温馨提示:有满足结果为true的条件表达式,就执行条件表达式{}代码,否则执行else {}代码。
Scala 实例:
package com.zzg.chapt02
import scala.io.StdIn
object IfExpressionDemo {
def main(args: Array[String]): Unit = {
println("input age:")
var age: Int = StdIn.readInt();
if(age <= 18) {
println("儿童")
} else if(age > 18 && age <= 45){
println("青年")
} else if(age > 45 && age <= 60){
println("中年");
} else{
println("老年")
}
}
}
嵌套分支
在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构称为内层。分支外面的分支结构称为外层分支。温馨提示:嵌套分支不要超过3层。
基本语法:
if(){
if(){
}else{
}
}
Scala 实例:针对年龄类判断,优化调整:对于小于等于18条件,在细分:小于等于6 为儿童,其他为少年。
package com.zzg.chapt02
import scala.io.StdIn
object IfExpressionDemo {
def main(args: Array[String]): Unit = {
println("input age:")
var age: Int = StdIn.readInt();
if(age <= 18) {
if(age <= 6){
println("儿童")
} else {
println("少年")
}
} else if(age > 18 && age <= 45){
println("青年")
} else if(age > 45 && age <= 60){
println("中年");
} else{
println("老年")
}
}
}
Switch分支结构
在Scala中没有Switch语句,而是使用模式匹配来处理。
此章节在模式匹配/正则表达式再详细讲解。
For循环控制/For 推导式/For 表达式
Scala 为For 循环控制这一常见的控制结构提供了非常多的特性。
范围数据循环(To)
基本语法:
for(i <- start to end){
println(i + " ")
}
说明:
(1)i 表示循环的变量,初始值start
(2)i 将会从 start-end 循环,前后闭合/[]
范围数据循环(Until)
基本语法:
for(i <- start until end) {
println(i + " ")
}
说明:
(1)这种方式和前面的区别在于i是从start到start - end
(2)即使前闭合后开的范围/[)
循环守卫
基本语法:
for(i <- start to end if condition) {
println(i + " ")
}
说明:
(1)循环守卫,即循环保护式(也称条件判断式,守卫)。保护式为true则进入循环体内部,为false则跳过,类似于continue。
对等代码
for(i <- start to endd){
if(condition){
println(i + " ")
}
}
循环步长
基本语法:
for (i <- start to end by step) {
println(“i=” + i)
}
嵌套循环
基本语法:
for(i <- start to end; j <- start to end) {
println(" i =" + i + " j = " + j)
}
说明:没有关键字,所以范围后一定要加;来隔断逻辑
引入变量
基本语法
for(i <- start to end; j = parameter - i)
{
println("i=" + i + " j=" + j)
}
说明:
(1)for推导式一行中有多个表达式时,所以要加 ; 来隔断逻辑
(2)for推导式有一个不成文的约定:当for推导式仅包含单一表达式时使用圆括号,当包含多个表达式时,一般每行一个表达式,并用花括号代替圆括号,如下
Scala 实例
package com.zzg.chapt03
object ForVariableDemo {
def main(args: Array[String]): Unit = {
// For 引入变量, 形式一
for(i <- 1 to 3; j= 5 -i){
println("i =" + i + ", j =" + j);
}
println("----------------------------")
// For 引入变量, 形式二
for{
i <- 1 to 3
j = 5 -i
}{
println("i =" + i + ", j =" + j);
}
}
}
循环返回值
基本语法:
val result = for(i <- start to end) yield i
说明: 将遍历过程中处理的结果返回到一个新Vector集合中,使用yield关键字。
温馨提示:在开发中使用场景较少。
倒序打印
基本语法:
for(i <- start to end reverse) {
println(“i=” + i)
}
说明:倒序打印一组数据,可以使用reverse
While和do…While循环控制
While和do…While的使用和Java语言中用法相同。
While循环控制
基本语法:
循环变量初始化
while (循环条件) {
循环体(语句)
循环变量迭代
}
说明:
(1)循环条件是返回一个布尔值的表达式
(2)while循环是先判断再执行语句
(3)与for语句不同,while语句没有返回值,即整个while语句的结果是Unit类型()
(4)因为while中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量,而变量需要声明在while循环的外部,那么就等同于循环的内部对外部的变量造成了影响,所以不推荐使用,而是推荐使用for循环。
Scala 实例
package com.zzg.chapt03
object WhileDemo {
def main(args: Array[String]): Unit = {
var a: Int = 10;
while (a > 1) {
println("a =" + a)
a -=1
}
}
}
do…while循环控制
基本语法:
循环变量初始化;
do{
循环体(语句)
循环变量迭代
} while(循环条件)
说明:
(1)循环条件是返回一个布尔值的表达式
(2)do…while循环是先执行,再判断
Scala 实例
package com.zzg.chapt03
object DoWhileDemo {
def main(args: Array[String]): Unit = {
var a:Int = 0;
do{
println("a =" + a);
a += 1
}while(a < 10)
}
}
循环中断
Scala内置控制结构特地去掉了break和continue,是为了更好的适应函数式编程.
Scala 推荐使用Breaks.break()控制结构来实现break和continue功能。
Scala 实例:采取异常方式,跳出for 循环
package com.zzg.chapt03
object ForExceptionDemo {
def main(args: Array[String]): Unit = {
try {
for (i <- 1 to 10) {
println("i = " + i);
if (i == 8) {
throw new RuntimeException()
}
}
println("for 循环正常结束")
}catch{
case e =>
println("for 循环通过异常结束")
}
}
}
Scala 实例:通过Scala 自带函数(Breaks.break()),退出for循环
package com.zzg.chapt03
import scala.Console.println
import scala.util.control.Breaks
object BreaksDemo {
def main(args: Array[String]): Unit = {
Breaks.breakable(
for (i <- 1 to 10) {
println("i = " + i);
if (i == 8) {
println("通过Breaks中止for 循环")
Breaks.break()
}
}
)
println("for 循环正常中止")
}
}
第五节:Scala 函数式编程
面向对象编程、函数式编程区别
1)面向对象编程
解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。
对象:用户
行为:登录、连接JDBC、读取数据库
属性:用户名、密码
Scala语言是一个完全面向对象编程语言。万物皆对象
对象的本质:对数据和行为的一个封装
2)函数式编程
解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。
例如:请求->用户名、密码->连接JDBC->读取数据库
Scala语言是一个完全函数式编程语言。万物皆函数。
函数的本质:函数可以当做一个值进行传递
3)在Scala中函数式编程和面向对象编程完美融合在一起了。
函数
基本语法:
Scala 实例
package com.zzg.chapt04
object FunctionDemo {
def main(args: Array[String]): Unit = {
/**
* 自定义sum 函数
* @param x
* @param y
* @return
*/
def sum(x:Int, y:Int):Int ={
x + y
}
// 调用sum 函数
println("sum is =" + sum(1, 2))
}
}
函数和方法的区别
核心概念
(1)为完成某一功能的程序语句的集合,称为函数。
(2)类中的函数称之方法。
Scala 实例
(1)Scala语言可以在任何的语法结构中声明任何的语法
(2)函数没有重载和重写的概念;方法可以进行重载和重写
(3)Scala中函数可以嵌套定义
package com.zzg.chapt04
object Function01Demo {
// 2.方法可以进行重载和重写,程序可以执行
def main(): Unit = {
println("main 方法重新")
}
def main(args: Array[String]): Unit = {
// 1.Scala语言可以在任何的语法结构中声明任何的语法
import java.util.Date
new Date()
// 3.函数没有重载和重写的概念,程序报错
def test(): Unit = {
println("无参,无返回值")
}
test()
// def test(): Unit = {
// println()
// }
// 4.Scala 中函数可以嵌套定义
def test2() :Unit = {
def test3(name:String):Unit={
println("函数可以嵌套定义")
}
test3("函数嵌套")
}
test2()
}
}
函数定义
1)函数定义
(1)函数1:无参,无返回值
(2)函数2:无参,有返回值
(3)函数3:有参,无返回值
(4)函数4:有参,有返回值
(5)函数5:多参,无返回值
(6)函数6:多参,有返回值
Scala 实例
package com.zzg.chapt04
object FunctionDefinitionDemo {
def main(args: Array[String]): Unit = {
// 无参函数, 调用函数省略()
this.one;
this.two();
this.three("Scala");
this.four("Scala");
this.five(1, 2);
this.six(1, 2);
}
/**
* 函数1:无参,无返回值
*/
def one :Unit ={
println("无参,无返回值")
}
/**
* 函数2:无参,有返回值
*/
def two() :Int ={
println("无参,有返回值")
return 1
}
/**
* 函数3:有参,无返回值
*/
def three(x:String) :Unit ={
println("x =" + x)
println("有参,无返回值")
}
/**
* 函数4:有参,有返回值
* @return
*/
def four(x:String) :String ={
println("x =" + x)
return x;
}
/**
* 函数5:多参,无返回值
* @param a
* @param b
*/
def five(a: Int, b:Int) : Unit ={
println("a =" + a +", b=" + b)
}
/**
* 函数6:多参,有返回值
* @param a
* @param b
* @return
*/
def six(a:Int, b:Int) : Int ={
println("a =" + a +", b=" + b)
return a + b
}
}
函数参数
1)参数说明
(1)可变参数
(2)如果参数列表中存在多个参数,那么可变参数一般放置在最后
(3)参数默认值,一般将有默认值的参数放置在参数列表的后面
(4)带名参数
package com.zzg.chapt04
object FunctionParamterDemo {
def main(args: Array[String]): Unit = {
this.one("Hello", "Scala")
this.two("Hi", "Scala", "!")
// 如果参数有默认值,在调用的时候,可以省略这个参数
this.three("zzx");
// 如果参数传递了值,那么会覆盖默认值
this.three("zzy", 12);
// 默认调用不传递参数名称
this.four("zzh");
// 调用传递参数名称
this.four(name="zty");
}
/**
* 函数:可变参数
* @param parame
*/
def one(parameters:String*):Unit={
println(parameters)
}
/**
* 函数:参数列表中存在多个参数,那么可变参数一般放置在最后
* @param name
* @param paramters
*/
def two(name:String, paramters:String*):Unit={
println("name =" + name);
println(paramters)
}
/**
* 函数:参数默认值
* @param name
* @param age
*/
def three(name:String, age:Int = 18):Unit={
println(s"${name} = ${age}")
}
/**
* 函数:参数携带参数名称传递
* @param name
*/
def four(name:String):Unit={
println("name =" + name)
}
}
函数至简原则(重点)
函数至简原则:能省则省
1)至简原则细节
(1)return可以省略,Scala会使用函数体的最后一行代码作为返回值
(2)如果函数体只有一行代码,可以省略花括号
(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
(4)如果有return,则不能省略返回值类型,必须指定
(5)如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
(6)Scala如果期望是无返回值类型,可以省略等号
(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
Scala 实例
待补充。
函数高级
在Scala中,函数是一等公民。
Scala 实例
package com.zzg.chapt04
object FunctionAdvancedDemo {
def main(args: Array[String]): Unit = {
this.one()
}
/**
* Scala 函数是第一公民
*/
def one():Unit={
println("函数未声明, 直接调用")
}
}
函数高级用法
函数值传递
(1)、函数赋值给变量
(2)、被调用函数后面加上 _, 相当于把函数当成一个整体,再赋值给变量
(3)、明确变量类型,不适用下划线也可以将函数作为整体传递给变量
Scala 实例
package com.zzg.chapt04
object FunctionAdvancedDemo {
def main(args: Array[String]): Unit = {
this.one()
// 函数赋值给变量 方式一
var func = this.two();
println(func)
// 变量直接赋值
var b = func;
println(b)
// 函数赋值给变量 方式二
var c = this.two _; // 等价于拷贝two 函数源码
println(c) // 输出函数地址引用
println(c()) // 整体调用函数
// 函数赋值给变量 方式三
var d : () => Int = two // 通过匿名函数, 接受函数返回值,再赋值给变量。
println(d())
}
/**
* Scala 函数是第一公民
*/
def one():Unit={
println("函数未声明, 直接调用")
}
/**
* Scala 函数赋值给变量
* @return
*/
def two():Int ={
return 1
}
}
函数参数传递
package com.zzg.chapt04
object Function02Demo {
def main(args: Array[String]): Unit = {
// (1)定义一个函数,函数参数还是一个函数签名,f表示函数名称;(Int,Int)表示输入两个Int参数;Int表示函数返回值
def complexF(f:(Int,Int) =>Int) :Int={
return this.add(2, 4)
}
// (3) 将add函数作为参数传递给complexF函数,如果能够推断出来不是调用,_可以省略
println(complexF(add));
println(complexF(add _))
}
// (2) 定义一个函数,参数和返回值类型和complexF的输入参数一致
def add(a:Int, b:Int):Int={
return a + b;
}
}
函数作为返回值返回
package com.zzg.chapt04
/**
* 函数作为返回值返回
*/
object Function03Demo {
def main(args: Array[String]): Unit = {
def a() ={
def b() ={
println("函数嵌套")
println("-------------------")
}
// 函数b后面加上 _, 相当于把函数b当成一个整体。
b _
}
var c = a();
println(c) // 打印函数整体的内存地址
println("---------------")
println(c()) // 执行函数
}
}
匿名函数
基本说明:
没有名字的函数就是匿名函数。
基本语法:
(x:Int)=>{函数体}
温馨提示:x:表示输入参数类型;Int:表示输入参数类型;函数体:表示具体代码逻辑。
Scala 实例
功能需求:传递的函数有一个参数
传递匿名函数至简原则:
(1)参数的类型可以省略,会根据形参进行自动的推导
(2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号。
(3)匿名函数如果只有一行,则大括号也可以省略
(4)如果参数只出现一次,则参数省略且后面参数可以用_代替
package com.zzg.chapt04
object Function04Demo {
def main(args: Array[String]): Unit = {
// (3)标准函数调用
val arr = operation(Array(1, 2, 3, 4), op)
println(arr.mkString(","))
// (4)采用匿名函数
val arr1 = operation(Array(1, 2, 3, 4), (elem: Int) => {
elem + 1
})
println(arr1.mkString(","))
// (4.1) 参数的类型可以省略,会根据形参进行自动的推导;
val arr2 = operation(Array(1, 2, 3, 4), (elem) => {
elem + 1
})
println(arr2.mkString(","))
// (4.2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号
val arr3 = operation(Array(1, 2, 3, 4), elem => {
elem + 1
})
println(arr3.mkString(","))
// (4.3)匿名函数如果只有一行,则大括号也可以省略
val arr4 = operation(Array(1, 2, 3, 4), elem => elem + 1)
println(arr4.mkString(","))
}
// (1)定义逻辑函数
def op(elem: Int): Int = {
elem + 1
}
// (2)定义一个函数,参数包含数据和逻辑函数
def operation(arr: Array[Int], op: Int => Int) = {
for (elem <- arr) yield op(elem)
}
}
高阶函数
package com.zzg.chapt04
import scala.collection.mutable.ArrayBuffer
object Function05Demo {
def main(args: Array[String]): Unit = {
// map 映射, 定义 arr 为 Int 数组, op 为返回值Int类型的函数
def map(arr:Array[Int], op:Int=>Int):Array[Int] ={
for(item <- arr) yield op(item)
}
// 定义op 函数对应实际操作函数
def opt(a: Int):Int={
a + 1
}
val arr:Array[Int] = map(Array(1,2,3,4), opt)
println(arr.mkString(","))
// 匿名函数调用
val arr1:Array[Int] = map(Array(1,2,3,4), (x:Int)=>{
x + 1
})
println(arr1.mkString(","))
// (2)filter过滤,有参数,且参数在后面只使用一次,则参数省略且后面的参数用_表示
def filter(arr:Array[Int], op:Int=>Boolean):ArrayBuffer[Int]={
// 嵌套函数
var filterArr:ArrayBuffer[Int] = ArrayBuffer[Int]()
// 数组遍历
for(item <- arr if op(item)){
filterArr.append(item)
}
return filterArr
}
// 定义op 函数对应实际操作函数
def filterOp(a: Int):Boolean={
if(a > 2){
return false
}
return true
}
val filterArr:ArrayBuffer[Int] = filter(Array(1,2,3,4), filterOp)
println(filterArr.mkString(","))
// 匿名函数调用
val filterArr1:ArrayBuffer[Int] = filter(Array(1,2,3,4), _ % 2 == 1)
println(filterArr1.mkString(","))
// (3)reduce聚合,有多个参数,且每个参数在后面只使用一次,则参数省略且后面参数用_表示,第n个_代表第n个参数
def reduce(arr: Array[Int], op: (Int, Int) => Int) = {
var init: Int = arr(0)
for (elem <- 1 until arr.length) {
println("init=" +init +", elem=" + elem +", num=" + arr(elem))
init = op(init, arr(elem))
}
init
}
// 定义op 函数对应实际操作函数
def add(x:Int, y:Int):Int={
x + y
}
val reduceArr:Int = reduce(Array(1,2,3,4), add)
println(reduceArr)
}
}
函数柯里化&闭包
闭包:函数式编程的标配
1)说明
闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包
函数柯里化:把一个参数列表的多个参数,变成多个参数列表。
Scala 实例
package com.zzg.chapt04
object Function06Demo {
def main(args: Array[String]): Unit = {
// 函数闭包
def a() ={
var const:Int = 10;
// 定义嵌套函数
def b(x: Int):Int={
const + x
}
b _
}
// 在调用时,a函数执行完毕后,局部变量a应该随着栈空间释放掉
val f = a()
println(f)
// 但是在此处,变量a其实并没有释放,而是包含在了b函数的内部,形成了闭合的效果
println(f(3))
// 函数柯里化,其实就是将复杂的参数逻辑变得简单化,函数柯里化一定存在闭包
def c()(x:Int) ={
x
}
println(c()(3))
}
}
递归
基本说明: 一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用
Scala 实例
package com.zzg.chapt04
object Function07Demo {
def main(args: Array[String]): Unit = {
def a(x:Int):Int ={
if(x == 1){
1
} else{
x * a(x -1)
}
}
// 调用递归方法
println(a(5))
}
}
惰性函数
基本说明:
当函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数。
Scala 实例
package com.zzg.chapt04
object Function08Demo {
def main(args: Array[String]): Unit = {
def sum(a:Int,b:Int):Int ={
return a + b
}
lazy val result = sum(10, 20);
println("-----------")
println("result=" + result)
}
}
第六节:Scala 面向对象
Scala的面向对象思想和Java的面向对象思想和概念是一致的。
Scala包
基本语法
package 包名.类名
Scala包的三大作用(与Java 一样)
(1)区分相同名字的类
(2)当类很多时,可以很好的管理类
(3)控制访问范围
包的命名
1)命名规则
只能包含数字、字母、下划线、小圆点.,但不能用数字开头,也不要使用关键字。
包说明(包语句)
基本说明:
Scala有两种包的管理风格,一种方式和Java的包管理风格相同,每个源文件一个包(包名和源文件所在路径不要求必须一致),包名用“.”进行分隔以表示包的层级关系,如com.zzg.chapt。另一种风格,通过嵌套的风格表示层级关系,如下
package com {
package zzg{
package chapt{
}
}
}
温馨提示:
(1)一个源文件中可以声明多个package
(2)子包中的类可以直接访问父包中的内容,而无需导包
Scala 实例
package com.zzg.chapt05
import com.zzg.chapt05.maths.Inner
object Outer {
var outerStr:String ="Outer"
def main(args: Array[String]): Unit = {
println(Inner.innerStr) //Inner
}
}
// 定义嵌套包
package maths{
// 定义对象
object Inner{
var innerStr:String ="Inner"
}
}
包对象
在Scala中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有class和object的共享变量,可以被直接访问。
Scala 实例
package com
// 包对象定义
package object zzg {
// 包对象属性定义
var packageName: String = "com"
// 包对象方法定义
def getPackageName() ={
println("package name is" + packageName)
}
// 定义对象
object Outers{
// 程序入口
def main(args: Array[String]): Unit = {
// 访问包对象方法
println(getPackageName())
}
}
}
1) 说明
(1)若使用Java的包管理风格,则包对象一般定义在其对应包下的package.scala文件中,包对象名与包名保持一致。
(2)如采用嵌套方式管理包,则包对象可与包定义在同一文件中,但是要保证包对象与包声明在同一作用域中。
package com
// 包对象定义
package object zzg {
// 包对象属性定义
var packageName: String = "com"
// 包对象方法定义
def getPackageName() ={
println("package name is" + packageName)
}
// 定义对象
object Outers{
// 程序入口
def main(args: Array[String]): Unit = {
// 访问包对象方法
println(getPackageName())
}
}
}
导包说明
1)和Java一样,可以在顶部使用import导入,在这个文件中的所有类都可以使用。
2)局部导入:什么时候使用,什么时候导入。在其作用范围内都可以使用
3)通配符导入:import java.util._
4)给类起名:import java.util.{ArrayList=>JL}
5)屏蔽类:import java.util.{ArrayList =>,}
6)导入相同包的多个类:import java.util.{HashSet, ArrayList}
7)导入包的绝对路径:new root.java.util.HashMap
知识拓展
Scala中的三个默认导入分别是
import java.lang._
import scala._
import scala.Predef._
类和对象
类:可以看成一个模板
对象:表示具体的事物
定义类
1)回顾:Java中的类
如果类是public的,则必须和文件名一致。
一般,一个.java有一个public类
注意:Scala中没有public,一个.scala中可以写多个类。
基本语法:
[修饰符] class类名 {
类体
}
说明
(1)Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)
(2)一个Scala源文件可以包含多个类
Scala 实例
package com.zzg.chapt05
object ClassAndObject01Demo {
def main(args: Array[String]): Unit = {
// 类实例化
var person = new Person("zzg", 31)
// 调用person 对象的getAge 和getName 方法
println("name =" + person.getName() +", age=" + person.getAge())
// 等价于上面的功能代码片段(温馨提示:可以省略Class 定义的get 和 set 属性方法,仅仅保留构造函数)
println("name =" + person.name +", age=" + person.age)
// 属性赋值
person.age = 32
println("name =" + person.name +", age=" + person.age)
}
class Person{
// 属性赋值系统默认值
var name:String =_;
var age:Int =_;
/**
* Scala 构造函数
* @param name
* @param age
*/
def this(name:String,age:Int) ={
this()
this.age =age
this.name = name
}
def getAge():Int={
this.age
}
def getName():String={
this.name
}
def setAge(age:Int):Unit={
this.age = age
}
def setName(name:String):Unit={
this.name = name
}
}
}
属性
属性是类的一个组成部分
1)基本语法
[修饰符] var|val 属性名称 [:类型] = 属性值
注:Bean属性(@BeanPropetry),可以自动生成规范的setXxx/getXxx方法
Scala 实例
package com.zzg.chapt05
import scala.beans.BeanProperty
object ClassAndObject02Demo {
def main(args: Array[String]): Unit = {
// Class Student 实例化
var stu = new Student("zzx")
println(stu.getName)
}
class Student{
// 自动生成set 和 get 方法
@BeanProperty var name:String =_;
/**
* 构造函数
* @param name
*/
def this(name:String) ={
this()
this.name = name
}
}
}
封装
封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。Java封装操作如下,
(1)将属性进行私有化
(2)提供一个公共的set方法,用于对属性赋值
(3)提供一个公共的get方法,用于获取属性的值
Scala中的public属性,底层实际为private,并通过get方法(obj.field())和set方法(obj.field_=(value))对其进行操作。所以Scala并不推荐将属性设为private,再为其设置public的get和set方法的做法。但由于很多Java框架都利用反射调用getXXX和setXXX方法,有时候为了和这些框架兼容,也会为Scala的属性设置getXXX和setXXX方法(通过@BeanProperty注解实现)
访问权限
在Java中,访问权限分为:public,private,protected和默认。在Scala中,你可以通过类似的修饰符达到同样的效果。但是使用上有区别。
(1)Scala 中属性和方法的默认访问权限为public,但Scala中无public关键字。
(2)private为私有权限,只在类的内部和伴生对象中可用。
(3)protected为受保护权限,Scala中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问。
(4)private[包名]增加包访问权限,包名下的其他类也可以使用
Scala 实例
package com.zzg.chapt05
import scala.beans.BeanProperty
object ClassAndObject03Demo {
def main(args: Array[String]): Unit = {
var project = new Project("NEO 大厦", "深圳市福田区沙头街道")
// 无法访问 name 属性
// println(project.getName)
// 无法访问 name 属性
// println(project.getAddress)
}
class Project{
// name 使用private 修饰符修饰
private var name:String = _
// address 使用protected 修饰符修饰
protected var address:String =_
def this(name:String, address:String) ={
this()
this.name = name
this.address = address
}
}
}
方法
基本语法
**def 方法名(参数列表) [:返回值类型] = { **
方法体
}
创建对象
基本语法
val | var 对象名 [:类型] = new类型()
(1)val修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。
(2)var修饰对象,可以修改对象的引用和修改对象的属性值
(3)自动推导变量类型不能多态,所以多态需要显示声明
构造器
和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。
Scala类的构造器包括:主构造器和辅助构造器
基本语法:
class 类名(**形参列表**) { **//** **主构造器**
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个...
}
}
温馨提示:
(1)辅助构造器,函数的名称this,可以有多个,编译器通过参数的个数及类型来区分。
(2)辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。
(3)构造器调用其他另外的构造器,要求被调用构造器必须提前声明。
构造器参数
Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰
(1)未用任何修饰符修饰,这个参数就是一个局部变量
(2)var修饰参数,作为类的成员属性使用,可以修改
(3)val修饰参数,作为类只读属性使用,不能修改
继承
基本语法
class 子类名 extends 父类名 { 类体 }
(1)子类继承父类的属性和方法
(2)scala是单继承
Scala 实例
(1)子类继承父类的属性和方法
(2)继承的调用顺序:父类构造器->子类构造器
package com.zzg.chapt05
import scala.beans.BeanProperty
object ClassAndObject04Demo {
// 父类
class animal {
@BeanProperty var name:String=_
}
// 子类
class Dog extends animal{
@BeanProperty var voice:String =_
}
def main(args: Array[String]): Unit = {
var dog = new Dog
dog.setName("狗狗")
dog.setVoice("汪汪")
println("name=" +dog.name + ",voice =" + dog.voice)
}
}
抽象属性和抽象方法
抽象属性和抽象方法
基本语法
(1)定义抽象类:abstract class Person{} //通过abstract关键字标记抽象类
(2)定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性
(3)定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法
package com.zzg.chapt05
object AbstractClassDemo {
def main(args: Array[String]): Unit = {
var student = new Student("zzy")
println("name is:" + student.say())
}
/**
* 抽象类定义
*/
abstract class Person{
var name:String // 抽象属性
def say():String // 抽象方法
}
/**
* 抽象类实现
*/
class Student extends Person{
override var name: String = _ //抽象属性实现
def this(name:String) ={ // 构造函数
this()
this.name = name
}
override def say(): String = { // 抽象方法实现
return this.name
}
}
}
继承&重写
(1)如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类
(2)重写非抽象方法需要用override修饰,重写抽象方法则可以不加override。
(3)子类中调用父类的方法使用super关键字
(4)子类对抽象属性进行实现,父类抽象属性可以用var修饰;
子类对非抽象属性重写,父类非抽象属性只支持val类型,而不支持var。
因为var修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写
(5)Scala中属性和方法都是动态绑定,而Java中只有方法为动态绑定。
匿名子类
基本说明:
Java一样,可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类。
Scala 实例
package com.zzg.chapt05
object AbstractClassDemo {
def main(args: Array[String]): Unit = {
var student = new Student("zzy")
println("name is:" + student.say())
// 匿名子类
var person = new Person {
override var name: String = "匿名子类"
override def say(): String = {
this.name
}
}
println("anonymous name is:" + person.say())
}
/**
* 抽象类定义
*/
abstract class Person{
var name:String // 抽象属性
def say():String // 抽象方法
}
/**
* 抽象类实现
*/
class Student extends Person{
override var name: String = _ //抽象属性实现
def this(name:String) ={ // 构造函数
this()
this.name = name
}
override def say(): String = { // 抽象方法实现
return this.name
}
}
}
单例对象(伴生对象)
基本说明:
Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。
单例对象语法
基本语法:
object Person{
val country:String=“China”
}
Scala 实例
(1)单例对象采用object关键字声明
(2)单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
(3)单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
package com.zzg.chapt05
object CompanionObjectDemo {
def main(args: Array[String]): Unit = {
// (3)伴生对象中的属性和方法都可以通过伴生对象名直接调用访问
println(Person.country)
// (4)伴生类中的属性和方法都可以通过伴生类的实例化对象直接调用访问
var person = new Person()
println(person.name)
}
// (2) 伴生对象对应的类称为伴生类,伴生对象的名称应该和伴生类名一致
class Person{
var name:String="taobao"
}
// (1) 伴生对象采用object关键字声明
object Person{
var country: String = "China"
}
}
apply方法
Scala 实例
(1)通过伴生对象的apply方法,实现不使用new方法创建对象。
(2)如果想让主构造器变成私有的,可以在()之前加上private。
(3)apply方法可以重载。
(4)Scala中obj(arg)的语句实际是在调用该对象的apply方法,即obj.apply(arg)。用以统一面向对象编程和函数式编程的风格。
(5)当使用new关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构建对象时,调用的其实时伴生对象的apply方法。
package com.zzg.chapt05
object ApplyDemo {
def main(args: Array[String]): Unit = {
// (1) 通过伴生对象的apply方法,实现不使用new关键字创建对象。
println("name is: " + Person_Apply.apply("zzx").name)
println("name is: " + Person_Apply.apply().name)
}
// (2) 如果想让主构造器变成私有的,可以在()之前加上private
class Person_Apply private(cName: String) {
var name: String = cName
}
object Person_Apply{
def apply(): Person_Apply = {
println("apply空参被调用")
new Person_Apply("xx")
}
def apply(name: String): Person_Apply = {
println("apply有参被调用")
new Person_Apply(name)
}
}
}
特质(Trait)
基本说明:
Scala语言中,采用特质trait(特征)来代替接口的概念,也就是说,多个类具有相同的特质(特征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明。
Scala中的trait中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质。这种感觉类似于Java中的抽象类。
Scala引入trait特征,第一可以替代Java的接口,第二个也是对单继承机制的一种补充。
特质声明
基本语法:
trait 特质名 {
trait主体
}
Scala 实例
package com.atguigu.chapter6
trait PersonTrait {
//声明属性
var name: String = _
//声明方法
def eat(): Unit = {}
//声明属性
var age: Int
//抽象方法
def say(): Unit
}
trait SexTrait {
var sex: String
}
通过查看字节码,可以看到特质=抽象类+接口
特质语法
一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with关键字连接。
基本与语法
基 有父类:class 类名 extends 父类 with 特质1 with 特质2 with 特质3
说明:
(1)类和特质的关系:使用继承的关系。
(2)当一个类去继承特质时,第一个连接词是extends,后面是with。
(3)如果一个类在同时继承特质和父类时,应当把父类写在extends后。
Scala 实例
(1)特质可以同时拥有抽象方法和具体方法
(2)一个类可以混入(mixin)多个特质
(3)所有的Java接口都可以当做Scala特质使用
(4)动态混入:可灵活的扩展类的功能
(4.1)动态混入:创建对象时混入trait,而无需使类混入该trait
(4.2)如果混入的trait中有未实现的方法,则需要实现
package com.zzg.chapt05
object TraitDemo {
def main(args: Array[String]): Unit = {
val teacher = new Teacher
teacher.say()
teacher.eat()
// (4) 动态混入:可灵活的扩展类的功能
val t2 = new Teacher with SexTrait {
override var sex: String = "男"
}
// 调用混入trait的属性
println(t2.sex)
}
// 1.定义一个特质trait
trait PersonTrait {
//声明属性
var name: String = _
//声明方法
def eat(): Unit = {}
//声明属性
var age: Int
//抽象方法
def say(): Unit
}
trait SexTrait {
var sex: String
}
//2.一个类可以实现/继承多个特质
//3.所有的Java接口都可以当做Scala特质使用
class Teacher extends PersonTrait with java.io.Serializable {
override def say() = {
println("say")
}
override def eat(): Unit = {
println("eat")
}
override var age: Int = _
}
}
特质叠加
由于一个类可以混入(mixin)多个trait,且trait中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。冲突分为以下两种:
第一种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法。
第二种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait继承自相同的trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala采用了特质叠加的策略。
所谓的特质叠加,就是将混入的多个trait中的冲突方法叠加起来。
Scala 实例
package com.zzg.chapt05
object OverlayTraitDemo {
def main(args: Array[String]): Unit = {
// MyBall 类实例化
var myBall = new MyBall()
println(myBall.describe())
}
/*
定义球:通用特质
*/
trait Ball{
def describe(): String = "ball"
}
/**
* 定义第一个子特质
*/
trait Color extends Ball{
var color: String = "yellow"
override def describe(): String = color + "-" + super.describe()
}
/**
* 定义第二个子特质
*/
trait Category extends Ball{
var category: String = "foot"
override def describe(): String = category + "-" + super.describe()
}
/**
* 定义类,同时继承Color 和Category
*/
class MyBall extends Category with Color {
override def describe(): String = "my ball is a " + super.describe()
}
}
控制台输出结果:
D:\Java\jdk1.8.0_121\bin\java.exe -Dvisualvm.id=285603311279600 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.1\lib\idea_rt.jar=57861:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.1\bin" -Dfile.encoding=UTF-8 -classpath D:\java\jdk1.8.0_121\jre\lib\charsets.jar;D:\java\jdk1.8.0_121\jre\lib\deploy.jar;D:\java\jdk1.8.0_121\jre\lib\ext\access-bridge-64.jar;D:\java\jdk1.8.0_121\jre\lib\ext\cldrdata.jar;D:\java\jdk1.8.0_121\jre\lib\ext\dnsns.jar;D:\java\jdk1.8.0_121\jre\lib\ext\jaccess.jar;D:\java\jdk1.8.0_121\jre\lib\ext\jfxrt.jar;D:\java\jdk1.8.0_121\jre\lib\ext\localedata.jar;D:\java\jdk1.8.0_121\jre\lib\ext\nashorn.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunec.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunjce_provider.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunmscapi.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunpkcs11.jar;D:\java\jdk1.8.0_121\jre\lib\ext\zipfs.jar;D:\java\jdk1.8.0_121\jre\lib\javaws.jar;D:\java\jdk1.8.0_121\jre\lib\jce.jar;D:\java\jdk1.8.0_121\jre\lib\jfr.jar;D:\java\jdk1.8.0_121\jre\lib\jfxswt.jar;D:\java\jdk1.8.0_121\jre\lib\jsse.jar;D:\java\jdk1.8.0_121\jre\lib\management-agent.jar;D:\java\jdk1.8.0_121\jre\lib\plugin.jar;D:\java\jdk1.8.0_121\jre\lib\resources.jar;D:\java\jdk1.8.0_121\jre\lib\rt.jar;E:\scala_workspace\ScalaDemo\out\production\ScalaDemo;D:\scala\lib\scala-library.jar;D:\scala\lib\scala-reflect.jar com.zzg.chapt05.OverlayTraitDemo
my ball is a yellow-foot-ball
特质叠加执行顺序
当一个类混入多个特质的时候,scala会对所有的特质及其父特质按照一定的顺序进行排序,而此案例中的super.describe()调用的实际上是排好序后的下一个特质中的describe()方法。,排序规则如下:
结论:
(1)案例中的super,不是表示其父特质对象,而是表示上述叠加顺序中的下一个特质,即,MyClass中的super指代Color,Color中的super指代Category,Category中的super指代Ball。
(2)如果想要调用某个指定的混入特质中的方法,可以增加约束:super[],例如super[Category].describe()。
OverlayTraitDemo.scala 代码优化调整:
/**
* 定义类,同时继承Color 和Category
*/
class MyBall extends Category with Color {
override def describe(): String = "my ball is a " + super[Category].describe()
}
控制台结果输出:
D:\Java\jdk1.8.0_121\bin\java.exe -Dvisualvm.id=285806048589000 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.1\lib\idea_rt.jar=58096:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.1\bin" -Dfile.encoding=UTF-8 -classpath D:\java\jdk1.8.0_121\jre\lib\charsets.jar;D:\java\jdk1.8.0_121\jre\lib\deploy.jar;D:\java\jdk1.8.0_121\jre\lib\ext\access-bridge-64.jar;D:\java\jdk1.8.0_121\jre\lib\ext\cldrdata.jar;D:\java\jdk1.8.0_121\jre\lib\ext\dnsns.jar;D:\java\jdk1.8.0_121\jre\lib\ext\jaccess.jar;D:\java\jdk1.8.0_121\jre\lib\ext\jfxrt.jar;D:\java\jdk1.8.0_121\jre\lib\ext\localedata.jar;D:\java\jdk1.8.0_121\jre\lib\ext\nashorn.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunec.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunjce_provider.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunmscapi.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunpkcs11.jar;D:\java\jdk1.8.0_121\jre\lib\ext\zipfs.jar;D:\java\jdk1.8.0_121\jre\lib\javaws.jar;D:\java\jdk1.8.0_121\jre\lib\jce.jar;D:\java\jdk1.8.0_121\jre\lib\jfr.jar;D:\java\jdk1.8.0_121\jre\lib\jfxswt.jar;D:\java\jdk1.8.0_121\jre\lib\jsse.jar;D:\java\jdk1.8.0_121\jre\lib\management-agent.jar;D:\java\jdk1.8.0_121\jre\lib\plugin.jar;D:\java\jdk1.8.0_121\jre\lib\resources.jar;D:\java\jdk1.8.0_121\jre\lib\rt.jar;E:\scala_workspace\ScalaDemo\out\production\ScalaDemo;D:\scala\lib\scala-library.jar;D:\scala\lib\scala-reflect.jar com.zzg.chapt05.OverlayTraitDemo
my ball is a foot-ball
特质自身类型
基本说明:
自身类型可实现依赖注入的功能。
package com.zzg.chapt05
object TraitMySelf{
def main(args: Array[String]): Unit = {
val user = new MyUser("user", 29)
user.insert()
}
//定义User类,作为trait的自身类型
class User(var name: String, var age: Int)
//定义一个特征,跟数据库打交道,做增删改查
trait UserDao {
//定义自身类型为user,表示继承UserDat特征的子类,必须有User的属性
_: User =>
//向数据库插入一条用户信息
def insert(): Unit = {
println("insert into db:" + this.name)
}
}
//实现子类
class MyUser(name: String, age: Int) extends User(name, age) with UserDao with java.io.Serializable {
}
}
特质和抽象类的区别
1.优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
2.如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行(有无参构造)。
扩展
类型检查和转换
基本说明
(1)obj.isInstanceOf[T]:判断obj是不是T类型。
(2)obj.asInstanceOf[T]:将obj强转成T类型。
(3)classOf获取对象的类名。
package com.zzg.chapt05
object ClassAndObjectTypeAndConvert {
def main(args: Array[String]): Unit = {
val person = new Person
// (1) 判断对象是否为某个类型的实例
val bool: Boolean = person.isInstanceOf[Person]
println("peson 是否为Person 类:" + bool)
// (2) 将对象转换为某个类型的实例
try{
var name:String ="Tom"
val copy: Person = name.asInstanceOf[Person]
println(copy)
}catch{
case ex: Exception => {
println(ex.getMessage)
}
}
// (3) 获取类的信息
val pClass: Class[Person] = classOf[Person]
println(pClass)
}
/**
* 定义 Person 类
*/
class Person{
var name:String = _
}
}
枚举类和应用类
基本说明:
枚举类:需要继承Enumeration
应用类:需要继承App
Scala 实例
package com.zzg.chapt05
object EnumDemo {
def main(args: Array[String]): Unit = {
// 输出ColorEnum.RED 枚举对象
println(ColorEnum.RED)
}
/**
* 定义枚举对象
*/
object ColorEnum extends Enumeration {
val RED = Value(1, "red")
val YELLOW = Value(2, "yellow")
val BLUE = Value(3, "blue")
}
/**
* 定义App
*/
object TestApp extends App{
println("Scala App")
}
}
Type定义新类型
基本说明:
数据类型起别名
Scala 实例
package com.zzg.chapt05
object AliasDemo {
def main(args: Array[String]): Unit = {
// 数据类型String 起别名S
type S = String
// 定义String数据类型, 基本声明方式一
var name:String ="基本声明方式"
// 定义String数据类型, 基本声明方式二
var address:S ="别名声明方式"
}
}
第七节:Scala 集合
集合简介
1)Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable特质。
2)对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本,分别位于以下两个包
不可变集合:scala.collection.immutable
可变集合: scala.collection.mutable
3)Scala不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而不会对原对象进行修改。类似于java中的String对象
4)可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象。类似于java中StringBuilder对象
知识拓展:在操作集合的时候,不可变用符号,可变用方法
不可变集合继承图
1)Set、Map是Java中也有的集合
2)Seq是Java没有的,我们发现List归属到Seq了,因此这里的List就和Java不是同一个概念了
3)我们前面的for循环有一个 1 to 3,就是IndexedSeq下的Range
4)String也是属于IndexedSeq
5)我们发现经典的数据结构比如Queue和Stack被归属到LinearSeq(线性序列)
6)大家注意Scala中的Map体系有一个SortedMap,说明Scala的Map可以支持排序
7)IndexedSeq和LinearSeq的区别:
(1)IndexedSeq是通过索引来查找和定位,因此速度快,比如String就是一个索引集合,通过索引即可定位
(2)LinearSeq是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找
可变集合继承图
数组
不可变数组
1)第一种方式定义数组
基本语法:
val 数组名称 = new Array[数据类型](容量大小)
Scala 实例
package com.zzg.chapt06
object ArrayDemo {
def main(args: Array[String]): Unit = {
// 定义一个存放Int数据类型且容量为10 的数组
var arr = new Array[Int](10)
// 输出数组容量大小
println(arr.length) // 10
//数组赋值
//修改某个元素的值
arr(2) = 2
arr(1) = 1
println(arr.mkString(","))
println("-----------------")
//采用方法的形式给数组赋值
arr.update(0, 100)
println(arr.mkString(","))
println("-----------------")
//数组遍历
//查看数组
println(arr.mkString(","))
println("-----------------")
//普通遍历
for (i <- arr) {
println(i)
}
println("-----------------")
//简化遍历: 将函数作为参数传递
def px(elem: Int): Unit = {
println(elem)
}
arr.foreach(px)
println("-----------------")
//简化遍历: 匿名函数
arr.foreach((x) =>{
println(x)
})
println("-----------------")
//简化遍历: 功能函数,传递当前元素
arr.foreach(println(_))
println("-----------------")
//简化遍历: 功能函数,默认传递当前元素
arr.foreach(println)
println("-----------------")
//增加元素
//数组后面追加
val ints: Array[Int] = arr :+ 5
println(ints)
println(arr.mkString(","))
println(ints.mkString(","))
println("-----------------")
//数组前面追加
val ints2: Array[Int] = 5 +: arr
println(ints2.mkString(","))
val ints3: Array[Int] = 6 +: ints
println(ints3.mkString(","))
}
}
2)第二种方式定义数组
基本语法:
val 数组名称 = Array(值1, 值2)
(1)在定义数组时,直接赋初始值
(2)使用apply方法创建数组对象
Scala 实例
// 定义数组方式二:
var arr01 = Array(1, 2, 3, "尚学堂")
println(arr01.length)
//遍历
for (i <- arr01) {
println(i)
}
println(arr01)
可变数组
1)定义变长数组
var arr = = ArrayBuffer[Any](1, 10, 100)
(1)[Any]存放任意数据类型
(2)(1, 10, 100)初始化好的三个元素
(3)ArrayBuffer需要引入scala.collection.mutable.ArrayBuffer
Scala 实例
(1)ArrayBuffer是有序的集合
(2)增加元素使用的是append方法(),支持可变参数
package com.zzg.chapt06
import scala.collection.mutable.ArrayBuffer
object ArrayBufferDemo {
def main(args: Array[String]): Unit = {
//1.创建并初始赋值可变数组
var arr01 = ArrayBuffer[Any](1, 10, 100)
//2.遍历数组
for (i <- arr01) {
println(i)
}
//3.增加元素
//追加元素
arr01.+=(1000)
println(arr01)
//向数组最后追加数据
arr01.append(10000, 100000)
println(arr01)
//修改元素
arr01(3) = 101
println(arr01)
}
}
不可变数组与可变数组的转换
1)基本说明
arr1.toBuffer //不可变数组转可变数组
arr2.toArray //可变数组转不可变数组
(1)arr2.toArray返回结果才是一个不可变数组,arr2本身没有变化
(2)arr1.toBuffer返回结果才是一个可变数组,arr1本身没有变化
Scala 实例
package com.zzg.chapt06
import scala.collection.mutable.ArrayBuffer
object ArrayConvertDemo {
def main(args: Array[String]): Unit = {
// 定义可变数组
var arr1 = new ArrayBuffer[Int]()
// 可变数组赋值
arr1.append(1,2,3,4,5)
// 输出可变数组
println(arr1)
// 可变数组转换为不可变数组
//3.可变数组转换成不可变数组
//3.1 arr2.toArray返回的结果是一个新的定长数组集合
//arr1 没有变化
val newArr = arr1.toArray
println(newArr)
println(newArr.mkString(","))
//4.不可变数组转换成可变数组
//4.1 newArr.toBuffer 返回一个变长数组 newArr2
//4.2 newArr没有任何变化,依然是定长数组
val newArr2 = newArr.toBuffer
println(newArr2)
println(newArr2.mkString(","))
newArr2.append(4, 5)
println(newArr2.mkString(","))
println(newArr) //定长数组
println(newArr2)
}
}
多维数组
多维数组定义语法:
var multiArray = Array.ofDim[数据类型]
Scala 实例
package com.zzg.chapt06
object MultiArrayDemo {
def main(args: Array[String]): Unit = {
// 定义多维数组,创建一个二位数组, 数组中包含一个数组元素,每个数组元素又含有二个值
var multiArray = Array.ofDim[Int](1,2)
// 二位数组赋值, 数组元素下标都是从0 开始,且使用()括号表示。
multiArray(0)(0) = 1
multiArray(0)(1) = 2
// 遍历二位数组
for (i <- multiArray) { // 遍历二维数组为普通数组
for ( j <- i) { // 遍历普通数组内元素
println(" " + j);
}
println("--------------");
}
println(multiArray)
}
}
Seq集合(List)
不可变List
基本说明:
(1)List默认为不可变集合
(2)创建一个List(数据有顺序,可重复)
(3)遍历List
(4)List增加数据
(5)集合间合并:将一个整体拆成一个一个的个体,称为扁平化
(6)取指定数据
(7)空集合Nil
Scala 实例
package com.zzg.chapt06
object ImmutableListDemo {
def main(args: Array[String]): Unit = {
//1.List默认为不可变集合
//2.创建一个List
val list: List[Int] = List(1, 2, 3, 4, 5, 6)
//3.遍历List
list.foreach(println)
println("---------")
//4.List增加数据
//4.1 ::的运算规则是从右向左
val list1 = 7 :: list
println(list1)
println("-------")
val list2 = 9 :: 8 :: list1
println(list2)
println("------------")
//4.2添加到第一个元素位置
val list3 = list.+:(0)
println(list3)
println("--------")
//5.集合间的合并:将一个整体拆成一个一个的个体,成为扁平化
val list4 = List(8, 9)
println(list4)
println("-------")
val list5 = list4 ::: list1
println(list5)
println("------------")
//6.取出指定数据
println(list(2))
}
}
可变ListBuffer
基本说明:
(1)创建一个可变集合ListBuffer
(2)向集合中添加数据
(3)打印集合数据
Scala 实例
package com.zzg.chapt06
import scala.collection.mutable.ListBuffer
object MutableList {
def main(args: Array[String]): Unit = {
// 1.创建一个可变集合
val buffer = ListBuffer(1, 2, 3, 4)
val buffer2 = ListBuffer(1, 2, 3, 4)
println(buffer)
// 2.向集合中添加数据
buffer.+=(5)
println(buffer)
println("----------")
buffer.append(6, 7)
println(buffer)
println("----------")
// 向可变List 指定下标插入指定元素
buffer.insert(0, 0)
println(buffer)
// 可变List 集合尾添加元素
buffer += 8 += 9
println(buffer)
println("----------")
// 可变List 集合头部添加元素
10 +=: buffer
println(buffer)
println("----------")
//3. 遍历可变List 集合元素
buffer.foreach(println)
println("----------")
//4. 更新可变List 指定下标的元素内容
buffer(1) = 11
buffer.update(2, 22)
println(buffer)
println("----------")
//5. 移除可变List 指定内容
println(buffer.-=(5))
println("----------")
// 移除指定下标元素, 获得移除下标的元素内容
val buffer1 = buffer.remove(1)
println(buffer1)
// 可变List集合 添加元素
val buffer3 = buffer ++ buffer2
println(buffer3)
}
}
Set集合
默认情况下,Scala使用的是不可变集合,如果你想使用可变集合,需要引用scala.collection.mutable.Set 包
不可变Set
基本说明:
(1)Set默认是不可变集合,数据无序
(2)数据不可重复
(3)遍历集合
Scala 实例
package com.zzg.chapt06
object ImmutableSetDemo {
def main(args: Array[String]): Unit = {
// (1)Set默认是不可变集合,数据无序
val set = Set(1, 2, 3, 4)
println(set)
// (2)数据不可重复
val set1 = Set(1, 2, 3, 1, 2, 3)
println(set1)
// (3)遍历集合
set.foreach(println)
println("-------------")
for (x <- set) {
println(x)
}
println("-------------")
// (4)集合添加/移除元素
val set2 = set1 + 4
println(set2)
val set3 = set2 - 4 + 6
println(set3)
println("-------------")
// (5)集合之间合并
val set4 = set2 ++ set3
println(set4)
}
}
可变mutable.Set
基本说明:
(1)创建可变集合mutable.Set
(2)打印集合
(3)集合添加元素
(4)向集合中添加元素,返回一个新的Set
(5)删除数据
Scala 实例
package com.zzg.chapt06
import scala.collection.mutable
object MutableSet {
def main(args: Array[String]): Unit = {
//1.创建可变集合
val set = mutable.Set(1, 2, 3, 4)
val set1 = mutable.Set(5, 6)
println(set)
// 2. 遍历可变集合
set.foreach(println)
// 3.集合添加元素
set += 5 += 6
set.add(7)
println(set)
// 添加元素是否成功
val flag1: Boolean = set.add(7) // false
println(flag1)
// 4.向集合添加元素,返回一个新的set
val ints = set.+(8)
println(ints)
println(set)
// 5.删除数据
set -= 5
println(set)
set.remove(4)
println(set)
println("-----------------------")
//合并两个set
val set3 = set ++ set1
println(set3)
println("-----------------------")
set1 ++= set
println(set1)
//set 并集
println(set.union(set1))
println(set.intersect(set1))
}
}
Map集合
Scala中的Map和Java类似,也是一个散列表,它存储的内容也是键值对(key-value)映射
不可变Map
基本说明:
(1)创建不可变集合Map
(2)循环打印
(3)访问数据
(4)如果key不存在,返回0
Scala 实例
package com.zzg.chapt06
object ImmutableMapDemo {
def main(args: Array[String]): Unit = {
// 1.创建不可变集合Map
val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
println(map)
//遍历
map.foreach(println)
map.foreach((kv) => {
println(kv)
})
map.foreach((kv: (String, Int)) => println(kv))
// 遍历键值对
map.keys.foreach{ i =>
print( "Key = " + i )
println(" Value = " + map(i) )}
//访问具体数据
println(map.get("a"))
println(map.get("d"))
//4.如果key不存在,返回0
println(map.get("d").getOrElse(0))
println(map.getOrElse("d", 0))
}
}
可变Map
基本说明:
(1)创建可变集合
(2)打印集合
(3)向集合增加数据
(4)删除数据
(5)修改数据
Scala 实例
package com.zzg.chapt06
import scala.collection.mutable
object MutableMap {
def main(args: Array[String]): Unit = {
//可变集合
val map = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3)
//增加数据
map.+=("d" -> 4)
map.+=(("e", 5))
val map1 = map.+(("f", 6))
println("map:", map)
println("map1:", map1)
println("--------------------")
map.put("g", 7)
println("map:", map)
println("--------------------")
// 修改数据
val map2: Option[Int] = map.put("a", 4)
println(map2.getOrElse(0))
println("map:", map)
//删除数据
map.-=("a", "b")
println(map)
map.remove("c")
println(map)
//修改数据
map.update("d", 44)
println(map)
map("g") = 5
println(map)
}
}
元组
元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组。
温馨提示:元组中最大只能有22个元素。
Scala 实例
(1)声明元组的方式:(元素1,元素2,元素3)
(2)访问元组
(3)Map中的键值对其实就是元组,只不过元组的元素个数为2,称之为对偶
package com.zzg.chapt06
object TupleDemo {
def main(args: Array[String]): Unit = {
//声明元组的方式:(元素1,元素2,元素3)
val tuple: (Int, String, Boolean) = (20, "bobo", true)
//访问元组
//通过元素的顺序进行访问,调用方式:_顺序号, 顺序号从1 开始
println(tuple._1)
println(tuple._2)
//通过索引访问数据, 下标从0 开始
println(tuple.productElement(2))
//通过迭代器访问数据
for (e <- tuple.productIterator) {
println(e)
}
//Map中的键值对其实就是元组,只不过元组的元素个数为2,称之为对偶
val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
val map1 = Map(("a" -> 1), ("b" -> 2))
map.foreach(tuple => {
println(tuple._1 + "=" + tuple._2)
})
map1.foreach(tuple => {
println(tuple._1 + "=" + map1(tuple._1))
})
}
}
集合常用函数
集合基本属性和常用操作
基本说明:
(1)获取集合长度
(2)获取集合大小
(3)循环遍历
(4)迭代器
(5)生成字符串
(6)是否包含
package com.zzg.chapt06
object ListDemo {
def main(args: Array[String]): Unit = {
val list = List(1, 2, 3, 4, 5, 6, 7)
// 1.获取集合长度
println(list.length)
// 2.获取集合大小,等同于length
println(list.size)
// 3.循环遍历
list.foreach(println)
// 4.迭代器
for (elem <- list.iterator) {
println(elem)
}
// 5.生成字符串
println(list.mkString(","))
// 6.是否包含
println(list.contains(3))
}
}
衍生集合
基本说明:
(1)获取集合的头
(2)获取集合的尾(不是头的就是尾)
(3)集合最后一个数据
(4)集合初始数据(不包含最后一个)
(5)反转
(6)取前(后)n个元素
(7)去掉前(后)n个元素
(8)并集
(9)交集
(10)差集
(11)拉链
(12)滑窗
package com.zzg.chapt06
object ListExpandDemo {
def main(args: Array[String]): Unit = {
val list1 = List(1, 2, 3, 4, 5, 6, 7)
val list2 = List(4, 5, 6, 7, 8, 9, 10)
// 1.获取集合的头
println(list1.head)
// 2.集合最后一个数据
println(list1.last)
// 3.集合的尾
println(list1.tail)
// 4.集合的初始数据
println(list1.init)
// 5.反转
println(list1.reverse)
// 6.取前、后几个元素
println(list1.take(3))
println(list1.takeRight(3))
// 7.去掉前、后几个 元素
println(list1.drop(3))
println(list1.dropRight(3))
// 8.并集
println(list1.union(list2))
// 9.差集
println(list1.diff(list2))
// 10.交集
println(list1.intersect(list2))
// 11.拉链 注:如果两个集合的元素个数不相等,那么会将同等数量的数据进行拉链,多余的数据省略不用
println(list1.zip(list2))
// 12.滑窗
list1.sliding(2, 5).foreach(println)
}
}
集合计算初级函数
基本说明:
(1)求和
(2)求乘积
(3)最大值
(4)最小值
(5)排序
package com.zzg.chapt06
object ListComputerDemo {
def main(args: Array[String]): Unit = {
val list: List[Int] = List(1, 5, -3, 4, 2, -7, 6)
// 1.求和
println(list.sum)
// 2.求乘积
println(list.product)
// 3.最大值
println(list.max)
// 4.最小值
println(list.min)
// 5.排序
// 5.1按照元素大小排序
println(list.sortBy(x => x))
// 5.2按照元素的绝对值大小排序
println(list.sortBy(x => x.abs))
// 5.3按照元素大小升序排序
println(list.sortWith((x, y) => x < y))
// 5.4按元素大小降序排序
println(list.sortWith((x, y) => x > y))
}
}
知识拓展:
(1)sorted
对一个集合进行自然排序,通过传递隐式的Ordering
(2)sortBy
对一个属性或多个属性进行排序,通过它的类型。
(3)sortWith
基于函数的排序,通过一个comparator函数,实现自定义排序的逻辑。
集合计算高级函数
基本说明:
(1)过滤
遍历一个集合并从中获取满足指定条件的元素组成一个新的集合
(2)转换/映射
遍历一个集合元素并通过自定义函数返回的元素组成一个新的集合
(3)扁平化
多个集合元素组成一个新的集合
(4)扁平化+映射 注:flatMap相当于先进行map操作,在进行flatten操作
(5)分组(group)
按照指定的规则对集合的元素进行分组
package com.zzg.chapt06
object ListAdvancedComputerDemo {
def main(args: Array[String]): Unit = {
// 集合初始化
val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
val nestedList = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
val wordList = List("Hello world", "Hello atguigu", "hello scala")
// 过滤
var filterList = list.filter(x => x % 2 == 0)
filterList.foreach(println)
// 2.转换映射
var convertList = list.map(x => x + 1)
convertList.foreach(println)
// 3.扁平化
println(nestedList.flatten)
// 4.扁平化+映射 注:flatMap相当于先进行map操作,再进行flatten操作
println(wordList.flatMap(x => x.split(" ")))
// 5.分组
println(list.groupBy(x=>x%2))
}
}
(6)Reduce方法
Reduce简化(归约) :通过指定的逻辑将集合中的数据进行聚合,从而减少数据,最终获取结果。
Scala 实例
package com.zzg.chapt06
object ReduceDemo {
def main(args: Array[String]): Unit = {
// 集合声明并初始化
val list = List(1, 2, 3, 4, 5, 6)
// 1. 将数据两两结合,实现运算规则
val i: Int = list.reduce((x, y) => x - y)
// 1-2-3-4-5-6
println("i=" + i)
// 2.从源码的角度,reduce底层调用的其实就是reduceLeft
val i1: Int = list.reduceLeft((x, y) => x - y)
println(i1)
// ((6-5)-(4-3)-2-1)
val i2: Int = list.reduceRight((x, y) => x - y)
println(i2)
}
}
(7)Fold方法
Fold折叠:化简的一种特殊情况。
Scala 实例
package com.zzg.chapt06
import scala.collection.mutable
object FoldDemo {
def main(args: Array[String]): Unit = {
// 集合声明和初始化
val list = List(1, 2, 3, 4)
/*// fold方法使用了函数柯里化,存在两个参数列表
// 第一个参数列表为 : 零值(初始值)
// 第二个参数列表为: 简化规则
* */
// fold底层其实为foldLeft
// 1-1-2-3-4
val i = list.foldLeft(1)((x, y) => x - y)
println(i)
// 2-1-2-3-4
val i2 = list.foldLeft(2)((x, y) => x - y)
println(i2)
// 10-(4-3-2-1)
val i3 = list.foldRight(10)((x, y) => x - y)
println(i3)
// Map 声明和初始化
val map1 = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3)
val map2 = mutable.Map("a" -> 4, "b" -> 5, "d" -> 6)
val map3: mutable.Map[String, Int] = map2.foldLeft(map1) {
(map, kv) => {
val k = kv._1
val v = kv._2
println(k)
println(v)
println("-------------")
map(k) = map.getOrElse(k, 0) + v
map
}
}
println(map3)
}
}
大数据之普通WordCount案例
1)需求
单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果
package com.zzg.chapt06
object HadoopWordCountDemo {
def main(args: Array[String]): Unit = {
// 单词计数:将集合中出现的相同的单词,进行 计数,取计数排名前三的结果
val stringList = List("Hello Scala Hbase kafka", "Hello Scala Hbase", "Hello Scala", "Hello")
// 第一步:字符串通过空格转换为一个个的单词
var wordList:List[String] = stringList.flatMap(str => str.split(" "))
// 第二步: 分组
var wordMap:Map[String, List[String]]= wordList.groupBy(item => item)
// 第三步: 基于分组结果,进行统计
var wordCount:Map[String, Int] = wordMap.map(item=>(item._1,item._2.size))
// 第四步: 对统计结果进行排序(Map 转List 再进行sortWith )
val wordSort: List[(String, Int)] = wordCount.toList.sortWith{(obj1, obj2)=>{
obj1._2 > obj2._2
}}
// 第五步:取值,前三位
val wordResult = wordSort.take(3)
// 第六步:迭代输出
for (elem <- wordResult.iterator) {
println(elem._1 +" = " + elem._2)
}
}
}
控制台输出:
D:\Java\jdk1.8.0_121\bin\java.exe -Dvisualvm.id=538265106144499 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.1\lib\idea_rt.jar=59129:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.1\bin" -Dfile.encoding=UTF-8 -classpath D:\java\jdk1.8.0_121\jre\lib\charsets.jar;D:\java\jdk1.8.0_121\jre\lib\deploy.jar;D:\java\jdk1.8.0_121\jre\lib\ext\access-bridge-64.jar;D:\java\jdk1.8.0_121\jre\lib\ext\cldrdata.jar;D:\java\jdk1.8.0_121\jre\lib\ext\dnsns.jar;D:\java\jdk1.8.0_121\jre\lib\ext\jaccess.jar;D:\java\jdk1.8.0_121\jre\lib\ext\jfxrt.jar;D:\java\jdk1.8.0_121\jre\lib\ext\localedata.jar;D:\java\jdk1.8.0_121\jre\lib\ext\nashorn.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunec.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunjce_provider.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunmscapi.jar;D:\java\jdk1.8.0_121\jre\lib\ext\sunpkcs11.jar;D:\java\jdk1.8.0_121\jre\lib\ext\zipfs.jar;D:\java\jdk1.8.0_121\jre\lib\javaws.jar;D:\java\jdk1.8.0_121\jre\lib\jce.jar;D:\java\jdk1.8.0_121\jre\lib\jfr.jar;D:\java\jdk1.8.0_121\jre\lib\jfxswt.jar;D:\java\jdk1.8.0_121\jre\lib\jsse.jar;D:\java\jdk1.8.0_121\jre\lib\management-agent.jar;D:\java\jdk1.8.0_121\jre\lib\plugin.jar;D:\java\jdk1.8.0_121\jre\lib\resources.jar;D:\java\jdk1.8.0_121\jre\lib\rt.jar;E:\scala_workspace\ScalaDemo\out\production\ScalaDemo;D:\scala\lib\scala-library.jar;D:\scala\lib\scala-reflect.jar com.zzg.chapt06.HadoopWordCountDemo
Hello = 4
Scala = 3
Hbase = 2
大数据之复杂WordCount案例
package com.zzg.chapt06
import scala.collection.mutable.ListBuffer
object HadoopComplexWroddCountDemo {
def main(args: Array[String]): Unit = {
// 定义元组,并初始化
val tuples = List(("Hello Scala Spark World", 4), ("Hello Scala Spark", 3), ("Hello Scala", 2), ("Hello", 1))
// 期望结果集
// (Hello,4), (Scala,4), (Spark,4), (World,4)
// (Hello,3), (Scala,3), (Spark,3)
// (Hello,2), (Scala,2)
// (Hello,1)
// 第一步:遍历元组,字符串通过空格转换为一个个的单词,并赋值统计次数,构成元组
val wordToCountList: List[(String, Int)] = tuples.flatMap {
t => {
val strings: Array[String] = t._1.split(" ")
strings.map(word => (word, t._2))
}
}
println(wordToCountList)
// 第二步: 分组
var wordMap:Map[String, List[(String, Int)]]= wordToCountList.groupBy(tuple => tuple._1)
println(wordMap)
// 遍历统计总和
val wordTotal:ListBuffer[(String, Int)] = ListBuffer[(String, Int)]()
wordMap.foreach(item =>{
println(item._1 + "=" + item._2)
var sum = 0;
item._2.foreach(obj =>{
println(obj._2)
sum += obj._2
})
wordTotal.append((item._1, sum))
})
println(wordTotal)
// 优化方法 (推荐使用)
val wordTotals = wordMap.map(tuple =>{
(tuple._1, tuple._2.map(t1 => t1._2))
})
val wordToTotalCountMap: Map[String, Int] = wordTotals.map(t => (t._1, t._2.sum))
println(wordTotals)
println(wordToTotalCountMap)
// 排序
var wordSort = wordTotal.sortWith{(obj1, obj2)=>{
obj1._2 > obj2._2
}}
// 第五步:取值,前三位
val wordResult = wordSort.take(3)
// 第六步:迭代输出
for (elem <- wordResult.iterator) {
println(elem._1 +" = " + elem._2)
}
// 定义元组,并初始化
val tupleList = List(("Hello Scala Spark Word", 4), ("Hello Scala Spark", 3), ("Hello Scala", 2), ("Hello", 1))
// 期望结果集
// Hello Scala Spark Word Hello Scala Spark Word Hello Scala Spark Word Hello Scala Spark Word
// Hello Scala Spark Hello Scala Spark Hello Scala Spark
// Hello Scala Hello Scala
// Hello
val stringList: List[String] = tupleList.map(t => (t._1 + " ") * t._2)
println(stringList)
// 剩下的操作就是简单单词数量统计步骤
// ***
}
}
队列
基本说明:
Scala也提供了队列(Queue)的数据结构,队列的特点就是先进先出。进队和出队的方法分别为enqueue和dequeue。
Scala 实例
package com.zzg.chapt06
import scala.collection.mutable
object QueueDemo {
def main(args: Array[String]): Unit = {
val que = new mutable.Queue[String]()
que.enqueue("a", "b", "c")
println(que.dequeue())
println(que.dequeue())
println(que.dequeue())
}
}
并行集合
基本说明:
Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。
第八节:Scala 模式匹配
Scala中的模式匹配类似于Java中的switch语法
Java示例:
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;
}
基本语法
模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有case都不匹配,那么会执行case _分支,类似于Java中default语句。
Scala 实例
package com.zzg.chapt07
object MatchCaseDemo {
def main(args: Array[String]): Unit = {
var a: Int = 10
var b: Int = 20
var operator: Char = 'd'
var result = operator match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case _ => "illegal"
}
println(result)
}
}
温馨提示:
(1)如果所有case都不匹配,那么会执行case _ 分支,类似于Java中default语句,若此时没有case _ 分支,那么会抛出MatchError。
(2)每个case中,不需要使用break语句,自动中断case。
(3)match case语句可以匹配任何类型,而不只是字面量。
(4)=> 后面的代码块,直到下一个case语句之前的代码是作为一个整体执行,可以使用{}括起来,也可以不括。
模式守卫
基本说明:
如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。
Scala 实例
package com.zzg.chapt07
object MatchGuardDemo {
def main(args: Array[String]): Unit = {
def abs(x: Int) = x match {
case i: Int if i >= 0 => i
case j: Int if j < 0 => -j
case _ => "type illegal"
}
println(abs(-10))
}
}
模式匹配类型
匹配常量
基本说明:
Scala中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。
package com.zzg.chapt07
object MatchConstantDemo {
def main(args: Array[String]): Unit = {
println(describe(5))
def describe(x: Any) = x match {
case 5 => "Int five"
case "hello" => "String hello"
case true => "Boolean true"
case '+' => "Char +"
}
}
}
匹配类型
基本说明
需要进行类型判断时,可以使用前文所学的isInstanceOf[T]和asInstanceOf[T],也可使用模式匹配实现同样的功能。
Scala 实例
package com.zzg.chapt07
object MatchTypeDemo {
def main(args: Array[String]): Unit = {
/**
* 判断传递参数类型
* @param x
* @return
*/
def describe(x: Any) = x match {
case i: Int => "Int"
case s: String => "String hello"
case m: List[_] => "List"
case c: Array[Int] => "Array[Int]"
case someThing => "something else" + someThing
}
// 泛型擦除
println(describe(List(1, 2, 3, 4)))
// 数组例外,可保留泛型
println(describe(Array(1, 2, 3, 4, 5, 6)))
println(describe(Array("abc")))
}
}
匹配数组
基本说明:
scala模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素为0的数组。
package com.zzg.chapt07
object MatchArrayDemo {
def main(args: Array[String]): Unit = {
for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) { //对一个数据集合进行遍历
val result = arr match {
case Array(0) => "0" // 匹配Array(0) 这个数组
case Array(x, y) => x + "," + y // 配置有两个元素的数组,然后将元素赋值给对应的x,y
case Array(0, _*) => "以0开头的数组" // 匹配以0开头的数组
case _ => "something else"
}
println("result=" + result)
}
}
}
匹配列表
Scala 实例
package com.zzg.chapt07
object MatchListDemo {
def main(args: Array[String]): Unit = {
// list是一个存放List集合的数组
// 请思考,如果要匹配List(88) 这样的只含有一个元素的列表,并原值返回,应该怎么写。
for (list <- Array(List(0), List(0, 0, 0), List(1, 0, 0), List(88))) {
val result = list match {
case List(0) => "0" // 匹配List(0)
case List(x, y) => x + "," + y // 匹配有两个元素的List
case List(0, _*) => "0 ..."
case _ => "something else"
}
println(result)
}
}
}
匹配元组
Scala 实例
package com.zzg.chapt07
object MatchTupleDemo {
def main(args: Array[String]): Unit = {
// 对一个元组集合进行遍历
for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) {
val result = tuple match {
case (0, _) => "0 ..." // 是第一个元素是0的元组
case (y, 0) => "" + y + " 0" // 匹配后一个元素是0的对偶元组
case (a, b) => "" + a + "" + b
case _ => "something else" //默认
}
println(result)
}
}
}
匹配数组元组、集合元组
Scala 实例
package com.zzg.chapt07
object GenericDemo {
def main(args: Array[String]): Unit = {
// 特殊的模式匹配-1: 数组元组匹配
for (elem <- Array(("a", 1), ("b", 2), ("c", 3))) {
println(elem._1)
println(elem._2)
}
println("------------------------------")
for ((word, count) <- Array(("a", 1), ("b", 2), ("c", 3))) {
println(word)
println(count)
}
println("------------------------------")
for ((word, _) <- Array(("a", 1), ("b", 2), ("c", 3))) {
println(word)
}
println("------------------------------")
for (("a", count) <- Array(("a", 1), ("b", 2), ("c", 3))) {
println(count)
}
println("-------------")
// 特殊的模式匹配-2: 给元组元素命名
var (id, name, age): (Int, String, Int) = (100, "zzg", 31)
println((id, name, age))
println("--------------")
// 特殊的模式匹配-3,遍历集合中的元组,给count * 2
var list: List[(String, Int)] = List(("a", 1), ("b", 2), ("c", 3))
var traverseList:List[(String, Int)] = list.map {
case (word, count) => (word, count * 2)
}
println(traverseList)
println("--------------")
val list1 = List(("a", ("a", 1)), ("b", ("b", 2)), ("c", ("c", 3)))
traverseList = list1.map {
case (groupkey, (word, count)) => (word, count * 2)
}
println(traverseList)
}
}
匹配对象及样例类
Scala 实例
package com.zzg.chapt07
object MatchObjectDemo {
/**
* 定义User 类
* @param name
* @param age
*/
class User(val name: String, val age: Int)
/**
* 定义User 样例类
*/
object User{
def apply(name: String, age: Int): User = new User(name, age)
def unapply(user: User): Option[(String, Int)] = {
if (user == null)
None
else
Some(user.name, user.age)
}
}
def main(args: Array[String]): Unit = {
// 定义User 类实例化对象
val user: User = User("zzg", 31)
// 对象匹配
val result = user match {
case User("zzg", 31) => "yes"
case _ => "no"
}
// 匹配结果输出
println(result)
}
}
总结:
Ø val user = User(“zzg”,11),该语句在执行时,实际调用的是User伴生对象中的apply方法,因此不用new关键字就能构造出相应的对象。
Ø 当将User(“zzg”, 31)写在case后时[case User(“zzg”, 31) => “yes”],会默认调用unapply方法(对象提取器),user作为unapply方法的参数,unapply方法将user对象的name和age属性提取出来,与User(“zzg”, 31)中的属性值进行匹配
Ø case中对象的unapply方法(提取器)返回Some,且所有属性均一致,才算匹配成功,属性不一致,或返回None,则匹配失败。
Ø 若只提取对象的一个属性,则提取器为unapply(obj:Obj):Option[T]
若提取对象的多个属性,则提取器为unapply(obj:Obj):Option[(T1,T2,T3…)]
若提取对象的可变个属性,则提取器为unapplySeq(obj:Obj):Option[Seq[T]]
样例类
基本语法:
case class User (name: String, age: Int)
(2)基本说明
1.样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如apply、unapply、toString、equals、hashCode和copy。
2.样例类是为模式匹配而优化的类,因为其默认提供了unapply方法,因此,样例类可以直接使用模式匹配,而无需自己实现unapply方法。
3构造器中的每一个参数都成为val,除非它被显式地声明为var(不建议这样做)
匹配对象优化
package com.zzg.chapt07
object MatchObjectOptimizationDemo {
case class User02(name: String, age: Int)
def main(args: Array[String]): Unit = {
val user: User02 = User02("zhangsan", 11)
val result: String = user match {
case User02("zhangsan", 11) => "yes"
case _ => "no"
}
println(result)
}
}
变量声明中的模式匹配
package com.zzg.chapt07
object MatchVariableDemo {
case class Person(value:String, value1: Int)
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 Person(name, age) = Person("zhangsan", 16)
println(s"name=$name,age=$age")
}
}
for表达式中的模式匹配
package com.zzg.chapt07
object MatchForExpressionDemo {
def main(args: Array[String]): Unit = {
val map = Map("A" -> 1, "B" -> 0, "C" -> 3)
for ((k, v) <- map) { // 直接将map中的k-v遍历出来
println(k + "->" + v)
}
println("---------")
// 遍历value = 0 的k-v,如果v不是0,过滤
for ((k, 0) <- map) {
println(k + "-->" + 0)
}
println("----------")
// 遍历value >=1 的k-v,否则过滤
for ((k, v) <- map if v >= 1) {
println(k + "-->" + v)
}
}
}
第九节: Scala 异常
语法处理上和Java类似,但是又不尽相同。
public class ExceptionDemo {
public static void main(String[] args) {
try {
int a = 10;
int b = 0;
int c = a / b;
} catch (ArithmeticException e) {
//catch时,需要将范围缩小的写到前面
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("finally");
}
}
}
温馨提示:
(1)Java语言按照try—catch—finally的方式来处理异常
(2)不管有没有异常捕获,都会执行finally,因此通常可以在finally代码块中释放资源。
(3)可以有多个catch,分别捕获对应的异常,这时需要把范围小的异常类写在前面,把范围大的异常类写在后面,否则编译错误。
Scala异常处理
package com.zzg.chap08
object ExceptionDemo {
def main(args: Array[String]): Unit = {
try {
val a = 1 / 0
} catch {
case e: ArithmeticException => {
println("算术异常")
} //算术异常
case e: Exception => {
println("其他异常")
}
} finally {
println("程序运行结束")
}
}
}
1)我们将可疑代码封装在try块中。在try块之后使用了一个catch处理程序来捕获异常。如果发生任何异常,catch处理程序将处理它,程序将不会异常终止。
2)Scala的异常的工作机制和Java一样,但是Scala没有“checked(编译期)”异常,即Scala没有编译异常这个概念,异常都是在运行的时候捕获处理。
3)异常捕捉的机制与其他语言中一样,如果有异常发生,catch子句是按次序捕捉的。因此,在catch子句中,越具体的异常越要靠前,越普遍的异常越靠后,如果把越普遍的异常写在前,把具体的异常写在后,在Scala中也不会报错,但这样是非常不好的编程风格。
4)finally子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作,这点和Java一样。
5)用throw关键字,抛出一个异常对象。所有异常都是Throwable的子类型。throw表达式是有类型的,就是Nothing,因为Nothing是所有类型的子类型,所以throw表达式可以用在需要类型的地方
def sum(x:Int, y:Int):Int ={
var target: Boolean = x isInstanceOf Int
if(!target){
throw new Exception("传递参数异常")
}
target = y isInstanceOf Int
if(!target){
throw new Exception("传递参数异常")
}
return x + y
}
6)java提供了throws关键字来声明异常。可以使用方法定义声明异常。它向调用者函数提供了此方法可能引发此异常的信息。它有助于调用函数处理并将该代码包含在try-catch块中,以避免程序异常终止。在Scala中,可以使用throws注解来声明异常
@throws(classOf[NumberFormatException])
def sum(x:Int, y:Int):Int ={
return x + y
}
第十节: Scala 隐式转换
当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译
隐式函数
基本说明:
隐式转换可以在不需改任何代码的情况下,扩展某个类的功能。
Scala 实例
功能说明:通过隐式转化为Int类型增加方法。
package com.zzg.chapt09
/**
* 1.定义RichText类
* @param self
*/
class RichText(var self : Int){
def getMax(i :Int):Int ={
if (self < i) i else self
}
def getMin(i: Int):Int ={
if (self < i) self else i
}
}
object ImplicitFunctionDemo {
// 2.使用implicit关键字声明的函数称之为隐式函数
implicit def convert(i:Int) :RichText ={
new RichText(i)
}
def main(args: Array[String]): Unit = {
//当想调用对象功能时,如果编译错误,那么编译器会尝试在当前作用域范围内查找能调用对应功能的转换规则,这个调用过程是由编译器完成的,所以称之为隐式转换。也称之为自动转换
println(2.getMax(6))
}
}
隐式参数
普通方法或者函数中的参数可以通过implicit关键字声明为隐式参数,调用该方法时,就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。
基本说明:
(1)同一个作用域中,相同类型的隐式值只能有一个
(2)编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。
(3)隐式参数优先于默认参数
Scala 实例
package com.zzg.chapt09
object ImplicitParameterDemo {
// 1.使用implicit关键字声明的变量称之为隐式变量
implicit var str:String="Hello Python"
// 2.使用implicit 关键字声明函数模式值称为隐式函数默认值
def say(implicit message:String ="Hello Scala"):Unit={
println(message)
}
def main(args: Array[String]): Unit = {
say
}
}
隐式类
在Scala2.10后提供了隐式类,可以使用implicit声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用。
隐式类说明
(1)其所带的构造参数有且只能有一个
(2)隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是*的。
Scala 实例
package com.zzg.chapt09
object ImplicitClassDemo {
implicit class RichInt(arg: Int) {
def getMax(i: Int): Int = {
if (arg < i) i else arg
}
def getMin(i: Int): Int = {
if (arg < i) arg else i
}
}
def main(args: Array[String]): Unit = {
println(2.getMin(10))
}
}
隐式解析机制
基本说明:
(1)首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)。(一般是这种情况)
(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生对象以及该类型所在包的包对象。
Scala 实例
package com.zzg.chapt09
import com.zzg.chapt09.TestTransform.Teacher
/**
* 第三步: TestTransform 集成PersonTrait 拓展
* 温馨提示:如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联全部伴生模块
*/
object TestTransform extends PersonTrait{
def main(args: Array[String]): Unit = {
var teacher = new Teacher()
teacher.eat()
teacher.say()
}
/**
* 第一步:定义Teacher 类
*/
class Teacher {
def eat() = {
println("eat...")
}
}
}
/***
* 第二步:定义拓展接口
*/
trait PersonTrait {
}
/**
* 第四步:定义隐式类
*/
object PersonTrait {
//隐式类:类型1=>类型2
implicit class Person5(user: Teacher) {
def say(): Unit = {
println("say...")
}
}
}
第十一节: Scala 泛型
协变和逆变
基本语法:
class MyList[+T]{ //协变
}
class MyList[-T]{ //逆变
}
class MyList[T] //不变
基本说明:
协变:Son是Father的子类,则MyList[Son] 也作为MyList[Father]的“子类”。
逆变:Son是Father的子类,则MyList[Son]作为MyList[Father]的“父类”。
不变:Son是Father的子类,则MyList[Father]与MyList[Son]“无父子关系”。
Scala 实例
package com.zzg.chapte0
class Parent{}
class Child extends Parent{}
class SubChild extends Child{}
object Scala_TestGeneric {
def main(args: Array[String]): Unit = {
val s = new MyList[SubChild]
}
}
//泛型模板
//class MyList<T>{}
//不变
class MyList[T]{}
//协变
//class MyList[+T]{}
//逆变
//class MyList[-T]{}
泛型上下限
基本语法:
Class PersonList[T <: Person]{ //泛型上限
}
Class PersonList[T >: Person]{ //泛型下限
}
基本说明
泛型的上下限的作用是对传入的泛型进行限定。
package com.zzg.chapt10
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}
object Scala_TestGeneric {
def main(args: Array[String]): Unit = {
}
// 泛型通配符上限
def test[A >: Child](a: Class[A]): Unit = {
println(a)
}
// 泛型通配符之下限
def test[A >: Child](a: Class[A]): Unit = {
println(a)
}
//泛型通配符之下限 形式扩展
def test[A >: Child](a: A): Unit = {
println(a.getClass.getName)
}
}
上下文限定
基本语法:
def f[A : B](a: A) = println(a) //等同于def f[A](a:A)(implicit arg:B[A])=println(a)
基本说明
上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过**implicitly[Ordering[A]]**获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误。
implicit val x = 1
val y = implicitly[Int]
val z = implicitly[Double]
Scala 实例
def f[A:Ordering](a:A,b:A) =implicitly[Ordering[A]].compare(a,b)
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)