Skip to content

变量和数据类型

1. 注释

Scala注释使用和Java完全一样。
基本语法

  1. 单行注释://
  2. 多行注释:/* */
  3. 文档注释:

/**

**/

2. 变量和常量

常量:在程序执行的过程中,其值不会被改变的变量。
变量: 值可以被改变的变量,但是类型不能变。

  1. 基本语法:
    var 变量名 [: 变量类型] = 初始值
    比如var i:Int = 10
    val 常量名 [: 常量类型] = 初始值
    比如val j:Int = 20

提示

  1. 能用常量的地方不用变量。
  2. 声明变量、常量时,类型可以省略,编译器自动推导,即类型推导。
  3. 类型确定后,就不能修改,说明Scala是强数据类型语言。
  4. 变量、常量声明时,必须要有初始值。
  5. 在声明/定义一个变量时,可以使用var或者val来修饰,var修饰的变量可改变,val修饰的变量不可改。
scala
def main(args: Array[String]): Unit = {
    // (1)声明变量时,类型可以省略,编译器自动推导,即类型推导
    var age = 18
    age = 30
    // (2)类型确定后,就不能修改,说明Scala是强数据类型语言。
    // age = "tom" // 错误
    // (3)变量声明时,必须要有初始值,和java不同,需要显式初始化
    // var name //错误
    // (4)在声明定义一个变量时,可以使用var或者val来修饰var修饰的变量可改变,val修饰的变量不可改
    var num1 = 10 // 可变
    val num2 = 20 // 不可变
    num1 = 30 // 正确
    // num2 = 100 //错误,因为num2是val修饰的
    // var修饰的对象引用可以改变, val修饰的对象则不可改变,但对象的状态(值)却是可以改变的。(比如:自定义对象、数组、集合等等)
    // p1是var修饰的,p1的属性可以变,而且p1本身也可以变
    var p1 = new Person()
    p1.name = "dalang"
    p1 = null
    // p2是val修饰的,那么p2本身就不可变(即p2的内存地址不能变),但是p2的属性是可以变,因为属性并没有用val修饰 。
    val p2 = new Person()
    p2.name = "jinlian"
    // p2 = null // 错误的,因为p2是val修饰的
}

var和val在编译后没有本质的区别,val只是在编译时被约束。
Alt text

提示

❓为何java中String是不可变字符串?
原因在于String源码底层使用不可变数组:
Alt text

3. 标识符的命名规范

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

