实训笔记9.12
Scala笔记
一、学习Scala的目的
Spark、Kafka等相关大数据技术框架底层都是由Scala编程语言编写的,Spark我们自己编写分布式程序时,Spark提供了多种编程语言风格,但是我们比较常用的是使用Scala编程。
二、Scala的基本概念
Scala是一门多范式编程语法,所谓多范式指的就是多种编程风格的语法,Scala主要是一门面向对象编程语言和函数式编程语言。
Scala的发明人是马丁奥德斯基,Scala语言2001年左右诞生的,融合了Java和JS中很多特性。
同时Java中很多特性也是从Scala语言中吸收到,JDK8.0版本拉姆达表达式就是从Scala中吸收到
2.1 JDK1.8版本的新特性
-
Java中函数编程式接口、拉姆达表达式、方法引用 接口的组成、时间日期类、Stream API、Optional类(解决NullPonitException的)
-
Java中的拉姆达(λ)表达式是和Java中的一个接口紧密相关的,接口函数式编程接口(接口中只有一个抽象方法)
语法:(形参列表)-> {方法体;}
package lambda; import java.io.PrintStream; public class Demo { public static void test(Flyable flyable){ flyable.fly("zs"); } public static void test1(A a){ } /** * 使用函数式编程接口时,如果我们采用匿名内部类的形式,必须要去重写唯一的抽象方法,而且匿名内部类最核心的也是抽象方法 * 所以此时我们就是使用拉姆达表达式将匿名内部类的代码给简化了即可 * * (形参列表) -> {方法体} 就是抽象方法的简化 * @param args */ public static void main(String[] args) { test(System.out::println); test(name-> System.out.println(name)); } } interface A{ default void run(String name){} default int call(){return 0;} } package lambda; @FunctionalInterface public interface Flyable { void fly(String name); static int run(){ return 0;} }
拉姆达表达式的简化:
- 形参列表的类型可以省略的,因为拉姆达表达式对应的函数式编程接口的抽象方法是确定的
- 如果参数列表只有一个 那么形参列表的()可以省略的
- 如果方法体只有一行代码,而且这一行代码没有return关键字,那么方法体的{}可以省略,而且这一行代码不需要加分号
-
Java中的方法引用相当于是拉姆达表达式的升级版本,主要就是用来替换整个拉姆达表达式的,当拉姆达表达式的方法体是引用了另外一个类的方法,并且方法体中没有多余的代码时,可以使用方法引用来简化代码
2.2 Scala的运行机制
- 编写xxx.scala源码文件,源码文件中可以使用Scala的SDK也可以使用Java的SDK
- 使用scalac编译xxx.scala源码文件成为Java的二进制字节码文件xxx.class
- 使用scala命令将xxx.class加载到Java的JVM虚拟机当中运行的
三、Scala的基本语法
3.1 Scala中输出语句、键盘输入、注释语法
3.1.1 Scala注释三种,和Java一模一样的
3.1.2 Scala键盘输入
- 直接无缝衔接使用Java的键盘输入
Scanner
- 使用Scala提供的自带的键盘输入
StdIn.readxxx()
3.1.3 Scala输出
- 使用Java的输出语句
System.out.xxx
- 普通输出:
print()/println()
- 模板输出:
print/println(s"xxxxx$变量名")
- 占位符输出:
print/println("xxxxx%s %d",变量,变量)
3.2 Scala变量和常量
语法: var|val 变量名|常量名【:数据类型】 = 初始值;
3.3 Scala中标识符
- Scala的标识符由字母、数字、下划线、数学符号、$美元符号组成的,其中数字不能开头
- 如果标识符以数学符号开头,那么标识符中只能包含数学符号
- Scala标识符可以是关键字和保留字,但是关键字和保留字需要使用``包括
3.4 Scala中的数据类型
Scala是一门纯面向对象的编程语言,因此在Scala中所有的数据类型都是对象
Scala中所有类型的顶尖父类:Any,Any有两个直接子类:AnyVal、AnyRef
AnyVal是值类型:Byte、Short、Int、Long、Float、Double、Char、Boolean、Unit
AnyRef是引用类型:Java中所有类、Scala中所有类、Scala中所有集合、Null
Unit、Null、Nothing三个比较特殊的类型
3.5 Scala中运算符
3.5.1 算术运算符
没有++ –
3.5.2 赋值运算符
+= -=…
3.5.3 比较运算符
Scala中==代表比较值相等,比较地址相等用eq函数
3.5.4 逻辑运算符
3.5.5 位运算符
3.6 Scala中流程控制
顺序流程:代码遵循从上而下依次执行
3.6.1 分支流程
-
if类型的分支:Java一模一样的
-
模式匹配
-
语法:
x match{ case 值|x [模式守卫if] => case分支语句 case 值|x [模式守卫if] => case分支语句 case _ => case分支语句 }
-
【模式守卫】模式守卫可以做范围匹配,一般使用模式守卫时,case需要x
-
3.6.2 循环流程
-
for循环
-
until型的for循环:
for(i <- start until end)
-
to型的for循环:
for(i <- start to end)
-
增强的for循环——遍历集合或者数组:
for(elem <- 集合/数组的变量)
-
for循环的步长(迭代,默认情况下迭代+1):
for(i <- start until|to end by num)
-
循环守卫(满足某个条件再执行循环体):
for(i <- start until|to end 【by num】 if 语句)
-
多重循环:
for(i <- start until|to end by num 循环守卫 ;j<- start until|to end by num 循环守卫)
-
循环的返回值问题(将循环的值赋予给一个Scala集合):
var array = for(i <- start until|to end by num 循环守卫) yield i
-
-
while循环
-
do while循环
四、Scala的函数式编程
4.1 函数的基本语法
def 函数名(形参列表):函数的返回值类型={ 函数体 }
4.2 函数的形参列表问题
-
可变长形参 参数名:数据类型*
,一个函数只能有一个可变长形参,而且形参必须位于函数形参列表的末尾 -
形参的默认值,Scala函数当中,形参是可以赋予默认值的,一旦形参赋予默认值,那么调用参数的时候,带有默认值的形参就可以不用传递参数了,带有默认值的形参一般要求放到形参列表的最后,如果没有放到最后,那么调用的时候,给其他形参传递参数,需要带有参数名传递
def test(name:String=zs,age:int){}
test(age=1)--具名实参
4.3 函数的返回值问题
4.4 函数的高阶函数
4.4.1 函数当变量使用:把函数重命名了
var d:函数的定义 = 函数名 _
示例:
函数的定义:
def test(a:Int,b:Int):Int={a+b}
函数的类型写法:
(Int,Int) => Int
4.4.2 函数当参数来使用
语法:
def test(a:Int,f:(Int,Int)=>Int):Unit={ }
4.4.3 函数当作返回值来使用
语法:
def test():(Int,Int)=>Int={ }
4.5 函数的高级使用
4.5.1 函数的闭包问题
函数闭包指的是将不属于本函数的变量或者对象也包含进来,直到该函数运行完成,外部变量或者对象才可以被释放。
var x:Int = 1 def test(a:Int):Int={ a*x }
4.5.2 函数的柯里化
将一个接受多个参数的函数给他转换成为一个接受单个参数的函数的过程
将一个接受多个参数的函数转换成为一个返回了函数的函数,返回的函数传递的值就是原先的第二个参数
其实闭包的一个使用场景
4.5.3 递归函数
-
函数内部调用本函数
-
递归三要素
- 递归入口:自己调用自己的的逻辑
- 递归出口:不能调用自己的逻辑
- 递归条件必须向出口迭代
4.5.4 函数的惰性加载
惰性加载指的是将函数的调用延迟到第一次使用函数的返回值的时候才会调用
使用语法: lazy val 变量名 = 函数名(实参)
此时函数的调用在第一次使用变量的时候才会调用 一旦惰性加载,变量名只能使用val修饰
4.6 函数的简化操作
4.6.1 声明的简化
-
如果函数没有参数,那么函数的括号可以省略
def test:Unit={}
-
函数的返回值可以省略的,可以根据函数体的最后一行自动推断,
-
函数体中,函数的返回值前的return关键字可以省略的,自动根据最后一行推断函数的返回值
-
如果函数的返回值类型是 Unit类型 那么 =号和函数 的返回值都可以省略
def test{}
-
匿名函数
- 定义:定义函数时,只关注函数的逻辑,不关注函数的名字,此时我们就可以使用匿名函数来定义函数:
(形参列表) => {函数体}
- 使用场景:当函数当作参数或者当作返回值使用的时候,可以使用匿名函数传递
- 简化
- 匿名函数的形参列表的类型可以省略的,因为当作参数或者返回值使用的时候,参数的类型定义死了
- 如果匿名函数的函数体只有一个,那么 {} 可以省略了
- 如果形参的参数名在函数体中只出现了一次,那么参数名就可以使用 _ 替代,同时形参列表可以省略了
- 定义:定义函数时,只关注函数的逻辑,不关注函数的名字,此时我们就可以使用匿名函数来定义函数:
4.6.2 调用的简化:一般不建议使用,建议运算符的函数调用简化
调用的语法:对象名|类名.函数名(实参列表)
- 调用的点.可以省略的,
对象名|类名 函数名(实参列表)
- 如果实参列表为空,那么 () 可以省略,如果声明函数的时候没有加 () 那么调用的时候一定不能加 ()
- 如果函数的实参列表只有一个 那么 () 也可以省略
对象名|类名 函数名 唯一的实参
五、 Scala中面向对象
Scala源于Java中,因此在Scala中也存在面向对象编程思想,面向对象编程最核心的逻辑就是以类来组织代码,以对象来调用代码。Scala的面向对象和Java基本上思维是一致的,只不过就是语法稍微不一样而已。
5.1 包和import导入
包package:包是用来分类管理Scala代码的,将不同类型的代码放到不同的包下,便于我们管理
5.1.1 Scala包有两种管理代码的方式
-
采用和Java一样的管理机制,新建包,包下可以新建类和子包
-
采用包对象的管理机制,实现一个文件中存在多个包和多个scala类
5.1.2 Scala中类的导入问题import
在当前Scala类中,如果要使用非本包下的代码,那么我们就得需要通过import关键字导入才能使用。
-
每一个Scala类默认导入三个包
java.lang.
_scala._
scala.Predef._
-
Scala类中导入有一些规则和Java有点不一样
-
Scala可以在任何位置进行导包操作,代码只能在导包位置之后使用 我们可以把所有的包放到package之后 class之前
-
如果我们要导入一个包下的所有代码,那么可以使用_当作通配符使用
-
我们现在只想导入某一个包下的两个类,而非所有类
import xxxx.{x1,x2}
-
导包重命名操作:可以将一个类重命名为另外一个名字在当前类中使用
import xxxx.{x1=>x2}
x1类在当前类中可以使用x2名字来替代 -
屏蔽某一个包下的部分类:导入一个包下的所有代码,除了某几个类之外
import xxxx{x1=>_,x2}
导入xxxx包下的x2类,不导入x1这个类
-
5.2 面向对象中的类
5.2.1 类的定义
访问控制修饰符 class 类名(主构造器){ 类体 }
5.2.2 访问控制符
三个 private protected public–不写
5.3 面向对象中类的属性
5.3.1 属性
属性用来描述类的特征
5.3.2 声明语法
访问控制修饰符 var|val 属性名:属性类型 = 值;
属性声明的时候必须加值,但是我不想给其他值,只想给默认值,那么值使用**_** 来代替
5.4 面向对象中类的方法
5.4.1 方法
方法就是函数,函数声明在类中,函数称之为方法
5.4.2 语法
访问控制修饰符 def 方法名(形参列表):方法的返回值类型 ={ 方法体 }
5.5 面向对象中类的代码块和内部类
{} 类中类
5.6 面向对象中类的构造器
5.6.1 构造器是创建该类的实例对象的
- Scala有两种构造器
- 主构造器:声明在类上的
class ClassName 访问控制修饰符 (形参列表)--主构造器
- 辅助构造器:声明在类中的构造器,语法:
def this(形参列表){ }
- 辅助构造器必须在首行直接或者间接的调用主构造器代码
- 主构造器:声明在类上的
六、Scala中集合
七、Scala中的隐式转换
八、Scala中_的作用
- _在模式匹配中,_代表的是未匹配到其他情况,默认情况
- 函数名 _ : _ 代表的是将函数当作一个整体来使用,而非调用函数