目录
 如果子类要覆盖父类中的一个非抽象方法,必须要使用override关键字
一、变量
val/var 变量名称:变量类型 = 初始值 
注意:
-  
val定义的是不可重新赋值的变量(值不可修改) -  
var定义的是可重新赋值的变量(值可以修改) 
-  
scala中声明变量是变量名称在前,变量类型在后,跟java是正好相反
 -  
scala的语句最后不需要添加分号、、
 
惰性变量
-  
Scala中使用==关键字lazy==来定义惰性变量,实现延迟加载(懒加载)。
 -  
惰性变量只能是不可变变量,并且只有在调用惰性变量时,才会去实例化这个变量。
 -  
语法格式
 
lazy val 变量名 = 表达式 
二、数据类型
| 基础类型 | 类型说明 | 
|---|---|
| Byte | 8位带符号整数 | 
| Short | 16位带符号整数 | 
| Int | 32位带符号整数 | 
| Long | 64位带符号整数 | 
| Char | 16位无符号Unicode字符 | 
| String | Char类型的序列(字符串) | 
| Float | 32位单精度浮点数 | 
| Double | 64位双精度浮点数 | 
| Boolean | true或false | 
1. scala中所有的类型都使用大写字母开头,说明是它是“类”
 2. 整形使用Int而不是Integer
 3. scala中定义变量可以不写类型,让scala编译器自动推断 
三、scala中的条件表达式
1、if表达式
这个和java差不多,可以加括号,或者不加括号
  def main(args: Array[String]): Unit = {
     val x:Int=2
     if (x>10) println(x)
     else println(x+x)
} 
打印为2
2、块表达式
定义变量时用 {} 包含一系列表达式,其中块的最后一个表达式的值就是块的值
  def main(args: Array[String]): Unit = {
    val x = 0
    val result = {
      val y = x + 10
      val z = y + "-hello"
      val m = z + "-kaikeba"
      "over"
    }
    println(result)
} 
打印结果
over
Process finished with exit code 0 
返回值就是m了
  def main(args: Array[String]): Unit = {
    val x = 0
    val result = {
      val y = x + 10
      val z = y + "-hello"
      val m = z + "-kaikeba"
      m
    }
    println(result)
} 
打印结果
10-hello-kaikeba 
四、循环
在scala中,可以使用for和while,但一般推荐使用for表达式,因为for表达式语法更简洁
for (i <- 表达式/数组/集合){
    //表达式
} 
1、简单for循环
def main(args: Array[String]): Unit = {
    var nums=1 to 10
    //for循环
    for(i <- nums) println(i)
} 
2、双层for循环
  def main(args: Array[String]): Unit = {
    //双层for循环
    for (i <- 1 to 3; j <- 1 to 3)
      println(i * 10 + j)
} 
打印的值为
11
12
13
21
22
23
31
32
33 
3、守卫:在for表达式中可以添加if判断语句,这个if判断称为守卫
 def main(args: Array[String]): Unit = {
//    守卫
    var nums = 1 to 10
    for (i <- nums if i>5 ) println(i)
} 
打印的值为:
6
7
8
9
10 
4、yield表达式
在for循环体中,以yield表达式开始,这类循环能构建出一个新的集合,我们把这类循环称为推导式
def main(args: Array[String]): Unit = {
    // for推导式:for表达式中以yield开始,该for表达式会构建出一个集合
    val v = for(i <- 1 to 5) yield i * 10
    //打印集合v的第一个元素
    println(v(0))
} 
打印的值为
10 
五、while循环
while(返回值为布尔类型的表达式){
    //表达式
} 
六、方法
def methodName (参数名:参数类型, 参数名:参数类型) : [return type] = {
    // 方法体:一系列的代码
} 
-  
参数列表的参数类型不能省略
 -  
返回值类型可以省略,由scala编译器自动推断
 -  
返回值可以不写return,默认就是{}块表达式的值
 -  
注意:
 -  
如果定义递归方法,必须指定返回值类型
 -  
示例:(方法三)
 -  
定义递归方法(求阶乘)
 -  
10 * 9 * 8 * 7 * 6 * ... * 1
 
  //方法一
  def yy(i: Int = 10, u: Int = 8): Int = {
    return i + u;
  }
  //方法二
  def tt(i: Int) = i * i
  /**
   * 递归必须添加返回值参数
   *
   * @param x
   * @return
   */
  //方法三
  def m1(x: Int): Int = {
    if (x == 1) 1
    else x * m1(x - 1)
  }
  /**
   * 参数值加*,可以传递多个参数
   *
   * @param num
   * @return
   */
  //    方法四
  def add(num: Int*) = num.sum
  def main(args: Array[String]): Unit = {
    
        println(yy(1))
        println(tt(8))
        println(m1(10))
        println(add(1,2,3))
} 
打印的值为
9
64
3628800
6 
6、函数
-  
函数在scala中属于头等公民
-  
数字能做的事,函数也可以
 -  
数字可以作为参数,所以函数也可以作为其他方法或函数的参数
 -  
数字可以作为返回值,所以函数也可以作为其他方法或函数的返回值
 -  
数字可以赋值给一个变量,所以函数也可以赋值给一个变量
 
 -  
 -  
scala支持函数式编程,将来编写Spark/Flink程序中,会大量使用到函数
 -  
语法
 
val 函数变量名 = (参数名:参数类型, 参数名:参数类型....) => 函数体 
注意
-  
函数是一个对象(变量)
 -  
类似于方法,函数也有输入参数和返回值
 -  
函数定义不需要使用def定义
 -  
无需指定返回值类型
 
 val add2 = (x: Int, y: Int) => x * y
  def main(args: Array[String]): Unit = {
        println(add2(2,4))
} 
打印的值为
8 
方法和函数的区别
-  
方法是隶属于类或者对象的,在运行时,它是加载到JVM的==方法区==中
 -  
可以将函数对象赋值给一个变量,在运行时,它是加载到JVM的==堆内存==中
 -  
函数是一个对象,继承自FunctionN,函数对象有apply,curried,toString,tupled这些方法,而方法则没有
 
方法转换为函数
-  
有时候需要将方法转换为函数,作为变量传递,就需要将方法转换为函数
 -  
使用
_即可将方法转换为函数(记得空格) -  
//方法三 def m1(x: Int): Int = { if (x == 1) 1 else x * m1(x - 1) } val hanshu=m1 _; def main(args: Array[String]): Unit = { println(hanshu(10)) }打印的结果
 -  
36288007、数组
 -  
scala中数组的概念是和Java类似,可以用数组来存放同类型的一组数据
 -  
scala中,有两种数组,一种是定长数组,另一种是变长数组
 
(1)、定长数组
-  
定长数组指的是数组的长度是不允许改变的
 -  
数组的元素是可以改变的
 
demo如下
def main(args: Array[String]): Unit = {
    val a = new Array[Int](10)
    println(a)
    a(0)=98
    println(a(0))
    println(a(1))
    println(a.length)
  } 
