Scala突击复习笔记
一、scala基础语法
1.变量和常量
val和var关键是声明变量使用(区分:val看做是Java中的final修饰的常量,不可改变。而var则可以看做变量,可变)。
(1).变量
使用var修饰, 内容和引用都可以改变。
scala> var a = 10
a: Int = 10
scala> a= 20
a: Int = 20
(2).常量
val可以看做是Java中的final,不能修改其引用的地址, 但是可以修改其引用的对象的内部的其他值, 在实际开发中, 推荐使用val修饰, 如果使用var可能会出现修改值的问题, 因为在网络传输中, 会对变量进行序列化和反序列化, var修饰的变量可能会发生值的改变, 数据传输错误或不安全, 所以一般情况下推荐使用val。
scala> val b = 10
b: Int = 10
scala> b = 20
scala> b = 20
^
error: reassignment to val
(3).lazy关键字
用来修饰val时,不是立即为其创建空间,直到被调用时,才开辟内存空间
scala> lazy val c = 10
c: Int = <lazy>
scala> c
res0: Int = 10
2.常用的数据类型和Java对照
在scala中, 一切都是类, 没有基础类型和包装类之分。基本数据类型: Byte Char Short Int Long Float Double Boolean, 初看之下和Java中的包装类很类似,但是这里需要注意和Java中的包装类使用完全不相同。scala中是不区分引用类型和基本类型的,所有都看做是对象, scala会自己负责基本数据类型和引用类型之间的转换。既然是对象,则本身会有方法,可以直接调用。
val a:String = 1.toString
3.运算符
scala的运算符和java的没有太大区别,但是没有++和–运算,可以用+=和-=代替。
4.流程控制
(1).条件判断语句
使用和java的差不多,但是scala的分支语句有返回值,可以传回。若返回的数据类型不一致, 则取其父类Any。
val res:Any = if(a < 10) "字符串" else 100
//当a<10的时候,返回“字符串”这三个字给res,若其他情况则返回100这个数值给res
(2).循环语句
和java一样,有do…while,while和for三种循环方式。使用相差不大,其中for的条件判断有些差异。scala的for循环条件判断不是java的(赋值,条件判断,条件运算)格式。而是直接指定循环区间,有to和until两种方式。区别在于until不包含最后一个值(含前不含后),而to包含最后一个值。
//to循环1到10
scala> for(i<-1 to 10){print (i+" ")}
1 2 3 4 5 6 7 8 9 10
//until循环1到10
scala> for(i<-1 until 10){print (i+" ")}
1 2 3 4 5 6 7 8 9
二、Scala方法和函数
方法和函数的区别:
方法:是类的一部分,是对象的一部分,和java类似,使用def关键字定义;
函数:是一个对象,可以作为参数传给方法,使用val关键字定义。
1.方法的定义
(1).标准定义(return可写可不写,scala会自行推断。逻辑复杂建议不省略)
def 方法名(参数名:参数类型...):返回值类型 = {方法体}
//例如
def sum(a:Int, b:Int):Int = {
a+b
}
(2).简化定义
因为scala是自动推断脚本语言,所以方法中的返回值类型可以不写,编译器会根据方法体中的返回值自行推断。
//简化1:不写方法返回值
def 方法名(参数列表) = {方法体}
eg:def sum(a:Int, b:Int) = {println(a+b)}
//简化2:不写等号
def sum2(a:Int,b:Int){println(a+b)}
//没有参数,没有返回值
def sum3():Unit = {
println("求和")
}
//但是若是递归函数,必须些返回类型
//递归: 实现斐波那契数列
def fab(n:Int):Int={
if((n == 1) ||(n == 2)){
1
}
else{
fab(n-1)+fab(n-2)
}
}
3.常见的函数的定义方式(有一种通过new Function2的方式不记录)
(1).声明函数返回类型定义
val 函数名:((参数类型...)=>返回值类型) = {(参数名...)=>(方法体)}
//举例
scala> val diff:((Int, Int)=>Int)={(x,y)=>(x-y)}
diff: (Int, Int) => Int = <function2>
scala> diff(3,2)
res21: Int = 1
(2).不声明函数返回类型定义
val 函数名=(参数名:参数类型...) => {方法体}
//举例
val add = (a:Int, b:Int)=>{a+b}
(3).参数名不写,使用‘_’代替
scala> val f1:(Int,Int)=>Int=(_+_)
f1: (Int, Int) => Int = <function2>
scala> f1(2,5)
res1: Int = 7
4.高阶函数
(1).参数是另一个函数
val f1 = (f:(Int, Int)=>Int) => {
val a = f(2,3)
println(a)
}
val f2:((Int, Int) => Int) = {(x,y)=>(x+y)}
scala> f1(f2)
5
(2).函数作为返回值
需求:根据参数为true或者false来调用转换大小写的函数
1.定义转换大写的函数f1
val f1:(String)=>String = {(x)=>{x.toUpperCase}}
2.定义转换小写的函数f2
val f2:(String)=>String = {(x)=>{x.toLowerCase}}
3.令f1或f2作为函数f3的返回值(这里传入true)
val f3:(Boolean)=>((String)=>String) = {(x)=>{
if(x == true){
f1
}else{
f2
}
}
}
4.调用函数
val f4 = f3(true)
f4("helloworld")
//输出
res3: String = HELLOWORLD
5.数组
(1).定长数组
长度不变的数组,实用scala包中的Array类。
定长数组的定义:
//第一种,通过new的方式定义,默认数组中元素初始值是0
val arrray = new Array[Int](5)
//第二种,直接给定初始值
val array = Array(1,2,3,4,5)
定长数组的操作:
1.打印定长数组
//如果是直接打印,则打印出来的是数组对象的hashCode
println(array)
[I@6c07ad6b
//需要转化为缓冲数组方能正常查看其中内容
array.toBuffer
scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3, 4, 5)
其余插入,修改数据的操作和java一样。
(2).变长数组
数组定义:
//第一种,使用new对象的方式
import scala.collection.mutable.ArrayBuffer
val array = new ArrayBuffer[Int](4)
//第二种,直接赋值数组
val array = ArrayBuffer(1,2,3,4)
变长数组的操作:
//在末尾添加元素+=
val array = ArrayBuffer(1,2,3)
array += 10
res17: array.type = ArrayBuffer(1, 2, 3, 10)
//在末尾添加多个元素,用()括起来
val array = ArrayBuffer(1,2)
array += (3,4)
res18: array.type = ArrayBuffer(1, 2, 3, 4)
//在尾端添加另一个数组,++=
val array1 = ArrayBuffer(1,2,3)
val array2 = ArrayBuffer(4,5,6)
array1 ++= array2
res24: array1.type = ArrayBuffer(1, 2, 3, 4, 5, 6)
//移除最后N个元素,trimEnd(n)
val array = ArrayBuffer(1,2,3,4,5,6)
array.trimEnd(3)
res27: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3)
//缓冲数组转成不可变数组,toArray()
(3).遍历数组
基本用法和java相同,这里仅仅记录较为特殊的不使用下标直接遍历的方式。
val array = Array(a,b,c,d)
for(i <- array){
println(i)
}
a
b
c
d
(4).数组常用算法
sum求和
max(min)求最大最小值
sorted从小到大排序
sortWith((x,y)=>{x>y})或者sortWith((x,y)=>{x<y})冒泡排序,可以指定正序排序或者倒序排序
reverse翻转
6.map映射
Scala提供两种类型的映射:一类是值可以改变的映射,位于scala.collection.immutable.Map包,另一类是值不可以改变的映射,位于scala.collection.mutable.Map包。
(1).构造值可变映射
//第一种方式,在构造时赋值
val map = scala.collection.mutable.Map("001"->"张三", "002"->"王五")
//第二种方式,在构造时不赋值,但是需要指定kv对泛型
val map = new scala.collection.mutable.HashMap[String, String]
修改或获取映射中的值
//1.获取映射的值
map("001")
res58: String = 张三
//2.更改映射中的值
map("001") = "二麻子"
map("001")
res60: String = 二麻子
//3.添加数据
map += ("003"->"大傻子")
//4.删除数据
map-=map("001")
(2).构造值不可变映射
//第一种方式,在构造时赋值
val map = Map("001"->"张三", "002"->"王五")
//第二种方式,在构造时不赋值,但是需要指定kv对泛型
val map = new scala.collention.immutable.HashMap[String, String]
(3).映射遍历的三种方式
//第一种
for((x,y) <- map){
println(x+"--->"+y)
}
001--->张三
002--->王五
//第二种
for(x <- map){
println(x)
}
(001,张三)
(002,王五)
//第三种
val a = map.keySet
for(k <- a){
println(k)
}
001
002
for(k <-a){
println(map(k))
}
张三
王五
7.元组Tuple
目前Scala支持的最大元组长度为22(可以理解为一个可以存储不同数据类型的数据结构),对于更长的元组可以使用集合或者扩展元组。
//直接创建
val t1 = (1,"张三",3.14) //这种创建方式只可以用_1,_2,_3来访问对应位置的元素
val t2,(a,b,c) = (1,"张三",3.14) //这种创建方式可以通过别名abc来访问元素
//通过new来创建元组,这样创建的元组也只可以用_1,_2,_3......来访问元素
val t3 = new Tuple2(1, 3.14)
val t4 = new Tuole3(1,"张三",3.14)
元组遍历:
t2.productIterator.foreach(x=>println(x))
1
"张三"
3.14
8.集合(List,Set)
大致与java相同,要区分可变集合(ListBuffer)和不可变集合。对于不可变集合,每一次变化都是生成新对象覆盖了原对象。
9.Iterable常用方法
Iterable是所有集合的父类,所以其方法对所有集合都适用。
//sum,max,min求集合中最大最小值和求和
//count按照自定义规则计数,示例为统计list1中所有偶数个数,返回一个整型
list1.count(x=>{x%2==0})
//filter过滤(可以理解为按规则筛选出符合的数据),示例为筛选出list1中所有的偶数,返回一个集合
list1.filter(x=>{x%2==0})
//flatten扁平化处理,若有一个集合中的元素是多个集合,则将里面的集合元素全部拆解,合并为一个大集合
val list1 = List(1,2)
val list2 = List(3,4)
val list3 = List(5,6)
val list4 = List(list1,list2,list3)
//list4: List[List[Int]] = List(List(1,2), List(3,4), List(5,6))
val list = list4.flatten
//list: List[Int] = List(1,2,3,4,5,6)
//diff 差集,intersect 交集, union并集。这三个方法返回集合数据类型。
//map遍历集合中元素,对每个元素作出自定义操作,返回一个集合类型
val list = List(1,2,3,4)
val list2 = list.map(x=>{x*2})
//list2: List[Int] = List(2,4,6,8)
//flatMap扁平化遍历集合。先将集合中的每一个元素遍历出来,再对总集合做flatten扁平化。
val list1 = List(1,1,1)
val list2 = List(2,2,2)
val list3 = List(3,3,3)
val list = List(list1,list2,list3)
val list4 = list.flatMap(x=>x.map(y=>y*2)) //第一步先将各个子集合取出到x中,第二步遍历x中各个元素到y中乘以2,得到List((2,2,2),(4,4,4),(6,6,6))这样的集合,第三步对这个集合做扁平化。
//list4: List[Int] = List(2,2,2,4,4,4,6,6,6)
//zip拉链操作。对两个集合为位置相同的元素做合并为元组的操作,若一方有,一方没有的元素舍弃。最后返回一个元组集合。
val list1 = List(1,2,3)
val list2 = List(2,4)
val list1.zip(list2) //又可以写为 list1 zip list2
//res9:List[(Int,Int)] = List((1,2), (2,4))
val list2.zip(list2)
//res10:List[(Int,Int)] = List((2,1), (4,2))
//forall对整个集合进行条件检查,返回布尔值。遍历集合所有元素,一但有一个元素不满足检查条件,返回false。若都满足则返回true
val list = List(2,3,4,9)
list.forall(x=>x<6) //false 因为9比6大
list.forall(x=>x<100) //true
//partition对集合进行分组,返回集合元组,将相同分组条件的元素放到同一个子集合中。
val list = List(2,3,45,5,6)
list.partition(x=>(x%2==0))
//res11:(List[Int], List[Int]) = (List(2,6), List(3,45,5))
//reduce聚合,按照规则对集合中各个元素做聚合
val list = List(2,3,4)
list.reduce((x,y)=>{x+y}) //x一开始代表第一个元素,后面代表每一次计算后的结果,y则代表下一个元素
//res12:Int = 9
//groupBy分组函数。返回一个map类型集合,主键是分组条件的返回值,键值是符合的数据集合
val list = List(1,2,3,4,5,6,7,8,9,10)
list.groupBy(x=>{x%2==0})
//res12:Map[Boolean, List[Int]] = hashMap(false->List(1,3,5,7,9), true->List(2,4,6,8,10))
//grouped分组函数。给定一个长度,将原集合分为N个符合这个长度的子集合。若剩余元素不满足该长度,则最后那个集合仅仅保存剩余元素。
val list = List(2,3,45,5,6)
list.grouped(2).toList
//res149: List[List[Int]] = List(List(2, 3), List(45, 5), List(6))
10.面向对象编程
(1).创建类
在Scala中有两种类,描述类class和执行类object。scala中的类不需要使用public修饰,一个文件中可以存在多个类,若没有提供构造方法,系统会提供默认的无参构造方法。
对于class可以理解为java中用来生成对象的类,而object则是具有执行能力,例如main方法所在的类。
class Student {
/**
* 定义属性, 需要给定一个初始值
* var修饰的属性相当于自带的get/set方法
* val修饰的属性相当于只有get方法, 没有set方法---只读
* _ 表示一个占位符, 可以作为初始化使用, 需要注意的是:
* 若使用下划线作为初始值, 必须声明数据类型
* _ 相当于给当前变量进行初始值赋值, 数值类型--> 0 引用数据类型 ---> null
* 引用类型在使用时需要注意空指针
*/
var name: String = _
var age: Int = _
var gender: String = _
//var add = _ 不能这么使用, 无法确定数据类型
//val add:String = _ 也不能这么使用, val相当于final, 无法进行再次赋值, 在这里禁止使用下划线
val add = "育新花园"
}
object Test {
def main(args: Array[String]): Unit = {
//创建对象 若创建对象没有构造方法, 使用的是默认构造, 所以可以省略小括号
val stu = new Student
//调用属性
println(stu.name)
println(stu.age)
println(stu.add)
//调用属性, 赋值, val修饰的属性不能进行再次赋值
stu.gender = "男"
println(stu.gender)
}
}
(2).构造方法
在类后面添加()完成
- 一个类只能有一个主构造方法,可以有多个辅助构造方法(相当于java中可以重载多个构造方法,用于不同参数(参数数量不同,类型不同)等情况)。
- 若类中提供了构造方法,可以省略书写属性,在构造方法中提供了这些形参名,其实就是类的属性,我们只需要提供修饰即可对外访问。
- 不使用修饰之前,这些形参只能够在类中使用。
- 在使用val和var修饰之后,这些形参就可以在外部使用。
- 在构造的时候,可以给属性一个默认值。
class Person(val name: String, val age: Int, val faceValue: Int = 50) {
var gender: String = _
//没有var或者val修饰的时候, 可以在类的内部进行调用
def show() = {
println(name)
}
//提供辅助构造器
def this(name: String, age: Int, faceValue: Int, gender: String) {
//第一行必须调用主构造方法
this(name, age, faceValue)
this.gender = gender
}
}
object PersonTest {
def main(args: Array[String]): Unit = {
//创建对象
val p1 = new Person("laoliang", 37, 80)
val p2 = new Person("laochen", 40)
println(p2.faceValue)
val p3 = new Person("laocao", 45, 2, "男")
}
}
(3).伴生类和伴生对象不太常用
(4).特质和抽象类
特质:Thrait,相当于java中的接口,可以一个类可以实现多个特质。和java8后续版本一样,已经支持实现方法。
/**
* scala中的特质, 除了关键字不一样外, 在当前代码中定义的和class类中定义的是完全一样的
* 但是不能提供构造方法
*/
trait Fly {
//声明有值的的属性
val height = 2000
//声明没有值的属性
val speed:String
//声明实现方法
def fly():String = {
"I can fly"
}
//声明一个没有实现的方法
def play():String
}
抽象类:和java一样,使用abstract关键字。
/**
* 企鹅类,抽象类, abstract修饰
*/
abstract class QQ {
//声明一个有值的字段
val age = 5
//声明一个无值的字段
val name:String
//声明实现的方法
def show():String={
"QQQQ"
}
//声明没有实现的方法
def run():String
var gender:String = "男"
}
继承特质和抽象类:
/**
* 若在没有继承类的前提下, 直接使用extends 就可以继承特质
* 若只是继承抽象类或其他类 也是使用extends
* 若继承一个类实现一个特质, extends 类名 with 特质名
* extends 类名 with 特质名 with 特知名 with 特知名......
*/
class Human extends QQ with Fly {
override val name: String = "fly_name"
override def run(): String = "跑"
override val speed: String = "1000KM/H"
override def play(): String = "斗地主"
//重写
override val age: Int = 20
//被var修饰的属性不能被重写, 重写不会报错, 但是运行的时候会报错
//override var gender:String="不详"
}
object Human{
def main(args: Array[String]): Unit = {
val h = new Human
println(h.gender)
}
}
(5).模式匹配(有一些类似java中的switch case)
1.标准模式:
数据 match{
case 条件 => 输出
}
2.偏函数:
def show()={
case 条件=>输出
}
- 模式匹配中, scala提供了一个类似于Java枚举的声明–>样例类
- Java中的switch-case需要使用break, 不然就会贯穿(从开始执行到结束)
- scala中的match-case没有break, 只要case满足就执行, 执行完就自动退出, 不会执行下一个case
- scala中使用_ 代替Java中的default, 最后的默认情况, 一定要放到最后
import scala.util.Random
object MatchCaseDemo {
def main(args: Array[String]): Unit = {
//创建一个数组
val arr1 = Array("laoliang", "laocao", "laochen", "dazhao")
//通过随机获取值进行匹配输出
arr1(Random.nextInt(arr1.length)) match {
case "laoliang" => println("laoliangjianggushi")
case "laocao" => println("laocaolaocao")
case "laochen" => println("laochenlaochen")
case "dazhao" => println("heiheihei")
case _ => println("默认")
}
println("-" * 50)
val arr2 = Array("hello123", 1, 2, 3L, MatchCaseDemo)
arr2(Random.nextInt(arr2.length)) match {
//可以向写方法形参一样
case x: Int => println("Int Type")
case y: Double if (y < 0) => println("double Type")
case MatchCaseDemo => {
println("MatchCaseDemo Type")
}
case _ => println("default")
}
val arr3: Array[Int] = Array(2, 3, 45, 5, 6, 6)
arr3 match {
case Array(2, 3, x, y) => println("x+y=" + (x + y))
case Array(2, 3, _*) => println("_* 代表后边所有值")
case _ => println("default")
}
val tup1 = (1, 2, 5)
tup1 match {
case (1, 2, x) => println("x=" + x)
case (x, y, z) => println("x+y+z: " + (x + y + z))
case (_, x, 5) => println("下划线尽量不要使用, 可以使用变量接收")
case _ => println("default")
}
}
}