Skip to content

2 数据类型与基本运算 ​

笔记衔接前序《Scala 入门教程》,严格匹配课程体系,针对有 Java 基础的大数据专业,全程Java 语法对比 + 可运行代码案例 + 随堂练习 + 避坑指南 + 作业全解析,贴合 Spark/Flink 大数据 ETL 开发的实际应用,所有案例可直接复制运行,学完可直接衔接 Scala 控制结构与面向对象编程。

  • 前置知识:Scala 入门基础、val/var 变量定义、Java 基础数据类型

  • 开发环境:JDK1.8+、Scala2.12.x、IDEA + Scala 插件

  • 核心学习目标:

    1. 掌握 Scala 全量数据类型,明确与 Java 数据类型的核心差异
  1. 熟练使用 Scala 字符串操作、插值、正则处理(大数据 ETL 核心技能)
  2. 掌握 Scala 各类运算符的使用、底层原理与优先级规则
  3. 彻底规避 Scala 数据类型与运算的高频坑点(浮点数精度、相等性判断等)

第一章 Scala 数据类型体系 ​

1.1 与 Java 的核心差异(必背) ​

Scala 是纯面向对象语言,这是和 Java 最核心的区别:

  • Java 分为基本数据类型(int/char 等)和包装类(Integer/Character 等),两套体系需要自动装箱 / 拆箱;
  • Scala 中所有数据类型都是对象,没有基本类型的概念,所有类型都继承自顶级父类Any,统一的对象体系避免了装箱拆箱的性能损耗,更适合大数据的海量数据计算场景。

1.2 Scala 数据类型总览 ​

Scala 数据类型分为数值类型和非数值类型两大类,完整类型体系如下表,所有类型的取值范围与 Java 对应类型完全一致,Scala 编译器会在字节码中自动转为 Java 的基本类型,保证性能:

大类数据类型核心描述取值范围
数值类型Byte8 位有符号整数-128 ~ 127
Short16 位有符号整数-32768 ~ 32767
Int32 位有符号整数(默认整数类型)-2147483648 ~ 2147483647
Long64 位有符号整数-9223372036854775808 ~ 9223372036854775807
Float32 位单精度浮点数符合 IEEE 754 标准
Double64 位双精度浮点数(默认浮点类型)符合 IEEE 754 标准
Char16 位无符号 Unicode 字符U+0000 ~ U+FFFF
Boolean布尔类型true / false
非数值类型String字符串,字符序列任意长度字符
Unit无值,等同于 Java 的 void唯一实例为()
Null空引用类型唯一实例为null
Nothing所有类型的子类,Scala 类层级最底端无实例
Any所有类型的顶级超类-
AnyRef所有引用类型的基类,等同于 Java 的 Object-

1.3 数值类型详解 ​

Scala的基本类型与Java的基本类型对应的取值区间完全相同,使得Scala编译器可以在产生的字节码中将Scala的值类型转成Java的基本类型。

1.3.1 整数类型 ​

Scala 整数类型包括 Byte、Short、Int、Long,核心规则与 Java 一致,但有 2 个关键差异:

  1. 默认整数类型:整数字面量默认是 Int 类型,若要声明 Long 类型,需在数值后加L/l后缀;
  2. 字面量规则:Scala不支持八进制和以 0 开头的字面量(Java 支持),仅支持十进制、十六进制(0x 开头)、二进制(0B 开头,Scala2.12 + 支持)。
代码案例与避坑 ​
scala
object IntTypeDemo {
  def main(args: Array[String]): Unit = {
    // 1. 基础声明
    val a: Byte = 123 // 合法,Byte取值范围内
    // val b: Byte = 128 // 编译报错,超出Byte最大值127
    val c: Short = 32767 // 合法,Short最大值
    // val d: Short = 32768 // 编译报错,超出Short最大值32767
    val e: Int = 100 // 十进制,默认Int类型
    val f: Long = 100L // 声明Long类型,必须加L后缀
    val g: Int = 0x000F // 十六进制,值为15
    val h: Int = 0B1010 // 二进制,值为10

    // 2. 非法字面量(与Java核心差异)
    // val i = 038 // 编译报错,Scala不支持0开头的八进制字面量

    // 3. 打印取值范围
    println(s"Byte最大值:${Byte.MaxValue},最小值:${Byte.MinValue}")
    println(s"Short最大值:${Short.MaxValue},最小值:${Short.MinValue}")
    println(s"Int最大值:${Int.MaxValue},最小值:${Int.MinValue}")
  }
}

