第 2 章 变量和数据类型

2.1 注释

Scala 注释使用和 Java 完全一样。

注释是一个程序员必须要具有的良好编程习惯。将自己的思想通过注释先整理出来,再 用代码去体现。

1)基本语法

1
2
3
4
5
1)单行注释:// 
2)多行注释:/* */
3)文档注释:/**
*
*/

2) 代码规范

  • 使用一次 tab 操作,实现缩进,默认整体向右边移动,用 shift+tab 整体向左移

  • 或者使用 ctrl + alt + L 来进行格式化

  • 运算符两边习惯性各加一个空格。比如:2 + 4 * 5。

  • 一行最长不超过 80 个字符,超过的请使用换行展示,尽量保持格式优雅

2.2 变量和常量(重点)

常量:在程序执行的过程中,其值不会被改变的变量

0)回顾:Java 变量和常量语法

变量类型 变量名称 = 初始值

  • int a = 10

final 常量类型 常量名称 = 初始值

  • final int b = 20

1)基本语法

var 变量名 [: 变量类型] = 初始值

  • var i:Int = 10

val 常量名 [: 常量类型] = 初始值

  • val j:Int = 20

注意:能用常量的地方不用变量

1
这是因为scala不仅仅是面向对象的语言,也是一门函数式编程语言,在函数式编程语言中,有一种想法就是,当年定义一个等式,从函数,数学的角度看 j=20,j那么就等于20,之后的值就不应该变了。所以在真正的函数式编程中,没有变量的概念,一个值等于就是什么。所以常量的使用是真正符合函数式编程思想的用法。scala目的是兼容面向对象,兼容Java的用法,所以把变量的使用也要定义出来,真正推荐的还是函数式编程

2)案例实操

  • 声明变量时,类型可以省略,编译器自动推导,即类型推导
  • 类型确定后,就不能修改,说明 Scala 是强数据类型语言。
  • 变量声明时,必须要有初始值。
  • 在声明/定义一个变量时,可以使用 var 或者 val 来修饰,var 修饰的变量可改变, val 修饰的变量不可改。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
object TestVar {
def main(args: Array[String]): Unit = {
//(1)声明变量时,类型可以省略,编译器自动推导,即类型推导
var age = 18
age = 30

//(2)类型确定后,就不能修改,说明 Scala 是强数据类型语言。
// age = "tom" // 错误
//(3)变量声明时,必须要有初始值
// var name //错误
//(4)在声明/定义一个变量时,可以使用 var 或者 val 来修饰,var 修饰的变量可改变,val 修饰的变量不可改。

var num1 = 10 // 可变
val num2 = 20 // 不可变
num1 = 30 // 正确
//num2 = 100 //错误,因为 num2 是 val 修饰的
}
}
  • var 修饰的对象引用可以改变,val 修饰的对象则不可改变,但对象的状态(值)却是可以改变的。(比如:自定义对象、数组、集合等等)

2.3 标识符的命名规范

Scala 对各种变量、方法、函数等命名时使用的字符序列称为标识符。即:凡是自己可以起名字的地方都叫标识符。

1)命名规则

Scala 中的标识符声明,基本和 Java 是一致的,但是细节上会有所变化,有以下三种规则:

  • 以字母或者下划线开头,后接字母、数字、下划线

  • 以操作符开头,且只包含操作符(+ - * / # !等)

  • 用反引号`….`包括的任意字符串,即使是 Scala 关键字(39 个)也可以

    • 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, for, return, yield
    • def, val, var
    • this, super
    • new
    • true, false, null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
object Test03_Identifier2 {

def main(args: Array[String]): Unit = {

// (1)以字母或者下划线开头,后接字母、数字、下划线
var hello: String = "" // ok
var Hello12: String = "" // ok
var 1 hello: String = "" // error 数字不能开头
var h - b: String = "" // error 不能用-
var x h: String = "" // error 不能有空格
var h_4: String = "" // ok
var _ab: String = "" // ok
var Int: String = "" // ok 因为在 Scala 中 Int 是预定义的字符,不是关键字,但不推荐

var _: String = "hello" // ok 单独一个下划线不可以作为标识符,因为_被认为是一个方法!!!

//(2)以操作符开头,且只包含操作符(+ - * / # !等)
var +*-/#! : String = "" // ok
var +*-/#!1: String = "" // error 以操作符开头,必须都是操作符


//(3)用反引号`....`包括的任意字符串,即使是 Scala 关键字(39 个)也可以
var if: String = "" // error 不能用关键字
var `if`: String = "" // ok 用反引号`....`包括的任意字符串,包括关键字
}

}

2.4 字符串输出

1)基本语法

