Scala学习笔记-入门(2)-Scala基础语法
注释
Scala中的注释和Java中的完全一样,具有单行注释、多行注释和文档注释
1 |
|
变量和常量
在Java中,变量和常量语法如下:
1 |
|
在Scala中,变量和常量的语法如下:
1 |
|
在Scala中,变量和常量有如下的特点:
声明变量的时候,类型可以省略,编译器会自动推导,即类型推导
类型确定之后,就不能修改,该变量的类型不再改变
变量声明的时候,必须要有初始值
常量使用val声明,常量特性与Java中使用final关键字修饰的常量一致
引用类型的常量,不能改变常量指向的对象,但是可以改变对象的字段
建议:能够使用常量的地方就使用常量而不使用变量
关于强数据类型、弱数据类型、动态类型和静态类型:
- 强弱数据类型指的是语言对类型检查的严格程度:弱类型相对于强类型来说类型检查更不严格,会允许一些变量类型的隐式转换等
- 静态类型指编译器在编译阶段进行类型检查,动态类型指在执行过程中进行类型检查
标识符的命名规范
在Scala中,标识符的命名规范比Java中多出了两条,具体命名规则如下:
- 以字母或者下划线开头,后面可以接字母、数字、下划线(这点和和Java是一样的)
- 可以以操作符开头,但是必须只包含操作符(后续会看到这样做能够导致超级灵活的运算符重载)
- 使用反引号包括的任意字符串都可以作为标识符,即使是Scala中的关键字也可以
Scala中的关键字:
1
2
3
4
5
6
7
8
package import class object trait extends with type for
private protected abstract sealed final implicit lazy override
try catch finally throw
if else match case do while return yield
def val var
this super
new
true false null其中Java没有的关键字:
object trait with implicit match yield def val var
字符串输出
字符串的输出有三种基本方式:
- 通过+号进行连接
- 利用printf输出模板字符串:通过
%
占位 - 使用插值字符串,通过
${}
来获取变量值,插值表达式中可以进行变量的运算
以上三种方式可以将变量值与字符串进行拼接,同时可以综合使用单行和多行的字符串
1 |
|
在scala中提供的两种特殊字符串,s字符串和raw字符串。在s字符串中,可以使用插值表达式。而raw字符串与s字符串类似,同样可以使用插值表达式,只不过raw字符串不会进行转义。
1 |
|
对应输出如下:
1 |
|
还可以使用多行字符串"""..."""
,获得更加格式化的输出:
1 |
|
其中|
表示每一行的开头,.stripMargin
为一个方法调用,输出结果如下,注意第一行的空行对应上面代码的第四行:
1 |
|
输入
键盘输入
基本语法,利用StdIn
提供的方法,例如StdIn.readLine()
、StdIn.readShort()
、StdIn.readDounle()
等,需要引入包scala.io.StdIn
文件输入和输出
1 |
|
数据类型
数据类型概览
在Java中,数据类型分为基本数据类型和引用类型,基本数据类型对应还有包装类。由于基本数据类型的存在,Java可以认为并不是完全意义上的面向对象,Java中基本类型和引用类型并没有共同的祖先,包装类和引用数据类型的共同祖先才是Object类
在Scala中,一切数据都是对象。
- 一切数据都是对象,都是Any的子类
- 在Scala中数据类型分为两大类:数值类型(AnyVal)和引用类型(AnyRef),但是两者都是对象
- 图中,实线表示继承关系,而虚线表示隐式的自动类型转换
String
属于引用类型(AnyRef),而在Scala中设置了一个StringOps
,属于数值类型,是对Java中String的增强- Unit属于AnyVal中的空值、Null属于AnyRef中的空值,是所有AnyRef的子类
- Noting是所有数据类型的子类
整数类型
- Byte:1字节
- Short:2字节
- Int:4字节(不同于Java中的Integer)
- Long:8字节
整数默认为Int
浮点类型
- Float:4字节
- Double:8字节
浮点数默认为Double
字符类型
- Char:2字节
布尔类型
- Boolean:1字节,允许取值true和false
空类型
数据类型 | 描述 |
---|---|
Unit | 表示没有值,等同于void,用作不返回任何结果的方法和结果类型。Unit只有一个实例值,输出成() |
Null | 表示空引用,只有一个实例null |
Nothing | Nothing类型是其他任何类型的子类型,当一个函数确定没有正常的返回值,如抛出异常等,就可以利用Noting来指定返回类型 |
- Unit
1 |
|
输出如下
1 |
|
- Null
1 |
|
- Nothing
1 |
|
类型转换
当Scala程序在进行赋值或者运算的时候,精度小的类型会自动转换成精度大的数值类型,这就是自动类型转换(隐式转换)。与Java中类似。
- 自动类型提升:多种数据类型混合运算,自动提升到精度最大的数据类型。
- 高精度赋值到低精度,直接报错。
- 除了图中的隐式类型转换,都需要强制类型转换。
Byte Short Char
计算时会直接提升为Int
。Boolean
不能参与整数浮点运算,不能隐式转换为整数。
强制类型转换
强制类型将数据由高精度转换为低精度,可能造成精度降低或者溢出。
强制类型转换利用到.toXXX
,实际上也是一个方法调用的形式,此时的数值均为对象。
1 |
|
运算符
基本运算符
Scala中的运算符与Java基本相同,只有个别在细节上存在不同
算术运算符:
+ - * / %
*
可以利用在字符串重复上关系运算:
== != < > <= >=
==
的测试:- 在Java中,
==
可以比较两个基本数据类型的值,也可以比较两个引用类型的地址 - 在Scala中,
==
相当于equals的调用,比较的更多是值(看对应的equals的实现) - 如果需要比较地址的话,需要使用
eq
测试代码(注意这里的new):
1
2
3
4
5
6val s1: String = "123"
val s2: String = new String("123")
println(s1==s2)
println(s1.equals(s2))
println(s1.eq(s2))输出如下:
1
2
3true
true
false- 在Java中,
逻辑运算:
&& || !
,支持短路运算赋值运算:
= += -= *= /= %= <<= >>= &= ^= !=
注意:在Scala中没有
++ --
操作符,相关功能使用+= -=
来实现位运算:
& | ^ ~ << >> >>>
运算符的本质
在Scala中其实是没有运算符的,所有的运算符都是方法调用
- 当调用对象的方法时,
.
可以省略 - 如果函数参数只有一个或者没有的时候,
()
可以省略
1 |
|
在Scala中还有一个非常常用的运算符
:=
。这个运算符并不是内置的运算符,通常使用者会将它进行重载,将其定义为任何他们喜欢的意思。这个符号在其他语言中经常被用作赋值运算符。
流程控制
分支控制
if- else if -else
代码块,使用方式基本与Java相同
- 与Java不同之处,Scala中
if-else
语句有返回值,返回值定义为执行到的最后一个语句的返回值 - 可以强制要求返回Unit类型,此时忽略最后一个表达式的值,直接返回Unit
- Scala中没有三元条件运算符
?:
,可以直接使用一行if else
来完成
1 |
|
输出为:
1 |
|
Scala中没有Switch,而是使用模式匹配来处理,这部分会在后面进行讲解
循环控制
For循环
Scala中为for循环这一常见的控制结构提供了非常多的特性,这些for循环的特性被成为for推导式
范围数据循环
范围数据循环可以对比Python中的Range,下面的循环背后是得到了一个Range对象,每次给循环变量赋值对应的值。可以指定是否包含对应的右边界,可以指定步长
to:包含右边界
1
2
3
4for (i <- 1 to 10) {
print(i)
}
// 结果为:12345678910until:不包含右边界
1
2
3
4for (i <- 1 until 10) {
print(i)
}
// 结果为:123456789上面的代码等价于
1
2
3for (i <- new Range(1, 10, 1)) {
print(i)
}循环步长
在循环中可以指定步长,通过by(步长不能为0)
1
2
3
4for (i <- 1 to 10 by 2) {
print(i)
}
// 结果为:13579可以指定负数步长,利用负数步长可以完成逆序输出
1
2
3
4
5
6
7
8
9for (i <- 1 to 10 by -1) {
print(i)
}
// 结果为:(空,啥也没有)
for (i <- 10 to 1 by -1) {
print(i)
}
// 结果为:10987654321也可以利用reverse指定逆序输出
1
2
3
4for (i <- 1 to 10 by 2 reverse) {
print(i)
}
// 结果为:97531步长可以设置为浮点数,但是需要指定循环范围的类型
1
2
3
4for(i <- 1.0 to 5.0 by 0.5){
print(i + " ")
}
// 结果为:1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0循环守卫
循环守卫,即循环保护式,也称条件判断式,只有满足条件才进入循环体内部,否则不进入。相当于在进入循环之前就进行判断,类似于
continue
1
2
3
4for (i <- 1 to 5 if i != 2) {
print(i)
}
// 结果为:1345嵌套循环
嵌套循环可以类似于Java中嵌套实现,也可以将嵌套合并到一个for中
1
2
3
4
5
6for (i <- 1 to 5) {
for (j <- 1 to 5) {
print(i + j + " ")
}
}
// 结果为:2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9 6 7 8 9 10等价于
1
2
3for (i <- 1 to 5; j <- 1 to 5) {
print(i + j + " ")
}引入变量
可以在for推导式中引入变量
- for推导式中一行有多个表达式时,使用
;
来隔断逻辑 - for推导式中,当只包含单一表达式的时候使用圆括号
()
,当有多个条件的时候使用花括号{}
,并且每一行一个表达式
1
2
3
4for (i <- 1 to 5; j = 5 - i) {
print(j + " ")
}
// 结果为:4 3 2 1 0等价于
1
2
3
4
5
6for {
i <- 1 to 5
j = 5 - i
} {
print(j + " ")
}- for推导式中一行有多个表达式时,使用
循环返回值
单独循环表达式的返回值是Unit,但是可以使用yield来在循环中返回,可以类比Python中的列表推导(注意:与Python中的yield不同)。在循环中使用yield,会将每次循环的结果加入一个List中,循环完毕之后再返回。
1
2
3
4val res = for (i <- 1 to 5) {
}
print(res)
// 结果为:()1
2
3val res = for (i <- 1 to 5) yield i
print(res)
// 结果为:Vector(1, 2, 3, 4, 5)
While循环
while
和do while
:
- 这两个关键字的用法与Java中是一模一样的,结果类型是
Unit
- 这两个关键字是为了保持对Java的兼容,在Scala中不推荐使用
循环中断
Scala内置控制结果中特地去除了break
和continue
关键字,为了更好地适应函数式编程。在Scala中推荐使用函数式的风格来解决break和continue的功能。
- 实现break
采用异常方式退出循环:
1 |
|
采用Scala自带的函数退出循环(底层还是使用的try-catch
的解决方案):
1 |
|
对Breaks进行省略:
1 |
|
- 实现continue
可以利用前面提到的循环守卫的方式,也可以利用if判断的方式做到