1.3.2 浮点类型 ​

Scala 浮点类型包括 Float、Double,核心规则:

  1. 默认浮点类型:小数字面量默认是 Double 类型,若要声明 Float 类型,需在数值后加F/f后缀;
  2. 科学计数法:支持E/e表示科学计数法,例如12E2 = 1200.0;
  3. 精度问题:浮点数存在二进制精度丢失问题,是所有编程语言的共性,金融 / 金额计算场景需用 BigDecimal 解决。
代码案例 ​
scala
object FloatTypeDemo {
  def main(args: Array[String]): Unit = {
    val a: Double = 1.2 // 默认Double类型
    val b: Float = 1.2f // 声明Float类型,必须加f后缀
    val c: Double = 12E2 // 科学计数法,值为1200.0
    val d: Float = 12E2f // Float类型科学计数法

    println(s"a=$a, b=$b, c=$c, d=$d")
    // 浮点数精度问题演示
    println(s"0.1 + 0.2 = ${0.1 + 0.2}") // 输出0.30000000000000004,而非0.3
  }
}

1.3.3 字符类型 Char ​

Scala 的 Char 是 16 位无符号 Unicode 字符,核心特性:

  1. 字面量表示:单引号包裹单个字符,例如'a';
  2. Unicode 编码:支持\uXXXX格式的 Unicode 编码表示,且\uXXXX可出现在 Scala 代码的任意位置(不仅限于字符字面量);
  3. 转义字符:支持标准的转义字符,用于表示特殊字符;
  4. 数值运算:Char 类型可直接参与数值运算,底层使用对应的 Unicode 编码值计算。
常用转义字符表 ​

表格

转义字符Unicode描述
\b\u0008退格
\t\u0009水平制表符(Tab)
\n\u000a换行
\r\u000d回车
\"\u0022双引号
\'\u0027单引号
\\\u005c反斜杠
代码案例 ​
scala
object CharTypeDemo {
  def main(args: Array[String]): Unit = {
    // 1. 基础字符声明
    val c1: Char = 'a'
    val c2: Char = '\u0041' // Unicode编码,对应'A'
    println(s"c1=$c1, c2=$c2")

    // 2. Unicode编码可出现在代码任意位置
    val \u0041\u0042 = 10 // 等价于 val AB = 10
    println(s"AB=$AB")

    // 3. 转义字符
    println("hello\tscala\n换行")
    println("双引号:\",单引号:\',反斜杠:\\")

    // 4. 字符参与数值运算
    println(s"'a' + 7 = ${'a' + 7}") // 'a'的Unicode值是97,输出104
    println(s"97.toChar = ${97.toChar}") // 数值转字符,输出'a'
  }
}

1.3.4 布尔类型 Boolean ​

Scala 的 Boolean 类型只有两个值:true和false,与 Java 完全一致,仅用于逻辑判断,不能参与数值运算(Java 中 boolean 和 int 可强制转换,Scala 禁止)。

1.4 非数值类型详解 ​

非数值类型是大数据开发的核心,尤其是 String 类型,ETL 数据清洗 90% 的场景都在处理字符串,本节重点讲解。

1.4.1 String 字符串类型 ​

Scala 的 String 本质就是 Java 的java.lang.String,完全兼容 Java 的所有 String 方法,同时扩展了更强大的特性,更适合大数据场景的字符串处理。

特性 1:字符串的两种声明方式 ​

表格

声明方式语法适用场景
双引号"内容"普通单行字符串,与 Java 一致
三引号"""内容"""多行字符串、包含转义字符 / 引号的字符串,无需手动转义
代码案例 ​
scala
object StringDeclareDemo {
  def main(args: Array[String]): Unit = {
    // 1. 双引号单行字符串
    val s1 = "hello scala"
    println(s1)

    // 2. 三引号多行字符串,无需转义
    val s2 =
      """
        |hello scala
        |hello java
        |"这是双引号",'这是单引号',\这是反斜杠,无需转义
        |""".stripMargin // stripMargin自动对齐,默认以|为边界
    println(s2)

    // 3. 按要求输出特殊字符串
    println("hello scala") // 输出hello scala字符串
    println("\"hello scala\"") // 输出“hello scala”字符串
    println("\\hello scala\\") // 输出\hello scala\字符串
  }
}
特性 2:字符串常用方法(大数据 ETL 高频) ​