打印的值为:
[I@ea4a92b
98
0
10 
(2)、变长数组
-  
变长数组指的是数组的长度是可变的,可以往数组中添加、删除元素
 -  
创建变长数组,需要提前导入ArrayBuffer类
 
import scala.collection.mutable.ArrayBuffer 
 
语法
-  
创建空的ArrayBuffer变长数组
 -  
val/var a = ArrayBuffer[元素类型]()创建带有初始元素的ArrayBuffer
 -  
val/var a = ArrayBuffer(元素1,元素2,元素3....)变长数组的增删改操作
 -  
使用
+=添加元素 -  
使用
-=删除元素 -  
使用
++=追加一个数组到变长数组 
demo如下
  def main(args: Array[String]): Unit = {
    //变长数组
    val a = ArrayBuffer[String]();
    a+=("test")
    a+=("张三")
    a++=Array("妞儿","所以算是")
    println(a(3))
    println(a.size)
  }
}
 
打印的值为
所以算是
4 
(3)、遍历数组
可以使用以下两种方式来遍历数组:
-  
使用==for表达式== 直接遍历数组中的元素
 -  
使用 ==索引== 获得数组中的元素
 
for(i <- a) println(i) 
/0 to n    ——包含0,也包含n
for(i <- 0 to a.length -1 ) println(a(i)) 
//0 until n ——生成一系列的数字,包含0,不包含n
for(i <- 0 until a.length) println(a(i)) 
(4)、数组常用操作
scala中的数组封装了丰富的计算操作,将来在对数据处理的时候,不需要我们自己再重新实现。
-  
求和——sum方法
 -  
求最大值——max方法
 -  
求最小值——min方法
 -  
排序——sorted方法
 
 def main(args: Array[String]): Unit = {
    val array = Array(1,3,4,2,5)
    println("求和:"+array.sum)
    println("最大值:"+array.max)
    println("最小值:"+array.min)
    println("排序(获取一个新数组,并翻转):"+array.sorted.reverse(0))
  } 
打印如下
求和:15
最大值:5
最小值:1
排序(获取一个新数组,并翻转):5 
8、元组
元组可以用来包含一组不同类型的值。例如:姓名,年龄,性别,出生年月。
元组的元素是不可变的。
1、定义元组
使用括号来定义元组
val/var 元组变量名称 = (元素1, 元素2, 元素3....) 
使用箭头来定义元素(元组只有两个元素 )
val/var 元组 = 元素1 -> 元素2 
2、访问元组
-  
使用
_1、_2、_3....来访问元组中的元素 -  
元组的index从1开始,_1表示访问第一个元素,依次类推
 
  def main(args: Array[String]): Unit = {
    val a = (1, "张三", 20, "北京市")
    val b = 1 -> 2
    println(a._1)
    println(a._4)
    println(b._1)
  } 
打印的值为
1
北京市
1 
9、映射Map
-  
Map可以称之为映射。它是由键值对组成的集合。scala当中的Map集合与java当中的Map类似,也是key,value对形式的。
 -  
在scala中,Map也分为不可变Map和可变 Map。
 
(1)、不可变map
定义语法
val/var map = Map(键->值, 键->值, 键->值...)    // 推荐这种写法,可读性更好 
val/var map = Map((键, 值), (键, 值), (键, 值), (键, 值)...) 
def main(args: Array[String]): Unit = {
    val map1 = Map("zhangsan"->30, "lisi"->40)
    val map2 = Map(("zhangsan", 30), ("lisi", 30))
    println(map1("lisi"))
    println(map2("zhangsan"))
  } 
打印的值为
40
30 
(2)、可变Map
1、导包
import scala.collection.mutable.Map 
  def main(args: Array[String]): Unit = {
    val map3 = Map("zhangsan" -> 30, "lisi" -> 40)
    //修改一个
    map3("zhangsan") = 50
    println("修改的" + map3)
    //添加一个
    map3 += ("yy" -> 22)
    println("添加的" + map3)
    //删去一个
    map3 -= "yy"
    println("删除的" + map3)
    //拿取到map所有key
    println("拿取到map所有key:" + map3.keys)
    println("拿取到map所有key:" + map3.keySet)
    //获取所有的value
    println("获取所有的value:" + map3.values)
  } 
打印的值为:
修改的Map(lisi -> 40, zhangsan -> 50)
添加的Map(yy -> 22, lisi -> 40, zhangsan -> 50)
删除的Map(lisi -> 40, zhangsan -> 50)
拿取到map所有key:Set(lisi, zhangsan)
拿取到map所有key:Set(lisi, zhangsan)
获取所有的value:HashMap(40, 50) 
(3)、遍历Map
     val map3 = Map("zhangsan" -> 30, "lisi" -> 40)
    //方法一。通过遍历key拿取到值
    for (i <- map3.keys) println(i + "->" + map3(i))
    //方法二,通过元组的方法拿取到值
    for (i <- map3) println(i._1 + "->" + i._2)
    //方法三
    for((k, v) <- map3) println(k + " -> " + v) 
lisi->40
zhangsan->30
lisi->40
zhangsan->30
lisi -> 40
zhangsan -> 30 
10、Set集合
-  
Set是代表没有重复元素的集合。
 -  
Set具备以下性质:
-  
1、元素不重复
 -  
2、不保证插入顺序
 
 -  
 -  
scala中的set集合也分为两种,一种是不可变集合,另一种是可变集合。
 
不可变set集合
//创建一个空的不可变集
val/var 变量名 = Set[类型]()
//给定元素来创建一个不可变集
val/var 变量名 = Set[类型](元素1, 元素2, 元素3...)
 
 def main(args: Array[String]): Unit = {
    val a = Set(1, 1, 2, 3, 4, 5)
    println("a的长度大小"+a.size)
    for(i<-a)println(i)
    println("添加一个元素的新set:",a + 6)
    println("删除一个元素的新set:",a -1)
    println("删除多个元素的新set:",a -- Set(2,3) )
    println("添加多个元素的新set:",a ++ Set(6,7,8)  )
    println("多个Set集合交集的新set:",a & Set(3,4,5,6))
    println(a)
  } 
打印如下:
a的长度大小5
5
1
2
3
4
(添加一个元素的新set:,Set(5, 1, 6, 2, 3, 4))
(删除一个元素的新set:,Set(5, 2, 3, 4))
(删除多个元素的新set:,Set(5, 1, 4))
(添加多个元素的新set:,Set(5, 1, 6, 2, 7, 3, 8, 4))
(多个Set集合交集的新set:,Set(5, 3, 4))
Set(5, 1, 2, 3, 4) 
注意:这里对不可变的set集合进行添加删除等操作,对于该集合来说是没有发生任何变化,这里是生成了新的集合,新的集合相比于原来的集合来说发生了变化
可变Set集合
要使用可变集,必须要手动导入:
import scala.collection.mutable.Set 
11、列表 List
-  
List是scala中最重要的、也是最常用的数据结构。
 -  
List具备以下性质:
-  
1、可以保存重复的值
 -  
2、有先后顺序
 
 -  
 -  
在scala中,也有两种列表,一种是不可变列表、另一种是可变列表
 -  
不可变列表就是列表的元素、长度都是不可变的
 -  
语法
不可变列表
 - 使用 List(元素1, 元素2, 元素3, ...) 来创建一个不可变列表,语法格式
 
val/var 变量名 = List(元素1, 元素2, 元素3...)
//使用 Nil 创建一个不可变的空列表
val/var 变量名 = Nil
//使用 :: 方法创建一个不可变列表
val/var 变量名 = 元素1 :: 元素2 :: Nil 
 def main(args: Array[String]): Unit = {
    val list1 = List(1, 2, 3, 4)
    val list2 = Nil
    val list3= 1::2::3::Nil
    println(list1(0))
    println(list3)
  } 
