Scala基础

130 阅读7分钟

Scala基础

Scala 简介

  • 跨平台与基于 Java Scala 可以在 Windows、Linux、Unix、Mac OS X 等多种系统上运行,运行在 JVM 上,能够调用 Java 类库。使用 Scala 前需要安装 Java(版本大于 1.5)。
  • 面向对象与函数式编程 Scala 将一切都视为对象,同时也支持函数式编程风格,如闭包和高阶函数。

基本语法与程序结构

  • Hello World 程序 一个典型的 Scala 程序如下:

    object HelloWorld {
        def main(args: Array[String]): Unit = {
            println("Hello, world!")
        }
    }
    

    程序文件名必须与对象名称保持一致(区分大小写),扩展名为 .scala

  • 注释、空格与换行 支持单行注释(//)和多行注释(/* ... */),多行注释可以嵌套。语句末尾的分号通常可省略,但在一行内有多个语句时需要使用分号分隔。

  • 包与引用 使用 package 定义包,可在文件顶部或局部范围内定义;使用 import 导入类或包。默认会导入 java.lang._scala._Predef._


数据类型与变量

  • 基本数据类型 Scala 支持与 Java 类似的基本数据类型,如 Byte、Short、Int、Long、Float、Double、Char、Boolean,以及特殊类型 Unit(无返回值)、Null、Nothing、Any(所有类型的超类)和 AnyRef(所有引用类型的超类)。

  • 字符串与多行字符串 Scala 中的 String 基于 Java String,是不可变的。多行字符串使用三个双引号包裹,如:

    val multiline = """这是一个多行字符串"""
    
  • 字符串格式化

    scala> "%1$s-%2$s-%3$s".format("spark","scala","ml")
    res29: String = spark-scala-ml
    
  • 变量声明 使用 var 声明变量(可变),使用 val 声明常量(不可变)。声明时可以指定类型,Scala 编译器也能自动推断类型。

  • 标识符与命名规则 Scala 区分大小写,类名首字母大写,方法名和变量名通常以小写字母开头。标识符可以由字母、数字和符号组成(部分符号可用于运算符重载)。


控制结构

  • 条件判断 使用 if...else 语句进行条件分支,支持嵌套;Scala 中的 if 表达式可以有返回值。

  • 循环结构

    • while / do…while:用于循环执行语句块。
    • for 循环:支持遍历区间、集合、数组,并且可以使用过滤器(if)以及 yield 返回新的集合。
    • 中断循环:Scala 不直接支持 breakcontinue,但可以通过 scala.util.control.Breaks 实现循环中断。

函数与闭包

  • 函数定义与调用 使用 def 定义函数,参数列表后跟返回类型,函数体以等号和大括号表示。函数可以在类、对象或单独定义。
  • 闭包 闭包是一个函数,它引用了定义在其外部的变量。变量在闭包内被捕获,当这些变量改变时,闭包内的计算结果也会随之变化。

面向对象特性

  • 类与对象 类是对象的模板,使用 class 定义;使用 new 关键字创建对象。Scala 中的类可以带有参数(主构造函数)。
  • 继承与多态 使用 extends 实现单继承;通过重写(override 关键字)实现方法覆盖。
  • 单例对象与伴生对象 使用 object 定义单例对象,Scala 没有 static 关键字。若一个 object 与一个 class 同名且在同一文件中,它们构成伴生对象和伴生类,可以互相访问私有成员。
  • Trait(特征) Trait 类似于 Java 的接口,但可以包含具体方法和属性。支持多重继承,是实现代码复用的重要机制。

模式匹配

  • 基本用法 模式匹配使用 match 关键字,可视为更强大的 switch 语句,支持对数据类型、结构以及守卫条件进行匹配。
  • 样例类与提取器 通过定义样例类(case class)和 unapply 方法,可用于模式匹配提取数据。

集合与数组

  • 数组 固定大小的同类型数据集合,使用 Array(...) 定义,可通过索引访问和修改。支持基本遍历、求和、最大最小值等操作。

  • 集合框架 Scala 集合分为不可变集合(默认)和可变集合。主要类型包括:

    • List:不可变列表,采用链表实现,支持 head、tail 等操作。
    • Set:集合中元素唯一,可使用 ++ 运算符合并集合。
    • Map:键值对映射,不同集合类型分别提供可变与不可变实现。
    • 元组(Tuple) :可以存储不同类型的元素,访问方式为 _1, _2, …
    • Option:用于处理可能为空的值,分为 SomeNone
  • 迭代器 用于顺序访问集合中的元素,主要方法有 hasNextnext


正则表达式

  • Scala 支持正则表达式处理,通过 scala.util.matching.Regex 类实现,常用方法包括 findFirstInfindAllInreplaceFirstInreplaceAllIn 等。

异常处理与文件 I/O

  • 异常处理 使用 try...catch...finally 结构,catch 部分支持模式匹配捕捉不同类型的异常,类似于 Java,但语法更简洁。
  • 文件 I/O Scala 直接调用 Java I/O 类进行文件的读写操作,例如使用 PrintWriter 写文件、使用 scala.io.Source 读取文件。

日常实践

JSON 解析方法

方法一:使用 fastjson

fastjson 是一个高效的 Java JSON 序列化/反序列化库。

在 Scala 中,你可以使用 com.alibaba.fastjson.JSON 来解析 JSON 字符串。

import com.alibaba.fastjson.JSON

object Json {
  def main(args: Array[String]): Unit = {
    val str2 = "{"et":"kanqiu_client_join","vtm":1435898329434,"body":{"client":"866963024862254","client_type":"android","room":"NBA_HOME","gid":"","type":"","roomid":""},"time":1435898329}"
    
    // 解析 JSON 字符串为 JSONObject
    val json = JSON.parseObject(str2)
    
    // 获取成员值
    val fet = json.get("et")
    val etString = json.getString("et")
    val vtm = json.getInteger("vtm")
    println(vtm)
    
    // 获取多级嵌套成员
    val client = json.getJSONObject("body").get("client")
    println(client)
  }
}

解析过程:

  1. 使用 JSON.parseObject(str2) 将字符串解析为 JSON 对象。
  2. 获取某个键(如 "et")的值。
  3. 使用 .getString().getInteger() 方法获取对应的值。
  4. 访问嵌套结构(如 "body")时,可以链式调用 .getJSONObject() 方法。

方法二:使用 spray-json

spray-json 是一个轻量级的 JSON 解析库,适合 Scala 项目,特别是处理 JSON 数据时。

  1. 添加依赖:

    sbt 项目中添加以下依赖:

    libraryDependencies += "io.spray" %%  "spray-json" % "1.3.2"
    
  2. 使用 spray-json 解析 JSON:

import spray.json._

object JsonExample {
  def main(args: Array[String]): Unit = {
    val result = http.get(url)
    val json = JsonParser(result)
    val jsObj = json.asJsObject()
    println(jsObj.getFields("code"))
  }
}

解析过程:

  1. 使用 JsonParser 将 JSON 字符串解析为 JsValue
  2. 使用 .asJsObject() 将其转换为 JsObject
  3. 使用 .getFields("code") 获取字段值。

方法三:使用 Scala 自带的 scala.util.parsing.json.JSON

这是 Scala 自带的 JSON 解析库,可以用来解析简单的 JSON 字符串。

import scala.util.parsing.json._

class JsonT {
  def regJson(json: Option[Any]): Map[String, Any] = json match {
    case Some(map: Map[String, Any]) => map
    case _ => Map.empty[String, Any]
  }

  val str = "{"host":"td_test","ts":1486979192345,"device":{"tid":"a123456","os":"android","sdk":"1.0.3"},"time":1501469230058}"
  val jsonS = JSON.parseFull(str)
  val first = regJson(jsonS)
  
  println(first.get("host").getOrElse("Unknown"))
  val dev = first.get("device")
  println(dev)
  
  val sec = regJson(dev)
  println(sec.get("tid").getOrElse("Unknown"))
}

解析过程:

  1. 使用 JSON.parseFull(str) 解析 JSON 字符串为 Option[Any] 类型。
  2. 使用 regJson 方法提取 Map 类型数据。
  3. 使用 getOrElse 来获取键对应的值,避免出现 NoSuchElementException 异常。

日常问题

import spark.implicits._如何导入

使用toDS()函数需要导入隐士转换的包 RDD转换成DataSet

import spark.implicits._

隐式转换介绍

(1) 包括隐式参数、隐式对象、隐式类

(2) scala独有的。

(3) 当调用对象中不存在的方法,系统会扫描上下文和伴对象看是否有implicit方法,如果有隐式方法则调用隐式方法,隐式方法传入原生对象返回包含扩展方法的对象。

(4) 原类型和伴生对象都找不到的隐式值,会找手动导入的implicit

死锁问题

批量执行产生死锁,单条则可以

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.mysql.jdbc.Util.handleNewInstance(Util.java:408)
	at com.mysql.jdbc.Util.getInstance(Util.java:383)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1074)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4226)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4158)
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2615)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2776)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2840)
	at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2082)
	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2334)
	at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1933)
	... 14 more