完全兼容 Java 方法,重点掌握以下高频方法:

表格

方法作用大数据场景
charAt(index)返回指定索引的字符提取固定位置字符(如身份证号省份编码)
indexOf(ch)返回指定字符首次出现的索引查找分隔符位置
split(regex)按正则 / 分隔符拆分字符串,返回数组文本拆分、单词统计、字段分隔
startsWith(prefix)判断是否以指定前缀开头数据筛选(如筛选北京身份证号)
matches(regex)判断是否匹配正则表达式数据格式校验(手机号、身份证)
replaceAll(regex, replacement)替换所有匹配正则的内容数据脱敏、内容替换
replaceFirst(regex, replacement)替换第一个匹配的内容局部内容修改
substring(start, end)截取子字符串提取固定长度内容(身份证生日)
大数据场景案例 ​
scala
object StringMethodDemo {
  def main(args: Array[String]): Unit = {
    // 案例1:判断身份证号是否为北京身份证(北京身份证前两位11)
    val idCard = "110101199001011234"
    val isBeijing = idCard.startsWith("11")
    println(s"是否为北京身份证:$isBeijing") // 输出true

    // 案例2:身份证号后四位脱敏(用*代替)
    val idCard2 = "210404197681232113"
    val desensitizedId = idCard2.substring(0, idCard2.length - 4) + "****"
    println(s"脱敏后身份证:$desensitizedId") // 输出210404197681232****

    // 案例3:主机名匹配,不区分大小写
    val hostStr = "host01,host03,hadoop01,hadoop010"
    val targetHost = "HADOOP01"
    val isExist = hostStr.toLowerCase.split(",").contains(targetHost.toLowerCase)
    if (isExist) println(s"find $targetHost") else println(s"unfind $targetHost")

    // 案例4:薪资字符串替换,将数字用*代替
    val salaryStr = "his basic salary is 10000,and bonus is 20000"
    val replaceStr = salaryStr.replaceAll("\\d+", "*")
    println(s"替换后:$replaceStr")
    // 输出:his basic salary is *,and bonus is *
  }
}
特性 3:字符串遍历与转换 ​

Scala 提供 3 种遍历方式,可对每个字符进行处理,是数据清洗的常用操作:

  1. for循环
  2. foreach遍历
  3. map映射转换
代码案例:字符大写转换 ​
scala
object StringTraverseDemo {
  def main(args: Array[String]): Unit = {
    val str = "scala"

    // 方式1:for循环遍历
    println("=== for循环遍历 ===")
    for (c <- str) {
      print(c + " ")
    }
    println()

    // 方式2:foreach遍历
    println("=== foreach遍历 ===")
    str.foreach(c => print(c.toUpper + " "))
    println()

    // 方式3:map转换,返回新字符串
    println("=== map转换大写 ===")
    val upperStr = str.map(c => c.toUpper)
    println(upperStr) // 输出SCALA

    // 练习:提取字符串"123456"偶数部分,返回新字符串
    val numStr = "123456"
    val evenStr = for (c <- numStr if c.toInt % 2 == 0) yield c
    println(s"偶数部分:$evenStr") // 输出246

    // 练习:将字符串"123456"变成"234567"
    val newStr = numStr.map(c => (c.toInt + 1).toChar)
    println(s"转换后:$newStr") // 输出234567
  }
}
特性 4:字符串插值(Scala 独有,开发首选) ​

Scala 提供 3 种字符串插值方式,彻底替代 Java 的字符串拼接,代码更简洁、可读性更高,是开发中必用的特性。

表格