打印的值为
1
List(1, 2, 3)
 
可变列表
1、使用ListBuffer元素类型 创建空的可变列表,语法结构
val/var 变量名 = ListBuffer[Int]() 
2、使用ListBuffer(元素1, 元素2, 元素3...)创建可变列表,语法结构
val/var 变量名 = ListBuffer(元素1,元素2,元素3...) 
    val a = ListBuffer[Int]()
    val b = ListBuffer(1, 2, 3, 4)
    println(b(0))
    println("list数组首部:", b.head)
    println("获取除了第一个元素外其他元素组成的列表", b.tail)
    b += 5
    println("添加应元素", b)
    b ++= List(6, 7)
    println("添加一个不可变列表", b)
    b ++= ListBuffer(8, 9)
    println("添加一个可变列表", b)
    b -= 9
    println("删除单个元素", b)
    b --= List(7,8)
    println("删除一个不可变的列表存在的元素", b)
    b --= ListBuffer(5,6)
    println("删除一个可变的列表存在的元素", b)
    println("toList根据可变的列表生成一个不可变列表",b.toList)
    println("toList根据可变的列表生成一个不可变列表,原列表不变",b)
    println("toArray根据可变的列表生成一个新的不可变数组",b.toArray)
    println("toArray根据可变的列表生成一个新的不可变数组,原列表不变",b) 
打印如下
1
(list数组首部:,1)
(获取除了第一个元素外其他元素组成的列表,ListBuffer(2, 3, 4))
(添加应元素,ListBuffer(1, 2, 3, 4, 5))
(添加一个不可变列表,ListBuffer(1, 2, 3, 4, 5, 6, 7))
(添加一个可变列表,ListBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9))
(删除单个元素,ListBuffer(1, 2, 3, 4, 5, 6, 7, 8))
(删除一个不可变的列表存在的元素,ListBuffer(1, 2, 3, 4, 5, 6))
(删除一个可变的列表存在的元素,ListBuffer(1, 2, 3, 4))
(toList根据可变的列表生成一个不可变列表,List(1, 2, 3, 4))
(toList根据可变的列表生成一个不可变列表,原列表不变,ListBuffer(1, 2, 3, 4))
(toArray根据可变的列表生成一个新的不可变数组,[I@3567135c)
(toArray根据可变的列表生成一个新的不可变数组,原列表不变,ListBuffer(1, 2, 3, 4)) 
12、函数式编程
-  
我们将来使用Spark/Flink的大量业务代码都会使用到函数式编程。
 -  
下面的这些操作是学习的重点,先来感受下如何进行函数式编程以及它的强大
 
(1)、遍历 - foreach
方法描述
foreach(f: (A) ⇒ Unit): Unit 
| foreach | API | 说明 | 
|---|---|---|
| 参数 | f: (A) ⇒ Unit | 接收一个函数对象作为参数 函数的输入参数为集合的元素 返回值为空 | 
| 返回值 | Unit | 空 | 
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4)
    //定义一个匿名函数传入到foreach方法中
    list.foreach((u: Int) => println(u))
    //匿名函数的输入参数类型可以省略,由编译器自动推断
    list.foreach(u => println(u))
    //  当函数参数,只在函数体中出现一次,而且函数体没有嵌套调用时,可以使用下划线来简化函数定义
    list.foreach(println(_))
    //最简单直接
    list.foreach(println)
  } 
(2)、映射 - map
-  
集合的映射操作是将来在编写Spark/Flink用得最多的操作,是我们必须要掌握。
 -  
方法描述
 
def map[B](f: (A) ⇒ B): TraversableOnce[B] 
-  
方法说明
 
| ap方法 | API | 说明 | 
|---|---|---|
| 泛型 | [B] | 指定map方法最终返回的集合泛型 | 
| 参数 | f: (A) ⇒ B | 传入一个函数对象作为参数 该函数接收一个类型A(要转换的集合的元素类型) 返回值为类型B | 
| 返回值 | TraversableOnce[B] | B类型的集合 | 
    val list = List(1, 2, 3, 4)
    //定义一个匿名函数
    val b=list.map((i:Int)=>i*10)
    println(b)
    //省略匿名函数参数类型
    val c=list.map(i=>i*10)
    println(c)
    //最简单用下划线的方法
    val d=  list.map(_ * 10)
    println(d) 
打印结果如下
List(10, 20, 30, 40)
List(10, 20, 30, 40)
List(10, 20, 30, 40) 
(3)、扁平化映射 - flatmap
-  
映射扁平化也是将来用得非常多的操作,也是必须要掌握的。
 -  
方法描述
 
def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): TraversableOnce[B] 
方法说明
| flatmap方法 | API | 说明 | 
|---|---|---|
| 泛型 | [B] | 最终要转换的集合元素类型 | 
| 参数 | f: (A) ⇒ GenTraversableOnce[B] | 传入一个函数对象作为参数 函数的参数是集合的元素 函数的返回值是一个集合 | 
| 返回值 | TraversableOnce[B] | B类型的集合 | 
def main(args: Array[String]): Unit = {
    val list = List("hadoop hive spark flink", "hbase spark")
    val tt = list.flatMap(x => x.split(" "));
    println(tt)
    //简写
    val t2 = list.flatMap(_.split(" "))
    println(t2)
    //flatMap该方法其本质是先进行了map 然后又调用了flatten
    val t3 = list.map(_.split(" ")).flatten
    println(t3)
  } 
打印结果如下
List(hadoop, hive, spark, flink, hbase, spark)
List(hadoop, hive, spark, flink, hbase, spark)
List(hadoop, hive, spark, flink, hbase, spark) 
(4)、过滤 - filter
-  
过滤符合一定条件的元素
 -  
方法描述
 
def filter(p: (A) ⇒ Boolean): TraversableOnce[A] 
方法说明
| filter方法 | API | 说明 | 
|---|---|---|
| 参数 | p: (A) ⇒ Boolean | 传入一个函数对象作为参数 函数的参数是集合中的元素 此函数返回布尔类型,满足条件返回true, 不满足返回false | 
| 返回值 | TraversableOnce[A] | 列表 | 
demo展示
def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    ///过滤出集合中大于5的元素
    val tt=list.filter(x=>x>5)
    println(tt)
    //把集合中大于5的元素取出来乘以10生成一个新的list集合
    val t3=list.filter(x=>x>5).map(u=>u*10)
    println(t3)
  } 
打印的值为:
List(6, 7, 8, 9, 10)
List(60, 70, 80, 90, 100) 
(5)、排序 - sort
在scala集合中,可以使用以下几种方式来进行排序
-  
sorted默认排序
 -  
sortBy指定字段排序
 -  
sortWith自定义排序
 
sorted默认排序
 def main(args: Array[String]): Unit = {
    val list = List(5, 1, 2, 4, 3)
    println(list.sorted)
  }
 
