Scala学习——基础语法学习
本文最后更新于355 天前,其中的信息可能已经过时,如有错误请发送邮件到takumijie@qq.com

Scala学习——基础语法学习

image-20241122104818882

Scala 是一种运行在 JVM 上的函数式面向对象语言。它的命名源于其设计目标:随着用户需求一起成长,能够应用于各种编程任务,从小型脚本到大型系统,都能胜任。因此,Scala 提供了一些出众的特性,包括集成面向对象编程和面向函数式编程的各种特性,以及更高层的并发模型。

Why Scala

  • Scala 基于 JVM,和 Java 完全兼容,同样具有跨平台、可移植性、方便的垃圾回收等特性;
  • Scala比Java更加面向对象;
  • Scala更适合大数据的处理。
    • Scalā对集合类型数据处理有非常好的支持;
    • Spark的底层使用Scala编写。
  • Scala应用场景:
    • Kafka:分布式消息队列,内部代码经常用来处理并发的问题,用Scala可以大大简化其代码。
    • Spark:方便处理多线程场景,另外parkcaIa这种函数式编程语言
      可以大大简化代码。

总结

  • Scala 是兼容的:兼容 Java,可以访问庞大的 Java 类库;
  • Scala 是精简的:Scala 表达能力强,一行代码抵得上多行 Java 代码,开发速度快。可以让程序短小精悍,看起来更简洁,更优雅;
  • Scala 是静态类型的:Scala 拥有非常先进的静态类型系统,支持类型推断和模式匹配等;
  • Scala 可以开发大数据应用程序:例如 Spark、Flink 等

基础语法

语句

  • scala可以不使用分号结尾,但是使用多行语句时必须使用分号分隔。
//一行语句独占一行
println("Hello World!")
//多行语句在一行
println("Hello World!"); println("Hello Scala!")

打印输出

  • 换行输出
println("Hello World!");println("Hello Scala!")
  • 不换行输出
print("Hello World!");print("Hello Scala!")
  • 不管是println还是print,都可以同时打印多个值
println("Hello Wor1d!","Hello Scala!")

常量(计算机中不变化的量)

  • 在scala中推荐使用常量,不推荐使用变量
  • 创建常量的关键字时val ,创建变量的关键字是var
  • 常量的完整语法是val常量名:常量类型=初始值,但是可以直接写val age = 10scala会自己进行类型推断。
  • 常量修改会报错。

变量

  • 声明格式var变量名:变量类型=初始值,也可以自动进行类型推断。

官方推荐使用val:更安全代码可读性更高
资源回收更快,方法执行完,val所定义的常量就会被回收

字符串

  • 双引号(与大多数语言一致)
  • 三引号
  • 插值表达式

语法

三引号
val|var 变量名 = """
字符串1
字符串2
"""
插值表达式
  • Scala中可以使用插值表达式来定义字符串,能有效避免大量字符串的拼接。
val|var 变量名 = s"${变量名|表达式}字符串" 
惰性赋值
  • 当有一些变量保存的数据较大时,且这些数据又不需要马上加载到内存中,就可以使用惰性赋值来提高效率。
// lazy 只支持val不支持var
lazy val 变量名 = 表达武

标识符

实际开发中,我们会编写大量的代码,这些代码中肯定会有变量、常量、方法、类等,那它们该如何命名呢?这就需
要标识符了,标识符就是用来给变量、常量、方法、类等起名字用的。

命名规则

  • 支持大小写英文字母,数字,下划线,美元符$;
  • 不能数字开头;
  • 不能使用关键字;
  • 遵循见名知意。
  • 支持“撇撇”
//撇撇命名方式如下
val 'worker.heartbeat.interval' = 3

>>excute print('worker.heartbeat.interval')
>>3
命名规范
  • 变量和方法:小驼峰,从第二个单词开始,每个单词的首字母大写。例如:maxSize、.selectUserByld。、
  • 类和特征(Trait):大驼峰,每个单词的首字母都大写。例如:UserController、,WordCount。
  • 包:全部小写,一般是公司或组织的域名反写,多级包之间用·隔开。例如:com.xxxxx.scala。

数据类型

常用类型