插值方式语法核心作用
s 插值s"内容$变量名"直接在字符串中嵌入变量、表达式
f 插值f"内容$变量名%格式化"格式化字符串,支持精度控制,等同于 Java 的 String.format
raw 插值raw"内容$变量名"原样输出字符串,不解析转义字符
代码案例 ​
scala
object StringInterpolateDemo {
  def main(args: Array[String]): Unit = {
    // 定义变量
    val name = "jason"
    val age = 10
    val weight = 180.0

    // 1. s插值:嵌入变量、表达式
    val sStr = s"name is $name, $age years old and weighs is $weight kg"
    println(sStr)
    // 嵌入表达式
    val exprStr = s"are you 10 ? ${age == 10}"
    println(exprStr) // 输出are you 10 ? true

    // 2. f插值:格式化输出,控制精度
    val fStr = f"name is $name, $age years old and weighs is $weight%.2f kg"
    println(fStr) // 保留2位小数,输出weighs is 180.00 kg

    // 3. raw插值:原样输出,不解析转义字符
    val rawStr = raw"name is $name, $age years old and weighs is $weight%.2f kg\n1000g"
    println(rawStr) // \n不会被解析为换行,直接原样输出
  }
}
特性 5:正则表达式处理 ​

Scala 提供极简的正则操作方式,用于字符串的查找、匹配、替换,是大数据日志解析的核心技能。

  • 正则对象创建:"正则表达式".r 或 new Regex("正则表达式")
  • 查找所有匹配:regex.findAllIn(str)
  • 查找第一个匹配:regex.findFirstIn(str)
代码案例 ​
scala
import scala.util.matching.Regex

object StringRegexDemo {
  def main(args: Array[String]): Unit = {
    val str = "his basic salary is 10000,and bonus is 20000"
    // 1. 创建正则对象:匹配数字
    val numRegex: Regex = "\\d+".r

    // 2. 查找所有匹配的数字
    println("=== 所有匹配的数字 ===")
    numRegex.findAllIn(str).foreach(println)

    // 3. 查找第一个匹配的数字
    val firstNum = numRegex.findFirstIn(str)
    println(s"第一个数字:$firstNum") // 输出Some(10000)
  }
}

1.4.2 其他非数值类型 ​

  1. Unit 类型:等同于 Java 的void,表示方法无返回值,唯一实例是()。

  2. Null 类型:所有引用类型(AnyRef)的子类,唯一实例是null,不能赋值给数值类型。

  3. Nothing 类型:所有类型的子类,无实例,主要用于异常抛出的返回值类型。

  4. 元组类型:Scala 特有的类型,是不同类型值的集合,最多支持 22 个元素,语法为(元素1, 元素2, ...),访问方式为元组名._索引(索引从 1 开始)。

    scala
    // 元组案例
    val tuple = ("张三", 20, 98.5)
    println(s"姓名:${tuple._1},年龄:${tuple._2},分数:${tuple._3}")
    // 元组遍历
    tuple.productIterator.foreach(println)

1.5 随堂练习(课堂练习全解析) ​

  1. 判断以下语句是否合法

    表格

    语句是否合法原因
    val a\u0042\u0046 = 10合法Unicode 编码可出现在标识符中,等价于val aBF = 10
    val octonray=038不合法Scala 不支持 0 开头的八进制字面量
    val binary=0B10101010合法(Scala2.12+)0B 开头表示二进制字面量
    val little:Short=123合法123 在 Short 的取值范围内
    val big:Short = 32768不合法超出 Short 最大值 32767
  2. 提取字符串 “123456” 偶数部分,返回新字符串

    scala
    val str = "123456"
    val result = for (c <- str if c.toInt % 2 == 0) yield c
    // 结果:"246"
  3. 判断身份证号是否为北京身份证

    scala
    val idCard = "110101199001011234"
    val isBeijing = idCard.startsWith("11")
  4. 将字符串 "123456" 变成 "234567"

    scala
    val str = "123456"
    val result = str.map(c => (c.toInt + 1).toChar)
    // 结果:"234567"

第二章 Scala 的基本运算 ​

Scala 的运算符本质上都是对象的方法调用,这是 Scala 纯面向对象的核心体现,例如1 + 2本质是1.+(2),3 > 2本质是3.>(2)。

2.1 算术运算符 ​

算术运算符用于数值计算,与 Java 完全一致,核心规则如下:

表格

运算符描述示例方法调用本质
+加法1 + 21.+(2)
-减法5 - 35.-(3)
*乘法2 * 32.*(3)
/除法6 / 26./(2)
%取余(模运算)7 % 37.%(3)

关键注意点 ​

  1. 整数除法:两个整数相除,结果只保留整数部分,舍弃小数,例如5 / 2 = 2,而非 2.5;
  2. 除法 / 取余运算中,除数不能为 0,否则运行时抛出算术异常。

2.2 关系运算符 ​