List(1, 2, 3, 4, 5) 
sortBy指定字段排序
def sortBy[B](f: (A) ⇒ B): List[A] 
| sortBy方法 | API | 说明 | 
|---|---|---|
| 泛型 | [B] | 按照什么类型来进行排序 | 
| 参数 | f: (A) ⇒ B | 传入函数对象作为参数 函数接收一个集合类型的元素为参数 返回B类型的元素进行排序 | 
| 返回值 | List[A] | 返回排序后的列表 | 
结果如下:
 val list2 = List("1 hadoop", "2 spark", "3 flink")
    println(list2.sortBy(x=>x.split(" ")(0))) 
打印的结果如下:
List(1 hadoop, 2 spark, 3 flink) 
sortWith自定义排序
-  
自定义排序,根据一个函数来进行自定义排序
 -  
方法描述
 
def sortWith(lt: (A, A) ⇒ Boolean): List[A] 
| sortWith方法 | API | 说明 | 
|---|---|---|
| 参数 | lt: (A, A) ⇒ Boolean | 传入一个比较大小的函数对象作为参数 函数接收两个集合类型的元素作为参数 返回两个元素大小,小于返回true,大于返回false | 
| 返回值 | List[A] | 返回排序后的列表 | 
   val list3 = List(2, 3, 1, 6, 4, 5)
    //降序
    var tt = list3.sortWith((x, y) => x > y)
    println(tt)
    //升序
    var tt2 = list3.sortWith((x, y) => x < y)
    println(tt2)
    //简写
    var tt3 =list3.sortWith(_ > _)
    println(tt3) 
打印的结果:
List(6, 5, 4, 3, 2, 1)
List(1, 2, 3, 4, 5, 6)
List(6, 5, 4, 3, 2, 1) 
 
(5)、聚合 - reduce
-  
reduce表示将列表,传入一个函数进行聚合计算
 -  
方法描述
 
def reduce[A1 >: A](op: (A1, A1) ⇒ A1): A1 
| reduce方法 | API | 说明 | 
|---|---|---|
| 泛型 | [A1 >: A] | (下界)A1必须是集合元素类型的子类 | 
| 参数 | op: (A1, A1) ⇒ A1 | 传入函数对象,用来不断进行聚合操作 第一个A1类型参数为:当前聚合后的变量 第二个A1类型参数为:当前要进行聚合的元素 | 
| 返回值 | A1 | 列表最终聚合为一个元素 | 
def main(args: Array[String]): Unit = {
    val a = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    //基础的写法
    var b = a.reduce((x, y) => x + y)
    println("基础的写法"+b)
    //简单的写法
    var c = a.reduce(_ + _)
    println("简单的写法"+c)
    //从左往右计算
    var d = a.reduceLeft(_ + _)
    println("从左往右计算"+d)
     //从右往左计算
    var f = a.reduceRight(_ + _)
    println("从右往左计算"+f)
  }
 
基础的写法55
简单的写法55
从左往右计算55
从右往左计算55 
(6)、折叠 - fold
fold与reduce很像,但是多了一个指定初始值参数
def fold[A1 >: A](z: A1)(op: (A1, A1) ⇒ A1): A1 
| reduce方法 | API | 说明 | 
|---|---|---|
| 泛型 | [A1 >: A] | (下界)A1必须是集合元素类型的子类 | 
| 参数1 | z: A1 | 初始值 | 
| 参数2 | op: (A1, A1) ⇒ A1 | 传入函数对象,用来不断进行折叠操作 第一个A1类型参数为:当前折叠后的变量 第二个A1类型参数为:当前要进行折叠的元素 | 
| 返回值 | A1 | 列表最终折叠为一个元素 | 
def main(args: Array[String]): Unit = {
    val a = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    var e = a.fold(0)(_ + _)
    println("简单折叠:",e)
    var g = a.fold(5)(_ + _)
    println("给定一个初始值,,折叠求和:"+g)
    var h=  a.foldLeft(10)(_ + _)
    println("从左往右折叠:",h)
  } 
(简单折叠:,55)
给定一个初始值,,折叠求和:60
(从左往右折叠:,65) 
13、高阶函数
使用函数值作为参数,或者返回值为函数值的“函数”和“方法”,均称之为“高阶函数”。
1、函数值作为参数
  def main(args: Array[String]): Unit = {
    //定义一个数组
    val array = Array(1, 2, 3, 4, 5)
    //定义一个函数
    val func = (x: Int) => x * 10
    //函数作为参数传递到方法中
    val tt=  array.map(func)
    println("通过map转化的新数组:",tt)
    println("通过map转化的新数组最大:",tt.max)
  } 