基础类型 类型说明
Byte 8位带符号整数
Short 16位带符号整数
Int 32位带符号整数
Long 64位带符号整数
Char 16位无符号Unicode字符
String Char类型的序列(字符串)
Float 32位单精度浮点数
Double 64位双精度浮点数
Boolean 1位,true或false
  • Scala所有类型都使用大写字母开头
  • 整形使用Int
  • Scala中定义变量可以不写类型,会自动推断类型
  • 默认整形为Int,默认浮点为Double

其他类型

类型 说明
Any 所有类型的父类,它有两个子类AnyVal与AnyRef。
Anyval 所有数值类型的父类。
AnyRef 所有引用类型的父类。
Unit 表示空,Unit是AnyVal的子类,它只有一个实例(),类似于Java的void。
Null AnyRef的子类,所有引用类型的子类,它的实例是null,可以将null赋值给任何引用类型。Null并不能 实例化,只是用来处理异常和错误的。
Option 平时不建议使用Null而是改用Option,在一些方法中最后要返回一个值,但这个返回值有时为空,有时不 为空,就可以将这个返回值类型设置为Option。Option中有Some子类用来封装有值的返回;用None来 处理没有值的返回。Option属于Scala的引l用类型。
Nothing 所有类型的子类,不能直接创建该类型实例,一般配合异常使用。某个方法抛出异常时,返回的就是 Nothing类型,因为Nothing是所有类的子类,那么它可以赋值为任何类型。

类型转换

当Scla程序在进行运算或者赋值操作时,范围小的数据类型会自动转换为范围大的数据类型。而有些时候,我们需
要将范围大的数据类型转换为小的数据类型,这时就需要进行强制转换。值得注意的是,如果是数值类型强转操作,会产
生丢失精度的问题。

自动类型转换

  • 范围小的类型可以自动转换为大范围的类型
  • 数值类型自动类型转换从小到大为:Byte,Shot,Char,lnt,Long,Float,Double。
//Scala中默认的整型是Int,默认的浮点型是Double
//所以相当于是Int和Double进行计算,根绝自动类型转换,最终结果为Double
val a:Double = 3 + 2.21
//以下代码会报错,因为最终计算结果是Int,最后将其赋值给Byte类型肯定不行
val b:Int = 3
val c:Byte = b + 1


<console>:12:error:type mismatch;
found    :Int
required :Byte
    var c:Byte = b + 1

强制类型转换

  • 大范围的类型转换小范围的类型需要使用强制转换
  • 强制转换可能会造成精度丢失
val|var 变量名 = 值.toXxx

例如:

val a:Double = 3.14
val b:Int = a.toInt
print(b)
//精度丢失
>>3

String类型转换

数值转换String语法

val|var 变量名 = 数值类型 + ""
val|var 变量名 = 数值类型.toString

键盘录入

类似于Java的Scanner方法

  • 导包:import scala.io.StdIn
  • 调用方法:通过StdIn.readXxx()接收用户键盘录入的数据
// 导包
import scala.io.StdIn

// 调用方法
println("请输入用户名")
val username = StdIn.readLine()
println("您的用户名为:" + username)

println("请输入年龄")
val age = StdIn.readInt()
println("您的年龄为:" + age)

运算符

算术运算符

运算符 功能
+ 正号、加法、字符串拼接符
负号、减法
* 乘法
/ 除法,除数不能为0
% 取余

赋值运算符

运算符 功能
基本赋值运算符 = 例如:var a = 2,把2赋值给变量a
扩展赋值运算符+=、-=、*=、/=、%= 例如:a += 3,把变量a加3后再重新赋值回a

Scala没有 ++ 与 — 操作因为+号是Scala中的一个方法

关系运算符

运算符 功能
> 判断左边是否大于右边
>= 判断左边是否大于或者等于右边
< 判断左边是否小于右边
<= 判断左边是否小于或者等于右边
== 判断左边是否等于右边
!= 判断左边是否不等于右边

Scala与java语法有相反的地方

比较数值使用==或者!=,比较引用值使用eq方法

逻辑运算符

运算符 功能
& 逻辑与,要求所有条件都满足。简单理解:左右两边,有false 则整体为false
| 逻辑或,要求只要满足任意一个条件即可。简单理解:左右两边,有true 则整体为 true
&& 逻辑短路与,要求所有条件都满足。简单理解:如果左边为 false 则右边不执行
|| 逻辑短路或,要求只要满足任意一个条件即可。简单理解:如果左边为true 则右边不执行
逻辑非,对表达式的结果进行取反操作。简单理解:表达式结果为 true 取反则为 false

