Scala第一章节
章节目标
- 理解Scala的相关概述
- 掌握Scala的环境搭建
- 掌握Scala小案例: 做最好的自己
1. Scala简介
1.1 概述
Scala(斯嘎拉)这个名字来源于”Scalable Language(可伸缩的语言)”, 它是一门基于JVM的多范式编程语言, 通俗的说: Scala是一种运行在JVM上的函数式的面向对象语言. 之所以这样命名, 是因为它的设计目标是: 随着用户的需求一起成长. Scala可被广泛应用于各种编程任务, 从编写小型的脚本到构建巨型系统, 它都能胜任. 正因如此, Scala得以提供一些出众的特性, 例如: 它集成了面向对象编程和面向函数式编程的各种特性, 以及更高层的并发模型.
总而言之, Scala融汇了许多前所未有的特性, 而同时又运行于JVM之上, 随着开发者对Scala的兴趣日增, 以及越来越多的工具支持, 无疑Scala语言将成为你手上一门必不可少的工具.
基于JVM解释:Scala的运行环境和Java类似, 也是依赖JVM的.
多范式解释: Scala支持多种编程风格
1.2 Scala之父
Scala之父是: Martin·Odersky(马丁·奥德斯基), 他是EPFL(瑞士领先的技术大学)编程研究组的教授. 也是Typesafe公司(现已更名为: Lightbend公司)的联合创始人. 他在整个职业生涯中一直不断追求着一个目标:让写程序这样一个基础工作变得高效、简单、且令人愉悦. 他曾经就职于IBM研究院、耶鲁大学、卡尔斯鲁厄大学以及南澳大利亚大学. 在此之前,他在瑞士苏黎世联邦理工学院追随Pascal语言创始人Niklaus Wirth(1984年图灵奖获得者)学习,并于1989年获得博士学位.
1.3 语言特点
-
Scala是兼容的
兼容Java,可以访问庞大的Java类库,例如:操作mysql、redis、freemarker、activemq等等
-
Scala是精简的
scala表达能力强,一行代码抵得上多行Java代码,开发速度快
-
Scala是高级的
scala可以让你的程序保持短小, 清晰, 看起来更简洁, 更优雅
-
Scala是静态类型的
scala拥有非常先进的静态类型系统, 支持: 类型推断和模式匹配等
-
Scala可以开发大数据应用程序
例如: Spark程序、Flink程序等等...
2. Scala程序和Java程序对比
2.1 程序的执行流程对比
Java程序编译执行流程
Scala程序编译执行流程
2.2 代码对比
需求:
定义一个学生类, 属性为: 姓名和年龄, 然后在测试类中创建对象并测试.
Java代码
//定义学生类
public class Student{
private String name; //姓名
private int age; //年龄
//空参和全参构造
public Student(){}
public Student(String name, int age){
this.name = name;
this.age = age;
}
//getXxx()和setXxx()方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
//测试类
public class StudentDemo {
public static void main(String[] args) {
Student s1 = new Student("张三", 23); //创建Student类型的对象s1, 并赋值
System.out.println(s1); //打印对象, 查看结果.
}
}
Scala代码
case class Student(var name:String, var age:Int) //定义一个Student类
val s1 = Student("张三", 23) //创建Student类型的对象s1, 并赋值
println(s1) //打印对象, 查看结果.
3. Scala环境搭建
3.1 概述
scala程序运行需要依赖于Java类库,那么必须要有Java运行环境,scala才能正确执行. 所以要编译运行scala程序,需要:
- JDK(JDK包含JVM)
- Scala编译器(Scala SDK)
接下来,需要依次安装以下内容:
- 安装JDK
- 安装Scala SDK
- 在IDEA中安装Scala插件
3.2 安装JDK
安装JDK 1.8 64位版本,并配置好环境变量, 此过程略.
3.3 安装Scala SDK
Scala SDK是scala语言的编译器,要开发scala程序,必须要先安装Scala SDK
本次安装的版本是: 2.11.12
步骤
-
下载Scala SDK.
官方下载地址: https://scala-lang.org/download/
-
安装Scala SDK.
2.1 双击scala-2.11.12.msi,将scala安装在指定目录, 傻瓜式安装, 下一步下一步即可.
2.2 安装路径要合法, 不要出现中文, 空格等特殊符号.
2.3 Mac或Linux版本下载对应版本解压即可(环境变量可以不配置,使用IDEA时要指定scala-sdk路径).
-
测试是否安装成功
打开控制台,输入: scala -version.(要配置环境变量才能显示)
3.4 安装IDEA scala插件
IDEA默认是不支持scala程序开发的,所以需要在IDEA中安装scala插件, 让它来支持scala语言。
步骤
3.4.1 下载指定版本IDEA scala插件.
1. 下载的Scala插件必须和你安装的IDEA版本一致
2. 官方下载地址: http://plugins.jetbrains.com/plugin/1347-scala
3.4.2 IDEA配置scala插件
- 选择配置 > 选择插件
-
点击小齿轮 > 选择从本地安装插件
-
找到下载的插件位置,点击OK
3.4.3 重新启动IDEA
4. Scala解释器
4.1 概述
后续我们会使用scala解释器来学习scala基本语法,scala解释器像Linux命令一样,执行一条代码,马上就可以让我们看到执行结果,用来测试比较方便。
我们接下来学习:
- 启动scala解释器
- 在scala解释器中执行scala代码
- 退出scala解释器
4.2 启动scala解释器
要启动scala解释器,只需要以下几步:
- 按住
windows键 + r
- 输入
scala
即可
4.3 执行scala代码
在scala的命令提示窗口中输入println("hello, world")
,回车执行.
4.4 退出解释器
方式一: 点击右上角的”×”
**方式二: 输入:quit
退出 **
5. 案例: 做最好的自己.
5.1 需求
提示用户录入他/她最想对自己说的一句话, 然后将这句话打印到控制台上.
5.2 目的
测试Scala和Java之间可以无缝互调(即: Scala兼容Java,可以访问庞大的Java类库
).
5.3 思路分析
- 因为涉及到键盘录入了, 所以先导包.
- 提示用户录入他/她最想对自己说的一句话.
- 接收用户录入的内容, 并打印.
5.4 参考代码
//1. 导入Java中的Scanner类. 引入Java类库
import java.util.Scanner
//2. 提示用户录入他/她最想对自己说的一句话. Scala代码
println("请录入一句您最想对自己说的一句话: ")
//3. 打印用户录入的内容. Scala代码 + Java类库
//不忘初心, 方得始终, 做最好的自己!
println("我最想对自己说: " + new Scanner(System.in).nextLine())
5.5 小彩蛋
其实Scala中也有自己独有的接收用户键盘录入数据
的功能, 远比上边的写法要简单的多, 要优雅的多. 这种写法在后续章节我们也会学习到, 预知后事如何, 且听下回分晓.
Scala第二章节
章节目标
- 掌握变量, 字符串的定义和使用
- 掌握数据类型的划分和数据类型转换的内容
- 掌握键盘录入功能
- 理解Scala中的常量, 标识符相关内容
1. 输出语句和分号
1.1 输出语句
方式一: 换行输出
格式: println(里边写你要打印到控制台的数据);
方式二: 不换行输出
格式: print(里边写你要打印到控制台的数据);
注意:
不管是println(), 还是print()语句, 都可以同时打印多个值.格式为: println(值1, 值2, 值3...)
1.2 分号
Scala语句中, 单行代码最后的分号可写可不写. 如果是多行代码写在一行, 则中间的分号不能省略, 最后一条代码的分号可省略不写.
示例:
println("Hello, Scala!") //最后的分号可写可不写
//如果多行代码写在一行, 则前边语句的分号必须写, 最后一条语句的分号可以省略不写.
println("Hello"); println("Scala")
2. Scala中的常量
2.1 概述
常量指的是: 在程序的运行过程中, 其值不能发生改变的量.
2.2 分类
- 字面值常量(常用的有以下几种)
- 整型常量
- 浮点型常量
- 字符常量
- 字符串常量
- 布尔常量
- 空常量
- 自定义常量(稍后解释)
2.3 代码演示
//整型常量
println(10)
//浮点型常量
println(10.3)
//字符常量, 值要用单引号括起来
println('a')
//字符串常量, 值要用双引号括起来
println("abc")
//布尔常量, 值只有true和false
println(true)
//空常量
println(null)
3. Scala中的变量
3.1 概述
我们将来每一天编写scala程序都会定义变量, 那什么是变量, 它又是如何定义的呢?
变量, 指的就是在程序的执行过程中, 其值可以发生改变的量. 定义格式如下:
3.2 语法格式
Java变量定义
int a = 0;
在scala中,可以使用val
或者var
来定义变量,语法格式如下:
val/var 变量名:变量类型 = 初始值
其中
val
定义的是不可重新赋值的变量, 也就是自定义常量.var
定义的是可重新赋值的变量
注意: scala中定义变量时, 类型写在变量名后面
3.3 示例
需求:定义一个变量保存一个人的名字”tom”
步骤
- 打开scala解释器
- 定义一个字符串类型的变量用来保存名字
参考代码
scala> val name:String = "tom"
name: String = tom
3.4 val和var变量的区别
示例
给名字变量进行重新赋值为Jim,观察其运行结果
参考代码
scala> name = "Jim"
<console>:12: error: reassignment to val
name = "Jim"
示例
使用var
重新定义变量来保存名字”tom”,并尝试重新赋值为Jim,观察其运行结果
参考代码
scala> var name:String = "tom"
name: String = tom
scala> name = "Jim"
name: String = Jim
注意: 优先使用
val
定义变量,如果变量需要被重新赋值,才使用var
3.5 使用类型推断来定义变量
scala的语法要比Java简洁,我们可以使用一种更简洁的方式来定义变量。
示例
使用更简洁的语法定义一个变量保存一个人的名字”tom”
参考代码
scala> val name = "tom"
name: String = tom
scala可以自动根据变量的值来自动推断变量的类型,这样编写代码更加简洁。
4. 字符串
scala提供多种定义字符串的方式,将来我们可以根据需要来选择最方便的定义方式。
- 使用双引号
- 使用插值表达式
- 使用三引号
4.1 使用双引号
语法
val/var 变量名 = “字符串”
示例
有一个人的名字叫”hadoop”,请打印他的名字以及名字的长度。
参考代码
scala> println(name + name.length)
hadoop6
4.2 使用插值表达式
scala中,可以使用插值表达式来定义字符串,有效避免大量字符串的拼接。
语法
val/var 变量名 = s"${变量/表达式}字符串"
注意:
- 在定义字符串之前添加
s
- 在字符串中,可以使用
${}
来引用变量或者编写表达式
示例
请定义若干个变量,分别保存:”zhangsan”、23、”male”,定义一个字符串,保存这些信息。
打印输出:name=zhangsan, age=23, sex=male
参考代码
scala> val name = "zhangsan"
name: String = zhangsan
scala> val age = 23
age: Int = 23
scala> val sex = "male"
sex: String = male
scala> val result = s"name=${name}, age=${age}, sex=${sex}"
result: String = name=zhangsan, age=23, sex=male
scala> println(result)
name=zhangsan, age=23, sex=male
4.3 使用三引号
如果有大段的文本需要保存,就可以使用三引号来定义字符串。例如:保存一大段的SQL语句。三个引号中间的所有内容都将作为字符串的值。
语法
val/var 变量名 = """字符串1
字符串2"""
示例
定义一个字符串,保存以下SQL语句
select
*
from
t_user
where
name = "zhangsan"
打印该SQL语句
参考代码
val sql = """select
| *
| from
| t_user
| where
| name = "zhangsan""""
println(sql)
4.4 扩展: 惰性赋值
在企业的大数据开发中,有时候会编写非常复杂的SQL语句,这些SQL语句可能有几百行甚至上千行。这些SQL语句,如果直接加载到JVM中,会有很大的内存开销, 如何解决这个问题呢?
当有一些变量保存的数据较大时,而这些数据又不需要马上加载到JVM内存中。就可以使用惰性赋值来提高效率。
语法格式:
lazy val/var 变量名 = 表达式
示例
在程序中需要执行一条以下复杂的SQL语句,我们希望只有用到这个SQL语句才加载它。
"""insert overwrite table adm.itcast_adm_personas
select
a.user_id,
a.user_name,
a.user_sex,
a.user_birthday,
a.user_age,
a.constellation,
a.province,
a.city,
a.city_level,
a.hex_mail,
a.op_mail,
a.hex_phone,
a.fore_phone,
a.figure_model,
a.stature_model,
b.first_order_time,
b.last_order_time,
...
d.month1_hour025_cnt,
d.month1_hour627_cnt,
d.month1_hour829_cnt,
d.month1_hour10212_cnt,
d.month1_hour13214_cnt,
d.month1_hour15217_cnt,
d.month1_hour18219_cnt,
d.month1_hour20221_cnt,
d.month1_hour22223_cnt
from gdm.itcast_gdm_user_basic a
left join gdm.itcast_gdm_user_consume_order b on a.user_id=b.user_id
left join gdm.itcast_gdm_user_buy_category c on a.user_id=c.user_id
left join gdm.itcast_gdm_user_visit d on a.user_id=d.user_id;"""
参考代码
scala> lazy val sql = """insert overwrite table adm.itcast_adm_personas
| select
| a.user_id,
....
| left join gdm.itcast_gdm_user_buy_category c on a.user_id=c.user_id
| left join gdm.itcast_gdm_user_visit d on a.user_id=d.user_id;"""
sql: String = <lazy>
// mac中scala2.13版本终端结果如下:
// lazy val sql: String // unevaluated
5. 标识符
5.1 概述
实际开发中, 我们会编写大量的代码, 这些代码中肯定会有变量, 方法, 类等. 那它们该如何命名呢? 这就需要用到标识符了. 标识符就是用来给变量, 方法, 类等起名字的. Scala中的标识符和Java中的标识符非常相似.
5.2 命名规则
- 必须由
大小写英文字母, 数字, 下划线_, 美元符$
, 这四部分任意组合组成. - 数字不能开头.
- 不能和Scala中的关键字重名.
- 最好做到见名知意.
5.3 命名规范
-
变量或方法: 从第二个单词开始, 每个单词的首字母都大写, 其他字母全部小写(小驼峰命名法).
zhangSanAge, student_Country, getSum
-
类或特质(Trait): 每个单词的首字母都大写, 其他所有字母全部小写(大驼峰命名法)
Person, StudentDemo, OrderItems
-
包: 全部小写, 一般是公司的域名反写, 多级包之间用.隔开.
com.itheima.add, cn.itcast.update
6. 数据类型
6.1 简述
数据类型是用来约束变量(常量)的取值范围的. Scala也是一门强类型语言, 它里边的数据类型绝大多数和Java一样.我们主要来学习
- 与Java不一样的一些用法
- scala中数据类型的继承体系
6.2 数据类型
基础类型 | 类型说明 |
---|---|
Byte | 8位带符号整数 |
Short | 16位带符号整数 |
Int | 32位带符号整数 |
Long | 64位带符号整数 |
Char | 16位无符号Unicode字符 |
String | Char类型的序列(字符串) |
Float | 32位单精度浮点数 |
Double | 64位双精度浮点数 |
Boolean | true或false |
注意下 scala类型与Java的区别
[!NOTE]
- scala中所有的类型都使用大写字母开头
- 整形使用
Int
而不是Integer- scala中定义变量可以不写类型,让scala编译器自动推断
- Scala中默认的整型是Int, 默认的浮点型是: Double
6.3 Scala类型层次结构
类型 | 说明 |
---|---|
Any | 所有类型的父类,它有两个子类AnyRef与AnyVal |
AnyVal | 所有数值类型的父类 |
AnyRef | 所有对象类型(引用类型)的父类 |
Unit | 表示空,Unit是AnyVal的子类,它只有一个的实例()。 它类似于Java中的void,但scala要比Java更加面向对象 |
Null | Null是AnyRef的子类,也就是说它是所有引用类型的子类。它的实例是null。可以将null赋值给任何对象类型 |
Nothing | 所有类型的子类, 不能直接创建该类型实例,某个方法抛出异常时,返回的就是Nothing类型,因为Nothing是所有类的子类,那么它可以赋值为任何类型 |
6.4 思考题
以下代码是否有问题?
val b:Int = null
Scala会解释报错: Null类型并不能转换为Int类型,说明Null类型并不是Int类型的子类
7. 类型转换
7.1 概述
当Scala程序在进行运算或者赋值动作时, 范围小的数据类型值会自动转换为范围大的数据类型值, 然后再进行计算.例如: 1 + 1.1的运算结果就是一个Double类型的2.1. 而有些时候, 我们会涉及到一些类似于”四舍五入”的动作, 要把一个小数转换成整数再来计算. 这些内容就是Scala中的类型转换.
Scala中的类型转换分为
值类型的类型转换
和引用类型的类型转换
, 这里我们先重点介绍:值类型的类型转换
.值类型的类型转换分为:
- 自动类型转换
- 强制类型转换
7.2 自动类型转换
-
解释
范围小的数据类型值会自动转换为范围大的数据类型值, 这个动作就叫: 自动类型转换.
自动类型转换从小到大分别为:Byte, Short, Char -> Int -> Long -> Float -> Double
-
示例代码
val a:Int = 3 val b:Double = 3 + 2.21 //因为是int类型和double类型的值进行计算, 所以最终结果为: Double类型 val c:Byte = a + 1 //这样写会报错, 因为最终计算结果是Int类型的数据, 将其赋值Byte类型肯定不行.
7.3 强制类型转换
-
解释
范围大的数据类型值通过一定的格式(强制转换函数)可以将其转换成范围小的数据类型值, 这个动作就叫: 强制类型转换.
注意: 使用强制类型转换的时候可能会造成精度缺失问题!
-
格式
val/var 变量名:数据类型 = 具体的值.toXxx //Xxx表示你要转换到的数据类型
- 参考代码
val a:Double = 5.21
val b:Int = a.toInt
7.4 值类型和String类型之间的相互转换
1. 值类型的数据转换成String类型
格式一:
val/var 变量名:String = 值类型数据 + ""
// scala2.13版本不再建议使用该方法,而是使用插值形式:
// val/var 变量名: String = s"${值类型数据的变量}${字符串变量}"
// 但是字符串在前的表达式正常:val/var 变量名 = ""+值类型数据
格式二:
val/var 变量名:String = 值类型数据.toString
示例
将Int, Double, Boolean类型的数据转换成其对应的字符串形式.
参考代码:
val a1:Int = 10
val b1:Double = 2.1
val c1:Boolean = true
//方式一: 通过和空字符串拼接的形式实现
val a2:String = a1 + ""
val b2:String = b1 + ""
val c2:String = c1 + ""
//方式二: 通过toString函数实现
val a3:String = a1.toString
val b3:String = b1.toString
val c3:String = c1.toString
2. String类型的数据转换成其对应的值类型
格式:
val/var 变量名:值类型 = 字符串值.toXxx //Xxx表示你要转换到的数据类型
注意:
- String类型的数据转成Char类型的数据, 方式有点特殊, 并不是调用toChar, 而是toCharArray
- 这点目前先了解即可, 后续我们详细解释
需求:
将字符串类型的整数, 浮点数, 布尔数据转成其对应的值类型数据.
参考代码:
val s1:String = "100"
val s2:String = "2.3"
val s3:String = "false"
//将字符串类型的数据转成其对应的: Int类型
val a:Int = s1.toInt
//将字符串类型的数据转成其对应的: Double类型
val b:Double = s2.toDouble
//将字符串类型的数据转成其对应的: Boolean类型
val c:Boolean = s3.toBoolean
// 注意没有toUnit方法,如:
// val s4: String = "()"
// s4.toUnit
8. 键盘录入
8.1 概述
前边我们涉及到的数据, 都是我们写”死”的, 固定的数据, 这样做用户体验并不是特别好. 那如果这些数据是由用户录入, 然后我们通过代码接收, 就非常好玩儿了. 这就是接下来我们要学习的Scala中的”键盘录入”功能.
8.2 使用步骤
-
导包
格式: import scala.io.StdIn
-
通过
StdIn.readXxx()
来接收用户键盘录入的数据接收字符串数据: StdIn.readLine()
接收整数数据: StdIn.readInt()
8.3 示例
-
提示用户录入字符串, 并接收打印.
println("请录入一个字符串: ") val str = StdIn.readLine() println("您录入的字符串内容为: " + str)
-
提示用户录入整数, 并接收打印.
println("请录入一个整数: ") val num = StdIn.readInt() println("您录入的数字为: " + num)
9. 案例: 打招呼
9.1 概述
聊了这么久, 赶紧来和小伙伴儿们来打个招呼吧.
需求: 提示用户录入他/她的姓名和年龄, 接收并打印.
9.2 具体步骤
- 提示用户录入姓名.
- 接收用户录入的姓名.
- 提示用户录入年龄.
- 接收用户录入的年龄.
- 将用户录入的数据(姓名和年龄)打印到控制台上.
9.3 参考代码
//1. 提示用户录入姓名.
println("请录入您的姓名: ")
//2. 接收用户录入的姓名.
val name = StdIn.readLine()
//3. 提示用户录入年龄.
println("请录入您的年龄: ")
//4. 接收用户录入的年龄.
val age = StdIn.readInt()
//5. 将用户录入的数据(姓名和年龄)打印到控制台上.
println(s"大家好, 我叫${name}, 我今年${age}岁了, 很高兴和大家一起学习Scala!")
Scala第三章节
章节目标
- 理解运算符的相关概述
- 掌握算术, 赋值, 关系, 逻辑运算符的用法
- 掌握交换变量案例
- 理解位运算符的用法
1. 算术运算符
1.1 运算符简介
用来拼接变量或者常量的符号就叫: 运算符, 而通过运算符连接起来的式子就叫: 表达式. 实际开发中, 我们会经常用到它.
例如:
10 + 3 这个就是一个表达式, 而+号, 就是一个运算符.
注意: 在Scala中, 运算符并不仅仅是运算符, 也是函数的一种, 这点大家先了解即可, 后续我们详细讲解.
1.2 运算符的分类
-
算术运算符
-
赋值运算符
-
关系运算符
-
逻辑运算符
-
位运算符
注意: Scala中是没有三元运算符的, 被if-else给替代了.
1.3 算术运算符
算术运算符指的就是用来进行算术操作的符号
, 常用的有以下几种:
运算符 | 功能解释 |
---|---|
+ | 加号, 功能有3点. 1) 表示正数 2) 普通的加法操作 3) 字符串的拼接 |
- | 减号, 功能有2点. 1) 表示负数 2) 普通的减法操作 |
* | 乘号, 用于获取两个数据的乘积 |
/ | 除法, 用于获取两个数据的商 |
% | 取余(也叫取模), 用于获取两个数据的余数 |
注意:
Scala中是没有++, –这两个算术运算符的, 这点和Java中不同.
整数相除的结果, 还是整数. 如果想获取到小数, 则必须有浮点型数据参与.
例如: 10 / 3 结果是3 10 / 3.0 结果是: 3.3333(无限循环)
关于+号拼接字符串: 任意类型的数据和字符串拼接, 结果都将是一个新的字符串.
关于%操作, 假设求
a % b
的值, 它的底层原理其实是:a - a/b * b
1.4 代码演示
需求: 演示算术运算符的常见操作.
参考代码:
//演示+号操作
println(+3)
println(10 + 3)
println("hello" + 10)
//演示-号操作
println(-5)
println(10 - 5)
//演示*号操作
println(5 * 3)
//演示/号操作
println(10 / 3)
println(10 / 3.0)
//演示%(取余)操作
println(10 % 3) //结果是1, 具体运算过程: 10 - 10/3 * 3 = 10 - 3 * 3 = 1
println(10 % -3) //结果是1, 具体运算过程: 10 - 10/-3 * -3 = 10 - -3 * -3 = 10 - 9 = 1
println(-10 % 3) //结果是-1, 具体运算过程: -10 - -10/3 * 3 = -10 - -3 * 3 = -10 + 9 = -1
println(10%3.3) // 0.10000000000000053
2. 赋值运算符
2.1 概述
赋值运算符指的就是用来进行赋值操作的符号
. 例如: 把一个常量值, 或者一个变量值甚至是某一段代码的执行结果赋值给变量, 这些都要用到赋值运算符.
2.2 分类
-
赋值运算符常用的有两类
-
基本赋值运算符
=
就是基本的赋值运算符, 例如: var a:Int = 3, 就是把常量值3赋值给变量a -
扩展赋值运算符
+=, -=, *=, /=, %=
注意:
-
赋值运算符的左边必须是: 变量, 不能是常量. 例如: 3 = 5, 这种写法就是错误的.
-
关于扩展赋值运算符, 其实就是把左边的数据和右边的数据进行指定的操作, 然后把结果赋值给左边.
例如; a += 3 就是把变量a的值和常量3进行加法操作, 然后把结果赋值给变量a
-
2.3 代码演示
//将常量值1赋值给变量a
var a:Int = 1 //注意: 因为后续代码要修改变量a的值, 所以变量a要用var修饰
//对变量a进行加3操作, 然后把结果重新赋值给变量a
a += 3 //a的最终结果为: a = 4
//对变量a进行减2操作, 然后把结果重新赋值给变量a
a -= 2 //a的最终结果为: a = 2
//对变量a进行乘3操作, 然后把结果重新赋值给变量a
a *= 3 //a的最终结果为: a = 6
//对变量a进行除2操作, 然后把结果重新赋值给变量a
a /= 2 //a的最终结果为: a = 3
//对变量a和2进行取余操作, 然后把结果重新赋值给变量a
a %= 2 //a的最终结果为: a = 1
3. 关系运算符
3.1 概述
关系运算符指的就是用来进行比较操作的符号
. 例如: 数据是否相等, 是否不等, 数据1大还是数据2大…等这些操作.
3.2 分类
运算符 | 功能解释 |
---|---|
> | 用来判断前边的数据是否大于 后边的数据 |
>= | 用来判断前边的数据是否大于或者等于 后边的数据 |
< | 用来判断前边的数据是否小于 后边的数据 |
<= | 用来判断前边的数据是否小于或者等于 后边的数据 |
== | 用来判断两个数据是否相等 |
!= | 用来判断两个数据是否不等 |
注意:
- 关系表达式不管简单还是复杂, 最终结果一定是Boolean类型的值, 要么是true, 要么是false.
- 千万不要把==写成=, 否则结果可能不是你想要的.
3.3 代码演示
//定义两个Int类型的变量a, b, 分别赋值为3, 5
var a:Int = 3
var b:Int = 5
//判断a是否大于b, 并打印结果
println(a > b) //false
//判断a是否大于等于b, 并打印结果
println(a >= 3) //true
//判断a是否小于b, 并打印结果
println(a < b) //true
//判断a是否小于等于b, 并打印结果
println(a <= 3) //true
//判断a和b是否不等, 并打印结果
println(a != b) //true
//判断a和b是否相等, 并打印结果
println(a == b) //false
//如果把==写成了=, 其实是把变量b的值赋值给变量a
println(a = b) //输出结果是一对小括号"()", 即: 没有打印值.
println(a) //再次打印变量a, 打印结果是:5
// scala2.13版本中测试:
scala> var a = 3
var a: Int = 3
scala> var b = 5
var b: Int = 5
scala> println(a==b)
false
scala> print(a=b)
^
error: unknown parameter name: a
// 报错原因:这样写把a当成print方法的声明式传参调用,而print方法没有名为a的参数。
// 建议为使用大括号:
scala> println({a=b})
()
3.4 关系运算符延伸
学过Java的同学会发现, 上述的Scala中的关系运算符用法和Java中是一样的, 那有和Java不一样的地方吗?
答案是: 有.
需求描述 | Scala代码 | Java代码 |
---|---|---|
比较数据值 | == 或者 != | equals()方法 |
比较引用值(地址值) | eq方法 | == 或者 != |
示例
有一个字符串”abc”,再创建第二个字符串,值为:在第一个字符串后拼接一个空字符串。
然后使用比较这两个字符串是否相等、再查看它们的引用值是否相等。
参考代码
val s1 = "abc"
val s2 = s1 + ""
s1 == s2 //结果是: true, 因为比较的是 数据值
s1.eq(s2) //结果是: false, 因为比较的是 地址值
4. 逻辑运算符
4.1 概述
逻辑运算符指的就是用来进行逻辑操作的符号
. 可以简单理解为它是: 组合判断. 例如: 判断多个条件是否都满足, 或者满足其中的某一个, 甚至还可以对某个判断结果进行取反操作.
4.2 分类
运算符 | 功能解释 |
---|---|
&& | 逻辑与, 要求所有条件都满足(即: 结果为true), 简单记忆: 有false则整体为false. |
|| | 逻辑或, 要求只要满足任意一个条件即可, 简单记忆: 有true则整体为true. |
! | 逻辑非, 用来进行取反操作的. 即: 以前为true, 取反后为false, 以前为false, 取反后为true. |
注意:
- 逻辑表达式不管简单还是复杂, 最终结果一定是Boolean类型的值, 要么是true, 要么是false.
- 在Scala代码中, 不能对一个Boolean类型的数据进行连续取反操作, 但是在Java中是可以的.
- 即: !!false, 这样写会报错, 不支持这种写法.
4.3 代码演示
//相当于: false && true
println(3 > 5 && 2 < 3) //结果为: false
//我们可以简写代码为:
//逻辑与: 有false则整体为false.
println(false && true) //结果为: false
println(true && false) //结果为: false
println(false && false) //结果为: false
println(true && true) //结果为: true
println(false || true) //结果为: true
println(true || false) //结果为: true
println(false || false) //结果为: false
println(true || true) //结果为: true
println(!false) //结果为: true
println(!true) //结果为: false
println(!!true) //这样写会报错, Scala不支持这种写法, 但是Java代码支持这种写法.
5. 位运算符
5.1 铺垫知识
要想学好位运算符
, 你必须得知道三个知识点:
- 什么是进制
- 什么是8421码
- 整数的原码, 反码, 补码计算规则
5.1.1 关于进制
通俗的讲, 逢几进一就是几进制, 例如: 逢二进一就是二进制, 逢十进一就是十进制, 常用的进制有以下几种:
进制名称 | 数据组成规则 | 示例 |
---|---|---|
二进制 | 数据以0b(大小写均可)开头, 由数字0和1组成 | 0b10001001, 0b00101010 |
八进制 | 数据以0开头, 由数字0~7组成 | 064, 011 |
十进制 | 数据直接写即可, 无特殊开头, 由数字0~9组成 | 10, 20, 333 |
十六进制 | 数据以0x(大小写均可)开头, 由数字0~9, 字母A-F组成(大小写均可) | 0x123F, 0x66ABC |
注意:
关于二进制的数据, 最前边的那一位叫: 符号位, 0表示正数, 1表示负数. 其他位叫: 数值位.
例如: 0b10001001 结果就是一个: 负数, 0b00101010 结果就是一个: 正数.
5.1.2 关于8421码
8421码就是用来描述二进制位和十进制数据之间的关系的
, 它可以帮助我们快速的计算数据的二进制或十进制形式.
8421码对应关系如下:
二进制位 0 0 0 0 0 0 0 0
对应的十进制数据 128 64 32 16 8 4 2 1
注意:
1. 计算规则: 二进制位从右往左数, 每多一位, 对应的十进制数据 乘以2.2. 二进制和十进制相互转换的小技巧: * 二进制转十进制: 获取该二进制位对应的十进制数据, 然后累加即可. * 例如: 0b101对应的十进制数据计算步骤: 4 + 0 + 1 = 5 * 十进制转二进制: 对十进制数据进行拆解, 看哪些数字相加等于它, 然后标记成二进制即可. * 例如: 10 对应的二进制数据计算步骤: 10 = 8 + 2 = 0b1010
5.1.3 关于整数的原反补码计算规则
所谓的原反补码, 其实指的都是二进制数据, 把十进制的数据转成其对应的二进制数据, 该二进制数据即为: 原码.
注意: 计算机底层存储, 操作和运算数据, 都是采用
数据的二进制补码形式
来实现的.
- 正数
- 正数的原码, 反码, 补码都一样, 不需要特殊计算.
- 负数
- 负数的反码计算规则: 原码的符号位不变, 数值位按位取反(以前为0现在为1, 以前为1现在为0)
- 负数的补码计算规则: 反码 + 1
5.2 概述
位运算符指的就是按照位(Bit)来快速操作数据值
, 它只针对于整型数据. 因为计算机底层存储, 操作, 运算采用的都是数据的二进制补码形式, 且以后我们要经常和海量的数据打交道, 为了提高计算效率, 我们就可以使用位运算符来实现快速修改数据值的操作.
5.3 分类
运算符 | 功能解释 |
---|---|
& | 按位与, 规则: 有0则0, 都为1则为1. |
| | 按位或, 规则: 有1则1, 都为0则为0. |
^ | 按位异或, 规则: 相同为0, 不同为1. |
~ | 按位取反, 规则: 0变1, 1变0. |
« | 按位左移, 规则: 每左移一位, 相当于该数据乘2, 例如: 2 « 1, 结果为4 |
>> | 按位右移, 规则: 每右移一位, 相当于该数据除2, 例如: 6 » 1, 结果为3 |
注意:
- 位运算符只针对于整型数据.
- 运算符操作的是数据的二进制补码形式.
- 小技巧: 一个数字被同一个数字位异或两次, 该数字值不变. 即: 10 ^ 20 ^ 20, 结果还是10
5.4 代码演示
//定义两个变量a和b, 初始化值分别为: 3, 5
val a = 3 //二进制数据: 0000 0011
val b = 5 //二进制数据: 0000 0101
//结果为: 0000 0001, 转化成十进制, 结果为: 1
println(a & b) //打印结果为: 1
//结果为: 0000 0111, 转化成十进制, 结果为: 7
println(a | b) //打印结果为: 7
//结果为: 0000 0110, 转换成十进制, 结果为: 6
println(a ^ b) //打印结果为: 6
//计算流程: 1111 1100(补码) -> 1111 1011(反码) -> 1000 0100(原码) -> 十进制数据: -4
println(~ a) //打印结果为: -4
//计算流程: 1000 0011(-3原码) -> 1111 1100(-3反码) -> 1111 1101(-3补码) -> 0000 0010(取反后新补码) -> 十进制数据: 2
println(~ -3) //打印结果为: 2
//计算流程: 0000 0011(3的补码) -> 0000 1100(新的补码) -> 十进制数据: 12
println(a << 2) //打印结果为: 12
//计算流程: 0000 0011(3的补码) -> 0000 0001(新的补码) -> 十进制数据: 1
println(a >> 1) //打印结果为: 1
println(a ^ b ^ b) //打印结果为: 3
6. 案例: 交换两个变量的值
6.1 需求
已知有两个Int类型的变量a和b, 初始化值分别为10和20, 请写代码实现变量a和变量b的值的交换.
即最终结果为: a=20, b=10.
注意: 不允许直接写
a=20, b=10
这种代码.
6.2 参考代码
-
方式一: 通过算术运算符实现.
//定义两个Int类型的变量a和b, 初始化值分别为10和20 var a = 10 var b = 20 //将变量a和b的计算结果赋值给变量a a = a + b //a = 30, b = 20 //计算并赋值 b = a - b //a = 30, b = 10 a = a - b //a = 20, b = 10 //打印结果 println("a: " + a) //a: 20 println("b: " + b) //b: 10
-
方式二: 通过定义临时变量实现
//定义两个Int类型的变量a和b, 初始化值分别为10和20 var a = 10 var b = 20 //定义临时变量temp, 记录变量a的值 var temp = a //a = 10, b = 20, temp = 10 //把变量b的值赋值给a a = b //a = 20, b = 20, temp = 10 //把临时变量temp的值赋值给b b = temp //a = 20, b = 10, temp = 10 //打印结果 println("a: " + a) //a: 20 println("b: " + b) //b: 10
-
方式三: 通过位运算符实现
//定义两个Int类型的变量a和b, 初始化值分别为10和20 var a = 10 var b = 20 //定义临时变量temp, 记录变量a和b的位异或值(这个值不需要我们计算) var temp = a ^ b //即: temp = 10 ^ 20 //通过位异或进行交换变量值 a = a ^ temp //运算流程: a = a ^ temp = a ^ a ^ b = 10 ^ 10 ^ 20 = 20 b = b ^ temp //运算流程: b = b ^ temp = b ^ a ^ b = 20 ^ 10 ^ 20 = 10 //打印结果 println("a: " + a) //a: 20 println("b: " + b) //b: 10
Scala第四章节
章节目标
- 掌握分支结构的格式和用法
- 掌握for循环和while循环的格式和用法
- 掌握控制跳转语句的用法
- 掌握循环案例
- 理解do.while循环的格式和用法
1. 流程控制结构
1.1 概述
在实际开发中, 我们要编写成千上万行代码, 代码的顺序不同, 执行结果肯定也会受到一些影响, 并且有些代码是满足特定条件才能执行的, 有些代码是要重复执行的. 那如何合理规划这些代码呢? 这就需要用到: 流程控制结构了.
1.2 分类
-
顺序结构
-
选择(分支)结构
-
循环结构
注意: Scala和Java中的流程控制结构是基本一致的.
2. 顺序结构
2.1 概述
顺序结构是指: 程序是按照从上至下, 从左至右的顺序, 依次逐行执行的, 中间没有任何判断和跳转.
如图:
注意: 顺序结构是Scala代码的默认流程控制结构.
2.2 代码演示
val a = 10
println("a: " + a) //打印结果为10
println("键盘敲烂, ")
println("月薪过万! ")
2.3 思考题
下边这行代码的打印结果应该是什么呢?
println(10 + 10 + "Hello,Scala" + 10 + 10)
提示: 代码是按照从上至下, 从左至右的顺序, 依次逐行执行的.
3. 选择结构(if语句)
3.1 概述
选择结构是指: 某些代码的执行需要依赖于特定的判断条件, 如果判断条件成立, 则代码执行, 否则, 代码不执行.
3.2 分类
- 单分支
- 双分支
- 多分支
3.3 单分支
所谓的单分支是指: 只有一个判断条件的if语句.
3.3.1 格式
if(关系表达式) {
//具体的代码
}
注意: 关系表达式不管简单还是复杂, 结果必须是Boolean类型的值.
3.3.2 执行流程
-
先执行关系表达式, 看其结果是true还是false.
-
如果是true, 则执行具体的代码, 否则, 不执行.
-
如图:
3.3.3 示例
**需求: **
定义一个变量记录某个学生的成绩, 如果成绩大于或者等于60分, 则打印: 分数及格.
参考代码
//定义变量, 记录成绩
val score = 61
//判断成绩是否不小于60分
if(score >= 60) {
println("成绩及格")
}
3.4 双分支
所谓的双分支是指: 只有两个判断条件的if语句.
3.4.1 格式
if(关系表达式) {
//代码1
} else {
//代码2
}
3.4.2 执行流程
- 先执行关系表达式, 看其结果是true还是false.
- 如果是true, 则执行代码1. 如果是false, 则执行代码2.
- 如图:
3.4.3 示例
**需求: **
定义一个变量记录某个学生的成绩, 如果成绩大于或者等于60分, 则打印: 分数及格, 否则打印分数不及格.
参考代码
//定义变量, 记录成绩
val score = 61
//判断成绩是否不小于60分
if(score >= 60) {
println("成绩及格")
} else {
println("成绩不及格")
}
3.5 多分支
所谓的多分支是指: 有多个判断条件的if语句.
3.5.1 格式
if(关系表达式1) {
//代码1
} else if(关系表达式2) {
//代码2
}else if(关系表达式n) { //else if可以有多组
//代码n
} else {
//所有的关系表达式都不成立的时候, 执行这里的代码.
//代码n+1.
}
3.5.2 执行流程
- 先执行关系表达式1, 看其结果是true还是false.
- 如果是true, 则执行代码1, 分支语句结束. 如果是false, 则执行关系表达式2, 看其结果是true还是false.
- 如果是true, 则执行代码2. 分支语句结束. 如果是false, 则执行关系表达式3, 看其结果是true还是false.
- 以此类推, 直到所有的关系表达式都不满足, 执行最后一个else中的代码.
- 如图:
3.5.3 示例
**需求: **
定义一个变量记录某个学生的成绩, 根据成绩发放对应的奖励, 奖励机制如下:
[90, 100] -> VR设备一套
[80, 90) -> 考试卷一套
[0, 80) -> 组合拳一套
其他 -> 成绩无效
参考代码
//定义变量, 记录成绩
val score = 80
//根据成绩发放对应的奖励
if(score >= 90 && score <= 100) {
println("VR设备一套")
} else if(score >= 80 && score < 90) {
println("考试卷一套")
} else if(score >= 0 && score < 80) {
println("组合拳一套")
} else {
println("成绩无效")
}
3.6 注意事项
if语句在使用时, 要注意的事项有以下三点:
- 和Java一样, 在Scala中, 如果大括号{}内的逻辑代码只有一行, 则大括号可以省略.
- 在scala中,条件表达式也是有返回值的
- 在scala中,没有三元表达式,可以使用if表达式替代三元表达式
示例
定义一个变量sex,再定义一个result变量,如果sex等于”male”,result等于1,否则result等于0
参考代码
//定义变量, 表示性别
val sex = "male"
//定义变量, 记录if语句的返回值结果
val result = if(sex == "male") 1 else 0
//打印结果为 result: 1
println("result: " + result)
3.7 嵌套分支
有些时候, 我们会涉及到”组合判断”, 即一个分支结构中又嵌套了另一个分支结构, 这种写法就叫嵌套分支. 里边的那个分支结构叫: 内层分支, 外边的那个分支结构叫: 外层分支.
示例
定义三个变量a,b,c, 初始化值分别为: 10, 20, 30, 通过if分支语句, 获取其中的最大值.
思路分析
- 定义三个变量a, b, c, 分别记录要进行操作的值.
- 定义变量max, 用来记录获取到的最大值.
- 先判断a是否大于或者等于b.
- 条件成立, 说明 a大(或者等于b), 接着比较a和c的值, 获取最大值, 并将结果赋值给变量max
- 条件不成立, 说明 b大, 接着比较b和c的值, 获取最大值, 并将结果赋值给变量max
- 此时, max记录的就是a, b, c这三个变量的最大值, 打印即可.
参考代码
//1. 定义三个变量a, b, c, 分别记录要进行操作的值.
val a = 10
val b = 20
val c = 30
//2. 定义变量max, 用来记录获取到的最大值.
var max = 0
//3. 先判断a是否大于或者等于b.
if(a >= b) {
//4. 走这里说明a大(或者等于b), 接着比较a和c的值
max = if(a >= c) a else c
} else {
//5. 走这里说明b大, 接着比较b和c的值
max = if(b >= c) b else c
}
//6. 打印max的值
println("max: " + max)
注意: 嵌套一般不超过3层.
3.8 扩展: 块表达式
- scala中,使用{}表示一个块表达式
- 和if表达式一样,块表达式也是有值的
- 值就是最后一个表达式的值
问题
请问以下代码,变量a的值是什么?
val a = {
println("1 + 1")
1 + 1
}
println("a: " + a)
scala> val a = {
| println("1 + 1")
| 1 + 1
| }
| println("a: " + a)
1 + 1
a: 2
val a: Int = 2
4. 循环结构
4.1 概述
循环,指的是事物周而复始的变化。而Scala中的循环结构,是指: 使一部分代码按照次数或一定的条件反复执行的一种代码结构。例如: 打印10次”Hello, Scala!”, 如果纯写输出语句, 需要写10次, 而通过循环来实现的话, 输出语句只需要写1次, 这样就变得很简单了.
4.2 分类
- for循环
- while循环
- do.while循环
注意: 这三种循环推荐使用for循环, 因为它的语法更简洁, 更优雅.
4.3 for循环
在Scala中, for的格式和用法和Java中有些差异, Scala中的for表达式功能更加强大.
4.3.1 格式
for(i <- 表达式/数组/集合) {
//逻辑代码
}
注意: 执行流程和Java一致
4.3.2 简单循环
**需求: **
打印10次”Hello, Scala!”
**参考代码: **
//定义一个变量, 记录1到10的数字
val nums = 1 to 10 //to是Scala中的一个关键字
//通过for循环, 打印指定的内容
for(i <- nums) {
println("Hello, Scala! " + i)
}
**上述代码可以简写成: **
for(i <- 1 to 10) println("Hello, Scala! " + i)
4.3.3 嵌套循环
*需求: **使用for表达式,打印以下字符, 每次只能输出一个””
*****
*****
*****
步骤
- 使用for表达式打印3行,5列星星
- 每打印5个星星,换行
参考代码
//写法一: 普通写法
for(i <- 1 to 3) { //外循环控制行数
for(j <- 1 to 5) { //内循环控制列数
print("*") //每次打印一个*
}
println() //打印完一行(5个*)之后, 记得换行
}
//写法二: 压缩版
for(i <- 1 to 3) {
//这是两行代码
for(j <- 1 to 5) if(j == 5) println("*") else print("*")
}
//写法三: 合并版
for(i <- 1 to 3; j <- 1 to 5) if(j == 5) println("*") else print("*")
4.3.4 守卫
for表达式中,可以添加if判断语句,这个if判断就称之为守卫。我们可以使用守卫让for表达式更简洁。
语法
for(i <- 表达式/数组/集合 if 表达式) {
//逻辑代码
}
示例
使用for表达式打印1-10之间能够整除3的数字
参考代码
// 添加守卫,打印能够整除3的数字
for(i <- 1 to 10 if i % 3 == 0) println(i)
4.4.5 for推导式
Scala中的for循环也是有返回值的, 在for循环体中,可以使用yield表达式构建出一个集合(可以简单理解为: 就是一组数据),我们把使用yield的for表达式称之为推导式.
示例
生成一个10、20、30…100的集合
参考代码
// for推导式:for表达式中以yield开始,该for表达式会构建出一个集合
val v = for(i <- 1 to 10) yield i * 10
println(v)
// scala> var l = for(i <- 1 to 10) yield i*10
// var l: IndexedSeq[Int] = Vector(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
4.4 while循环
scala中while循环和Java中是一致的, 所以学起来非常简单.
4.4.1 格式
初始化条件
while(判断条件) {
//循环体
//控制条件
}
4.4.2 执行流程
- 执行初始化条件.
- 执行判断条件, 看其结果是true还是false.
- 如果是false则循环结束.
- 如果是true则执行循环体.
- 执行控制条件.
- 返回第二步, 重复执行.
4.4.3 示例
**需求: **
打印1-10的数字
参考代码
//初始化条件
var i = 1
//判断条件
while(i <= 10) {
//循环体
println(i)
//控制条件
i = i + 1
}
4.5 do.while循环
scala中do.while循环和Java中是一致的, 所以学起来非常简单.
4.4.1 格式
初始化条件
do{
//循环体
//控制条件
}while(判断条件)
4.4.2 执行流程
- 执行初始化条件.
- 执行循环体.
- 执行控制条件.
- 执行判断条件, 看其结果是true还是false.
- 如果是false则循环结束.
- 如果是true则返回第2步继续执行.
注意:
do.while循环不管判断条件是否成立, 循环体都会执行一次.
for循环, while循环都是如果判断条件不成立, 则循环体不执行.
4.4.3 示例
**需求: **
打印1-10的数字
参考代码
//初始化条件
var i = 1
do{
//循环体
println(i)
//控制条件
i = i + 1
}while(i <= 10) //判断条件
4.6 break和continue
- 在scala中,类似Java和C++的break/continue关键字被移除了
- 如果一定要使用break/continue,就需要使用scala.util.control包下的Breaks类的breable和break方法。
4.6.1 实现break
用法
-
导包.
import scala.util.control.Breaks._
-
使用breakable将for表达式包起来
-
for表达式中需要退出循环的地方,添加
break()
方法调用
示例
使用for表达式打印1-10的数字,如果遇到数字5,则退出for表达式
参考代码
// 导入scala.util.control包下的Break
import scala.util.control.Breaks._
breakable{
for(i <- 1 to 10) {
if(i == 5) break() else println(i)
}
}
4.6.2 实现continue
用法
continue的实现与break类似,但有一点不同:
注意:
- 实现break是用breakable{}将整个for表达式包起来.
- 而实现continue是用breakable{}将for表达式的循环体包含起来就可以了.
示例
用for表达式打印1~10之间, 所有不能整除3的数字.
// 导入scala.util.control包下的Break
import scala.util.control.Breaks._
for(i <- 1 to 100 ) {
breakable{
if(i % 3 == 0) break()
else println(i)
}
}
5. 综合案例
5.1 九九乘法表
**需求: **
打印九九乘法表, 如下图:
步骤
-
通过外循环控制打印的行数.
-
通过内循环控制每行打印的列数.
注意: 因为列数是随着行数递增的, 即:
行数 该行的总列数 1 1 2 2 3 3 n n 结论: 如果用i表示行数, 那么该行的列数取值范围为: [1, i]
参考代码
- 方式一: 普通写法
//外循环控制行
for(i <- 1 to 9) {
//内循环控制列
for(j <- 1 to i) {
print(s"${i} * ${j} = ${i * j}\t")
}
println() //别忘了换行
}
- 方式二: 合并版写法
//外循环控制行
for(i <- 1 to 9; j <- 1 to i) {
print(s"${i} * ${j} = ${i * j}\t")
if(j == i) println() //别忘了换行
}
5.2 模拟登陆
**需求: **
老王要登陆黑马官网学习Scala, 假设老王的账号和密码分别为”itcast”, “heima”, 且同一账号只有3次登陆机会, 如果3次都录入错误, 则提示账号被锁定. 请用所学模拟该场景.
步骤
- 导包
- scala.io.StdIn
- scala.util.control.Breaks._
- 定义变量, 记录用户录入的账号和密码.
- 因为涉及到break的动作, 所以要用breakable{}把整个for表达式包裹起来
- 因为只有3次登陆机会, 所以推荐使用for循环.
- 提示用户录入他/她的账号和密码, 并接收.
- 判断用户录入的账号和密码是否正确.
- 如果录入正确, 则提示”登陆成功, 开始学习Scala!”, 循环结束.
- 如果录入错误, 则判断是否还有登陆机会
- 有, 则提示”用户名或者密码错误, 您还有*次机会”, 然后返回第5步继续执行.
- 没有, 则提示”账号被锁定, 请与管理员联系”, 循环结束.
参考代码
//1. 导包
import scala.io.StdIn
import scala.util.control.Breaks._
//2. 定义变量, 记录用户录入的账号和密码
var username = ""
var password = ""
//3. 因为涉及到break的动作, 所以要用breakable{}把整个for表达式包裹起来
breakable {
//4. 因为只有3次登陆机会, 所以推荐使用for循环.
for(i <- 1 to 3) {
//5. 提示用户录入他/她的账号和密码, 并接收.
println("请录入您的账号: ")
username = StdIn.readLine()
println("请录入您的密码: ")
password = StdIn.readLine()
//6. 判断用户录入的账号和密码是否正确.
if(username == "itcast" && password == "heima") {
//7. 走到这里, 说明登陆成功, 循环结束.
println("登陆成功, 开始学习Scala吧!")
break()
} else {
//8. 走到这里, 说明登陆失败. 则判断是否还有登陆机会
if(i == 3) println("账号被锁定, 请与管理员联系!")
else println(s"用户名或者密码错误, 您还有${3 - i}次机会")
}
}
}
Scala第五章节
章节目标
- 掌握方法的格式和用法
- 掌握函数的格式和用法
- 掌握九九乘法表案例
1. 方法
1.1 概述
实际开发中, 我们需要编写大量的逻辑代码, 这就势必会涉及到重复的需求. 例如: 求10和20的最大值, 求11和22的最大值, 像这样的需求, 用来进行比较的逻辑代码需要编写两次
, 而如果把比较的逻辑代码放到方法中, 只需要编写一次就可以了, 这就是方法. scala中的方法和Java方法类似, 但scala与Java定义方法的语法是不一样的。
1.2 语法格式
def 方法名(参数名:参数类型, 参数名:参数类型) : [return type] = {
//方法体
}
注意:
- 参数列表的参数类型不能省略
- 返回值类型可以省略,由scala编译器自动推断
- 返回值可以不写return,默认就是{}块表达式的值
1.3 示例
**需求: **
- 定义一个方法getMax,用来获取两个整型数字的最大值, 并返回结果(最大值).
- 调用该方法获取最大值, 并将结果打印到控制台上.
参考代码
- 方式一: 标准写法
//1. 定义方法, 用来获取两个整数的最大值.
def getMax(a:Int, b:Int): Int = {
return if(a > b) a else b
}
//2. 调用方法, 获取最大值.
val max = getMax(10, 20)
//3. 打印结果.
println("max: " + max)
- 方式二: 优化版
//1. 定义方法, 用来获取两个整数的最大值.
def getMax(a:Int, b:Int) = if(a > b) a else b
//2. 调用方法, 获取最大值.
val max = getMax(22, 11)
//3. 打印结果.
println("max: " + max)
1.4 返回值类型推断
scala定义方法可以省略返回值的数据类型,由scala自动推断返回值类型。这样方法定义后更加简洁。
注意: 定义递归方法,不能省略返回值类型
示例
定义递归方法, 求5的阶乘.
步骤
-
定义方法factorial, 用来计算某个数字的阶乘
规律: 1的阶乘等于1, 其他数字的阶乘为: n! = n * (n - 1)!
-
调用方法, 获取5的阶乘, 并将结果打印到控制台上.
参考代码
//1. 定义方法factorial, 用来计算某个数字的阶乘
def factorial(n:Int):Int = if(n == 1) 1 else n * factorial(n - 1)
//2. 调用方法, 获取5的阶乘.
val result = factorial(5)
//3. 将结果打印到控制台上.
println("result: " + result)
1.5 惰性方法
当记录方法返回值的变量被声明为lazy时, 方法的执行将被推迟, 直到我们首次使用该值时, 方法才会执行, 像这样的方法, 就叫: 惰性方法.
注意:
1. Java中并没有提供原生态的”惰性”技术, 但是可以通过特定的代码结构实现, 这种结构被称之为: 懒加载(也叫延迟加载) 2. lazy不能修饰var类型的变量.
使用场景:
-
打开数据库连接
由于表达式执行代价昂贵, 因此我们希望能推迟该操作, 直到我们确实需要表达式结果值时才执行它
-
提升某些特定模块的启动时间.
为了缩短模块的启动时间, 可以将当前不需要的某些工作推迟执行
-
确保对象中的某些字段能优先初始化
为了确保对象中的某些字段能优先初始化, 我们需要对其他字段进行惰性化处理
需求
定义一个方法用来获取两个整数和, 通过”惰性”技术调用该方法, 然后打印结果.
参考代码
//1. 定义方法, 用来获取两个整数和
def getSum(a:Int, b:Int) = {
println("getSum方法被执行了...")
a + b
}
//2. 通过"惰性"方式调用该方法.
lazy val sum = getSum(1, 2) //此时我们发现getSum方法并没有执行, 说明它的执行被推迟了.
//3. 打印结果, 并观察
println("sum: " + sum) //打印结果为sum: 3, 说明首次使用方法返回值时, 方法才会加载执行.
1.6 方法参数
scala中的方法参数,使用比较灵活。它支持以下几种类型的参数:
- 默认参数
- 带名参数
- 变长参数
1.6.1 默认参数
在定义方法时可以给参数定义一个默认值。
示例
- 定义一个计算两个整数和的方法,这两个值分别默认为10和20
- 调用该方法,不传任何参数
参考代码
//1. 定义一个方法, 用来获取两个整数的和
// x,y的默认值分别为10和20
def getSum(x:Int = 10, y:Int = 20) = x + y
//2. 通过默认参数的形式, 调用方法
val sum = getSum()
//3. 打印结果
println("sum: " + sum)
1.6.2 带名参数
在调用方法时,可以指定参数的名称来进行调用。
示例
- 定义一个计算两个整数和的方法,这两个值分别默认为10和20
- 调用该方法,只设置第一个参数的值
参考代码
//1. 定义一个方法, 用来获取两个整数的和
def getSum(x:Int = 10, y:Int = 20) = x + y
//2. 通过默认参数的形式, 调用方法
val sum = getSum(x=1)
//3. 打印结果
println("sum: " + sum)
1.6.3 变长参数
如果方法的参数是不固定的,可以将该方法的参数定义成变长参数。
语法格式:
def 方法名(参数名:参数类型*):返回值类型 = {
//方法体
}
注意:
1. 在参数类型后面加一个*
号,表示参数可以是0个或者多个 2. 一个方法有且只能有一个变长参数, 并且变长参数要放到参数列表的最后边.
**示例一: **
- 定义一个计算若干个值相加的方法
- 调用方法,传入以下数据:1,2,3,4,5
参考代码
//1. 定义一个计算若干个值相加的方法
def getSum(a:Int*) = a.sum
//2. 调用方法,传入一些整数, 并获取它们的和
val sum = getSum(1,2,3,4,5)
//3. 打印结果
println("sum: " + sum)
1.7 方法调用方式
在scala中,有以下几种方法调用方式:
- 后缀调用法
- 中缀调用法
- 花括号调用法
- 无括号调用法
注意: 在编写spark、flink程序时,会经常使用到这些方法调用方式。
1.7.1 后缀调用法
这种方法与Java没有区别, 非常简单.
语法
对象名.方法名(参数)
示例
使用后缀法调用Math.abs
, 用来求绝对值
参考代码
//后缀调用法
Math.abs(-1) //结果为1
1.7.2 中缀调用法
语法
对象名 方法名 参数
例如:1 to 10
注意: 如果有多个参数,使用括号括起来
示例
使用中缀法调用Math.abs
, 用来求绝对值
//中缀调用法
Math abs -1 //结果为1
扩展: 操作符即方法
来看一个表达式, 大家觉得这个表达式像不像方法调用?
1 + 1
在scala中,+ - * / %等这些操作符和Java一样,但在scala中,
- 所有的操作符都是方法
- 操作符是一个方法名字是符号的方法
1.7.3 花括号调用法
语法
Math.abs{
// 表达式1
// 表达式2
}
注意: 方法只有一个参数,才能使用花括号调用法
示例
使用花括号调用法Math.abs
求绝对值
参考代码
//花括号调用法
Math.abs{-10} //结果为: 10
1.7.4 无括号调用法
如果方法没有参数,可以省略方法名后面的括号
示例
- 定义一个无参数的方法,打印”Hello, Scala!”
- 使用无括号调用法调用该方法
参考代码
//1. 定义一个无参数的方法,打印"Hello, Scala!"
def sayHello() = println("Hello, Scala!")
//2. 调用方法
sayHello
注意:
- 在Scala中, 如果方法的返回值类型是Unit类型, 这样的方法称之为过程(procedure)
- 过程的等号(=)可以省略不写. 例如:
def sayHello() = println("Hello, Scala!") //可以改写为 def sayHello() { println("Hello, Scala!") } //注意: 这个花括号{}不能省略
2. 函数
scala支持函数式编程,将来编写Spark/Flink程序会大量使用到函数, 目前, 我们先对函数做一个简单入门, 在后续的学习过程中, 我们会逐步重点讲解函数的用法.
2.1 定义函数
语法
val 函数变量名 = (参数名:参数类型, 参数名:参数类型....) => 函数体
注意:
- 在Scala中, 函数是一个对象(变量)
- 类似于方法,函数也有参数列表和返回值
- 函数定义不需要使用
def
定义- 无需指定返回值类型
2.2 示例
**需求: **
- 定义一个计算两个整数和的函数
- 调用该函数
参考代码
//1. 定义一个用来计算两个整数和的函数, 并将其赋值给变量sum
val getSum = (x:Int, y:Int) => x + y
//2. 调用函数.
val result = getSum(1,2)
//3. 打印结果
println("result: " + result)
2.3 方法和函数的区别
在Java中, 方法和函数之间没有任何区别, 只是叫法不同. 但是在Scala中, 函数和方法就有区别了, 具体如下:
- 方法是隶属于类或者对象的,在运行时,它是加载到JVM的方法区中
- 可以将函数对象赋值给一个变量,在运行时,它是加载到JVM的堆内存中
- 函数是一个对象,继承自FunctionN,函数对象有apply,curried,toString,tupled这些方法。方法则没有
结论: 在Scala中, 函数是对象, 而方法是属于对象的, 所以可以理解为: 方法归属于函数.
示例
演示方法无法赋值给变量
//1. 定义方法
def add(x:Int,y:Int)= x + y
//2. 尝试将方法赋值给变量.
//val a = add(1, 2) //不要这样写, 这样写是在"调用方法", 而不是把方法赋值给变量
val a = add
//3. 上述代码会报错
<console>:12: error: missing argument list for method add
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `add _` or `add(_,_)` instead of `add`.
val a = add
2.4 方法转换为函数
有时候需要将方法转换为函数. 例如: 作为变量传递,就需要将方法转换为函数
格式
val 变量名 = 方法名 _ //格式为: 方法名 + 空格 + 下划线
注意: 使用
_
即可将方法转换为函数
示例
- 定义一个方法用来计算两个整数和
- 将该方法转换为一个函数,并赋值给变量
参考代码
//1. 定义一个方法用来计算两个整数和
def add(x:Int, y:Int)= x + y
//2. 将该方法转换为一个函数,并赋值给变量
val a = add _
//3. 调用函数, 用来获取两个整数的和.
val result = a(1, 2)
//4. 打印结果
println("result: " + result)
3. 案例: 打印nn乘法表
3.1 需求
定义方法实现, 根据用户录入的整数, 打印对应的乘法表。
例如: 用户录入5,则打印55乘法表,用户录入9,则打印99乘法表。
3.2 目的
- 考察
键盘录入和方法, 函数
的综合运用. - 体会方法和函数的不同.
3.3 步骤
- 定义方法(或者函数), 接收一个整型参数.
- 通过for循环嵌套实现, 根据传入的整数, 打印对应的乘法表.
- 调用方法(函数), 输出结果.
3.4 参考代码
- 方式一: 通过方法实现
//1. 定义一个方法, 接收一个整型参数.
def printMT(n:Int) = { //Multiplication Table(乘法表)
//2. 通过for循环嵌套实现, 根据传入的整数, 打印对应的乘法表.
for(i <- 1 to n; j <- 1 to i) {
print(s"${j} * ${i} = ${j * i}\t");
if(j==i) println()
}
}
//3. 调用方法
printMT(5)
//优化版: 上述定义方法的代码可以合并为一行(目前只要能看懂即可)
/*def printMT(n:Int) = for(i <- 1 to n; j <- 1 to i) print(s"${j} * ${i} = ${j * i}" + (if(j==i) "\r\n" else "\t"))*/
- 方式二: 通过函数实现
//1. 定义一个函数, 接收一个整型参数.
val printMT = (n:Int) => {
//2. 通过for循环嵌套实现, 根据传入的整数, 打印对应的乘法表.
for(i <- 1 to n; j <- 1 to i) {
print(s"${j} * ${i} = ${j * i}\t");
if(j==i) println()
}
}
//3. 调用函数
printMT(9)
//优化版: 上述定义函数的代码可以合并为一行(目前只要能看懂即可)
/*val printMT = (n:Int) => for(i <- 1 to n; j <- 1 to i) print(s"${j} * ${i} = ${j * i}" + (if(j==i) "\r\n" else "\t"))*/