(1)字符串,通过+号连接

(2)printf 用法:字符串,通过%传值。

(3)字符串模板(插值字符串):通过$获取变量值

2)案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
object Test04_String {
def main(args: Array[String]): Unit = {
//(1)字符串,通过+号连接
val name: String = "alice"
val age: Int = 18
println(age + "岁的" + name + "在尚硅谷学习")

// *用于将一个字符串复制多次并拼接
println(name * 3)

//(2)printf用法:字符串,通过%传值。
printf("%d岁的%s在尚硅谷学习", age, name)
println()

//(3)字符串模板(插值字符串):通过$获取变量值
println(s"${age}岁的${name}在尚硅谷学习")

val num: Double = 2.3456
println(f"The num is ${num}%2.2f") // 格式化模板字符串
println(raw"The num is ${num}%2.2f")

// 三引号表示字符串,保持多行字符串的原格式输出
val sql = s"""
|select *
|from
| student
|where
| name = ${name}
|and
| age > ${age}
|""".stripMargin
println(sql)
}
}

image-20210329205440288

2.5 键盘输入

在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取。

1)基本语法

StdIn.readLine()、StdIn.readShort()、StdIn.readDouble()

2)案例实操

需求:可以从控制台接收用户信息,【姓名,年龄,薪水】。

1
2
3
4
5
6
7
8
9
10
11
12
object Test05_StdIn {
def main(args: Array[String]): Unit = {
// 输入信息
println("请输入您的大名:")
val name: String = StdIn.readLine()
println("请输入您的芳龄:")
val age: Int = StdIn.readInt()

// 控制台打印输出
println(s"欢迎${age}岁的${name}来到尚硅谷学习")
}
}

image-20210329205537400

2.6 读写文件

1)基本语法

2) 案例实操

image-20210329210343284

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.io.{File, PrintWriter}
import scala.io.Source

object Test06_FileIO {
def main(args: Array[String]): Unit = {
// 1. 从文件中读取数据
Source.fromFile("src/main/resources/test.txt").foreach(print)

// 2. 将数据写入文件
val writer = new PrintWriter(new File("src/main/resources/output.txt"))
writer.write("hello scala from java writer")
writer.close()
}
}

image-20210329210740475

2.7 数据类型(重点)

回顾:Java数据类型

Java基本类型:char、byte、short、int、long、float、double、boolean

Java引用类型:(对象类型)

由于Java有基本类型,而且基本类型不是真正意义的对象,即使后面产生了基本类型的包装类,但是仍然存在基本数据类型,所以Java语言并不是真正意思的面向对象。

Java基本类型的包装类:Character、Byte、Short、Integer、Long、Float、Double、Boolean

注意:Java中基本类型和引用类型没有共同的祖先。

image-20210329212418129

Scala数据类型

1 )Scala中一切数据都是对象,都是Any的子类。

  1. Scala中数据类型分为两大类: 数值类型(AnyVal) 、引用类型(AnyRef),不管是值类型还是引用类型都是对象。

3)Scala数据类型仍然遵守,低精度的值类型向高精度值类型,自动转换(隐式转换)

4)Scala中的StringOps是对Java中的String增强

5)Unit:对应Java中的void,用于方法返回值的位置,表示方法没有返回值。Unit是一个数据类型,只有一个对象就是()。Void不是数据类型,只是一个关键字

6)Null是一个类型,只有一个对象就是null。它是所有引用类型(AnyRef)的子类。

7)Nothing,是所有数据类型的子类,主要用在一个函数没有明确返回值时使用,因为这样我们可以把抛出的返回值,返回给任何的变量或者函数。

2.8 整数类型(Byte、Short、Int、Long)

Scala 的整数类型就是用于存放整数值的,比如 12,30,3456 等等。

1)整型分类

数据类型 描述
Byte [1] 8 位有符号补码整数。数值区间为 -128 到 127
Short [2] 16 位有符号补码整数。数值区间为 -32768 到 32767
Int [4] 32 位有符号补码整数。数值区间为 -2147483648 到 2147483647
Long [8] 64 位有符号补码整数。数值区间为 -9223372036854775808 到9223372036854775807 = 2 的(64-1)次方-1