3.1 命名规则

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

  1. 以字母或者下划线开头,后接字母、数字、下划线。
  2. 以操作符开头,且只包含操作符(+ - * / # !等)。
  3. 用反引号"""包含的任意字符串,即使是Scala关键字(39个)也可以使用。
scala
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 单独一个下划线不可以作为标识符,因为_被认为是一个方法
    println(_)
    // (2)以操作符开头,且只包含操作符(+ - * / # !等)
    var +*-/#! : String = "" // ok
    var +*-/#!1: String= "" // error 以操作符开头,必须都是操作符
    // (3)用反引号`....`包括的任意字符串,即使是 Scala 关键字(39 个) 也可以
    var if: String = "" // error 不能用关键字
    var `if`: String = "" // ok 用反引号`....`包括的任意字符串,包括关键字
    // val 💘 = "一箭穿心"; //error  不支持表情符号
    // println(💘)
    val :-> = "表情符号"    // 支持部分颜文字
    println(:->)
}

提示

标识符如果是颜文字,底层scala编译器将会进行转义,从而兼容JVM的字节码规范。下图是反编译结果,可以看到转义后都是以$开头,所以标识符命名尽量不要$开头,避免和编译发生冲突。 Alt text

4. 字符串输出

Scala中没有字符串类型,实际使用的是java中的java.lang.String对象:
Alt text 字符串拼接有三种方式,基本语法:

  1. 字符串,通过+号连接。
  2. printf 用法:字符串,通过%传值。
  3. 字符串模板(插值字符串):通过$获取变量值。
scala
def main(args: Array[String]): Unit = {
    var name: String = "jinlian"
    var age: Int = 18
    //(1)字符串,通过+号连接
    println(name + " " + age)
    //(2)printf 用法字符串,通过%传值。
    printf("name=%s age=%d\n", name, age)
    //(3)字符串,通过$引用   插值字符串拼接,不能拼出json字符串
    val s = """
    | select
    | name,
    | age
    | from user
    | where name="zhangsan"
        """.stripMargin
    println(s)
    //如果需要对变量进行运算,那么可以加${}
    val s1 =
        s"""
            |select
            | name,
            | age
            |from user
            |where name="$name" and age=${age + 2}
        """.stripMargin
    println(s1)
    val s2 = s"name=$name"
    println(s2)
}

提示

多行字符串常用编写SQL和JSON字符串,在Scala中利用三个双引号包围多行字符串就可以实现。输入的内容带有空格、\t之类,导致每一行的开始位置不能整洁对齐。需要应用Scala的stripMargin方法,此外stripMargin默认是"|"作为连接符,因此在多行换行的行头前面需要加一个"|"符号。stripMargin源码:
Alt text

5. 数据类型

5.1 回顾Java数据类型

Java基本类型:char、byte、short、int、long、float、double、boolean
Java引用类型:(对象类型)
由于Java有基本类型,而且基本类型不是真正意义的对象,即使后面产生了基本类型的包装类,但是仍然存在基本数据类型,所以Java语言并不是真正意义的面向对象。
注意:Java中基本类型和引用类型没有共同的祖先。
Java基本类型的包装类:Character、Byte、Short、Integer、Long、Float、Double、Boolean

5.2 Scala数据类型

Alt text

  1. Scala中一切数据都是对象,都是Any的子类。
  2. Scala中数据类型分为两大类:数值类型(AnyVal)引用类型(AnyRef),不管是值类型还是引用类型都是对象。
  3. Scala数据类型仍然遵守,低精度的值类型向高精度值类型,自动转换(隐式转换)
  4. Scala中的StringOps是对Java中的String增强
  5. Unit:对应Java中的void,用于方法返回值的位置,表示方法没有返回值。Unit是一个数据类型,只有一个实例对象,控制台打印为()。Void不是数据类型,只是一个关键字
  6. Null是一个类型,只有一个对象就是null。它是所有引用类型(AnyRef)的子类。
  7. Nothing是所有数据类型的子类,主要用在一个函数没有明确返回值时使用,比如异常处理。

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

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

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

5.4 浮点类型(Float、Double)

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

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

5.5 字符类型(Char)

表示单个字符,字符类型是Char。用单引号' '括起来的单个字符。其中有一些特殊字符:

  • \t :一个制表位,实现对齐的功能
  • \n :换行符
  • \\ :表示\
  • \" :表示"

5.6 布尔类型Boolean

布尔类型也叫Boolean类型,Boolean类型数据只允许取值true和false, Boolean类型占1个字节。

5.7 Unit类型、Null类型和Nothing类型

数据类型描述
Unit表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit 只有一个实例值,写成()。
Nullnull , Null类型只有一个实例值null
NothingNothing类型在Scala的类层级最低端;它是任何其他类型的子类型。当一个函数我们确定没有正常的返回值,可以用Nothing来指定返回类型,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数或者变量(兼容性)
  1. Unit 类型用来标识过程,也就是没有明确返回值的函数。由此可见,Unit类似于Java里的void。Unit只有一个实例——( ),这个实例也没有实质意义 Alt text
  2. Null 类只有一个实例对象,Null类似于Java中的null引用。Null可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
scala
//null 可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型 (AnyVal )
var cat = new Cat();
cat = null // 正确
var n1: Int = null // 错误
println("n1:" + n1)
  1. Nothing,可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于Nothing是其他任意类型的子类,他还能跟要求返回值的方法兼容。
scala
def main(args: Array[String]): Unit = {
    def test() : Nothing={
        throw new Exception()
    }
    test
 }

5.8 隐式类型转换

  1. Java中的隐式转换
java
public static void main(String[] args) {
    byte a = 6;
    test(a);
}

//    private static void test(byte a) {
//        System.out.println("byte");
//    }
private static void test(short a) {
    System.out.println("short");
}
private static void test(int a) {
    System.out.println("int");
}

运行结果:
Alt text 2. 当Scala程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数值类型,这个就是自动类型转换(隐式转换)。转换原则如下:

  • 自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数据类型,然后再进行计算。
  • 把精度大的数值类型赋值给精度小的数值类型时,就会报错。
  • byte,short,char他们三者可以计算,在计算时首先转换为Int类型。
scala
def main(args: Array[String]): Unit = {
    val name: StringOps = "hello"
    var i: Int = null
    println(name)
    println(i)
}

运行结果:
Alt text 原因就是在于null是Null类型,Int不能转换为Null, 他们的父类不同,一个是AnyVal,一个是AnyRef, 不能直接强转,可以改为var i: Any = null,使用Any类利用多态实现。

scala
val a: Byte = '3'
val b:Int = a
println(b)
// 常量(val)是可以在编译时计算的,不涉及运行时的类型转换。如果将val改为var, 表达式将在运行时计算,则会报错。
val c: Char = 'A'+1

5.9 强制类型转换

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

scala
Java : int num = (int)2.5
scala中有相应的方法调用实现显式类型转换
Scala : var num : Int = 2.7.toInt

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

  • 基本类型转 String 类型(语法:将基本类型的值+"" 即可)
  • String类型转基本数值类型(语法:s1.toInt、s1.toFloat、s1.toDouble、s1.toByte、s1.toLong、s1.toShort)