• Scala最基础入门教程


    一、简介

    1、概述

    官方编程指南https://www.scala-lang.org/

    Scala将面向对象和函数式编程结合成一种简洁的高级语言。

    语言特点如下:

    (1)Scala和Java一样属于JVM语言,使用时都需要先编译为class字节码文件,并且Scala能够直接调用Java的类库。

    (2)Scala支持两种编程范式面向对象和函数式编程

    (3)Scala语言更加简洁高效;语法能够化简,函数式编程的思想使代码结构简洁。

    (4)作者马丁·奥德斯基设计Scala借鉴了Java的设计思想,同时优秀的设计也推动了Java语言的发展。

    2、Idea环境

    1. 首先确保JDK1.8安装成功
    2. 下载对应的Scala安装文件scala-2.12.10.zip
    3. 解压scala-2.12.10.zip,解压到任意没有中文的路径,例如D:\Tools
    4. 配置Scala的环境变量

    新建项目
    在这里插入图片描述

    添加scala插件
    在这里插入图片描述

    添加项目支持
    在这里插入图片描述

    添加Scala安装包
    在这里插入图片描述

    新建Scala文件夹,并指定为源文件
    在这里插入图片描述

    新建代码文件
    在这里插入图片描述

    object HelloWorld {
    
    
      def main(args: Array[String]): Unit = {
        // java的方法调用
        System.out.println("hello scala")
    
        // scala的方法调用
        println("hello scala")
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    二、变量和数据类型

    1、注释

    基本语法

    基本语法
    (1)单行注释://
    (2)多行注释:/* */
    (3)文档注释:/**
    	*
        */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2、变量和常量(重点)

    **常量:**在程序执行的过程中,其值不会被改变的变量。

    1)基本语法

    var 变量名 [: 变量类型] = 初始值		var i:Int = 10
    val 常量名 [: 常量类型] = 初始值		val j:Int = 20
    
    • 1
    • 2

    注意:能用常量的地方不用变量。

    特征:

    • 声明变量时,类型可以省略,编译器自动推导,即类型推导。
    • 类型确定后,就不能修改,说明Scala是强数据类型语言。
    • 变量声明时,必须要有初始值。
    • 在声明/定义一个变量时,可以使用var或者val来修饰,var修饰的变量可改变,val修饰的变量不可改。
    • var修饰的对象引用可以改变,val修饰的对象则不可改变,但对象的状态(值)却是可以改变的。(比如:自定义对象、数组、集合等等)。

    3、标识符的命名规范

    • 以字母或者下划线开头,后接字母、数字、下划线
    • 以操作符开头,且只包含操作符(+ - * / # !等)
    • 用反引号....包括的任意字符串,即使是Scala关键字(39个)也可以

    4、关键字(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

    5、字符串输出

    1、字符串,通过+号拼接

    //  1、字符串,通过+号拼接
    System.out.println()
    println("hello" + "world")
    
    • 1
    • 2
    • 3

    2、重复字符串拼接

    //  2、重复字符串拼接
    println("lilei " * 20)
    
    • 1
    • 2

    3、printf用法:字符串,通过%传值

    //  3、printf用法:字符串,通过%传值
    printf("name:%s age %d\n", "linlai", 8)
    
    • 1
    • 2

    4、字符串模板(插值字符串):通过$获取变量值

    s"":标明当前是需要取值计算的字符串

    $name:取变量name值,赋值到字符串中。

    ${age+1}:取变量age值,并进行计算+1.

    //  4、字符串模板(插值字符串):通过$获取变量值
    val name = "linhai"
    val age = 8
    val s1 = s"name:$name,age:${age}"
    println(s1)
    val s2 = s"name:${name + 1},age:${age + 2}"
    println(s2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5、多行字符串

    //	5、多行字符串
    println("我" +
      "是" +
      "中国人")
    println(
      """我
        |是
        |中国
        |人
        |""".stripMargin)
    val margin =
      """
        |我
        |是
        |中国人
        |""".stripMargin
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    6、数据类型

    在这里插入图片描述

    6.1 整数类型(Byte、Short、Int、Long)
    数据类型描述
    Byte [1]8位有符号补码整数。数值区间为 -128 到 127
    Short [2]16位有符号补码整数。数值区间为 -32768 到 32767
    Int [4]32位有符号补码整数。数值区间为 -2147483648 到 2147483647
    Long [8]64位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807 = 2的(64-1)次方-1
    val a1: Byte = 1
    val a2: Short = 2
    val a3: Int = 3
    val a4: Long = 4
    
    • 1
    • 2
    • 3
    • 4
    6.2 浮点类型(Float、Double)
    数据类型描述
    Float [4]32 位, IEEE 754标准的单精度浮点数
    Double [8]64 位 IEEE 754标准的双精度浮点数
    val b1: Double = 3.14
    //  默认为Double类型
    val b2 = 3.14
    val b3: Float = 3.14f
    
    • 1
    • 2
    • 3
    • 4
    6.3 字符类型(Char)

    字符类型可以表示单个字符,字符类型是Char。

    val c1: Char = 'a'
    val c2: Char = 523
    val c3: Char = '\t'
    val c4: Char = '\n'
    val c5: Char = '\\'
    val c6 = '\"'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    6.4 布尔类型(Boolean)
    • 布尔类型也叫Boolean类型,Booolean类型数据只允许取值true和false
    • boolean类型占1个字节。
    val bo1: Boolean = true
    val bo2: Boolean = false
    
    • 1
    • 2
    6.5 Unit类型、Null类型和Nothing类型(重点)
    数据类型描述
    Unit表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。
    Nullnull , Null 类型只有一个实例值null
    NothingNothing类型在Scala的类层级最低端;它是任何其他类型的子类型。当一个函数,我们确定没有正常的返回值,可以用Nothing来指定返回类型,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数或者变量(兼容性)
    • Unit类型用来标识过程,也就是没有明确返回值的函数,就算是有值也不接收数值。
    val u1: Unit = {
      10
      println(10)
    }
    println(u1)
    // 如果标记对象的类型是unit的话  后面有返回值也没法接收
    // unit虽然是数值类型  但是可以接收引用数据类型   因为都是表示不接收返回值
    val u2: Unit = 10
    println(u2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    打印结果:

    10
    ()
    ()
    
    • 1
    • 2
    • 3
    • Null类只有一个实例对象,Null类似于Java中的null引用。Null可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)。
    var n2: String = "bb"
    n2 = "cc"
    n2 = null
    
    //	值类型不能等于null,idea不会识别报错  编译器会报错
    var i4 = 10
    //        i4 = null
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • Nothing,可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于Nothing是其他任意类型的子类,他还能跟要求返回值的方法兼容。
    //	可以用来抛出异常
    //	可以接收任意数据类型(Int使用Nothing接收)
    val value: Nothing = {
      println("hello")
      1 + 1
      throw new RuntimeException()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    7、类型转换

    7.1 数值类型自动转换

    当Scala程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数值类型,这个就是自动类型转换(隐式转换)。数据类型按精度(容量)大小排序为:

    在这里插入图片描述

    • 自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数据类型,然后再进行计算。
    • 把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
    • (byte,short)和char之间不会相互自动转换。
    • byte,short,char他们三者可以计算,在计算时首先转换为int类型。
    //  自动类型提升,以最大的类型进行结果存储
    val f1: Float = 1 + 1L + 3.14f
    val f2: Double = 1 + 1L + 3.14
    val f3: Double = 1 + 1L + 3.14f + 3.14
    //  把精度大的赋值给精度小的会报错,反之会进行类型转换
    val i = 10
    val b: Double = i
    //  (byte、short、char)之间不会相互转换
    val b1: Byte = 10
    val c1: Char = 20
    //  byte,short,char他们三者可以计算,在计算时首先转换为int类型。
    val b2: Byte = 20
    val i1: Int = b1 + b2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    7.2 强制类型转换

    自动类型转换的逆过程,将精度大的数值类型转换为精度小的数值类型。使用时要加上强制转函数,但可能造成精度降低或溢出,格外要注意。

    Java  :  int num = (int)2.5
    Scala :  var num : Int = 2.7.toInt
    
    • 1
    • 2
    //  1、将高精度转为低精度,就需要使用强制转换
    val int = 2.99.toInt
    val int1 = (10 * 3.5 + 6 * 1.5).toInt
    
    • 1
    • 2
    • 3
    7.3 数值类型与String类型间转换
    • 基本类型转String类型(语法:将基本类型的值+“” 即可)。

    • String类型转基本数值类型(语法:s1.toInt、s1.toFloat、s1.toDouble、s1.toByte、s1.toLong、s1.toShort)。

    //  1、基本类型转为String类型
    val str = 1 + ""
    //  2、string类型转为数值类型
    val d1 = "3.14".toDouble
    val d2 = "1".toInt
    //  3、小数类型,需先转为Double再转为Int类型
    val i1 = "3.14".toDouble.toInt
    //  标记为f的float数能够识别
    val i02 = "12.5f".toFloat
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    三、运算符

    1、算数运算符

    运算符运算范例结果
    +正号+33
    -负号b=4; -b-4
    +5+510
    -6-42
    *3*412
    /5/51
    %取模(取余)7%52
    +字符串相加“He”+”llo”“Hello”

    2、关系运算符

    运算符运算范例结果
    ==相等于4==3false
    !=不等于4!=3true
    <小于4<3false
    >大于4>3true
    <=小于等于4<=3false
    >=大于等于4>=3true
    //	==比较两个变量本身的值,即两个对象在内存中的首地址;
    //	equals比较字符串中所包含的内容是否相同。
    println("a".equals("b"))
    println("a" == "b")
    
    • 1
    • 2
    • 3
    • 4

    3、逻辑运算符

    运算符描述实例
    &&逻辑与(A && B) 运算结果为 false
    ||逻辑或(A || B) 运算结果为 true
    !逻辑非!(A && B) 运算结果为 true

    4、赋值运算符

    运算符描述实例
    =简单的赋值运算符,将一个表达式的值赋给一个左值C = A + B 将 A + B 表达式结果赋值给 C
    +=相加后再赋值C += A 等于 C = C + A
    -=相减后再赋值C -= A 等于 C = C - A
    *=相乘后再赋值C *= A 等于 C = C * A
    /=相除后再赋值C /= A 等于 C = C / A
    %=求余后再赋值C %= A 等于 C = C % A
    <<=左移后赋值C <<= 2等于 C = C << 2
    >>=右移后赋值C >>= 2 等于 C = C >> 2
    &=按位与后赋值C &= 2 等于 C = C & 2
    ^=按位异或后赋值C ^= 2 等于 C = C ^ 2
    |=按位或后赋值C |= 2 等于 C = C | 2

    5、位运算符

    运算符描述实例
    &按位与运算符(a & b) 输出结果 12 ,二进制解释: 0000 1100
    |按位或运算符(a | b) 输出结果 61 ,二进制解释: 0011 1101
    ^按位异或运算符(a ^ b) 输出结果 49 ,二进制解释: 0011 0001
    ~按位取反运算符(~a ) 输出结果 -61 ,二进制解释: 1100 0011, 在一个有符号二进制数的补码形式。
    <<左移动运算符a << 2 输出结果 240 ,二进制解释: 0011 0000
    >>右移动运算符a >> 2 输出结果 15 ,二进制解释: 0000 1111
    >>>无符号右移a >>>2 输出结果 15, 二进制解释: 0000 1111
    var a:Int=1000
    // 4000 
    println(a << 2)
    //  250
    println(a >> 2)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    6、Scala运算符本质

    在Scala中其实是没有运算符的,所有运算符都是方法。

    • 当调用对象的方法时,点.可以省略。
    • 如果函数参数只有一个,或者没有参数,()可以省略。
    var nu1: Int = 1.+(1)
    val nu2: Int = 1 + (1)
    val nu3: Int = 1 + 1
    
    • 1
    • 2
    • 3

    四、流程控制

    1、分支控制(if-else)

    让程序有选择的的执行,分支控制有三种:单分支、双分支、多分支。

    //	1、简单If-else
    if (age < 18) {
      println("童年")
    } else if (age > 18 && age < 60) {
      println("中年")
    } else {
      println("老年")
    }
    
    //	2、使用方法计算
    val result: String = {
      if (age < 18) {
        "童年"
      } else {
        "老年"
      }
    }
    println(result)
    
    //	3、不确定返回值类型。使用Any代替
    val res: Any = {
      if (age > 18) {
        "童年"
      } else {
        100
      }
    }
    println(res)
    
    //	4、三元运算符使用
    val res01: Any = if (age > 18) "童年" else "中年"
    println(res01)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    2、For循环控制

    for (i <- 0 to 5) {
      println(i)
    }
    for (i <- 0 until 5) {
      println(i)
    }
    for (i <- 0 to 5) {
      if (i > 1) {
        print(i)
      }
    }
    for (i <- 0 to 5 if i > 2) {
      print(i)
    }
    //  (10, 10, 10, 10)
    val ints: immutable.IndexedSeq[Int] = for (i <- 0 to 3) yield {
      10
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3、while和do…while循环

    var i = 0
    //	while循环
    while (i < 5) {
      println(i)
      i += i
    }
    //	do-while循环
    do {
      println(i)
      i += i
    } while (i < 5)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    4、循环中断

    Scala内置控制结构特地去掉了break和continue,是为了更好的适应函数式编程,推荐使用函数式的风格解决break和continue的功能,而不是一个关键字。Scala中使用breakable控制结构来实现break和continue功能。

    //	1、采用异常的方式退出循环
    try {
      for (e <- 1 to 5) {
        println(e)
        if (e > 4) {
          throw new Exception()
        }
      }
    } catch {
      case e: Throwable =>
    }
    
    //	2、采用Scala自带的函数,退出循环
    Breaks.breakable(
      for (e <- 1 to 5) {
        println(e)
        if (e > 4) {
          Breaks.break()
        }
      }
    )
    
    //	3、对break进行省略
    breakable {
      for (e <- 1 to 10) {
        if (e > 9) {
          break
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    四、函数式编程

    • 面向对象编程
      解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。
      对象:用户;
      行为:登录、连接jdbc、读取数据库
      属性:用户名、密码
      Scala语言是一个完全面向对象编程语言。万物皆对象
    • 函数式编程
      解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。
      例如:请求->用户名、密码->连接jdbc->读取数据库
      Scala语言是一个完全函数式编程语言。万物皆函数

    1、方法定义

    在这里插入图片描述

    object Hello {
      def main(args: Array[String]): Unit = {
        //  自定义方法:f:方法名。arg:参数名。String:参数类型。Unit:返回值空
        def f(arg: String): Unit = {
          println(arg)
        }
        //  调用方法
        f("hello world")
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 方法1:无参,无返回值
    • 方法2:无参,有返回值
    • 方法3:有参,无返回值
    • 方法4:有参,有返回值
    • 方法5:多参,无返回值
    //  有参。有返回值
    def f1(arg: String): String = {
      arg + " world"
    }
    println(f1("hello"))
    
    //  有参。无返回值
    def f2(args: String): Unit = {
      println(args)
    }
    println(f2("hello"))
    
    //  无参。有返回值
    def f3(): String = {
      "hello world"
    }
    println(f3())
    
    //  无参。无返回值
    def f4(): Unit = {
      println("hello world")
    }
    f4()
    
    //  多参。无返回值
    def f5(args01: String, args02: String): Unit = {
      args01 + args02
    }
    f5("hello", "world")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    方法至简原则

    • return可以省略,Scala会使用方法体的最后一行代码作为返回值
    • 如果方法体只有一行代码,可以省略花括号
    • 返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)特别注意事项:
    • 如果有return,则不能省略返回值类型,必须指定
    • 如果方法明确声明unit,那么即使方法体中使用return关键字也不起作用
    • Scala如果期望是无返回值类型,可以省略等号(=号和方法体大括号不能同时省略)
    • 如果方法无参,但是声明了参数列表,那么调用时,小括号,可加可不加(声明无括号调用时也没有括号)
    • 如果方法没有参数列表,那么小括号可以省略,调用时小括号必须省略
    //    1、return可以省略,Scala会使用方法体的最后一行代码作为返回值
    def s1(): String = {
      "hello01"
      "hello02"
    }
    //    2、如果方法体只有一行代码,可以省略花括号
    def s2(): Int = 1 + 2
    //    3、返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)特别注意事项:
    def s3() = 1 + 2
    //    4、如果有return,则不能省略返回值类型,必须指定
    def s4(): Int = {
      return 1 + 2
    }
    //    5、如果方法明确声明unit,那么即使方法体中使用return关键字也不起作用
    def s5(): Unit = {
      return 1 + 2
    }
    //    6、Scala如果期望是无返回值类型,可以省略等号(=号和方法体大括号不能同时省略)
    def s6() {
      println(1 + 1)
    }
    //    7、如果方法无参,但是声明了参数列表,那么调用时,小括号,可加可不加(声明无括号调用时也没有括号)
    def s7(): Unit = {
      println(1 + 2)
    }
    s7()
    s7
    //    8、如果方法没有参数列表,那么小括号可以省略,调用时小括号必须省略
    def s8 {
      println("hello")
    }
    s8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    2、可变参数

    • 可变参数:本质是1个数组
    • 参数位置:如果参数列表中存在多个参数,那么可变参数一般放置在最后,(不能和默认值一起用,和带名参数用时,不能改变带名参数的顺序)
    • 参数默认值:一般将有默认值的参数放置在参数列表的后面
    //  1、可变参数。本质是1个数组
    def sayHi(name: String*): Unit = {
      println(s"hi $name")
      for (e <- name) {
        println(name)
      }
    }
    sayHi("hello01", "hello02", "hell03")
    //  2、可变参数必须要放在参数列表最后
    def sayHi2(age: Int, name: String*): Unit = {
      println(s"hi $name")
    }
    sayHi2(18, "hello01", "hello02")
    //  3、参数默认值
    def sayHi3(age: Int, name: String = "张三"): Unit = {
      println(s"hi $age $name")
    }
    //  hi 18 张三
    sayHi3(18)
    //  4、默认值参数在使用的时候,可以不在最后
    def say4(name: String = "张三", age: Int): Unit = {
      println(s"hi $age $name")
    }
    sayHi3(18)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    3、函数

    函数的返回值就是函数体中最后一个表达式的结果值/return语句的返回值。

    在这里插入图片描述

    函数和方法的区别

    • 方法定义在类中可以实现重载,函数不可以重载。
    • 方法是保存在方法区,函数是保存在堆中。
    • 定义在方法中的方法可以称之为函数,不可以重载。
    • 方法可以转成函数, 转换语法: 方法名 _。
    //	不支持重载
    val test = () => {
      println("无参")
    }
    test()
    //    val test =(name:String)=>{
    //      println("构造")
    //    }
    
    //	3、函数可以嵌套
    val test02 = () => {
      val test03 = () => {
        println("无参+构造")
      }
      println("无参")
    }
    test02()
    
    //	4、方法可以转为函数
    def add(): Unit = {
      println("方法")
    }
    val add2 = add _
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    4、高阶函数

    参数/返回值为函数的方法/函数称为高阶函数

    //  函数ope作为参数传入,
    def cal(a: Int, b: Int, ope: (Int, Int) => Int): Int = {
      ope(a, b)
    }
    //  函数求和
    def plus(x: Int, y: Int): Int = {
      x + y
    }
    println(cal(1, 3, plus))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5、匿名函数

    没有名字的函数/方法就是匿名函数。

    (x:Int)=>{函数体}

    x:表示输入参数名称;Int:表示输入参数类型;函数体:表示具体代码逻辑

    • 参数的类型可以省略,会根据形参进行自动的推导

    • 类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号。

    • 匿名函数如果只有一行,则大括号也可以省略

    • 如果参数只出现一次,且按照顺序出现则参数省略且后面参数可以用_代替

    val f0: (Int, Int) => Int = (x: Int, y: Int) => x + y
    //  1、参数的类型可以省略,会根据形参自动推导
    val f1: (Int, Int) => Int = (x, y) => x + y
    //2、类型省略后,发现只有1个参数,则圆括号可以省略
    val f2: (Int, Int) => Int = (x, y) => x + y
    val f3: Int => Int = x => x + 22
    val f4: () => Int = () => 22
    //  3、匿名函数如果只有一行,则大括号也可以省略
    val f5: (Int, Int) => Int = (x, y) => {
      x + y
    }
    val f6: (Int, Int) => Int = (x, y) => x + y
    //  4、如果参数只出现一次,且按照顺序出现,则参数省略且后面的参数可以用_代替
    val f7: (Int, Int) => Int = _ + _
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    不能化简为下划线的情况: 1.化简之后只有一个下划线 2.化简后的函数存在嵌套

    //  1、传入的参数类型可以推断 所以可以省略
    val f8: (Int, Int) => Int = (x, y) => y - x
    val f8_1: (Int, Int) => Int = _ - _
    //  2、参数必须只使用一次,使用的顺序必要和定义的顺序一直
    val f9: (Int, Int) => Int = -_ + _
    
    • 1
    • 2
    • 3
    • 4
    • 5

    6、函数柯里化&闭包

    **函数柯里化:**将一个接收多个参数的函数转化成一个接受一个参数的函数过程,可以简单的理解为一种特殊的参数列表声明方式。
    **闭包:**就是一个函数和与其相关的引用环境(变量)组合的一个整体(实体)

    //  外部参数
    var z: Int = 10
    //  闭包
    def f(y: Int): Int = {
      z + y
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1

    //	原样
    val sum = (x: Int, y: Int, z: Int) => x + y + z
    sum(1, 2, 3)
    //	1、
    val sum1 = (x: Int) => {
      y: Int => {
        z: Int => {
          x + y + z
        }
      }
    }
    sum1(1)(2)(3)
    
    //	2、第二种
    val sum2 = (x: Int) => (y: Int) => (z: Int) => x + y + z
    sum2(1)(2)(3)
    //	3、第三种
    def sum3(x: Int)(y: Int)(z: Int) = x + y + z
    sum3(1)(2)(3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    7、递归

    一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用

    def main(args: Array[String]): Unit = {
      println(test(5))
    }
    //	递归方法(test方法)
    def test(i: Int): Int = {
      if (i == 1) {
        1
      } else {
        i * test(i - 1)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    五、面向对象

    1、定义

    • Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)。
    • 一个Scala源文件可以包含多个类。

    基本语法

    [修饰符] class 类名 {
        类体
    } 
    
    • 1
    • 2
    • 3
    package com.atguigu.chapter06
    
    //(1)Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)
    class Person {
    
    }
    
    //(2)一个Scala源文件可以包含多个类
    class Teacher{
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2、属性和封装

    class Person {
      @BeanProperty
      var age: Int = _
    
      //  val只能生成get方法
      @BeanProperty
      val name: String = "张三"
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    测试类

    object Test {
      def main(args: Array[String]): Unit = {
        val person = new Person
    
        person.age = 18
        person.setAge(18)
        println(person.getName)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3、访问权限

    • Scala 中属性和方法的默认访问权限为public,但Scala中无public关键字。
    • private为私有权限,只在类的内部和伴生对象中可用。
    • protected为受保护权限,Scala中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问。
    • private[包名]增加包访问权限,包名下的其他类也可以使用

    4、方法

    def 方法名(参数列表) [:返回值类型] = { 
    	方法体
    }
    
    • 1
    • 2
    • 3

    案例

    class Person {
      def sum(n1: Int, n2: Int): Int = {
        n1 + n2
      }
    }
    
    object Test01 {
      def main(args: Array[String]): Unit = {
        val test: Test01 = new Test01()
        println(test.sum(1, 2))
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    5、构造器

    和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。

    Scala类的构造器包括:主构造器和辅助构造器

    class 类名(形参列表) {  // 主构造器
       // 类体
       def  this(形参列表) {  // 辅助构造器
       }
       def  this(形参列表) {  //辅助构造器可以有多个...
       }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    案例:

    //  主构造器
    class Person01(name: String) {
      val name1: String = name
      var age: Int = _
    
      //  辅助构造器01
      def this() = {
        this("张三")
      }
    
      //  复制构造器02
      def this(name: String, age1: Int) = {
        this()
        this.age = age1
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    测试类

    object Test {
      def main(args: Array[String]): Unit = {
        val person01: Person01 = new Person01()
        val person02: Person01 = new Person01("张三")
        val person03: Person01 = new Person01("张三", 18)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    6、构造器参数

    Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰

    • 未用任何修饰符修饰:这个参数就是一个局部变量,底层有属性的特性。
    • var修饰参数:作为类的成员属性使用,可以修改。
    • val修饰参数:作为类只读属性使用,不能修改。
    //  主构造器参数 分为3类:
    //  没有修饰符: 作为构造方法中的传入参数使用
    //  val 修饰: 会自动生产同名的属性 并且定义为val
    //  var 修饰 : 会自动生产同名的属性 并且定义为var
    class Person02(name1: String, var age1: Int, val sex1: Int) {
      val name: String = name1
    }
    
    //	等效方法
    class Person02(name1: String, age1: Int,sex1 : Int) {
      val name: String = name1
      val age: Int = age1
      val sex: Int = sex1
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    7、scala的object

    java中存在静态属性、静态方法、非静态属性、非静态方法。

    • scala当中不存在静态与非静态。object中定义的所有属性与方法、函数,除开private修饰的,都可以通过对象名.属性、对象名.方法、对象名.函数 的方式调用,可以理解为java中的static修饰的。
    • Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。
    object TestObject {
      val name: String = "zhansan"
      var age: Int = 30
    
      private val address: String = "深圳"
    
      def getName(): String = {
        this.name + " " + this.name
      }
    
      val func = (x: Int, y: Int) => {
        x * y
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    object Test102 {
    
      def main(args: Array[String]): Unit = {
        //  object中的属性直接通过 类名.属性名 方式调用(zhansan)
        println(TestObject.name)
        //  30
        println(TestObject.age)
        //  设置属性
        TestObject.age = 66
        //  66
        println(TestObject.age)
        println("====")
        //  object中的方法直接通过 类名.方法名 方式调用(zhansan zhansan)
        println(TestObject.getName())
        //  object中的方法直接通过 类名.函数名 方式调用(6)
        println(TestObject.func(2, 3))
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    8、伴生类与伴生对象

    • 如果有一个class,另外还有一个object,并且二者同名。
    • class与object在同一个文件中。

    如果满足上两个条件,那么就称这个object为class的伴生对象,称class为object的伴生类。

    • 伴生类与伴生对象可以互相访问对方的私有成员。
    • 伴生类可以互相访问private修饰的值,但是在外部不能够访问。
    class ClassObjectTest {
      
      val name:String = "lisi"
      //用private修饰的只能在类或者伴生对象中使用
      private val age = 20
      //此时可以调用伴生对象中用private修饰的 address属性
      def getAddress() = ClassObjectTest.address
    }
    
    object ClassObjectTest{
      
      private val address = "shenzhen"
    
      def getName() = {
        //创建伴生类的对象
    	val obj = new ClassObjectTest()
    	//此时可以调用伴生类中用private修饰的name属性
    	obj.name
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    9、apply方法

    • 通过伴生对象的apply方法,实现不使用new方法创建对象。
    • 如果想让主构造器变成私有的,可以在()之前加上private。
    • apply方法可以重载。
    • Scala中obj(arg)的语句实际是在调用该对象的apply方法,即obj.apply(arg)。用以统一面向对象编程和函数式编程的风格。
    • 当使用new关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构建对象时,调用的其实时伴生对象的apply方法。
    object Test11_Apply {
    
      def main(args: Array[String]): Unit = {
    
        // 如果调用的方法是apply的话  方法名apply可以不写
        val one: Person11 = Person11()
        // 类的apply方法调用(打印类方法)
        one()
      }
    }
    
    class Person11 private() {
      var name:String = _
      def this(name:String){
        this()
        this.name = name
      }
    
      def apply(): Unit = println("类的apply方法调用")
    }
    
    object Person11 {
      // 使用伴生对象的方法来获取对象实例
      def getPerson11: Person11 = new Person11
    
      // 伴生对象的apply方法
      def apply(): Person11 = new Person11()
    
      // apply方法的重载
      def apply(name: String): Person11 = new Person11(name)
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    10、类型检查和转换

    //	判断obj是不是T类型。
    obj.isInstanceOf[T]
    //	将obj强转成T类型。
    obj.asInstanceOf[T]
    //	获取类模板。
    classOf
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    // 	判断person类型是不是为Teacher
    if (person1.isInstanceOf[Teacher]) {
      //	假如类型为Teacher,则强转为Teacher
      val teacher1: Teacher = person1.asInstanceOf[Teacher]
      teacher1.sayHi1()
    }
    // 调用固定的方法  返回类模板
    val value: Class[Student15] = classOf[Student15]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    六、集合

    1、简介

    • Scala的集合有三大类:序列Seq(List)、集Set、映射Map,所有的集合都扩展自Iterable特质。

    • 对于几乎所有的集合类,Scala都同时提供了可变不可变的版本,分别位于以下两个包。

      • 不可变集合:scala.collection.immutable
      • 可变集合: scala.collection.mutable
    • Scala不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而不会对原对象进行修改。类似于java中的String对象。

    • 可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象。类似于java中StringBuilder对象。

    建议:在操作集合的时候,不可变用符号,可变用方法。

    不可变集合

    在这里插入图片描述

    • Set、Map是Java中也有的集合。
    • Seq是Java没有的,我们发现List归属到Seq了,因此这里的List就和Java不是同一个概念了。.
    • 我们前面的for循环有一个 1 to 3,就是IndexedSeq下的Range。
    • String也是属于IndexedSeq。
    • 我们发现经典的数据结构比如Queue和Stack被归属到LinearSeq(线性序列)。
    • 大家注意Scala中的Map体系有一个SortedMap,说明Scala的Map可以支持排序。
    • IndexedSeq和LinearSeq的区别。
      • IndexedSeq是通过索引来查找和定位,因此速度快,比如String就是一个索引集合,通过索引即可定位。
      • LinearSeq是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找。

    可变集合

    在这里插入图片描述

    2、数组

    2.1 不可变数组
    val arr1 = new Array[Int](10)
    
    • 1
    • new是关键字。
    • [Int]是指定可以存放的数据类型,如果希望存放任意数据类型,则指定Any。
    • (10),表示数组的大小,确定后就不可以变化。
    object Test01 {
      def main(args: Array[String]): Unit = {
    
        //  创建不可变数组
        val ints = new Array[Int](10)
        //  可以使用伴生对象的apply方法
        val array01: Array[Int] = Array(1, 2, 3, 4, 5)
    
        //  遍历数组(print打印)
        println(array01.toList)
        //  遍历数组(遍历)
        for (e <- array01) {
          println(e)
        }
        //  遍历数组(迭代器)
        val iterator: Iterator[Int] = array01.iterator
        while (iterator.hasNext) {
          println(iterator.next())
        }
        //  遍历数组(匿名函数)
        array01.foreach(e => println(e))
        //  遍历数组(使用系统函数)
        array01.foreach(println)
        
        //  修改数组值
        array01(0) = 10
        println(array01(0))
    
        // 添加元素,生成新数组array02、原数组array01不变
        val array02: Array[Int] = array01 :+ 1
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    2.2 可变数组

    定义:

    • [Any]存放任意数据类型。
    • (3, 2, 5)初始化好的三个元素。
    • ArrayBuffer需要引入scala.collection.mutable.ArrayBuffer
     val array: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3)
    
    • 1

    ArrayBuffer是有序的集合。

    增加元素使用的是append方法(),支持可变参数。

    //  创建可变数组
    val array: ArrayBuffer[Int] = new ArrayBuffer[Int]()
    val arrayBuffer02: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)
    //  向可变数组中添加元素
    array.append(10)
    array.appendAll(Array(1, 2, 3, 4))
    //  循环打印
    array.foreach(println)
    println(array)
    //  更新元素值
    array.update(0, 100)
    array(1) = 200
    println(array)
    println(array(0))
    //  删除元素
    array.remove(0)
    array.remove(1, 3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    2.3 不可变数组与可变数组的转换
    • arr2.toArray返回结果才是一个不可变数组,arr2本身没有变化。
    • arr1.toBuffer返回结果才是一个可变数组,arr1本身没有变化。
    //	不可变数组转可变数组
    arr1.toBuffer
    //	不可变数组转可变数组
    arr2.toArray
    
    • 1
    • 2
    • 3
    • 4
    //  不可变数组
    val array: Array[Int] = Array(1, 2, 3, 4)
    //  可变数组
    val arrayBuffer: ArrayBuffer[Int] = ArrayBuffer(5, 6, 7, 8)
    //  添加元素(不可变数组)
    val arra01: Array[Int] = array :+ 1
    arrayBuffer.append(1)
    //  可变 => 不可变
    val array01: Array[Int] = arrayBuffer.toArray
    //  不可变 => 可变
    val buffer: mutable.Buffer[Int] = array.toBuffer
    val arrayBuffer01: ArrayBuffer[Int] = buffer.asInstanceOf[ArrayBuffer[Int]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    2.4 多维数组
    • 二维数组中有三个一维数组,每个一维数组中有四个元素。
    //	3行4列
    val array02: Array[Array[Double]] = Array.ofDim[Double](3, 4)
    
    • 1
    • 2
    val arrayDim: Array[Array[Double]] = Array.ofDim[Double](3, 4)
    //  赋值
    arrayDim(0) = Array(1, 2, 3, 4)
    arrayDim(0)(1) = 100
    //	遍历
    for (array <- arrayDim) {
      for (elem <- array) {
        print(elem + " ")
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3、Seq集合(List)

    不可变List

    //  创建集合(List)
    val list01: List[Any] = List(1, 1, 1, 1.0, "hello", 'c')
    val list02: List[Int] = List(1, 2, 3, 4)
    //  遍历集合
    list01.foreach(println)
    
    //  增加数据(末尾增加)
    val list011: List[Any] = list01 :+ 1
    //  增加数据(开头增加)
    val list012: List[Any] = 2 :: list011
    
    //  合并2个集合(1个集合插入另1个集合中)(List(List(1, 1, 1, 1.0, hello, c), 1, 2, 3, 4))
    val list013: List[Any] = list01 :: list02
    //  合并2个集合(1个集合元素遍历插入另1集合)(List(1, 1, 1, 1.0, hello, c, 1, 2, 3, 4))
    println(list01 ::: list02)
    
    //  取值
    val value: Int = list02(2)
    
    // 空集合Nil(List(1, 2, 3, 4))
    val ints: List[Int] = 1 :: 2 :: 3 :: 4 :: Nil
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    可变ListBuffer

    //  可变List创建(ListBuffer())
    val listBuffer: ListBuffer[Int] = new ListBuffer[Int]()
    //  ListBuffer(1, 2, 3, 4)
    val listBuffer1: ListBuffer[Int] = ListBuffer(1, 2, 3, 4)
    
    //  添加元素(末尾头)(ListBuffer(5))
    listBuffer.append(5)
    //  添加元素(开头)(ListBuffer(0, 5))
    listBuffer.prepend(0)
    
    //  删除元素(ListBuffer(5))
    listBuffer.remove(0)
    //  更新值(ListBuffer(5))
    listBuffer(0)=1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4、Set集合

    默认情况下,Scala使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set包。

    不可变集合

    • Set默认是不可变集合。
    • 数据无序不可重复。
    • 默认使用hash set
    val set: Set[Int] = Set(3, 2, 4, 1)
    // set的特点 无序不可重复
    println(set)
    // 不可变使用符号(无变化s)(Set(3, 2, 4, 1))
    val set2: Set[Int] = set + 1
    println(set)
    println(set2)
    // 作用 判断集合是否包含某个元素(true)
    val bool: Boolean = set.contains(2)
    println(bool)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可变集合mutable.Set

    //  创建可变Set(Set(1, 5, 2, 3, 4))
    val set: mutable.Set[Int] = mutable.Set(1, 2, 3, 4, 5)
    //  添加数据(1, 5, 2, 3, 4)
    val bool: Boolean = set.add(5)
    //  遍历
    set.foreach(println)
    //  删除元素(Set(1, 5, 3, 4))
    val bool1: Boolean = set.remove(2)
    println(set)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5、Map集合

    Scala中的Map和Java类似,**也是一个散列表,它存储的内容也是键值对(key-value)**映射。

    不可变Map

    //  1、创建不可变Map
    val map: Map[String, Int] = Map("hello" -> 100, "world" -> 200)
    val map02: Map[String, Int] = Map(("hello", 100), ("world", 200))
    
    //  2、遍历打印
    for (elem <- map) {
      println(elem)
    }
    //  遍历打印(foreach打印)
    map.foreach(print)
    //  遍历打印(打印key)
    val keys: Iterable[String] = map.keys
    keys.foreach(print)
    //  遍历打印(打印value)
    val values: Iterable[Int] = map.values
    //  遍历打印(直接打印)
    println(map)
    
    //  3-1、获取value的值(Option)
    val option: Option[Int] = map.get("hello")
    //  根据Option取值(option.isDefined/option.isEmpty)
    if (!option.isEmpty) {
      println(option.get)
    }
    println(option.getOrElse(1))
    //  3-1、获取value的值(getOrElse)
    val map01: Map[Int, String] = Map((1, "4324"))
    //  不确定存在
    val str1: String = map01.getOrElse(1, "4324")
    //  按key取值
    val str: String = map01(1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    可变Map

    //  创建可变map
    val map: mutable.Map[String, Int] = mutable.Map(("a", 1), ("b", 1), ("c", 2), ("d", 4))
    println(map)
    
    //  添加元素
    map.put("z", 10)
    println(map)
    //  修改元素值
    map.update("b", 20)
    map("a") = 30
    //  删除元素
    map.remove("a")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    6、元组

    • 元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组。
    • 注意:元组中最大只能有22个元素。
    //  1、声明元组
    val tuple: (Int, String, Boolean) = (40, "bobo", true)
    //  2-1、访问元组(单个)
    println(tuple._1)
    //  2-2、访问元组(单个)
    val value: Any = tuple.productElement(0)
    println(value)
    //  2-3、访问元组(迭代器遍历)
    for (elem <- tuple.productIterator) {
      println(elem)
    }
    //	3、Map中使用(以元组方式初始化Map)
    val map: Map[String, Int] = Map("a" -> 1, "b" -> 2, "c" -> 3)
    val map02: Map[String, Int] = Map(("a", 1), ("b", 2), ("c", 3))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    7、集合常用函数

    7.1 基本操作
    • 获取集合长度
    • 获取集合大小
    • 循环遍历
    • 迭代器
    • 生成字符串
    • 是否包含
    val list: List[Int] = List(1, 2, 3, 4, 5, 6)
    //  获取集合长度
    println(list.length)
    //  获取集合的大小(size=length)
    println(list.size)
    //  循环遍历
    list.foreach(println)
    //  迭代器
    for (elem <- list.iterator) {
      println(elem)
    }
    //  按规定字符串(1,2,3,4,5,6)
    println(list.mkString(","))
    //  是否包含(true)
    println(list.contains(3))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    7.2 衍生集合
    • 获取集合的头
    • 获取集合的尾(不是头的就是尾)
    • 集合最后一个数据
    • 集合初始数据(不包含最后一个)
    • 反转
    • 取前(后)n个元素
    • 去掉前(后)n个元素
    • 并集
    • 交集
    • 差集
    • 拉链
    • 滑窗
    val list: List[Int] = List(1, 2, 3)
    val list02: List[Int] = List(3,4, 5)
    //  获取集合的头(1)
    println(list.head)
    //  获取集合最后1个元素(3)
    println(list.last)
    //  集合头数据(不包含最后1个)(List(1, 2))
    println(list.init)
    //  获取集合的尾(不包含第1个)(List(2, 3))
    println(list.tail)
    //  反转(List(3, 2, 1))
    println(list.reverse)
    //  取前(后)n个元素
    println(list.take(2))
    println(list.takeRight(2))
    
    //  去掉前(后)n个元素(List(3))(List(1))
    println(list.drop(2))
    println(list.dropRight(2))
    //  并集(List(1, 2, 3, 4, 5))
    println(list.union(list02))
    //  交集(List(3))
    println(list.intersect(list02))
    //  差集(List(1, 2))
    println(list.diff(list02))
    //  拉链(List((1,3), (2,4), (3,5)))
    println(list.zip(list02))
    
    //  划窗(List(1, 2))
    list.sliding(2, 5).foreach(println)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    7.3 集合计算初级函数
    • 求和
    • 求乘积
    • 最大值
    • 最小值
    • 排序
      • sorted:对一个集合进行自然排序,通过传递隐式的Ordering。
      • sortBy:对一个属性或多个属性进行排序,通过它的类型。
      • sortWith:基于函数的排序,通过一个comparator函数,实现自定义排序的逻辑。
    val list: List[Int] = List(1, 5, -3, 4, 2, -7, 6)
    val list1: ListBuffer[Int] = ListBuffer(1, 5, -3, 4, 2, -7, 6)
    //  1、求和
    println(list.sum)
    //  2、求乘积
    println(list.product)
    //  3、最大值
    println(list.max)
    //  4、最小值
    println(list.min)
    //  5-1、排序(默认)(从小到大)
    println(list.sorted)
    //  排序(从大到小)
    println(list.sorted(Ordering[Int].reverse))
    //  5-2、排序(元组)
    val tuples: List[(String, Int)] = List(("hello", 10), ("world", 12), ("scala", 9))
    //  排序(元组)(默认字典序)
    println(tuples.sorted)
    //  排序(元组)(第2个元素从小到大排序)
    val tuples1: List[(String, Int)] = tuples.sortBy(one => one._2)
    tuples.sortBy(_._2)
    //  排序(元组)(第2个元素从大到小排序)
    println(tuples.sortBy((one => one._2))(Ordering[Int].reverse))
    //  5-3、自定义排序规则
    val tuples2: List[(String, Int)] = tuples.sortWith((left: (String, Int), right: (String, Int)) => {
      //  自定义规则
      left._2 > right._2
    })
    tuples.sortWith((a, b) => a._2 > b._2)
    tuples.sortWith(_._2 > _._2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    7.4 高级函数
    • **过滤:**遍历一个集合并从中获取满足指定条件的元素组成一个新的集合。

    • **转化/映射(map):**将集合中的每一个元素映射到某一个函数。

    • 扁平化

    • **扁平化+映射:**注:flatMap相当于先进行map操作,在进行flatten操作。集合中的每个元素的子元素映射到某个函数并返回新集合。

    • **分组(groupBy):**按照指定的规则对集合的元素进行分组。

    • 简化(归约)

    • 折叠

    val list: List[Int] = List(1, 2, 3, 4, 5)
    val nestedList: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6))
    val wordList: List[String] = List("hello world", "hello scala")
    //  1、过滤(List(2, 4))
    println(list.filter(x => x % 2 == 0))
    //  2、转化、映射(List(2, 3, 4, 5, 6))
    println(list.map(x => x + 1))
    //  3、扁平化(List(1, 2, 3, 4, 5, 6))
    println(nestedList.flatten)
    //  4、扁平化+映射(List(hello, world, hello, scala))
    println(wordList.flatMap(x => x.split(" ")))
    //  5、分组(Map(1 -> List(1, 3, 5), 0 -> List(2, 4)))
    println(list.groupBy(x => x % 2))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • Reduce简化(归约) :通过指定的逻辑将集合中的数据进行聚合,从而减少数据,最终获取结果。
    val list: List[Int] = List(1, 2, 3, 4)
    //  将数据两两结合,实现运算规则(1-2-3-4 = -8)
    println(list.reduce((x, y) => x - y))
    //  从源码的角度,reduce底层调用的其实就是reduceLeft(1-2-3-4 = -8)
    println(list.reduceLeft((x, y) => x - y))
    //  (((4-3)-2-1) = -2)
    println(list.reduceRight((x, y) => x - y))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • Fold折叠:化简的一种特殊情况,可以添加初始值
    val list: List[Int] = List(1, 2, 3, 4)
    // fold方法使用了函数柯里化,存在两个参数列表
    // 第一个参数列表为 : 零值(初始值)
    // 第二个参数列表为: 简化规则
    // fold底层其实为foldLeft
    println(list.foldLeft(1)((x, y) => x - y))
    println(list.foldRight(10)((x, y) => x - y))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    8、队列

    Scala也提供了队列(Queue)的数据结构,队列的特点就是先进先出。进队和出队的方法分别为enqueuedequeue

    //	初始化对接
    val que: mutable.Queue[String] = new mutable.Queue[String]()
    //	插入元素(Queue(a, b, c))
    que.enqueue("a","b","c")
    //  弹出元素(a)
    println(que.dequeue())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    七、模式匹配

    1、基本语法

    Scala中的模式匹配类似于Java中的switch语法

    模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有case都不匹配,那么会执行case _分支,类似于Java中default语句。

    • 如果所有case都不匹配,那么会执行case _ 分支,类似于Java中default语句,若此时没有case _ 分支,那么会抛出MatchError。
    • 每个case中,不需要使用break语句,自动中断case。
    • match case语句可以匹配任何类型,而不只是字面量。
    • => 后面的代码块,直到下一个case语句之前的代码是作为一个整体执行,可以使用{}括起来,也可以不括。
    val num: Int = 3
    val result: String = num match {
      case 1 => "1"
      case 2 => "2"
      case 3 => "3"
      case _ => "未匹配上"
    }
    //  3
    println(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2、常见匹配用法

    匹配类型

    需要进行类型判断时,可以使用前文所学的isInstanceOf[T]asInstanceOf[T],也可使用模式匹配实现同样的功能。

    def func(x: Any): String = {
      x match {
        case a: Int => "整数"
        case b: Char => "字符"
        case c: String => "字符串"
        case _ => "其它"
      }
    }
    println(func(1))
    println(func('\t'))
    println(func("1232"))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    执行结果:

    整数
    字符
    字符串
    
    • 1
    • 2
    • 3

    匹配元组

    for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2), (1, 2, 3, 4))) {
      val result: String = tuple match {
        //  匹配0开头
        case (0, _) => "0 ..."
        //  匹配2个值
        case (y, _) => "" + y + "0"
        //  匹配3个值
        case (a, b, c) => "" + a + " " + b + " " + c
        case _ => "other"
      }
      println(result)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    执行结果

    0 ...
    10
    10
    1 0 2
    other
    
    • 1
    • 2
    • 3
    • 4
    • 5

    匹配对象

    案例:

    object Test05 {
      def main(args: Array[String]): Unit = {
        val person: Person5 = new Person5("张三", 18)
    
        person match {
          case Person5("张三", 18) => println("找到张三了")
          case _ => println("你不是张三")
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    class Person05 (val name:String,var age:Int){
    
    }
    
    object Person05{
      // 创建对象的方法
      def apply(name: String, age: Int): Person05 = new Person05(name, age)
    
      // 解析对象的方法
      def unapply(arg: Person05): Option[(String, Int)] = {
        // 如果解析的参数为null
        if (arg == null ) None else Some((arg.name,arg.age))
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • val user = Person05("zhangsan",11),该语句在执行时,实际调用的是Person05伴生对象中的apply方法,因此不用new关键字就能构造出相应的对象。

    • 若只提取对象的一个属性,则提取器为unapply(obj:Obj):Option[T]

      若提取对象的多个属性,则提取器为unapply(obj:Obj):Option[(T1,T2,T3…)]

      若提取对象的可变个属性,则提取器为unapplySeq(obj:Obj):Option[Seq[T]]

    匹配样例类

    case class Person05 (name: String, age: Int)
    
    • 1
    case class Person05(var name: String, age: Int)
    
    • 1
    • 样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如applyunapplytoStringequalshashCodecopy
    • 样例类是为模式匹配而优化的类,因为其默认提供了unapply方法,因此,样例类可以直接使用模式匹配,而无需自己实现unapply方法。
    • 构造器中的每一个参数都成为val,除非它被显式地声明为var(不建议这样做)

    3、偏函数中的模式匹配

    偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。例如该偏函数的输入类型为List[Int],而我们需要的是第一个元素是0的集合,这就是通过模式匹配实现的

    在这里插入图片描述

    //	返回输入的List集合的第二个元素。
    val second: PartialFunction[List[Int], Option[Int]] = {
      case x :: y :: _ => Some(y)
    }
    
    • 1
    • 2
    • 3
    • 4

    上述代码会被scala编译器翻译成以下代码,与普通函数相比,只是多了一个用于参数检查的函数——isDefinedAt,其返回值类型为Boolean。

    val second = new PartialFunction[List[Int], Option[Int]] {
    
        //检查输入参数是否合格
        override def isDefinedAt(list: List[Int]): Boolean = list match {
            case x :: y :: _ => true
            case _ => false
        }
    
        //执行函数逻辑
        override def apply(list: List[Int]): Option[Int] = list match {
            case x :: y :: _ => Some(y)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    偏函数调用

    偏函数不能像second(List(1,2,3))这样直接使用,因为这样会直接调用apply方法,而应该调用applyOrElse方法

    second.applyOrElse(List(1,2,3), (_: List[Int]) => None)
    
    • 1
    val list: List[Any] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, "test")
    //  1、filter中使用
    val list1: List[Any] = list.filter(a => {
      a match {
        case s: String => false
        case i: Int => true
      }
    })
    //	2、map中使用
    val list2: List[Int] = list1.map {
      case i: Int => i + 1
    }
    //	3、collect中使用
    val list3: List[Int] = list.collect({
      case i: Int => i + 1
    })
    //	4、PartialFunction 偏函数中使用
    val value: PartialFunction[Any, Int] = {
      case i: Int => i + 1
    }
    //	5、偏函数中使用
    val function111: Any => Int = (a: Any) => a match {
      case i: Int => i + 1
    }
    //	6、isInstanceOf中使用
    List(1, 2, 3, 4, 5, 6, "test").filter(_.isInstanceOf[Int]).map(_.asInstanceOf[Int] + 1).foreach(println)
    //	7、collect中使用
    List(1, 2, 3, 4, 5, 6, "test").collect { case x: Int => x + 1 }.foreach(println)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    4、下划线使用总结

    • 1、用于类中的var属性,使用默认值。
    • 2、用于高阶函数的第一种用法,表示函数自身。
    • 3、匿名函数化简,用下划线代替变量。
    • 4、用于导包下的所有内容。
    • 5、用于起别名时表示匿名。
    • 6、用于模式匹配表示任意数据。
    class Person001 {
      //  1、用于类中的var属性,使用默认值。
      var name1: String = _
    }
    
    • 1
    • 2
    • 3
    • 4
    //  2、用于高阶函数的第一种用法,表示函数自身。
    def sayHi(name: String): Unit = {
      println(s"hi $name")
    }
    val function: String => Unit = sayHi _
    //  3、匿名函数化简,用下划线代替变量。
    val function01: (Int, Int) => Int = (a: Int, b: Int) => a + b
    val function02: (Int, Int) => Int = _ + _
    //  4、用于导包下的所有内容。
    import scala.util.control.Breaks._
    //  5、用于起别名时表示匿名。
    import scala.util.control.{Breaks => _}
    //   a / Breaks
    //  6、用于模式匹配表示任意数据。
    10 match {
      case 10 => "10"
      case _ => "other"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    八、异常

    • Scala没有“checked(编译期)”异常,即Scala没有编译异常这个概念,异常都是在运行的时候捕获处理。
    • 如果有异常发生,catch子句是按次序捕捉的。
    • finally子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作。
    • 用throw关键字,抛出一个异常对象。所有异常都是Throwable的子类型。throw表达式是有类型的,就是Nothing,因为Nothing是所有类型的子类型,所以throw表达式可以用在需要类型的地方
    def test():Nothing = {
        throw new Exception("不对")
    }
    
    • 1
    • 2
    • 3
    • Java提供了throws关键字来声明异常。可以使用方法定义声明异常。它向调用者函数提供了此方法可能引发此异常的信息。它有助于调用函数处理并将该代码包含在try-catch块中,以避免程序异常终止。在Scala中,可以使用throws注解来声明异常。
    def main(args: Array[String]): Unit = {
      	f11()
    }
    
    @throws(classOf[NumberFormatException])
    def f11()={
      	"abc".toInt
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    完整代码:

    def main(args: Array[String]): Unit = {
      try {
        val n = 10 / 0
      } catch {
        case ex: ArithmeticException => {
          println("发生算数异常")
        }
        //	抛出异常
        case ex: NullPointerException => {
          throw NullPointerException
        }
        case ex: Exception => {
          println("发生了其他异常")
        }
      } finally {
        println("finally")
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    执行结果

    发生算数异常
    finally
    
    • 1
    • 2

    九、隐式转换

    当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译,用于拓展类的方法。

    隐式转换可以在不需改任何代码的情况下,扩展某个类的功能。

    object Test02_Imp {
      def main(args: Array[String]): Unit = {
    
        // 隐式函数
        // 将当前作用域下所有传入参数的类型   隐式转换为  返回值类型
        implicit def changeInt(self: Int) = {
          new MyRichInt(self)
        }
    
        val i: Int = 10
    
        // 比较自身和传入参数的大小  返回较大的值
        val value: Int = i.myMax(20)
        println(value)
    
    
        val i1: Int = i << 2
        println(i1)
    
      }
    
    
      // 隐式转换的目标
      class MyRichInt(val self: Int) {
    
        def myMax(i: Int): Int = {
          if (i > self) i else self
        }
    
        // 如果隐式转换和自身的方法冲突  会使用它自身的  因为不会编译失败
        def <<(x: Int): Int = {
          0
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
  • 相关阅读:
    面对密集型的I/O任务处理,python该如何提高执行效率
    Pycharm2022 pycharm64.exe.Vmoptions
    etcd MVCC 存储结构及流程
    vue - Vue组件化编程
    vue/react/js 常用的原生获取当前页面的url网址的相关方法
    leetcode:2478. 完美分割的方案数【预处理 + dp定义 + 前缀和优化】
    拦截导弹问题(贪心算法)
    Django学习(三) 之 模板中标签的使用
    iOS制作微信(weChat)支付SDK过程
    Linux CentOS 8(用户管理)
  • 原文地址:https://blog.csdn.net/weixin_44624117/article/details/133219900