2)案例实操

  • Scala 各整数类型有固定的表示范围和字段长度,不受具体操作的影响,以保证Scala 程序的可移植性。

  • Scala 的整型,默认为 Int 型,声明 Long 型,须后加‘l’或‘L’

  • Scala 程序中变量常声明为 Int 型,除非不足以表示大数,才使用 Long

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1. 整数类型
val a1: Byte = 127
val a2: Byte = -128

// val a2: Byte = 128 // error

val a3 = 12 // 整数默认类型为Int
val a4: Long = 1324135436436L // 长整型数值定义

val b1: Byte = 10
val b2: Byte = 10 + 20 //可以编译运行
println(b2)

// val b3: Byte = b1 + 20 //编译直接报错了 需要的是Byte 传入的是Int
val b3: Byte = (b1 + 20).toByte
println(b3)

2.9 浮点类型(Float、Double)

Scala 的浮点类型可以表示一个小数,比如 123.4f,7.8,0.12 等等。

1)浮点型分类

数据类型 描述
Float [4] 32 位, IEEE 754 标准的单精度浮点数
Double [8] 64 位 IEEE 754 标准的双精度浮点数

2)案例实操

Scala 的浮点型常量默认为 Double 型,声明 Float 型常量,须后加‘f’或‘F’。

1
2
3
// 浮点类型
val f1: Float = 1.2345f
val d1 = 34.2245

2.10 字符类型(Char)

1)基本说明

字符类型可以表示单个字符,字符类型是 Char。

2)案例实操

1
2
3
4
5
-  字符常量是用单引号 ' ' 括起来的单个字符。
- \t :一个制表位,实现对齐的功能
- \n :
- \\ :表示\
- \" :表示"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 字符类型
val c1: Char = 'a'
println(c1)

val c2: Char = '9'
println(c2)

// 控制字符
val c3: Char = '\t' // 制表符
val c4: Char = '\n' // 换行符
println("abc" + c3 + "def")
println("abc" + c4 + "def")

// 转义字符
val c5 = '\\' // 表示\自身
val c6 = '\"' // 表示"
println("abc" + c5 + "def")
println("abc" + c6 + "def")

// 字符变量底层保存ASCII码
val i1: Int = c1
println("i1: " + i1)
val i2: Int = c2
println("i2: " + i2)

val c7: Char = (i1 + 1).toChar
println(c7)
val c8: Char = (i2 - 1).toChar
println(c8)

image-20210330205001517

2.11 布尔类型:Boolean

1)基本说明

  • 布尔类型也叫 Boolean 类型,Booolean 类型数据只允许取值 true 和 false

  • boolean 类型占 1 个字节。

2)案例实操

1
2
3
4
5
var isResult : Boolean = false
var isResult2 : Boolean = true
// 4. 布尔类型
val isTrue: Boolean = true
println(isTrue)

2.12 Unit 类型、Null类型和Nothing 类型(重点)

1)基本说明

数据类型 描述
Unit 表示无值,和其他语言中 void 等同。用作不返回任何结果的方法的结果类型。Unit 只有一个实例值,写成()。
Null null , Null 类型只有一个实例值 null
Nothing Nothing 类型在 Scala 的类层级最低端;它是任何其他类型的子类型。当一个函数,我们确定没有正常的返回值,可以用 Nothing 来指定返回类,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数

2)案例实操

  • Unit 类型用来标识过程,也就是没有明确返回值的函数。
    • 由此可见,Unit 类似于 Java 里的 void。Unit 只有一个实例——**( )**,这个实例也没有实质意义
1
2
3
4
5
6
7
//空值Unit
def m1(): Unit = {
println("m1被调用执行")
}

val a: Unit = m1()
println("a: " + a)
  • Null 类只有一个实例对象,Null 类似于 Java 中的 null 引用。Null可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
1
2
3
4
5
//   空引用Null 
// val n: Int = null // error
var student: Student = new Student("alice", 20)
student = null
println(student)
  • Nothing,可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于 Nothing 是其他任意类型的子类,他还能跟要求返回值的方法兼容。
1
2
3
4
5
6
7
8
9
// Nothing
def m2(n: Int): Int = {
if (n == 0)
throw new NullPointerException
else
return n
}
val b: Int = m2(2)
println("b: " + b)

2.13 类型转换