语句块

  • Scala使用{}来表示一个语句块
  • 语句块默认将最后一个逻辑行当作返回值
val result = {
    println("我是语句块")
    1 + 1
}
println("result = " + result)

val result = {
    println("我是语句块")
    if (true) {
        println("我是最后一个逻辑行")
        2 + 2
    } else {
        1 + 1
    }
}
println("result = " + result)

//默认最后一个逻辑行为返回值
>>result: Int = 4

流程控制

image-20241122151334708

顺序结构

val a = 2
val b = 3
println(a+b)

选择结构

  • 单分支
if (关系表达式) {
    // 语句块
}
  • 双分支
if (关系表达式) {
    语句块1
} else {
    语句块2
}
  • 多分支
if (关系表达式1) {
    // 语句块1
} else if (关系表达式2) {
    // 语句块2
} else if (关系表达式n) { // else if 可以有多组
    // 语句块n
} else { // 所有关系表达式都不成立的时候,执行这里的代码
    // 语句块n+1
}

三元表达式

  • 在scala中没有三元表达式
  • 可以使用if来代替三元表达式
  • 和java一样在{}中只有一行代码时括号可以省略
// 定义变量,表示年龄
var age = 18

// 定义常量,接收 if 语句的返回值
val result = if (age >= 18) "已成年" else "未成年"

println("age:" + age + "," + result)

嵌套分支

  • 和其他语言一样可以在选择分支中再嵌套使用分支。

循环结构

for循环

for (i <- 表达式|数组|集合){
    //语句块
}
//打印10次hello world
for (i <- 1 to 10){
    println(s"hello world ${i}")
}

>> hello world 1
>> hello world 2
>> hello world 3
>> hello world 4
>> hello world 5
>> hello world 6
>> hello world 7
>> hello world 8
>> hello world 9
>> hello world 10

嵌套循环

// 打印 5 * 5 的方形,方形图案用 # 进行填充
// 方案一:普通写法
for (i <- 1 to 5) {
    for (j <- 1 to 5) {
        print("#")
    }
    println()
}

// 方案二:压缩版
for (i <- 1 to 5) {
    for (j <- 1 to 5) {
        if (j == 5) println("#") else print("#")
    }
}

// 方案三:合并版
for (i <- 1 to 5; j <- 1 to 5) {
    if (j == 5) println("#") else print("#")
}

守卫

for (i <- 表达式|数组|集合 if 表达式){
    //语句块
}
//打印1-10的偶数
for (i <- 1 to 10 if i % 2 == 0){
    println(i)
}

yield生成器

  • 类似于return
  • 但是不会结束函数return会结束函数
  • 如果在循环中使用了yield每次循环遇到yield时就会将后面的值放入一个集合
  • 结束时返回这个集合
  • 使用了yield的表达式叫做推导式
  • yield可以用作函数的参数,只要函数支持被迭代
//将1~10的偶数返回
var result = for (i <- 1 to 10 if  i % 2 == 0) yield i
println(result)
//生成10,20,...,90,100
var result = for (i <- 1 to 10) yield i * 10
println(result)

while 循环

// 初始化条件
while (条件表达式) {
    // 语句块
    // 控制条件
}

do while循环

// 初始化条件
do {
    // 语句块
    // 控制条件
} while (条件表达式)

break和Continue

  • Scala中break和continue需要导包实现
//实现break
// 先导包
import scala.util.control.Breaks._

// 当 i == 5 时结束循环
breakable {
    for (i <- 1 to 10) {
        if (i == 5) break() else println(i)
    }
}

//实现continue
// 先导包
import scala.util.control.Breaks._

// 当 i == 5 时跳过当次循环,继续下一次
for (i <- 1 to 10) {
    breakable {
        if (i == 5) break() else println(i)
    }
}

方法

定义方法

def 方法名(参数名:参数类型, 参数名:参数类型, ...): [返回值类型] = {
    // 语句块(方法体)
}
  • 举例