关系运算符用于两个值的比较,返回 Boolean 类型的 true/false,与 Java 完全一致:

表格

运算符描述示例
==等于(值相等)5 == 5 → true
!=不等于5 != 3 → true
>大于5 > 3 → true
<小于5 < 3 → false
>=大于等于5 >= 5 → true
<=小于等于5 <= 3 → false

重点:Scala 的==与 Java 完全不同,后续相等性判断章节会详细讲解。

2.3 逻辑运算符与短路运算 ​

逻辑运算符用于多个 Boolean 表达式的组合运算,核心规则与 Java 一致,重点掌握短路运算。

表格

运算符描述运算规则
&&逻辑与(短路与)所有表达式为 true,结果才为 true;第一个表达式为 false,直接返回 false,后续表达式不执行
``逻辑或(短路或)任意一个表达式为 true,结果就为 true;第一个表达式为 true,直接返回 true,后续表达式不执行
!逻辑非取反,true 变 false,false 变 true
&逻辑与(非短路)所有表达式为 true,结果才为 true;无论第一个表达式结果如何,所有表达式都会执行
``逻辑或(非短路)任意一个表达式为 true,结果就为 true;无论第一个表达式结果如何,所有表达式都会执行

代码案例:短路运算演示 ​

scala
object LogicDemo {
  def main(args: Array[String]): Unit = {
    // 定义两个测试方法
    def isChinese(): Boolean = {
      println("执行isChinese方法")
      false
    }
    def isSoldier(): Boolean = {
      println("执行isSoldier方法")
      true
    }

    println("=== 短路与 && ===")
    val result1 = isChinese() && isSoldier()
    println(s"结果:$result1")
    // 输出:仅执行isChinese方法,isSoldier方法不执行,结果false

    println("\n=== 短路或 || ===")
    val result2 = isSoldier() || isChinese()
    println(s"结果:$result2")
    // 输出:仅执行isSoldier方法,isChinese方法不执行,结果true

    println("\n=== 非短路与 & ===")
    val result3 = isChinese() & isSoldier()
    println(s"结果:$result3")
    // 输出:两个方法都执行,结果false
  }
}

2.4 赋值运算符 ​

赋值运算符用于给变量赋值,分为基础赋值和复合赋值,与 Java 基本一致,但有 2 个核心差异。

基础赋值运算符 ​

  • =:将右边的值赋值给左边的变量,例如val a = 10。

复合赋值运算符 ​

表格

运算符描述等价于
+=相加后赋值a += b → a = a + b
-=相减后赋值a -= b → a = a - b
*=相乘后赋值a *= b → a = a * b
/=相除后赋值a /= b → a = a / b
%=取余后赋值a %= b → a = a % b
<<=左移后赋值a <<= 2 → a = a << 2
>>=右移后赋值a >>= 2 → a = a >> 2
&=按位与后赋值a &= 2 → a = a & 2
^=按位异或后赋值a ^= 2 → a = a ^ 2
`=`按位或后赋值`a= 2→a = a2`

与 Java 的核心差异(必背) ​

  1. Scala 没有++和--自增自减运算符,用+=1和-=1替代;
  2. Scala 不支持连续赋值a = b = c,因为 Scala 中赋值表达式的返回值是 Unit,无法赋值给 Int 类型的变量。

代码案例与随堂练习解析 ​

scala
object AssignDemo {
  def main(args: Array[String]): Unit = {
    // 练习1:计算a、b、c的值
    var a = 2
    var b = 3
    var c = 4
    a += b // a = 2 + 3 = 5
    b -= c // b = 3 - 4 = -1
    c *= a // c = 4 * 5 = 20
    println(s"a=$a, b=$b, c=$c") // 输出a=5, b=-1, c=20

    // 练习2:连续赋值a=b=c,编译报错
    // var a2=2; var b2=3; var c2=4; a2 = b2 = c2;

    // 练习3:交换a和b的值
    var x = 2
    var y = 3
    // 方式1:临时变量
    val tmp = x
    x = y
    y = tmp
    println(s"x=$x, y=$y") // 输出x=3, y=2
    // 方式2:Scala极简写法(元组解构)
    var m = 2
    var n = 3
    val (m1, n1) = (n, m)
    println(s"m=$m1, n=$n1") // 输出m=3, n=2

    // 替代++自增
    var num = 0
    num += 1 // 等同于Java的num++
    println(s"num=$num") // 输出1
  }
}