打印的值为:
(通过map转化的新数组:,[I@17550481)
(通过map转化的新数组:,50) 
2、匿名函数
一个没有名称的函数----匿名函数
 def main(args: Array[String]): Unit = {
    val array = Array(1, 2, 3, 4, 5)
    var a = array.map(x => x * 10)
    println(a.max)
  } 
打印的值为:
50 
3、柯里化
方法可以定义多个参数列表,当使用较少的参数列表调用多参数列表的方法时,会产生一个新的函数,该函数接收剩余的参数列表作为其参数。这被称为柯里化。
 def main(args: Array[String]): Unit = {
    def getAddress(a: String): (String, String) => String = {
      (b: String,c: String) => a + "-" + b + "-" + c
    }
    var b=getAddress("china");
    var c=b("beijing","tianAnMen")
    println(c)
  } 
china-beijing-tianAnMen 
 

4、闭包
函数里面引用外面类成员变量叫作闭包
  def main(args: Array[String]): Unit = {
    var factor = 1
    val f1 = (x: Int) => x * factor
    println(f1(8))
    factor=2
    println(f1(8))
  } 
8
16 
//定义的函数f1,它的返回值是依赖于不在函数作用域的一个变量
 //后期必须要要获取到这个变量才能执行
 //spark和flink程序的开发中大量的使用到函数,函数的返回值依赖的变量可能都需要进行大量的网络传输获取得到。这里就需要这些变量实现序列化进行网络传输。
 def main(args: Array[String]): Unit = {
   
    def multiply(x: Double) = (y: Double) => x * y
    //先进行科尔化
    val doubleFunc = multiply(2)
    val tripleFunc = multiply(3)
    //    再对闭包进行计算
    var u = doubleFunc(10)
    var u2 = tripleFunc(10)
    println(u)
    println(u2)
  } 
20.0
30.0 
14、scala面向对象编程之类
1、类的定义
scala是支持面向对象的,也有类和对象的概念。
-  
定义一个Customer类,并添加成员变量/成员方法
 -  
添加一个main方法,并创建Customer类的对象,并给对象赋值,打印对象中的成员,调用成员方法
 
与java相比,他无需get set方法,就可以对变量进行赋值封装操作
import java.util.Date
class Customer {
  var name: String = _
  var sex: String = _
  val registerDate: Date = new Date
  def sayHi(msg: String) = {
    println(msg)
  }
}
object Main {
  def main(args: Array[String]): Unit = {
    val customer = new Customer
    //给对象的成员变量赋值
    customer.name = "张三"
    customer.sex = "男"
    println(s"姓名: ${customer.name}, 性别:${customer.sex}, 注册时间: ${customer.registerDate}")
    //对象调用方法
    customer.sayHi("你好!")
  }
} 
打印的结果为:
姓名: 张三, 性别:男, 注册时间: Fri Apr 22 16:23:21 CST 2022
你好! 
 
 
2、类的构造器
主构造器
-  
主构造器是指在类名的后面跟上一系列参数,例如
 
class 类名(var/val 参数名: 类型 = 默认值, var/val 参数名: 类型 = 默认值){
    // 构造代码块
} 
辅助构造器
-  
在类中使用this来定义,例如
def this(参数名: 类型, 参数名: 类型) { ... }package com.testScala.D_lei class Student(val name: String, val age: Int) { val address: String = "beijing" // 定义一个参数的辅助构造器 def this(name: String) { // 辅助构造器的第一行必须调用主构造器或其他辅助构造器或者super父类的构造器 this(name, 20) } def this(age: Int) { this("某某某", age) } } object test { def main(args: Array[String]): Unit = { val tt = new Student("张三"); println(s"name等于:${tt.name},address:${tt.address},age:${tt.age}") } }15、scala面向对象编程之对象
 
1、scala中的object
-  
scala中是没有Java中的静态成员的。如果将来我们需要用到static变量、static方法,就要用到scala中的单例对象object
 -  
定义object
-  
定义单例对象和定义类很像,就是把class换成object
 
 -  
 -  
演示
-  
定义一个工具类,用来格式化日期时间
 
 -  
 
object DateUtils {
//  / 在object中定义的成员变量,相当于Java中定义一个静态变量
  // 定义一个SimpleDateFormat日期时间格式化对象
  val simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
  // 构造代码
  println("构造代码")
  // 相当于Java中定义一个静态方法
  def format(date: Date) = simpleDateFormat.format(date)
  // main是一个静态方法,所以必须要写在object中
  def main(args: Array[String]): Unit = {
    println { DateUtils.format(new Date()) };
  }
} 
 
2、scala中的伴生对象
-  
在==同一个scala文件,有一个class和object具有同样的名字==,那么就称这个object是class的伴生对象,class是object的伴生类;
 -  
伴生类和伴生对象的最大特点是,可以相互访问;
 
package com.testScala.E_duixiang
class Dog {
  val id = 1
  private var name = "kkb"
  def printName(): Unit = {
    //在Dog类中可以访问伴生对象Dog的私有属性
    println(Dog.CONSTANT + name )
  }
}
object Dog{
  //伴生对象中的私有属性
  private val CONSTANT = "汪汪汪 : "
  def main(args: Array[String]) {
    val dog = new Dog
    //访问私有的字段name
    dog.name = "123"
    dog.printName()
  }
} 
汪汪汪 : 123 
 
3、scala中object的apply方法
-  
:==实现伴生对象Array的apply方法==
 -  
伴生对象的apply方法用来快速地创建一个伴生类的对象。
 
package com.testScala.E_duixiang
class Person(var name: String, var age: Int) {
  override def toString = s"Person($name, $age)"
}
object Person {
  // 实现apply方法
  // 返回的是伴生类的对象
  def apply(name: String, age: Int): Person = new Person(name, age)
  // apply方法支持重载
  def apply(name: String): Person = new Person(name, 20)
  def apply(age: Int): Person = new Person("某某某", age)
  def apply(): Person = new Person("某某某", 20)
}
object Main2 {
  def main(args: Array[String]): Unit = {
    val p1 = Person("张三", 20)
    val p2 = Person("李四")
    val p3 = Person(100)
    val p4 = Person()
    println(p1)
    println(p2)
    println(p3)
    println(p4)
  }
} 
Person(张三, 20)
Person(李四, 20)
Person(某某某, 100)
Person(某某某, 20) 
 
4、scala中object的main方法
-  
scala和Java一样,如果要运行一个程序,必须有一个main方法。
 -  
而在Java中main方法是静态的,而在scala中没有静态方法。
 
object Main1{
  def main(args: Array[String]) = {
    println("hello, scala")
  }
} 
16、scala面向对象编程之继承
1、继承extends
-  
scala和Java一样,使用extends关键字来实现继承。可以在子类中定义父类中没有的字段和方法,或者重写父类的方法。
 -  
package com.testScala.F_jicheng class Person1 { var name = "jianghaojie" def getName = this.name } class Student extends Person1 object main1 { def main(args: Array[String]): Unit = { val p1 = new Person1() val p2 = new Student() p2.name = "蒋皓洁" println(p1.getName) println(p2.getName) } }jianghaojie 蒋皓洁2、override和super
 如果子类要覆盖父类中的一个非抽象方法,必须要使用override关键字
 -  
可以使用override关键字来重写一个val字段
 -  
可以使用super关键字来访问父类的成员
 -  
==示例1:class继承class==
 
class Person3 {
  val name = "super"
  def getName = name
}
class Student3 extends Person3 {
  // 重写val字段
  override val name: String = "child"
  // 重写getName方法
  override def getName: String = "hello, " + super.getName
}
object Main3 {
  def main(args: Array[String]): Unit = {
    val p = new Student3();
    println(p.name)
    println(p.getName)
  }
}
 
child
hello, child 
3、isInstanceOf和asInstanceOf
-  
我们经常要在代码中进行类型的判断和类型的转换。在Java中,我们可以使用instanceof关键字、以及(类型)object来实现,在scala中如何实现呢?
 -  
scala中对象提供==isInstanceOf==和==asInstanceOf==方法。
-  
isInstanceOf判断对象是否为指定类的对象
 -  
asInstanceOf将对象转换为指定类型
 
 -  
 
| Java | Scala | |
|---|---|---|
| 判断对象是否是C类型 | obj instanceof C | obj.isInstanceof[C] | 
| 将对象强转成C类型 | (C ) obj | obj.asInstanceof[C] | 
| 获取类型为T的class对象 | C.class | classOf[C] | 
-  
==示例==
 
class Person4
class Student4 extends Person4
object Main4 {
  def main(args: Array[String]): Unit = {
    val s1: Person4 = new Student4
    // 判断s1是否为Student4类型
    if(s1.isInstanceOf[Student4]) {
      // 将s1转换为Student3类型
      val s2 =  s1.asInstanceOf[Student4]
      println(s2)
    }
  }
} 
com.testScala.F_jicheng.Student4@ea4a92b 
4、getClass和classOf
isInstanceOf 只能判断出对象是否为指定类以及其子类的对象,而不能精确的判断出,对象就是指定类的对象。如果要求精确地判断出对象就是指定类的对象,那么就只能使用 getClass 和 classOf 。
-  
对象.getClass可以精确获取对象的类型
 -  
classOf[x]可以精确获取类型
 -  
使用==操作符就可以直接比较
 
class Person5
class Student5 extends Person5
object Student5{
  def main(args: Array[String]) {
    val p: Person5 = new Student5
    //判断p是否为Person5类的实例
    println(p.isInstanceOf[Person5])//true
    //判断p的类型是否为Person5类
    println(p.getClass == classOf[Person5])//false
    //判断p的类型是否为Student5类
    println(p.getClass == classOf[Student5])//true
  }
} 
5、访问修饰符
java中的访问控制,同样适用于scala,可以在成员前面添加private/protected关键字来控制成员的可见性。但在scala中,==没有public关键字,任何没有被标为private或protected的成员都是公共的==。
  
==private[this]修饰符==
-  
被修饰的成员只能在当前类中被访问。或者可以理解为:
只能通过this.来访问(在当前类中访问成员会自动添加this.) 
package com.testScala.F_jicheng
class Person6 {
  // 只有在当前对象中能够访问
  private[this] var name = "super"
  def getName = this.name	// 正确!
  def sayHelloTo(p: Person6) = {
    println("hello" + p.name)     // 报错!无法访问
  }
}
object Person6 {
  def showName(p: Person6) = println(p.name)  // 报错!无法访问
}
 
protected[this]修饰符==
-  
==被修饰的成员只能在当前类和当前类的子类中被访问==。也可以理解为:当前类通过this.访问或者子类通过this.访问
 -  
示例
 
package com.testScala.F_jicheng
class Person7 {
  // 只有在当前类以及继承该类的当前对象中能够访问
  protected[this] var name = "super"
  def getName = {
    // 正确!
    this.name
  }
  def sayHelloTo1(p: Person7) = {
    // 编译错误!无法访问
//    println(p.name)
  }
}
object Person7 {
  def sayHelloTo3(p: Person7) = {
    // 编译错误!无法访问
//    println(p.name)
  }
}
class Student7 extends Person7 {
  def showName = {
    // 正确!
    println(name)
  }
  def sayHelloTo2(p: Person7) = {
    // 编译错误!无法访问
//    println(p.name)
  }
}
object  yy{
  def main(args: Array[String]): Unit = {
    var yu=    new Student7();
    yu.showName
//    println()
  }
} 
6、调用父类的constructor
-  
==实例化子类对象,必须要调用父类的构造器==,在scala中,只能在子类的
主构造器中调用父类的构造器 -  
示例
 
package com.testScala.F_jicheng
class Person8(var name: String){
  println("name:"+name)
}
// 直接在子类的类名后面调用父类构造器
class Student8(name: String, var clazz: String) extends Person8(name)
object Main8 {
  def main(args: Array[String]): Unit = {
    val s1 = new Student8("张三", "三年二班")
    println(s"${s1.name} - ${s1.clazz}")
  }
}
 
name:张三
张三 - 三年二班 
7、抽象类
-  
如果类的某个成员在当前类中的定义是不包含完整的,它就是一个抽象类
 -  
不完整定义有两种情况:
-  
1.方法没有方法体
 -  
2.变量没有初始化
 
 -  
 -  
没有方法体的方法称为抽象方法,没有初始化的变量称为抽象字段。定义抽象类和Java一样,在类前面加上abstract关键字就可以了
 
package com.testScala.F_jicheng
abstract class Person9(val name: String) {
  //抽象方法
  def sayHello: String
  def sayBye: String
  //抽象字段
  val address: String
}
class Student9(name: String) extends Person9(name){
  //重写抽象方法或字段,def前不必加override关键字
  def sayHello: String = "Hello," + name
  def sayBye: String ="Bye," + name
  //重写抽象字段
   val address: String = "beijing "
}
object Main9{
  def main(args: Array[String]) {
    val s = new Student9("tom")
    println(s.sayHello)
    println(s.sayBye)
    println(s.address)
  }
}
 
Hello,tom
Bye,tom
beijing  
17、scala面向对象编程之trait特质
-  
特质是scala中代码复用的基础单元
 -  
它可以将方法和字段定义封装起来,然后添加到类中
 -  
与类继承不一样的是,类继承要求每个类都只能继承
一个超类,而一个类可以添加任意数量的特质。 -  
特质的定义和抽象类的定义很像,但它是使用
trait关键字 
1、作为接口使用
-  
使用
extends来继承trait(scala不论是类还是特质,都是使用extends关键字) -  
如果要继承多个trait,则使用
with关键字 
==示例一:继承单个trait==
trait Logger1 {
  // 抽象方法
  def log(msg: String)
}
class ConsoleLogger1 extends Logger1 {
  override def log(msg: String): Unit = println(msg)
}
object LoggerTrait1 {
  def main(args: Array[String]): Unit = {
    val logger = new ConsoleLogger1
    logger.log("控制台日志: 这是一条Log")
  }
} 
控制台日志: 这是一条Log 
==示例二:继承多个trait==
trait Logger2 {
  // 抽象方法
  def log(msg: String)
}
trait MessageSender {
  def send(msg: String)
}
class ConsoleLogger2 extends Logger2 with MessageSender {
  override def log(msg: String): Unit = println(msg)
  override def send(msg: String): Unit = println(s"发送消息:${msg}")
}
object LoggerTrait2 {
  def main(args: Array[String]): Unit = {
    val logger = new ConsoleLogger2
    logger.log("控制台日志: 这是一条Log")
    logger.send("你好!")
  }
} 
控制台日志: 这是一条Log
发送消息:你好! 
2、定义具体的方法
-  
和类一样,trait中还可以定义具体的方法。
 -  
==示例==
 
package com.testScala.G_trait
trait LoggerDetail {
  // 在trait中定义具体方法
  def log(msg: String) = println(msg)
}
class PersonService extends LoggerDetail {
  def add() = log("添加用户")
}
object MethodInTrait {
  def main(args: Array[String]): Unit = {
    val personService = new PersonService
    personService.add()
  }
} 
添加用户
 
3、定义具体方法和抽象方法
-  
在trait中,可以混合使用具体方法和抽象方法
 -  
使用具体方法依赖于抽象方法,而抽象方法可以放到继承trait的子类中实现,这种设计方式也称为模板模式
 
==示例==
package com.testScala.G_trait
trait Logger3 {
  // 抽象方法
  def log(msg: String)
  // 具体方法(该方法依赖于抽象方法log
  def info(msg: String) = log("INFO:" + msg)
  def warn(msg: String) = log("WARN:" + msg)
  def error(msg: String) = log("ERROR:" + msg)
}
class ConsoleLogger3 extends Logger3 {
  override def log(msg: String): Unit = println(msg)
}
object LoggerTrait3 {
  def main(args: Array[String]): Unit = {
    val logger3 = new ConsoleLogger3
    logger3.log("这是一条日志信息")
    logger3.info("这是一条普通信息")
    logger3.warn("这是一条警告信息")
    logger3.error("这是一条错误信息")
  }
}
 
这是一条日志信息
INFO:这是一条普通信息
WARN:这是一条警告信息
ERROR:这是一条错误信息 
4、定义具体字段和抽象字段
-  
在trait中可以定义具体字段和抽象字段
 -  
继承trait的子类自动拥有trait中定义的字段
 -  
字段直接被添加到子类中
 
package com.testScala.G_trait
import java.text.SimpleDateFormat
import java.util.Date
trait LoggerEx {
  // 具体字段
  val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm")
  val INFO = "信息:" + sdf.format(new Date)
  // 抽象字段
  val TYPE: String
  // 抽象方法
  def log(msg: String)
}
class ConsoleLoggerEx extends LoggerEx {
  // 实现抽象字段
  override val TYPE: String = "控制台"
  // 实现抽象方法
  override def log(msg: String): Unit = print(s"$TYPE$INFO $msg")
}
object FieldInTrait {
  def main(args: Array[String]): Unit = {
    val logger = new ConsoleLoggerEx
    logger.log("这是一条消息")
  }
}
 
控制台信息:2022-04-24 15:05 这是一条消息 
5、实例对象混入trait
-  
trait还可以混入到
实例对象中,给对象实例添加额外的行为 -  
只有混入了trait的对象才具有trait中的方法,其他的类对象不具有trait中的行为
 -  
使用with将trait混入到实例对象中
 -  
==示例==
 
package com.testScala.G_trait
trait LoggerMix {
  def log(msg: String) = println(msg)
}
class UserService
object FixedInClass {
  def main(args: Array[String]): Unit = {
    // 使用with关键字直接将特质混入到对象中
    val userService = new UserService with LoggerMix
    userService.log("你好")
  }
}
 
你好
 
18、模式匹配和样例类
-  
scala有一个十分强大的模式匹配机制,可以应用到很多场合。
-  
switch语句
 -  
类型查询
 -  
以及快速获取数据
 
 -  
 -  
并且scala还提供了样例类,对模式匹配进行了优化,可以快速进行匹配。
 
1、匹配字符串
import scala.util.Random
//todo:匹配字符串
object CaseDemo01 extends App{
  //定义一个数组
  val arr = Array("hadoop", "zookeeper", "spark", "storm")
  //随机取数组中的一位,使用Random.nextInt
  val name = arr(Random.nextInt(arr.length))
  println(name)
  name match {
    case "hadoop"     => println("大数据分布式存储和计算框架...")
    case "zookeeper"  => println("大数据分布式协调服务框架...")
    case "spark"      => println("大数据分布式内存计算框架...")
    //表示以上情况都不满足才会走最后一个
    case _            => println("我不认识你")
  }
}
 
hadoop
大数据分布式存储和计算框架... 
2、匹配类型
//todo:匹配类型
object CaseDemo02 extends App{
  //定义一个数组
  val arr = Array("hello", 1, -2.0, CaseDemo02)
  //随机获取数组中的元素
  val value = arr(Random.nextInt(arr.length))
  println(value)
  value match {
    case x: Int                => println("Int=>"+x)
    case y: Double if(y >= 0)    => println("Double=>"+y)
    case z: String             => println("String=>"+z)
    case _                     => throw new Exception("not match exception")
  }
}
 
hello
String=>hello 
3、匹配数组
package com.testScala.H_pipei
//匹配数组
object CaseDemo03 extends App{
  //匹配数组
  val arr = Array(1, 3, 5)
  arr match {
    case Array(1, x, y) => println(x + "---" + y)
    case Array(1, _*)   => println("1...")
    case Array(0)       => println("only 0")
    case _              => println("something else")
  }
}
 
1... 
4、匹配集合
//匹配集合
object CaseDemo04 extends App{
  val list = List(0, 3, 6)
  list match {
    case 0::Nil        => println("only 0")
    case 0::tail       => println("0....")
    case x::y::z::Nil  => println(s"x:$x y:$y z:$z")
    case _             => println("something else")
  }
} 
0.... 
5、匹配元组
package com.testScala.H_pipei
//匹配元组
object CaseDemo05 extends App{
  val tuple = (1, 3, 5)
  tuple match {
    case (1, x, y)    => println(s"1,$x,$y")
    case (2, x, y)    => println(s"$x,$y")
    case _            => println("others...")
  }
} 
1,3,5 
6、样例类
-  
样例类是一种特殊类,它可以用来快速定义一个用于保存数据的类(类似于Java POJO类),==而且它会自动生成apply方法,允许我们快速地创建样例类实例对象==。后面在并发编程和spark、flink这些框架也都会经常使用它。
 -  
定义样例类
 -  
语法结构
 
package com.testScala.H_pipei
// 定义一个样例类
// 样例类有两个成员name、age
case class CasePerson(name: String, age: Int)
// 使用var指定成员变量是可变的
case class CaseStudent(var name: String, var age: Int)
object CaseClassDemo {
  def main(args: Array[String]): Unit = {
    // 1. 使用new创建实例
    val zhagnsan = new CasePerson("张三", 20)
    println(zhagnsan)
    // 2. 使用类名直接创建实例
    val lisi = CasePerson("李四", 21)
    println(lisi)
    // 3. 样例类默认的成员变量都是val的,除非手动指定变量为var类型
    //lisi.age = 22  // 编译错误!age默认为val类型
    val xiaohong = CaseStudent("小红", 23)
    xiaohong.age = 24
    println(xiaohong)
  }
} 
CasePerson(张三,20)
CasePerson(李四,21)
CaseStudent(小红,24) 
样例对象
-  
使用case object可以创建样例对象。样例对象是单例的,而且它没有主构造器。样例对象是可序列化的。格式:
 
case class SendMessage(text: String)
// 消息如果没有任何参数,就可以定义为样例对象
case object startTask
case object PauseTask
case object StopTask 
case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
case object CheckTimeOutTask
object CaseDemo06 extends App{
  val arr = Array(CheckTimeOutTask,
    HeartBeat(10000),
    SubmitTask("0001", "task-0001"))
  arr(Random.nextInt(arr.length)) match {
    case SubmitTask(id, name) => println(s"id=$id, name=$name")
    case HeartBeat(time)      => println(s"time=$time")
    case CheckTimeOutTask     => println("检查超时")
  }
} 
检查超时 
7、Option类型
-  
在Scala中Option类型用样例类来表示可能存在或也可能不存在的值
 -  
Option类型有2个子类
 -  
一个是Some
-  
Some包装了某个值
 
 -  
 

-  
一个是None
-  
None表示没有值
 
 -  
 
 
object TestOption {
  def main(args: Array[String]) {
    val map = Map("a" -> 1, "b" -> 2)
    val value: Option[Int] = map.get("b")
    println(value)
    val v1 = value match {
      case Some(i) => i
      case None => 0
    }
    println(v1)
    //更好的方式
    val v2 = map.getOrElse("c", 0)
    println(v2)
  }
} 
Some(2)
2
0 
8、偏函数
-  
被包在花括号内==没有match的一组case语句==是一个偏函数
 -  
它是PartialFunction[A, B]的一个实例,
-  
A代表输入参数类型
 -  
B代表返回结果类型
 -  
可以理解为:偏函数是一个参数和一个返回值的函数。
 
 -  
 -  
==示例==
 
object TestPartialFunction {
  // func1是一个输入参数为Int类型,返回值为String类型的偏函数
  val func1: PartialFunction[Int, String] = {
    case 1 => "一个"
    case 2 => "二个"
    case 3 => "三个"
    case _ => "其他"
  }
  def main(args: Array[String]): Unit = {
    println(func1(1))
    val list = List(1, 2, 3, 4, 5, 6)
    //使用偏函数操作
    val result = list.filter{
      case x if x >3 => true
      case _ => false
    }
    println(result)
  } 
19、异常处理
def main(args: Array[String]): Unit = {
   val i = 10 / 0
    
    println("你好!")
  }
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at ForDemo$.main(ForDemo.scala:3)
	at ForDemo.main(ForDemo.scala) 
执行程序,可以看到scala抛出了异常,而且没有打印出来"你好"。说明程序出现错误后就终止了。那怎么解决该问题呢?
1、 捕获异常
-  
在scala中,可以使用异常处理来解决这个问题。
-  
在Scala里,借用了==模式匹配的思想来做异常的匹配==
 -  
以下为scala中try...catch异常处理的语法格式:
 
 -  
 
-  
try中的代码是我们编写的业务处理代码
 -  
在catch中表示当出现某个异常时,需要执行的代码
 -  
在finally中,是不管是否出现异常都会执行的代码
 
2、抛出异常
-  
我们也可以在一个方法中,抛出异常。语法格式和Java类似,使用throw new Exception...
 
-  
==示例==
 
  def main(args: Array[String]): Unit = {
    throw new Exception("这是一个异常")
  }
Exception in thread "main" java.lang.Exception: 这是一个异常
	at ForDemo$.main(ForDemo.scala:3)
	at ForDemo.main(ForDemo.scala) 
20、提取器(Extractor)
-  
=提取器是从传递给它的对象中提取出构造该对象的参数==。(回想样例类进行模式匹配提取参数)
 -  
scala 提取器是一个带有unapply方法的对象。
-  
==unapply方法算是apply方法的反向操作==
 -  
unapply接受一个对象,然后从对象中提取值,提取的值通常是用来构造该对象的值。
 
 -  
 
package com.testScala.I_Extractor
class Student {
  var name: String = _   // 姓名
  var age: Int = _       // 年龄
  // 实现一个辅助构造器
  def this(name: String, age: Int) = {
    this()
    this.name = name
    this.age = age
  }
}
object Student {
  def apply(name: String, age: Int): Student = new Student(name, age)
  // 实现一个解构器
  def unapply(arg: Student): Option[(String, Int)] = Some(arg.name, arg.age)
}
object extractor_DEMO {
  def main(args: Array[String]): Unit = {
    val zhangsan = Student("张三", 20)
    zhangsan match {
      case Student(name, age) => println(s"姓名:$name 年龄:$age")
      case _ => println("未匹配")
    }
  }
}
 
姓名:张三 年龄:20 
21、泛型
scala和Java一样,类和特质、方法都可以支持泛型。我们在学习集合的时候,一般都会涉及到泛型。
在scala中,使用方括号来定义==类型参数==。
1、定义一个泛型方法
object learn01 {
  def getMiddle[A](arr: Array[A]) = arr(arr.length / 2)
  def main(args: Array[String]): Unit = {
    val arr1 = Array(1, 2, 3, 4, 5)
    val arr2 = Array("a", "b", "c", "d", "f")
    println(getMiddle[Int](arr1))
    println(getMiddle[String](arr2))
    // 简写方式,scala自动进行类型推断
    println(getMiddle(arr1))
    println(getMiddle(arr2))
  }
} 
2、定义一个泛型类
定义一个Pair类包含2个类型不固定的泛型
package com.testScala.J_fanxing
  // 类名后面的方括号,就表示这个类可以使用两个类型、分别是T和S
  // 这个名字可以任意取
  class Pair[T, S](val first: T, val second: S)
  case class People(var name: String, val age: Int)
  object Pair {
    def main(args: Array[String]): Unit = {
      val p1 = new Pair[String, Int]("张三", 10)
      println(p1.first)
      val p2 = new Pair[String, String]("张三", "1988-02-19")
      println(p2.first)
      val p3 = new Pair[People, People](People("张三", 20), People("李四", 30))
      println(p3.first.age)
    }
  } 
张三
张三
20
 
22、上下界
-  
==在指定泛型类型时,有时需要界定泛型类型的范围,而不是接收任意类型==。比如,要求某个泛型类型,必须是某个类的子类,这样在程序中就可以放心的调用父类的方法,程序才能正常的使用与运行.
 -  
scala的上下边界特性允许泛型类型是某个类的子类,或者是某个类的父类
-  
1、 ==U >: T==
这是类型==下界==的定义,也就是U必须是类型T的父类或者是T类型本身。
 -  
2、 ==U <: T==
这是类型==上界==的定义,也就是U必须是类型T的子类或者是T类型本身。
 
 -  
 -  
==示例一==
 
package com.testScala.J_fanxing
// 类名后面的指定泛型的范围 ----上界
class Pair1[T <: Person, S <: Person](val first: T, val second: S) {
  def chat(msg: String) = println(s"${first.name}对${second.name}说: $msg")
}
class Person(var name: String, val age: Int)
object Pair1 {
  def main(args: Array[String]): Unit = {
    val p3 = new Pair1[Person, Person](new Person("张三", 20), new Person("李四", 30))
    p3.chat("你好啊!")
  }
}
 
张三对李四说: 你好啊! 
==示例二==
class Pair22[T <: Person, S >: Policeman <: Person](val first: T, val second: S) {
  def chat(msg: String) = println(s"${first.name}对${second.name}说: $msg")
}
class Person(var name: String, val age: Int)
class Policeman(name: String, age: Int) extends Person(name, age)
class Superman(name: String) extends Policeman(name, -1)
object Pai {
  def main(args: Array[String]): Unit = {
    // 编译错误:第二个类型参数必须是Person的子类(包括本身)、Policeman的父类(包括本身)
    val p3 = new Pair22[Person, Policeman](new Person("张三", 20), new Policeman("李四",20))
    p3.chat("你好啊!")
  }
}
 
张三对李四说: 你好啊!
 
23、协变、逆变、非变
-  
来一个类型转换的问题
 
class Pair[T](a: T)
object Pair {
  def main(args: Array[String]): Unit = {
    val p1 = new Pair("hello")
    // 编译报错,无法将p1转换为p2
    val p2: Pair[AnyRef] = p1
    println(p2)
  }
} 
==协变==
==逆变==
==非变==
 
package com.testScala.K_xieBian
class Super
class Sub extends Super
//非变
class Temp1[A](title: String)
//协变
class Temp2[+A](title: String)
//逆变
class Temp3[-A](title: String)
object Covariance_demo {
  def main(args: Array[String]): Unit = {
    val a = new Sub()
    // 没有问题,Sub是Super的子类
    val b: Super = a
    // 非变
    val t1: Temp1[Sub] = new Temp1[Sub]("测试")
    // 报错!默认不允许转换
    // val t2: Temp1[Super] = t1
    // 协变
    val t3: Temp2[Sub] = new Temp2[Sub]("测试")
    val t4: Temp2[Super] = t3
    // 逆变
    val t5: Temp3[Super] = new Temp3[Super]("测试")
    val t6: Temp3[Sub] = t5
  }
}
 
==总结==
24、隐式转换和隐式参数
1 隐式转换
-  
Scala提供的隐式转换和隐式参数功能,是非常有特色的功能。是Java等编程语言所没有的功能。
 -  
它可以允许你手动指定,将某种类型的对象转换成其他类型的对象或者是给一个类增加方法。通过这些功能,可以实现非常强大、特殊的功能。
 -  
隐式转换其核心就是定义一个使用 ==implicit== 关键字修饰的方法 实现把一个原始类转换成目标类,进而可以调用目标类中的方法
 
.2 隐式参数
-  
所谓的隐式参数,指的是在函数或者方法中,定义一个用implicit修饰的参数,此时Scala会尝试找到一个指定类型的用implicit修饰的值,即==隐式值==,并注入参数。
 
-  
==所有的隐式转换和隐式参数必须定义在一个object中==
 -  
package com.kaikeba.implic_demo //todo:隐式转换案例二:超人变身 class Man(val name: String) class SuperMan(val name: String) { def heat = print("超人打怪兽") } object SuperMan{ //隐式转换方法 implicit def man2SuperMan(man: Man) = new SuperMan(man.name) def main(args: Array[String]) { val hero = new Man("hero") //Man具备了SuperMan的方法 hero.heat } } 










