一、变量
1.在scala中,可以使用val或var定义变量,语法格式如下
val/var 变量标识:变量类型 = 初始值
其中
- val定义的是不可重新赋值的变量
- var定义的是可以重新赋值的变量
2.通过类型推断来定义变量
scala可以自动根据变量的值来推断变量类型,使代码更简洁
var name = "tom"
3.惰性赋值
当有一些变量保存的数据比较大,但不需要马上加载到JVM中。可以使用惰性赋值,语法格式如下
lazy val/var 变量名 = 表达式
二、字符串
1.scala提供多种定义字符串的方式
- 使用双引号
val/var 变量名 = "字符串"
- 使用插值表达式,避免大量字符串拼接
//在定义字符串之前添加s
//在字符串中,可以使用${}来引用变量或者表达式
val/var 变量名 = s"${变量/表达式}字符串"
举例
- 使用三引号。三个引号中间的所有字符串都将作为字符串的值
val/var 变量名 = """字符串"""
三、数据类型与操作符
scala中的类型以及操作符绝大数和java一样。我们主要学习与java不同的地方
1.数据类型
注意
- scala 中所有类型都是使用大写字母开头
2.运算符
注意
- scala中没有++和–
- 与java不一样,在scala中,可以直接==,!=进行比较,它们 equals方法保持一致。而比较两个对象的引用之,则使用eq
3.数据类型结构体系
说明
- scala所有数据类型都是Any
- 基本数据类型是继承AnyVal
- 引用类型是继承AnyRef的
- Null是所有引用类型的字类
- Nothing是所有数据类型的字类,可以赋值给任意类型,当一个方法抛出异常时,将会放回Nothing
- Unit表示空,只有一个实例(),类似于java中void
四 条件表达式和循环
1.有返回值的if
与java不一样的是
- 在scala中,条件表达式也是有返回值的
- 在scala中,没有三元表达式,可以使用if表达式代替
示例
var sex = "male"
val result = if(sex == "male") 1 else 0
println(result)
2.块表达式
- scala中,使用{}表示一个块表达式
- 块表达式也是有值的,为最后一个表达式的值
3. for表达式
语法
for(i <- 表达式/数组/集合)
{
}
示例:使用for打印1到10
我们先获得一个数字集合
然后在进行循环
简写方式(中缀调动法)
3.1 for嵌套循环
示例
for(i <- 1 to 3; j<- 1 to 5)
{print("*");if(j == 5)print("")}
3.2守卫
for表达式中,可以添加if判断语句
只有符合if表达式,才会执行for中的代码
for(i<-表达式/数组/集合 if表达式)
{
}
3.3for推导式
可以使用for推导式生成一个新的集合
示例
val v = for(i <- 1 to 10) yield i*10
像不像java8集合的stream操作?
4.while循环
scala中while循环和Java是一致的
示列
var i:Int = 1
while(i <= 10){
|println(i)
|i= i+1
|}
补充
scala中没有关键字break和continue
如果一定要使用他俩,就需要用scala.util.control包Break类的breable和break方法
五、方法
1.定义方法
def 方法名(参数名:参数类型,参数名:参数类型):[返回类型] ={
}
注意
- 参数类型不能省略
- 返回值类型可以省略,有scala编译器自动推断
- 返回值可以不写return,默认就是{}块表达式的值
2.方法参数
scala中的方法参数非常灵活
2.1默认参数
在定义方法时可以给参数定义个默认值
示例
def add(x:Int = 0 ,y:Int = 0)=
{ x+y }
2.2 带名参数
在调用方法时,可以指定参数的名称来进行调用
示例
def add(x:Int = 0 ,y:Int = 0)=
{ x+y }
add(x = 1)
2.3变长参数
方法参数的个数可以是不确定的
语法如下
def 方法名(参数名:参数类型*):返回值类型={}
3.方法的调用方式
3.1 后缀调用法
语法
对象名.方法名(参数)
3.2 中缀调用法
语法
对象名 方法名 参数
如果有多个参数,使用{}将参数括起来
补充:
在scala中,操作符即方法
1 + 1
观察一下,操作符的使用方式和中缀调用法是一样的
3.3 花括号调用法
方法只有一个参数,才能使用花括号调用法
Math.abs{
//表达式
//表达式
}
3.4无括号调用法
如果方法没有参数,可以省略方法名后面的括号
Hello()
//如果这个方法没有参数,则可以直接写成这样
Hello
六、函数
scala支持函数编程,将来我们会大量使用到函数
1.定义函数
语法
val 函数变量名 = (参数名:参数类型,参数名:参数类型...)=> 函数体
说明
- 函数是一个对象
- 类似于方法,函数也有输入参数和返回值
- 函数定义不需要用def定义
- 无需指定返回值类型
示例:定义一个两个数值相加的函数
val addFunc = (x:Int,y:Int)=> x+y
addFunc(1,2)
2.方法和函数的区别
- 方法是隶属于类和对象,在运行时,它是加载到JVM的方法区中
- 可以将函数对象赋值给一个变量,在运行时,它是加载到JVM堆内存
- 函数是一个对象,继承自FunctionN,函数对象有apply,curried,toString,tupled这些方法。方法则没有
3.方法转换为函数
有时候需要将方法转换成函数,作为变量传递
使用_即可将方法转换成函数
示例
七、数组
在scala中,有定长和变长数组
1.定长数组
定长数组的长度不允许改变
val 变量名 = new Array[元素类型](数组长度)
用元素直接初始化数组
val 变量名 = Array(元素1,元素2)
注意
- 在scala中,数组的泛型使用[]给定
- 使用()来获取元素
2.变长数组
2.1定义变长数组
创建变产数组,需要导入scala.collection.mutable.ArrayBuffer类
语法
创建空的ArrayBuffer
val a = ArrayBuffer[元素类型]()
创建带有元素的ArrayBuffeer
val a = ArrayBuffer(元素1,元素2)
2.2 增删改元素
- 使用+=添加元素
- 使用-=删除元素
- 使用++=追加一个数组到变长数组
3.遍历数组
在scala中有两种方式遍历数组
- 使用 for表达式
- 使用索引
3.1使用for表达式遍历数组
for (i<-a) pringln(i)
3.2 使用索引遍历数组
for(i<- 0 to a.length-1) println(a(i))
4.数组常用算法
scala中的数组封装了一些常用的算法,不需要我们自己实现
4.1求和
a.sum
4.2 最大值
a.max
4.3 最小值
a.min
4.4排序
数组的sorted方法可以对数组进行升序排序,reverse方法可以将数组反装
a.sorted
a.reverse
8.元组
元组可以用来包含一组不同类型的值,元组的元素是不可变的
1.定义元组
使用括号
val 元组=(元素1,元素2,元素3)
使用箭头来定义元组(元组只有2个元素)
val 元组=元素1->元素2
2.访问元组
使用_1,_2,_3…来依次访问元素的元素
示例
八、scala中的集合
1.列表
列表是scala中最常用的数据结构
List可以保存重复的值,有先后顺序
在scala中有不可变和可变两种列表
1.1不可变列表
语法
val 变量名 = List(元素1,元素2)
//创建一个不可变的空表
val 变量名 = Nil
//使用::方法创建一个不可便列表,必须在最后添加一个Nil
val 变量名 = 元素1::元素2::Nil
1.2 可变列表
使用可变列表,需要导入scala.collection.mutable.ListBuffer
可变集合都在mutable包中。
不可变集合都在immutable包中(默认导入)
1.2.1 定义可变列表
val 变量名 = ListBuffer[元素类型](元素1,元素2)
val 变量名 = ListBuffer(元素1,元素2)
1.2.2可变列表操作
- 获取元素:使用括号访问
- 添加元素:+=
- 追加一个列表:++=
- 删除:-=
- 转换为List:toList
- 转成Array:toArray
1.3 列表常用操作
说明的地方
- tail获取的是首个元素之后的所有元素
- take方法返回你给出的前n个元素,drop获取take以为的元素
- 扁平化flaten是将列表中的所有元素放到一个列表中(消灭所有子表)
- 拉链是将两个列表组成一个元素为元组的列表,拉开是将一个包含元组的列表解成包含两个列表的元组
2.Set
Set是代表没有重复元素的集合,不保证插入顺序
可变集合不可变集的创建方式一致,需要导入scala.collection,mutable.set。导入之后,我们创建的就是可变set
语法
val a =Set[类型]()
val a = Set(元素1,元素2)
3.Map
Map也分为元素可变和不可变
同样,导入scala.collection.mutable.Map之后,我们定义的Map就是可变的
3.1 定义Map
val map = Map(键->值,键->值)
val map = Map((键,值),(键,值))
4.迭代器
与java集合的迭代器基本上是一样的
- 使用集合的iterator方法可以获得迭代器
- hasNext方法判断是否有下一个元素
- next返回下一个元素
九.函数式编程
将来我们用Spark/Flink编写业务时会大量用到函数式编程。下面这些操作是我们重点要掌握的
- 遍历(foreach)
- 映射map
- 映射扁平化flatmap
- 过滤filter
- 是否存在exists
- 排序sorted,sortBy,sortWith
- 分组groupBy
- 聚合计算reduce
- 折叠fold
和java8的stream差不多
PS,如果参数只在函数体中出现一次,且没有嵌套调用,可以使用_代替参数究极简化
1.foreach
方法前面
foreach(f : (A) => Unit):Unit
说明
示列
2.map
map方法接收一个函数,将这个函数应用到每一个元素,最后返回一个新集合
3.flatMap
可以把flat理解成先把集合map返回一个新集合,然后再把新集合flatten
示列
4.filter
将集合中符合条件的元素筛选出来,返回一个新集合
5.排序
在scala集合中,可以使用以下三种方式排序
- sorted默认升序排序
- sortBy指定字段排序
- sortWith自定义排序
5.1 sorted
5.2 sortBy
根据传入的函数转换后,再进行排序
示列
将列表按元素中的单词首字母排序而不是数字
5.3 sortWith
根据传入的函数直接排序
这个函数接收两个参数
6.groupBy
groupBy接收一个函数,将集合相同特性的元素归为同一个key,最后返回一个map
7.聚合
聚合可以将列表中的数据合并成为一个。
7.1 reduce
reduce接收一个函数,用来对元素不断地进行聚合操作
函数的第一个参数类型为:当前聚合后的变量
第二个参数类型为:当前要聚合的元素
reduce的执行流程
注意
- reduce和reduceLeft效果一致,表示从左向右进行聚合
- reduceRight表示从右到左计算
示例
7.2 fold
fold和reduce很像,只是要给定一个原始值
fold接收一个函数,第一个参数列表为初始值
第二个参数列表就是reduce那俩
同样fold有foldLeft和foldRight
十、面向对象
1.创建类和对象
用法
- 使用class来定义类
- 使用new来创建对象
//object的意思是单例对象,main方法只能存在于单例对象中
object test1 {
class Person{
}
def main(args: Array[String]): Unit = {
val person = new Person()
}
}
2.简写方式
如果一个类没有任何成员,则可以省略{}
如果构造器是无参的,则new 对象时可以省略括号
object test1 {
class Person
def main(args: Array[String]): Unit = {
val person = new Person
}
}
3.成员变量和成员方法
3.1成员变量
用法
- 在类中使用var/val来定义成员变量
- 对象直接使用成员变量名称来访问成员变量
class Person{
var name:String = ""
var age:Int = 0
}
def main(args: Array[String]): Unit = {
val person = new Person
person.name = "张三"
person.age = 20
}
3.2使用下划线来初始化成员变量
在定义var类型成员变量时,可以使用_来初始化成员变量。比如String类型初始化就是null,Int类型就是0
val类型从成员变量,必须要手动初始化
class Person{
var name:String = _
var age:Int = _
}
3.3 成员方法
使用def来定义成员方法
class Person{
var name:String = _
var age:Int = _
def setAndgetName(x:String):String =
{
this.name = x
this.name
}
}
4.访问修饰符
和java类似,scala中可以在成员前面添加private/protected关键字来控制成员的可见性
在scala没有public关键字,任何没有标注private/protected的成员都是公共的
5.类的主构造器
在scala中
- 主构造器的参数列表是直接定义在类名后面,可以通过主构造器直接定义成员变量
- 整个class中,除了字段定义和方法定义,都是构造代码
6.类的辅助构造器
在scala类中,名为this的方法就类的辅助构造方法
语法
def this(参数名:类型,参数名:类型)
{
//第一行必须要调用主构造器或者其他构造器
}
7 单例对象
scala中没有Java中的static,我们想要实现相同的功能,就需要使用单例对象:object
- 单例对象表示全局仅有一个对象
- 在objiect中定义的成员变量类似于Java的静态变量
- 可以使用object直接引用成员变量
8 伴生对象
在Java类中,可同时拥有实例成员和静态成员
在scala中,要实现类似的效果,可以使用伴生对象
8.1 定义伴生对象
一个class和object具有同样的名字。这个object称为伴生对象,这个class成为伴生类
- 伴生对象和伴生类名字必须相同
- 伴生对象和伴生类在同一个scala源文件中
- 伴生对象可以互相访问伴生类的private属性(但不能访问被private[this]修饰的成员)
class Person(var name:String ="初始值",var age:Int =0){
println(this.name)
println(Person.classnumber)
def setAndgetName(x:String):String =
{
this.name = x
this.name
}
}
object Person
{
private val classnumber =123
}
8.2 apply方法
apply方法可以让我们不使用new就可以创建对象
创建一个类的伴生对象,实现apply方法,当创建类的时候,就可以省略new
object 伴生对象名{
def apply(参数名:参数类型,参数名:参数类型) = new 类(...)
}
object test1 {
class Person(var name:String ="初始值",var age:Int =0){
println(this.name)
println(Person.classnumber)
def setAndgetName(x:String):String =
{
this.name = x
this.name
}
}
object Person
{
private val classnumber =123
def apply(name:String,age:Int)=new Person(name,age)
}
def main(args: Array[String]): Unit = {
val person = Person("张山",18)
println(person.name)
}
}
9.继承
scala中和java一样,使用extends实现继承
用法
class/object extends 类
- 字类要覆盖父类中的方法或者val类型的字段(var不行),必须使用override关键字
- 使用super引用父类
10. 类型判断
在scala中
- isIntanceOf判断对象是否为指定类以及其字类的对象
- asInstanceOf将对象转换为指定类型
用法 - getClass可以精确获取对象的类型
- classOf[x]可以获取x的类型
- 使用==操作复可以直接比较类型
对象.isInstanceOf[类型]
对象.asInstanceOf[类型]
11.抽象类
如果类的某个成员在当前类中的定义是不完整,它就是一个抽象类
- 方法没有方法体(抽象方法)
- 变量没有初始化(抽象字段)
定义抽象类和java一样,就是在类前加上abstract
12.匿名内部类
匿名内部类是没有名称的字类,直接用来创建实例对象
用法和java相同
语法
val 变量名 = new 类{
//重写方法
}
13.特质(trait)
scala没有inferface,替代的概念是trait
定义
- 特质是scala种代码复用的基础单元
- 它可以将方法和字段定义封装起来,添加到类中
- 与类继承不一样的是,一个类可以添加多个特质
- 特质的定义和抽象类很像,但它是使用trait关键字
- 特质可以定义具体字段和具体方法
trait 名称{
//抽象字段
//抽象方法
//具有方法
}
继承特质也是使用extends
class 类 extends 特质1 with 特质2{
}
对象混入trait
scala中可以将trait混入到对象中,就是将trait中定义的方法、 字段添加到一个类中
语法
val 对象名 = new 类 with 特质
trait的构造机制
trait也有构造代码,但特质不能有构造器参数
每个特质只有一个无参数的构造器
一个类继承另一个类,以及多个trait,当创建该类的实例时,它的构造顺序如下
1.执行父类构造器
2.从左到右依次执行trait的构造器
3.如果trait有父trait,先构造父trait,如果多个trait有同样的父trait,则只初始化依次
4.执行字类构造器
trait继承class
trait也可以继承class,将class的成员继承下来。。。。。。。。。。。