2.5 位运算符 ​

位运算符直接对二进制位进行操作,运算效率极高,大数据开发中常用于性能优化、权限控制等场景。

位运算基础规则 ​

表格

运算符描述运算规则(p、q 为二进制位)
&按位与两位都为 1,结果为 1,否则为 0
``按位或两位有一个为 1,结果为 1,否则为 0
^按位异或两位不同为 1,相同为 0
~按位取反0 变 1,1 变 0
<<左移二进制位整体左移,低位补 0,高位溢出舍弃
>>有符号右移二进制位整体右移,高位用符号位填充(正数补 0,负数补 1)
>>>无符号右移二进制位整体右移,高位统一补 0

随堂练习全解析(手动计算原理) ​

scala
object BitDemo {
  def main(args: Array[String]): Unit = {
    // 练习1:5 & 9
    // 5的二进制:00000101
    // 9的二进制:00001001
    // 按位与:  00000001 → 结果1
    println(s"5 & 9 = ${5 & 9}") // 输出1

    // 练习2:5 | 9
    // 5的二进制:00000101
    // 9的二进制:00001001
    // 按位或:  00001101 → 结果13
    println(s"5 | 9 = ${5 | 9}") // 输出13

    // 练习3:5 ^ 9
    // 5的二进制:00000101
    // 9的二进制:00001001
    // 按位异或:00001100 → 结果12
    println(s"5 ^ 9 = ${5 ^ 9}") // 输出12

    // 练习4:~(-5)
    // -5的补码:11111011
    // 按位取反:00000100 → 结果4
    println(s"~(-5) = ${~(-5)}") // 输出4

    // 练习5:5 << 2 和 -5 << 2
    // 5 << 2:左移2位,00000101 → 00010100 → 20
    // -5 << 2:左移2位,结果-20
    println(s"5 << 2 = ${5 << 2}") // 输出20
    println(s"-5 << 2 = ${-5 << 2}") // 输出-20

    // 练习6:-5 >> 2 和 -5 >>> 2
    // -5 >> 2:有符号右移,高位补1,结果-2
    // -5 >>> 2:无符号右移,高位补0,结果1073741822
    println(s"-5 >> 2 = ${-5 >> 2}") // 输出-2
    println(s"-5 >>> 2 = ${-5 >>> 2}") // 输出1073741822
  }
}

2.6 运算符优先级 ​

运算符优先级决定了表达式的执行顺序,与 Java 基本一致,优先级从高到低如下表,无需死记,不确定时直接用()改变优先级即可:

表格

优先级运算符关联性
1() []从左到右
2! ~从右到左
3* / %从左到右
4+ -从左到右
5>> >>> <<从左到右
6> >= < <=从左到右
7== !=从左到右
8&从左到右
9^从左到右
10``从左到右
11&&从左到右
12``从左到右
13= += -= *= /= %= 等赋值运算符从右到左
14,从左到右

第三章 数据类型与运算的高频坑点与应用 ​

3.1 Scala 的相等性判断(与 Java 核心差异,必背) ​

Scala 的相等性判断分为值相等和引用相等,与 Java 的规则完全不同,是新手最高频的踩坑点。

表格

判断方式关键字作用与 Java 的差异null 安全
值相等== / !=判断两个对象的值是否相等,等同于 Java 的equals()方法Java 中==对基本类型是值相等,对引用类型是引用相等;Scala 中==统一判断值相等安全,null 比较不会抛空指针异常
引用相等eq / ne判断两个对象的内存地址是否相等,等同于 Java 的==对引用类型的作用Java 无此关键字,用==判断引用相等安全

代码案例与课堂练习解析 ​

scala
object EqualDemo {
  def main(args: Array[String]): Unit = {
    val s1 = "hello"
    val s2 = "he" + "llo" // 编译期常量拼接,指向常量池的同一个对象
    val s3 = "he"
    val s4 = "llo"
    val s5 = s3 + s4 // 运行期拼接,创建新对象
    val s6 = new String("hello") // 强制创建新对象

    // 1. 值相等判断 ==
    println("=== 值相等 == ===")
    println(s1 == s2) // true
    println(s1 == s5) // true
    println(s1 == s6) // true
    println(s5 == s6) // true

    // 2. 引用相等 eq
    println("\n=== 引用相等 eq ===")
    println(s1 eq s2) // true,常量池同一个对象
    println(s1 eq s5) // false,不同对象
    println(s1 eq s6) // false,不同对象
    println(s5 eq s6) // false,不同对象

    // 3. null安全,不会抛异常
    println("\n=== null安全 ===")
    val a: String = null
    val b: String = null
    println(a == b) // true,不会抛空指针
    println(a eq b) // true
  }
}