扩展 Java 面试题(隐式类型转换):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class TestDataTypeConversion {
public static void main(String[] args) {
byte b = 10;
test(b); // byte 会先转换short 不存在在转换int

char c = 'a';
test(c); // 直接转换成int


short c2 = (short) c;
test(c2); //强转之后,会按short处理
}

public static void test(short s) {
System.out.println("ssss");
}

public static void test(int i) {
System.out.println("iiii");
}
}

结果
ssss
iiii
ssss

2.13.1 数值类型自动转换

当 Scala 程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数值类型,这个就是自动类型转换(隐式转换)。数据类型按精度(容量)大小排序为

image-20210330223648030

1)基本说明

  • 自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数据类型,然后再进行计算。

  • 把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。

  • (byte,short)和 char 之间不会相互自动转换。

  • byte,short,char 他们三者可以计算,在计算时首先转换为 int 类型。

2)案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//  自动类型转换
// (1)自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数据类型,然后再进行计算。
val a1: Byte = 10
val b1: Long = 2353
val result1: Long = a1 + b1
val result11: Int = (a1 + b1.toInt) // 强转

// (2)把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
val a2: Byte = 10
val b2: Int = a2
// val c2: Byte = b2 // error

// (3)(byte,short)和char之间不会相互自动转换。
val a3: Byte = 10
val b3: Char = 'b'
// val c3: Byte = b3 // error
val c3: Int = b3
println(c3)

// (4)byte,short,char他们三者可以计算,在计算时首先转换为int类型。
val a4: Byte = 12
val b4: Short = 25
val c4: Char = 'c'
val result4: Int = a4 + b4
val result44: Int = a4 + b4 + c4
println(result44)

注意:Scala 还提供了非常强大的隐式转换机制(隐式函数,隐式类等),我们放在高级部分专门用一个章节来讲解。

2.12.2 强制类型转换

1)基本说明

自动类型转换的逆过程,将精度大的数值类型转换为精度小的数值类型。使用时要加上强制转函数,但可能造成精度降低或溢出,格外要注意。

1
2
Java : int num = (int)2.5
Scala : var num : Int = 2.7.toInt

2)案例实操

  • 将数据由高精度转换为低精度,就需要使用到强制转换

  • 强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级

1
2
3
4
5
6
7
8
9
10
11
// 2. 强制类型转换
// (1)将数据由高精度转换为低精度,就需要使用到强制转换
val n1: Int = -2.9.toInt
println("n1: " + n1) // -2

// (2)强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
val n2: Int = 2.6.toInt + 3.7.toInt
val n3: Int = (2.6 + 3.7).toInt
println("n2: " + n2) // 5
println("n3: " + n3) // 6

2.12.3 数值类型和 String 类型间转换

1)基本说明

在程序开发中,我们经常需要将基本数值类型转成 String 类型。或者将 String 类型转成基本数值类型。

2)案例实操

  • 基本类型转 String 类型(语法:将基本类型的值+”” 即可)

  • String 类型转基本数值类型(语法:s1.toInt、s1.toFloat、s1.toDouble、s1.toByte、s1.toLong、s1.toShort)

1
2
3
4
5
6
7
8
9
10
11
// 3. 数值类型和String类型的转换
// (1) 数值转String
val n: Int = 27
val s: String = n + ""
println(s)

// (2) String转数值
val m: Int = "12".toInt
val f: Float = "12.3".toFloat
val f2: Int = "12.3".toDouble.toInt
println(f2)

3)注意事项

在将 String 类型转成基本数值类型时,要确保 String 类型能够转成有效的数据,比如我们可以把”123”,转成一个整数,但是不能把”hello”转成一个整数。

  • var n5:Int = “12.6”.toInt 会出现 NumberFormatException 异常。

扩展面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
object Test09_Problem_DataTypeConversion {
def main(args: Array[String]): Unit = {
var n: Int = 130
val b: Byte = n.toByte
println(b) //-126
}
}


计算机在操作的时候,会转成对应补码
130: Int类型,占据4个字节,32
原码 0000 0000 0000 0000 0000 0000 1000 0010
补码 0000 0000 0000 0000 0000 0000 1000 0010

强转成Byte 会截取最后一个字节,Byte
得到补码 1000 0010
对应原码 1111 1110 表示 -126



128: Int类型,占据4个字节,32
原码 0000 0000 0000 0000 0000 0000 1000 0000
补码 0000 0000 0000 0000 0000 0000 1000 0000

截取最后一个字节,Byte
得到补码 1000 0000
表示最大负数 -128