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 不直接支持
break和continue,但可以通过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:用于处理可能为空的值,分为
Some与None。
-
迭代器 用于顺序访问集合中的元素,主要方法有
hasNext和next。
正则表达式
- Scala 支持正则表达式处理,通过
scala.util.matching.Regex类实现,常用方法包括findFirstIn、findAllIn、replaceFirstIn与replaceAllIn等。
异常处理与文件 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)
}
}
解析过程:
- 使用
JSON.parseObject(str2)将字符串解析为 JSON 对象。 - 获取某个键(如
"et")的值。 - 使用
.getString()或.getInteger()方法获取对应的值。 - 访问嵌套结构(如
"body")时,可以链式调用.getJSONObject()方法。
方法二:使用 spray-json
spray-json 是一个轻量级的 JSON 解析库,适合 Scala 项目,特别是处理 JSON 数据时。
-
添加依赖:
在
sbt项目中添加以下依赖:libraryDependencies += "io.spray" %% "spray-json" % "1.3.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"))
}
}
解析过程:
- 使用
JsonParser将 JSON 字符串解析为JsValue。 - 使用
.asJsObject()将其转换为JsObject。 - 使用
.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"))
}
解析过程:
- 使用
JSON.parseFull(str)解析 JSON 字符串为Option[Any]类型。 - 使用
regJson方法提取Map类型数据。 - 使用
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