3.2 浮点数精度问题与解决方案 ​

问题成因 ​

计算机用二进制存储浮点数,部分十进制小数无法用二进制精确表示,导致精度丢失,例如0.1 + 0.2的结果是0.30000000000000004,而非0.3,这是所有编程语言的共性问题。

解决方案 ​

  1. 自定义精度阈值:判断两个浮点数的差值的绝对值是否小于指定精度(如 0.0001);
  2. 使用 BigDecimal:金融、金额等高精度场景,必须用 BigDecimal 进行计算。

代码案例 ​

scala
object FloatPrecisionDemo {
  def main(args: Array[String]): Unit = {
    // 问题复现
    println(s"0.1 + 0.2 = ${0.1 + 0.2}")
    println(s"0.1 + 0.2 == 0.3 ? ${0.1 + 0.2 == 0.3}") // 输出false

    // 解决方案1:自定义精度阈值
    def isEqual(d1: Double, d2: Double, precision: Double = 0.0001): Boolean = {
      (d1 - d2).abs < precision
    }
    println(s"自定义精度判断:${isEqual(0.1 + 0.2, 0.3)}") // 输出true

    // 解决方案2:使用BigDecimal(推荐)
    val bd1 = BigDecimal(0.1)
    val bd2 = BigDecimal(0.2)
    val bd3 = BigDecimal(0.3)
    println(s"BigDecimal判断:${bd1 + bd2 == bd3}") // 输出true
  }
}

3.3 大数处理 ​

当数值超出 Long/Double 的取值范围时,使用 Scala 提供的BigInt和BigDecimal类,支持任意精度的整数和小数运算,完全兼容 Java 的 BigInteger/BigDecimal。

scala
object BigNumberDemo {
  def main(args: Array[String]): Unit = {
    // 超大整数
    val bigInt1 = BigInt("9223372036854775808") // 超出Long最大值
    val bigInt2 = BigInt("10000000000000000000")
    println(s"大数相加:${bigInt1 + bigInt2}")
    println(s"大数相乘:${bigInt1 * bigInt2}")

    // 高精度小数
    val bigDecimal1 = BigDecimal("123456789.123456789123456789")
    val bigDecimal2 = BigDecimal("987654321.987654321987654321")
    println(s"高精度小数相加:${bigDecimal1 + bigDecimal2}")
  }
}

3.4 随机数生成 ​

Scala 通过scala.util.Random类生成随机数,大数据开发中常用于生成测试数据,核心用法如下:

scala
import scala.util.Random

object RandomDemo {
  def main(args: Array[String]): Unit = {
    // 创建随机数对象
    val random = new Random()
    // 设置随机种子,保证每次运行生成相同的随机序列
    // val random = new Random(100)

    // 1. 生成0-100的伪随机序列,3个元素
    val numSeq = (1 to 3).map(_ => random.nextInt(101))
    println(s"0-100随机序列:$numSeq")

    // 2. 随机长度的数字集合,最多5个元素
    val numList = (1 to random.nextInt(6)).map(_ => random.nextInt(100)).toList
    println(s"随机长度数字集合:$numList,长度:${numList.length}")

    // 3. 随机长度的字符集合,最多10个元素
    val charList = (1 to random.nextInt(11)).map(_ => random.nextPrintableChar()).toList
    println(s"随机长度字符集合:$charList")

    // 4. 长度为8的随机字符序列(随机密码)
    val password = (1 to 8).map(_ => random.nextPrintableChar()).mkString
    println(s"8位随机密码:$password")
  }
}

第四章 课后作业全解析 ​

一、填空题 ​

(1)'a'+7+"hello"的值是104hello,"hello"+'a'+7的值是helloa7。

解析:'a'的 Unicode 值是 97,97+7=104,数字在前先做数值运算,再拼接字符串;字符串在前,后续所有内容都做字符串拼接。

二、简答题 ​

(1)简述 5&9 和 5|9 求解原理