// 定义方法
def addNum(a:Int, b:Int): Int = {
    a + b
}
  • 方法参数列表的参数类型不能省略
  • 方法返回值类型可以省略,由 Scala 编译器自动推断,但是定义递归方法,不能省略。
  • 方法返回值可以不写 return,默认是 {} 块的最后一个逻辑行。
  • 在 Scala 中,方法传参默认是 val 类型,即不可变,这意味着你在方法体内部不能改变传入的参数。

惰性方法

  • lazy可以用于常量也可以用于方法
  • 并不是在方法前面加上lazy
// 定义方法
def addNum(a:Int, b:Int): Int = {
    a + b
}

// 惰性加载
lazy val result = addNum(2, 3)
// 首次使用该值时,方法才会执行
println(result)
def addNum(a:Int = 1, b:Int = 1): Int = {
    a + b
}

方法参数

  • 默认参数
def addNum(a:Int = 1, b:Int = 1): Int = {
    a + b
}
val result = addNum()
println(result)
  • 带名参数
def addNum(a:Int = 1, b:Int = 1): Int = {
    a + b
}
val result = addNum(b = 5)
println(result)
  • 变长参数
    • 变长参数只能定义到最后一个
def addNum(a:Int*): Int = {
    a.sum
}
val result = addNum(1, 2, 3, 4, 5)
println(result)

方法调用

在Scala中 + - * / 都属于方法

  • 后缀调用法
//语法
对象.方法名(参数)
//案例
Math.abs(-1)
  • 中缀调用法
//语法
对象名 方法名 参数
//案例
//求两数大小
Math max (1.2)
  • 花括号调用法
//语法
Math.abs{
    //语句块
}

//绝对值
Math.abs{
    println("求绝对值")
    -1
}
  • 无括号调用法
// 定义一个无参数的方法
def hello(): Unit = {
    println("Hello World!")
}
// 调用方法
hello

函数

  • 函数可以存储在变量中
  • 函数可以作为参数
  • 函数可以作为返回值

定义函数

// 因为函数是对象,所以函数有类型:(函数参数类型1, 函数参数类型2,...) => 函数返回值类型
val 函数名: (函数参数类型1, 函数参数类型2,...) => 函数返回值类型 = (参数名:参数类型, 参数名:参数类型, ...) => {
    函数体
}

函数的本质就是引用类型,相当于 Java 中 new 出来的实例,所以函数是在堆内存中开辟空间;

函数的定义不需要使用 def 关键字,但定义的函数一定要有输入和返回值,没有返回值相当于返回的是 Unit;

函数不建议写 return 关键字,Scala 会使用函数体的最后一行代码作为返回值;

因为函数是对象,所以函数有类型,但函数类型可以省略,Scala 编译期可以自动推断类型。

  • 案例

    // 定义一个函数,接收两个参数,将它们相加后的结果返回
    val addNum: (Int, Int) => Int = (a: Int, b: Int) => {
        a + b
    }
    // 省略函数返回值
    val addNum = (a: Int, b: Int) => {
        a + b
    }
    // 调用函数
    val result = addNum(2, 3)
    println(result)
    

方法与函数的区别

  • 方法是隶属于类或者对象的,在运行时,它会被加载到 JVM 的方法区中
  • 函数是一个对象,继承自 FunctionN,函数对象有 apply,curried,toString,tupled 这些方法,方法则没有。

方法转函数

//语法
val|var 变量名 = 方法名 _


//案例
// 定义方法
def addNum(a:Int, b:Int): Int = {
    a + b
}
// 将方法转换为函数
var a = addNum _
// 调用函数
a(2, 3)

Option

开发中,在返回一些数据时,难免会遇到空指针异常,遇到一次就处理一次相对来讲比较繁琐。而在 Scala 中,我们返回某些数据
时,可以返回一个 Option 类型的对象来封装具体的数据,从而有效的避免空指针异常

语法

  • Some(x)表示实际的值
  • None表示没有值
//案例
def division(a: Int, b: Int): Option[Int] = {
    // 定义一个两个数相除的方法,使用 Option 类型来封装结果
    if (b == 0) { // 当除数为零时,打印异常信息
        None
    } else { // 当除数不为零时,打印相除结果
        Some(a / b)
    }
}
val a = 10
val b = 0
val result = division(10, 0)
// 配合 Option 的 isEmpty 方法来检测元素是否为 None
println(result.isEmpty)
// 配合 Option 的 getOrElse 方法返回友好提示
println(result.getOrElse("对不起,除数不能为零"))
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