Scala 基础知识小结

298 阅读3分钟

「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战」。

简介

Scala 是一门基于 JVM 的多范式编程语言。

特点

  1. 兼容性:兼容 Java,可以访问庞大的 Java 类库,例如操作 mysql、redis 等。

  2. 精简的:Scala 表达能力强,一行代码抵得上多行 Java 代码,开发速度快。

  3. 高级的:Scala 可以让程序保持短小、清晰,看起来更简洁、优雅。

  4. 静态类型:Scala 拥有非常先进的静态类型系统,支持类型推断和模式匹配等。

  5. 可以开发大数据应用程序:例如 spark 、flink 程序等。

对比

  • Java 源代码通过 Javac 编译,编译结果为 Java 字节码、Java 类库
  • Scala 源代码通过 scalac 编译,编译结果为 Java 字节码、Java 类库、Scala 类库

基本知识重点

变量

  • val 定义的是不可重新赋值的变量,也就是自定义常量。
  • var 定义的变量可以被重新赋值。

字符串

  • 使用双引号:var name = "zhangsan"
  • 使用插值表达式,有效避免大量字符串拼接:val/var 变量名 = s"{变量/表达式}字符串"
  • 使用三引号,可以保存大量文本,例如大段 SQL 语句,三引号中的所有内容都会作为字符串的值:val/var 变量名 = """ 字符串的值 """
  • 惰性赋值,在大数据开发中有时会编写非常复杂的 SQL 语句,当一些变量保存的数据较大时,而这些数据又不需要马上加载到 JVM 中,可以使用惰性赋值来提高效率:lazy val/var 变量名 = 值

Scala 的类型关系图

截屏2021-11-03 下午3.00.22.png

算数运算符

  • Scala中没有 ++--这两个运算符

  • a % b,底层是 a - a/b * b

  • Scala 中把字符串和整数数字 n 相乘相当于让字符串重复 n 次

关系运算符

  • 比较数据值,使用==或者!=
  • 比较引用值需要使用 eq 方法

break和continue

在 Scala 中,移除了 break 和 continue 关键字,如果要使用,需要 scala.util.controle 包下的 Breaks 类的 breakable 和 break 方法。

import scala.util.control.Breaks._

break

  • 使用 breakable 将 for 表达式包起来
  • for 表达式中需要退出循环的地方,添加 break() 方法调用。
//输出 1-10 的数字,遇到5退出:
scala> breakable{
     |   for( i <- 1 to 10){
     |     if(i == 5) break() else print(i)
     |   }
     | }
1234

continue

continue 的实现和 break 类似,不同的是需要使用 continue 的地方是用 breakable 将 for 表达式的循环体包起来即可。

//输出 1-10 不能整除 3 的数字:
scala> for(i <- 1 to 10){
     |   breakable{
     |     if(i % 3 == 0) break() else print(i)
     |   }
     | }
12457810

方法

  • 参数列表的参数类型不能省略
  • 返回值类型可以省略,由 Scala 编译器自动推断
  • 返回值可以不写 return ,默认就是块表达式的值

函数

  • 函数是一个对象
  • 类似于方法,函数也有参数列表和返回值
  • 函数定义不需要使用 def 定义
  • 无需指定返回值类型

方法和函数区别

  • 方法属于类或者对象,在运行时会加载到 JVM 的方法区。
  • 可以将函数对象赋值给一个变量,在运行时会加载到 JVM 的堆中。
  • 函数是一个对象,继承自 FunctionN,函数对象有apply、curried、toString、tupled这些方法,方法则没有。
  • 结论:在 Scala 中,函数是对象,而方法是属于对象的,可以理解为:方法归属于函数。
  • 可以通过在方法后加上空格和下划线将其转为函数。

成员变量

  • 在定义 var 类型的成员变量时,可以使用 _ 初始化成员变量。
  • val 类型的成员变量,必须要自己手动初始化。

访问修饰符

  • 在 Scala 中没有 public 关键字,没有被标记为 private 和 protected 的成员都是公共的。
  • Scala 中的权限修饰符只有:privat、private[this]、protected、默认。

构造器

  • 主构造器:
class 类名(var/val 参数名:类型 = 默认值,...){
	//构造代码块
}
  • 辅助构造器:辅助构造器方法名必须叫 this。
def this(参数名:类型,...){
	//第一行需要调用主构造器或其他构造器
	//构造器代码
}

单例对象

  • 定义单例对象和定义类很像,就是把 class 换成 object。
  • 在 object 中定义的成员变量类似 Java 中的静态变量,在内存中只有一个对象。
  • 单例对象中,可以直接使用 单例对象名.的方式调用成员。
  • 单例对象中的方法类似 Java 中的静态方法。