答:

  • 5&9 是按位与运算,先将两个数转为二进制:5 的二进制是00000101,9 的二进制是00001001;按位与规则是两位都为 1 结果才为 1,逐位运算后得到00000001,转为十进制结果为 1。
  • 5|9 是按位或运算,规则是两位有一个为 1 结果就为 1,对 5 和 9 的二进制逐位运算后得到00001101,转为十进制结果为 13。

(2)简述 5<<2 和 - 5<<2 求解原理

答:

  • 5<<2 是左移 2 位运算,5 的二进制是00000101,整体左移 2 位,低位补 0,得到00010100,转为十进制结果为 20;左移 n 位等价于乘以 2 的 n 次方,5*2²=20。
  • -5<<2 是负数左移 2 位,-5 的补码是11111011,整体左移 2 位,低位补 0,得到11110100,其原码对应的十进制值为 - 20,等价于 - 5*2²=-20。

(3)使用输入值 2.7255,生成字符串”you own $2.73” 可以使用插值字符串实现么?

答:可以,使用 Scala 的 f 插值即可实现,通过格式化控制保留 2 位小数,自动四舍五入。代码实现如下:

scala
val money = 2.7255
val result = f"you own $$$money%.2f"
println(result) // 输出you own $2.73

解析:$$用于输出 $ 符号,%.2f将数值格式化为保留 2 位小数,2.7255 四舍五入后为 2.73。

三、编程题 ​

(1)将数字 128 转换成 Char、String 和 Double,然后再转成 Int

scala
object TypeConvertDemo {
  def main(args: Array[String]): Unit = {
    val num: Int = 128
    println(s"原始数字:$num")

    // 1. 转成Char
    val charNum: Char = num.toChar
    println(s"转Char:$charNum")

    // 2. 转成String
    val strNum: String = num.toString
    println(s"转String:$strNum")

    // 3. 转成Double
    val doubleNum: Double = num.toDouble
    println(s"转Double:$doubleNum")

    // 4. 分别转回Int
    val intFromChar: Int = charNum.toInt
    val intFromStr: Int = strNum.toInt
    val intFromDouble: Int = doubleNum.toInt
    println(s"Char转回Int:$intFromChar")
    println(s"String转回Int:$intFromStr")
    println(s"Double转回Int:$intFromDouble")
  }
}

(2)统计字符串 “hello,scala,hello ,word” 中每个单词的个数

scala
object WordCountDemo {
  def main(args: Array[String]): Unit = {
    val str = "hello,scala,hello ,word"
    // 单词统计完整流程
    val wordCount = str
      .split("[,,\\s]+") // 按中文逗号、英文逗号、空格拆分
      .map((_, 1)) // 每个单词标记为(单词,1)
      .groupBy(_._1) // 按单词分组
      .map(kv => (kv._1, kv._2.size)) // 统计每个单词的数量

    // 打印结果
    println("单词统计结果:")
    wordCount.foreach(println)
  }
}

运行结果:

plaintext
单词统计结果:
(hello,2)
(scala,1)
(word,1)

学习总结 ​

  1. 数据类型体系:Scala 是纯面向对象语言,所有数据类型都是对象,统一继承自 Any,分为数值类型和非数值类型,与 Java 对应类型取值范围完全一致,核心差异是取消了基本类型与包装类的区分。

  2. 字符串处理:Scala 完全兼容 Java 的 String 方法,同时扩展了三引号多行字符串、s/f/raw 三种插值、极简正则操作,是大数据 ETL 数据清洗的核心技能,必须熟练掌握。

  3. 运算符本质:Scala 中所有运算符都是对象的方法调用,与 Java 核心差异是取消了++/--运算符,用+=/-=替代,不支持连续赋值。

  4. 高频坑点:

    • ==判断值相等,eq判断引用地址相等,与 Java 完全相反,且 null 判断安全;
    • 浮点数存在精度丢失问题,高精度场景必须用 BigDecimal;
    • Scala 不支持 0 开头的八进制字面量,与 Java 不同。
  5. 大数据应用:字符串处理、正则匹配、随机数生成、位运算优化,都是 Spark/Flink 开发中高频使用的技能,需重点掌握。

下一章预习内容:Scala 的内建控制结构、Scala 的类与面向对象编程。

基于 Vite 强力驱动 | 纯静态轻量托管