main方法

在 Java 中 main 方法是静态的,Scala 中没有 main 方法,所以必须将其放在一个单例对象中。

  • 创建单例对象
object ClassDemo {

  def main(args: Array[String]): Unit = {
    print("hello scala")
  }

}
  • 继承App特质
object ClassDemo extends App {
    print("hello scala")
}

伴生对象

  • 在 Java 中有一些类会同时有静态内容和非静态内容,在 Scala 中想要实现类似效果可以使用伴生对象来实现。
  • 一个 class 和 object 具有相同的名字,这个 object 被称为伴生对象,这个 class 被称为半生类。
  • 伴生对象和半生类可以互相访问 private 属性,必须写在一个源文件。
  • 如果某个成员变量权限为 private[this],表示只能在当前类访问,伴生对象也不能直接访问。

继承

  • 子类重写方法必须使用 override 修饰,可以使用 override 重新一个 val 字段,父类的 var 字段不可重写。

类型判断

  • isInstanceOf 判断对象是否为指定类的对象。
  • isInstanceOf 只能判断对象是否为指定类以及其子类的对象而不能精确判断其类型,如果精确判断可以使用 getClass 和 classOf 来实现。
  • asInstanceOf 将对象转换为指定类型。

抽象类

如果类中有抽象字段或抽象方法,那么该类就必须是抽象类。

  • 抽象字段:没有初始化的变量。
  • 抽象方法:没有方法体。

特质

Scala 中的特质要用关键字 trait 修饰。

特点

  • 特质可以提高代码的复用性。
  • 特质可以提高代码的扩展性和可维护性。
  • 类与特质是继承关系,类与类只支持单继承,类与特质之间可以单继承也可以多继承。
  • Scala 的特质中可以有普通字段、抽象字段、普通方法、抽象方法。
  • 如果特质只有抽象内容也叫瘦接口,如果既有抽象内容又有具体内容叫做富接口。
  • 在 Scala 中,类和特质之间无任何继承关系,但通过特定关键字让该类对象具有指定特质中的成员:val/var 对象名 = new 类 with 特质。

trait构造机制

每个特质只有一个无参构造器。 遇到一个类继承另一个类以及多个trait的情况,创建该类实例时构造器执行顺序:

  • 执行父类构造器
  • 从左到右依次执行 trait 的构造器
  • 如果trait 有父 trait,先执行父 trait
  • 如果多个 trait 有相同的父 trait,父 trait 构造器只初始化一次
  • 执行子类构造器

trait继承class

  • trait 可以继承 class,会将所有的成员都继承下来。

包就是文件夹,用 package 修饰,可以区分重名类。

作用域:

  • 子包可以直接访问父包中的内容。
  • 上层访问下层内容时,可以通过导包(import)或者写全包名的形式实现。
  • 如果上下层有相同的类,使用时采用就近原则(优先使用下层)。

包对象:

  • 要定义在父包中,一般用于对包的功能进行补充、增强。

可见性:

  • 通过访问权限修饰符 private、protected、默认来限定访问修饰符:访问修饰符[包名]。

引入

  • Scala 默认引入了 java.lang 包的全部内容,scala 包以及 Predef 包的部分内容。
  • 包的引入不限于Scala 文件的顶不,而是可以编写到任何需要使用的地方。
  • 如果需要导入某个包中的所有类和特质,使用下划线 _ 实现。
  • 如果需要的时某个包的某几个类和特质,可以通过选取器 {}实现。
  • 如果引入的多个包含有相同的类,可以通过重命名或隐藏解决。
  • 重命名格式:import java.util.{HashSet => JavaSet}。
  • 隐藏格式:import java.util.{HashSet => ,}//引入util包下除了HashSet的类。

样例类

样例类是一种特殊类,一般用于保存数据,在并发编程以及 Flink 等框架中会经常使用。

case class 样例类名([var/val] 成员变量名1:类型1...)

如果不写,变量默认修饰符是val。 如果要实现某个成员变量值可以被修改,则需要手动添加var 修饰。

样例类的默认方法

  • apply:可以快速使用类名创建对象,省略 new 关键字。
  • toString:可以在打印时直接打印该对象各个属性值。
  • equals:可以直接使用 == 直接比较属性值。
  • hashCode:同一个对象哈希值一定相同,不同对象哈希值一般不同。
  • copy:可以用来快速创建属性值相同的实例对象,还可以使用带名参数的形式给指定的成员变量赋值。
  • unapply

样例对象

用 case 修饰的单例对象就叫样例对象,而且它没有主构造器,主要用在:

  • 枚举值
  • 作为没有任何参数的消息传递