大数据开发语言Scala(一)——scala入门
创始人
2025-05-30 21:59:30
0

累了,基础配置不想写了,直接抄了→Scala的环境搭建

这里需要注意的是,创建新项目时,不要用默认的Class类,用Object,原因看→scala中的object为什么可以直接运行

一、Scala简介

1.1 图解Scala和Java的关系

1.2 关键字说明

package: 包,等同于java中的package

object:关键字,声明一个单例对象(伴生对象)

main方法:从外部可以直接调用执行的方法

def 方法名称 ( 参数名称 : 参数类型 ) : 返回值类型 = { 方法体 }

Scala 完全面向对象,故scala去掉了Java中非面向对象的元素,如static关键字,void类型
1) static
scala无static关键字,由object实现类似静态方法的功能(类名.方法名)
class关键字和Java中的class关键字作用相同,用来定义一个类
2) void
对于无返回值的函数,scala定义其返回值类型为Unit类型

1.3 代码案例

package com.scala.chapter1object Hello {def main(args: Array[String]): Unit = {println("hello scala")System.out.println("hello scala")}
}

1.4 Scala语言特点

Scala是一门以Java虚拟机 (JVM)为运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言(静态语言需要提前编译的如: Java、C、C++等,动态语言如:JS)。

1)Scala是一门多范式的编程语言,Scala支持面向对象和函数式编程。 (多范式,就是多种编程方法的意思。有面向过程、面向对象、泛型、函数式四种程序设计方法。)

2)Scala源代码 (.scala) 会被编译成Java字节码 (.class),然后运行于JVM之上,并可以调用现有的Java类库,实现两种语言的无缝对接。

3)Scala单作为一门语言来看,非常的简洁高效

4)Scala在设计时,马丁·奥德斯基是参考了Java的设计思想,可以说Scala是源于Java,同时马丁·奥德斯基也加入了自己的思想,将函数式编程语言的特点融合到JAVA中,因此,对于学习过Java的同学,只要在学习Scala的过程中,搞清楚Scala和Java相同点和不同点,就可以快速的掌握Scala这门语言。

二、变量和数据类型

2.1 注释

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

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

1)基本语法

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

2)案例实操

object Test1 {
def main(args: Array[String]): Unit = {
//单行注释
println("单行")//多行
/*
println("多行")
*///文档注释
/*** println("文档注释")*/
}
}

3) 代码规范

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

(2)或者使用ctrl + alt + L来进行格式化

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

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

2.2 变量和常量(重点)

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

1)基本语法

​ var 变量名 [: 变量类型] = 初始值 var i:Int = 10

​ val 常量名 [: 常量类型] = 初始值 val j:Int = 20

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

2)案例实操

(1)声明变量时,类型可以省略,编译器自动推导,即类型推导

(2)类型确定后,就不能修改,说明Scala是强数据类型语言。

(3)变量声明时,必须要有初始值

(4)在声明/定义一个变量时,可以使用var或者val来修饰,var修饰的变量可改变,val修饰的变量不可改。

object TestValueTransfer {
def main(args: Array[String]): Unit = {
var n = 1 + 2.2222
println(n) //Doublevar n2: Double = 2.2222
//    var n3: Int = n2 //报错var n4 : Byte = 44
//    var c1 : Char = n4 //报错
var n5 : Int = n4var n6 : Byte = 66
var c2 : Char = 22
//    var n : Short = n6 + c2  //报错,结果是Int类型
//    var n7 : Short = 10 + 20  //报错,byte,short,char他们三者可以计算,在计算时首先转换为int类型。}
}

(5)var修饰的对象引用可以改变,val修饰的对象则不可改变,但对象的状态(值)却是可以改变的。(比如:自定义对象、数组、集合等等)

object TestVar {
def main(args: Array[String]): Unit = {
var age = 22
age = 29
//    age = "hh"  //报错var n1 = 1 //可变
n1 = 2val n2 = 2 //不可变
//n2 = 3//p1 是var修饰的,p1的属性可以变,p1本身也可以变
var p1 = new Person()
p1.name = "varp1"
p1 = nullval p2 = new Person()
p2.name = "valp2"
//    p2 = null //报错
}class Person {
var name: String = "who"
}
}

2.3 标识符的命名规范

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

1)命名规则

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

(1)以字母或者下划线开头,后接字母、数字、下划线

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

(3)用反引号....包括的任意字符串,即使是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

2)案例实操

需求:判断hello、Hello12、1hello、h-b、x h、h_4、ab、Int、、+-/#!、+-/#!1、if、if,这些名字是否合法。

object TestName {def main(args: Array[String]): Unit = {//1.以字母或者下划线开头,后接字母、数字、下划线var hello: String = ""var hello2: String = " "//    var 1 hello:String = ""  数字不能开头//    var h - b: String = ""  不能用-//    var x h: String = ""  不能用空格var h_4: String = ""val _ab: String = ""var Int: String = "" //ok 因为在Scala中Int是预定义的字符,不是关键字,但不推荐var _: String = "" // ok 单独一个下划线不可以作为标识符,因为_被认为是一个方法//2.以操作符开头,且只包含操作符(+ - * / # ! 等)var +*-/#! : String = ""//    var +*-/#!1 : String = "" 以操作符开头必须都是操作符//3.用反引号`...`包括的任意字符串,即使是Scala关键字(39)个也可以//    var if:String = ""  不用用关键字var `if`: String = ""}
}

2.4 字符串输出

1)基本语法

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

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

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

2)案例实操

object TestCharType {
def main(args: Array[String]): Unit = {
var name: String = "zhangsan"
var age: Int = 22//1.字符串 通过 + 号连接
println(name + " " + age)//2.printf 用法字符串,通过%传值
printf("name=%s age=%d\n", name, age)//3.字符串,通过$引用
//多行字符串,在Scala中,利用三个双引号包围多行字符串就可以实现。//输入的内容,带有空格、\t之类,导致每一行的开始位置不能整洁对齐。
//应用scala的stripMargin方法,在scala中stripMargin默认是“|”作为连接符,//在多行换行的行头前面加一个“|”符号即可。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
val s2 = s"name=$name"
println(s2)
}
}

2.5 键盘输入

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

1)基本语法

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

2)案例实操

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

import scala.io.StdInobject TestInput {
def main(args: Array[String]): Unit = {
//    输入姓名
println("input name:")
var name = StdIn.readLine()//    输入年龄
println("input age:")
var age = StdIn.readShort()//    输入薪水
println("input sal:")
var sal = StdIn.readDouble()//    打印
println("name=" + name)
println("age=" + age)
println("sal=" + sal)
}}

从文件中读取数据:

import scala.io.Source;Source.fromFile("C:\\Users\\90513\\Desktop\\datas.csv").foreach(print)

2.6 数据类型(重点)

  • Java 数据类型

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

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

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

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

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

  • Scala 数据类型

2.7 整数类型(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)案例实操

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

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

object TestDataType {
def main(args: Array[String]): Unit = {// 正确
var n1: Byte = 127
var n2: Byte = -128// 错误
//    var n3:Byte = 128
//    var n4:Byte = -129// Scala的整型,默认为Int型,声明Long型,然后加”i“ 或者 ”L“
var n5 = 10
println(n5)var n6 = 98989898989898989L
println(n6)var n7 = 2.43434309898f
var n8 = 2.4343662343223
println(n7)
println(n8)var God = new God()
God = null//var n1 : Int = null //错误
//println("n1:" + n1)
}class God {
}}

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

2.8 浮点类型(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’。

object TestDataType {
def main(args: Array[String]): Unit = {// 正确
var n1: Byte = 127
var n2: Byte = -128// 错误
//    var n3:Byte = 128
//    var n4:Byte = -129// Scala的整型,默认为Int型,声明Long型,然后加”i“ 或者 ”L“
var n5 = 10
println(n5)var n6 = 98989898989898989L
println(n6)var n7 = 2.43434309898f
var n8 = 2.4343662343223
println(n7)
println(n8)var God = new God()
God = null//var n1 : Int = null //错误
//println("n1:" + n1)
}class God {
}}

2.9 字符类型(Char)

1)基本说明

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

2)案例实操

(1)字符常量是用单引号 ’ ’ 括起来的单个字符。

(2)\t :一个制表位,实现对齐的功能

(3)\n :换行符

(4)\\ :表示\

(5)" :表示"

package com.atguigu.chapter1object TestCharType2 {
def main(args: Array[String]): Unit = {
var c1: Char = 'a'
println("c1=" + c1)//注意:这里涉及自动类型提升,其实编译器可以自定判断是否超出范围
//不过idea提示报错
var c2: Char = 'a' + 1
println(c2)//    \t :一个制表位,实现对齐的功能
println("姓名\t年龄")//   \n :换行符
println("西门庆\n潘金莲")//(4)\\ :表示\
println("c:\\天黑了\\饿狼来了")//(5)\" :表示"
println("你过来:\"看我一拳打死你\"")
}
}

2.10 布尔类型:Boolean

1)基本说明

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

(2)boolean类型占1个字节。

2)案例实操

object TestBooleanType {
def main(args: Array[String]): Unit = {
var isResult : Boolean = false
var isResult2 : Boolean = true}}

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

1)基本说明

数据类型

描述

Unit

表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。

Null

null , Null 类型只有一个实例值null

Nothing

Nothing类型在Scala的类层级最低端;它是任何其他类型的子类型。 当一个函数,我们确定没有正常的返回值(如抛出异常),可以用Nothing来指定返回类型,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数或者变量(兼容性)

在Java中有时候有返回值,如int,但有些情况下会抛出异常。为了兼容多种可能性,就可以使用nothing类型。

2)案例实操

(1)Unit类型用来标识过程,也就是没有明确返回值的函数。

由此可见,Unit类似于Java里的void。Unit只有一个实例——( ),这个实例也没有实质意义

def m1():Unit={println("m1被执行")
}
val a:Unit=m1()
println("a="+a)// 输出:a=()

(2)Null类只有一个实例对象,Null类似于Java中的null引用。Null可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)

object TestDataType {
def main(args: Array[String]): Unit = {// 正确
var n1: Byte = 127
var n2: Byte = -128// 错误
//    var n3:Byte = 128
//    var n4:Byte = -129// Scala的整型,默认为Int型,声明Long型,然后加”i“ 或者 ”L“
var n5 = 10
println(n5)var n6 = 98989898989898989L
println(n6)var n7 = 2.43434309898f
var n8 = 2.4343662343223
println(n7)
println(n8)// null可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
var God = new God()
God = null//    var n1: Int = null //错误
//    println("n1:" + n1)
}class God {
}}

(3)Nothing,可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于Nothing是其他任意类型的子类,他还能跟要求返回值的方法兼容。

object TestSpecialType {
def test(): Nothing = {
throw new Exception
}test()
}
}

2.12 类型转换

2.12.1 数值类型自动转换

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

1)基本说明

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

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

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

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

2)案例实操

object TestValueTransfer {
def main(args: Array[String]): Unit = {//(1)自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数值类型,然后再进行计算。
var n = 1 + 2.2222
println(n) //Double//(2)把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
var n2: Double = 2.2222
//    var n3: Int = n2 //报错//(3)(byte,short)和char之间不会相互自动转换。
var n4: Byte = 44
//    var c1 : Char = n4 //报错
var n5: Int = n4//(4)byte,short,char他们三者可以计算,在计算时首先转换为int类型。
var n6: Byte = 66
var c2: Char = 22
//    var n : Short = n6 + c2  //报错,结果是Int类型
//    var n7 : Short = 10 + 20  //报错,byte,short,char他们三者可以计算,在计算时首先转换为int类型。}
}

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

2.12.2 强制类型转换

1)基本说明

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

2)案例实操

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

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

object TestForceTransfer {def main(args: Array[String]): Unit = {//(1)将数据由高精度转换为低精度,就需要使用到强制转换
var n1: Int = 2.5.toInt // 这个存在精度损失//(2)强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
var r1: Int = 10 * 3.5.toInt + 6 * 1.5.toInt // 10 *3 + 6*1 = 36
var r2: Int = (10 * 3.5 + 6 * 1.5).toInt // 44.0.toInt = 44println("r1=" + r1 + " r2=" + r2)
}
}

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

1)基本说明

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

2)案例实操

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

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

object TestStringTransfer {
def main(args: Array[String]): Unit = {
//(1)基本类型转String类型(语法:将基本类型的值+"" 即可)
var str1: String = true + "你kin"
println(str1)
var str2: String = 4.444 + "拜拜"
println(str2)
var str3: String = 4444 + ""
println(str3)//(2)String类型转基本数值类型(语法:调用相关API)
var s1: String = "11"
var n1: Byte = s1.toByte
var n2: Short = s1.toShort
var n3: Int = s1.toInt
var n4: Long = s1.toLong
println(s1)
println(n1)
println(n2)
println(n3)
println(n4)
}
}

(3)注意事项

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

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

三、运算符

Scala运算符的使用和Java运算符的使用基本相同,只有个别细节上不同。

3.1 算术运算符

1)基本语法

运算符

运算

范例

结果

+

正号

+3

3

-

负号

b=4; -b

-4

+

5+5

10

-

6-4

2

*

3*4

12

/

5/5

1

%

取模(取余)

7%5

2

+

字符串相加

“He”+”llo”

“Hello”

(1)对于除号“/”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。

(2)对一个数取模a%b,和Java的取模规则一样。

2)案例实操

object TestArithmetic {
def main(args: Array[String]): Unit = {//    1)对于除号“/”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。
var r1: Int = 10 / 3
println(r1) //3var r2: Double = 10 / 3
println(r2) //3.0var r3: Double = 10.0 / 3
println(r3) //3.3333333333333335
println(r3.formatted("%.2f")) //3.33  含义:保留小数点2位,使用四舍五入//(2)对一个数取模a%b,和Java的取模规则一样。
var r4 = 10 % 3
println(r4) //1}}

3.2 关系运算符(比较运算符)

1)基本语法

运算符

运算

范例

结果

==

相等于

4==3

false

!=

不等于

4!=3

true

<

小于

4<3

false

>

大于

4>3

true

<=

小于等于

4<=3

false

>=

大于等于

4>=3

true

2)案例实操

(1)需求1:

object TestRelation {
def main(args: Array[String]): Unit = {// 测试:>、>=、<=、<、==、!=
var a: Int = 2
var b: Int = 1println(a > b) // true
println(a >= b) // true
println(a < b) // false
println(a <= b) // false
println("a==b" + (a == b)) // false
println(a != b) // true
}
}

(2)需求2:Java和Scala中关于==的区别

Java:==比较两个变量本身的值,即两个对象在内存中的首地址;equals比较字符串中所包含的内容是否相同。

public class TestRelation2 {
public static void main(String[] args) {String s1 = "hhhh";String s2 = new String("hhhh");System.out.println(s1 == s2);   // falseSystem.out.println(s1.equals(s2));  // true
}
}

Scala:==更加类似于Java中的equals,参照jd工具

object TestRelationScala {
def main(args: Array[String]): Unit = {
val s1 = "aaa"
val s2 = new String("aaa")println(s1 == s2) //  true
println(s1.eq(s2))   // false
}
}

3.3 逻辑运算符

1)基本语法

用于连接多个条件(一般来讲就是关系表达式),最终的结果也是一个Boolean值。

假定:变量A为true,B为false

运算符

描述

实例

&&

逻辑与

(A && B) 运算结果为 false

||

逻辑或

(A || B) 运算结果为 true

!

逻辑非

!(A && B) 运算结果为 true

2)案例实操

object TestLogic {def main(args: Array[String]): Unit = {//  测试:&&、||、!
var a = true
var b = falseprintln("a&&b=" + (a && b)) //  a&&b=false
println("a || b =" + (a || b))  //  a || b =true
println("!(a&&b)" + (!(a && b)))  // !(a&&b)true
}
}

3.4 赋值运算符

1)基本语法

赋值运算符就是将某个运算后的值,赋给指定的变量。

运算符

描述

实例

=

简单的赋值运算符,将一个表达式的值赋给一个左值

C = A + B 将 A + B 表达式结果赋值给 C

+=

相加后再赋值

C += A 等于 C = C + A

-=

相减后再赋值

C -= A 等于 C = C - A

*=

相乘后再赋值

C *= A 等于 C = C * A

/=

相除后再赋值

C /= A 等于 C = C / A

%=

求余后再赋值

C %= A 等于 C = C % A

<<=

左移后赋值

C <<= 2等于 C = C << 2

>>=

右移后赋值

C >>= 2 等于 C = C >> 2

&=

按位与后赋值

C &= 2 等于 C = C & 2

^=

按位异或后赋值

C ^= 2 等于 C = C ^ 2

|=

按位或后赋值

C |= 2 等于 C = C | 2

注意:Scala中没有++、–操作符,可以通过+=、-=来实现同样的效果;

2)案例实操

object TestAssignment {
def main(args: Array[String]): Unit = {
var r1 = 11//  没有++
r1 += 1 //12
println(r1)
// 没有--
r1 -= 2 //10
println(r1)
}
}

3.5 位运算符

1)基本语法

下表中变量 a 为 60,b 为 13。

运算符

描述

实例

&

按位与运算符

(a & b) 输出结果 12 ,二进制解释: 0000 1100

|

按位或运算符

(a | b) 输出结果 61 ,二进制解释: 0011 1101

^

按位异或运算符

(a ^ b) 输出结果 49 ,二进制解释: 0011 0001

~

按位取反运算符

(~a ) 输出结果 -61 ,二进制解释: 1100 0011, 在一个有符号二进制数的补码形式。

<<

左移动运算符

a << 2 输出结果 240 ,二进制解释: 0011 0000

>>

右移动运算符

a >> 2 输出结果 15 ,二进制解释: 0000 1111

>>>

无符号右移

a >>>2 输出结果 15, 二进制解释: 0000 1111

2)案例实操

object TestPositon {
def main(args: Array[String]): Unit = {//  测试:1000 << 1 =>10000
var n1: Int = 8
n1 = n1 << 1
println(n1) // 16
}}

3.6 Scala运算符本质

在Scala中其实是没有运算符的,所有运算符都是方法。

1)当调用对象的方法时,点.可以省略

2)如果函数参数只有一个,或者没有参数,()可以省略

object TestOpt {def main(args: Array[String]): Unit = { 
// 标准的加法运算val i:Int = 1.+(1)// (1)当调用对象的方法时,.可以省略val j:Int = 1 + (1)// (2)如果函数参数只有一个,或者没有参数,()可以省略val k:Int = 1 + 1println(1.toString())println(1 toString())println(1 toString)}}

四、流程控制

4.1 分支控制if-else

让程序有选择的的执行,分支控制有三种:单分支、双分支、多分支

4.1.1 单分支

1)基本语法

if (条件表达式) {
执行代码块
}

说明:当条件表达式为ture时,就会执行{ }的代码。

2)案例实操

需求:输入人的年龄,如果该同志的年龄小于18岁,则输出“童年”

if (age < 18){println("童年")
}

4.1.2 双分支

1)基本语法

if (条件表达式) {
执行代码块1
} else {
执行代码块2
}

2)案例实操

需求:输入年龄,如果年龄小于18岁,则输出“童年”。否则,输出“成年”。

if (age < 18) {println("童年")
} else {println("成年")
}

4.1.3 多分支

1)基本语法

if (条件表达式1) {
执行代码块1
}
else if (条件表达式2) {
执行代码块2
}
……
else {
执行代码块n
}

2)案例实操

(1)需求1:需求:输入年龄,如果年龄小于18岁,则输出“童年”。如果年龄大于等于18且小于等于30,则输出“中年”,否则,输出“老年”。

if (age < 18) {println("童年")
} else if (age >= 18 && age < 30) {println("中年")
} else {println("老年")
}

(2)需求2:Scala中if else表达式其实是有返回值的,具体返回值取决于满足条件的代码体的最后一行内容。

val res :String = if (age < 18) {"童年"
} else if (age >= 18 && age < 30) {"中年"
} else {"老年"
}println(res)

(3)需求3:Scala中返回值类型不一致,取它们共同的祖先类型。

val res :Any = if (age < 18) {"童年"
} else if (age >= 18 && age < 30) {"中年"
} else {100
}println(res)

(4)需求4:Java中的三元运算符可以用if else实现

如果大括号{}内的逻辑代码只有一行,大括号可以省略。如果省略大括号,if只对最近的一行逻辑代码起作用。

// Java中三元运算符:a?b:c
// 如:int result = flag ? 1 : 0// Scala
val res:String = if(age>=18) "成年" else "未成年"println(res)

4.2 嵌套分支

在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构称为内层。分支外面的分支结构称为外层分支。嵌套分支不要超过3层。

1)基本语法

if(){
if(){
}else{
}
}

2)案例实操

需求:如果输入的年龄小于18,返回“童年”。如果输入的年龄大于等于18,需要再判断:如果年龄大于等于18且小于30,返回“中年”;如果其他,返回“老年”。

import scala.io.StdInobject TestIfElse {
def main(args: Array[String]): Unit = {// Scala
println("input age:")
var age = StdIn.readShort()
val res: Any = if (age < 18) {
"童年"
} else {
if (age >= 18 && age < 30) {"中年"
} else {"老年"
}
}println(res)
}
}

4.3 Switch分支结构

在Scala中没有Switch,而是使用模式匹配来处理。

模式匹配涉及到的知识点较为综合,因此我们放在后面讲解。

4.4 For循环控制

Scala也为for循环这一常见的控制结构提供了非常多的特性,这些for循环的特性被称为for推导式或for表达式。

4.4.1 范围数据循环(To)

1)基本语法

// java for循环语法
for(int i = 0; i < 10; i++){System.out.println("i=" + i);
}// scala for循环语法
for(i <- 1 to 10){println("i=" + i)
}
// 输出:从1~10(包含 1 和 10 )

(1)i 表示循环的变量,<- 规定to

(2)i 将会从 1-3 循环,前后闭合

4.4.2 范围数据循环(Until)

1)基本语法

// 方法一:
for(i <- 1 until 10) {print("i=" + i)
}
// 输出:从1~10(包含 1,不包含 10 )// 方法二:
for(i <- Range(1,10)) {print("i=" + i)
}

(1)这种方式和前面的区别在于i是从1到3-1

(2)即使前闭合后开的范围

4.4.3 循环守卫

1)基本语法

for(i <- 1 to 3 if i != 2) {print(i + " ")
}

说明:

(1)循环守卫,即循环保护式(也称条件判断式,守卫)。保护式为true则进入循环体内部,为false则跳过,类似于continue。

(2)上面的代码等价

for (i <- 1 to 3){if (i != 2) {print(i + " ")}
}

4.4.4 循环步长

1)基本语法

for (i <- 1 to 10 by 2) {println(“i=” + i)
}

说明:by表示步长

2)案例实操

需求:输出1到10以内的所有奇数

for (i <- 1 to 10 by 2) {
println(“i=” + i)
}输出结果
i=1
i=3
i=5
i=7
i=9

4.4.5 嵌套循环

1)基本语法

for(i <- 1 to 3; j <- 1 to 3) {println(" i =" + i + " j = " + j)
}

说明:没有关键字,所以范围后一定要加;来隔断逻辑

2)基本语法

上面的代码等价

for (i <- 1 to 3) {for (j <- 1 to 3) {println(“i =” + i + " j=" + j)}
}

4.4.6 引入变量

1)基本语法

for(i <- 1 to 3; j = 4 - i) {println("i=" + i + " j=" + j)
}

说明:

(1)for推导式一行中有多个表达式时,所以要加 ; 来隔断逻辑

(2)for推导式有一个不成文的约定:当for推导式仅包含单一表达式时使用圆括号,当包含多个表达式时,一般每行一个表达式,并用花括号代替圆括号,如下:

for {i <- 1 to 3j = 4 - i
} {println(“i=” + i + " j=" + j)
}

2)案例实操

上面的代码等价于

for (i <- 1 to 3) {var j = 4 - iprintln(“i=” + i + " j=" + j)
}

4.4.7 循环返回值

1)基本语法

val res = for(i <- 1 to 10) yield i
println(res)

说明:将遍历过程中处理的结果返回到一个新Vector集合中,使用yield关键字。

注意:开发中很少使用。

2)案例实操

需求:将原数据中所有值乘以2,并把数据返回到一个新的集合中。

var res = for(i <-1 to 10) yield {i * 2
}
println(res)

输出结果:

Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

4.4.8 倒序打印

1)说明:如果想倒序打印一组数据,可以用reverse。

2)案例实操:

需求:倒序打印10到1

for(i <- 1 to 10 reverse){println(i)}

4.5 While和do…While循环控制

While和do…While的使用和Java语言中用法相同。

4.5.1 While循环控制

1)基本语法

循环变量初始化while (循环条件) {循环体(语句)循环变量迭代}

说明:

(1)循环条件是返回一个布尔值的表达式

(2)while循环是先判断再执行语句

(3)与for语句不同,while语句没有返回值,即整个while语句的结果是Unit类型()

(4)因为while中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量,而变量需要声明在while循环的外部,那么就等同于循环的内部对外部的变量造成了影响,所以不推荐使用,而是推荐使用for循环。

object For01 {
def main(args: Array[String]): Unit = {//    1.范围循环 To
for (i <- 1 to 3)
println("生如")
println("to----------------------")//  2.范围循环 Unit
for (i <- 1 until 3)
println("夏花")
println("until----------------------")//  3.循环守卫
for (i <- 1 to 6) {
if (i != 2) {println(i)
}
}
println("!=----------------------")
//    4.循环步长 by
for (i <- 1 to 6 by 2)
println(i)println("by----------------------")
for (i <- -10 to -1 by 2)
println(i)
println("by----------------------")//    5.循环嵌套
for (i <- 1 to 4; j <- 1 to 6) {
print("i=" + i + ",j=" + j + "\t")
if (j == 6)println()
}
println("嵌套----------------------")//  6.引入变量
for (i <- 1 to 10; j = 10 - i) {
println("i=" + i + ",j=" + j)
}
println("引入变量----------------------")for {
i <- 1 to 3
j = 3 - i
} {
println("i=" + i + ",j=" + j)
}
println("引入变量----------------------")//  7.循环返回值
val res = for (i <- 1 to 10) yield i
println(res)
println("----------------------")//需求:将原数据中所有值乘以2,并把数据返回到一个新的集合中。
var res1 = for (i <- 1 to 10) yield {
i * 2
}
println(res1)
println("循环返回值----------------------")//  8.倒序打印
for (i <- 1 to 10 reverse) {
println(i)
}
println("倒序----------------------")
}}

4.5.2 do…while循环控制

1)基本语法

 循环变量初始化;do{循环体(语句)循环变量迭代} while(循环条件)

说明

(1)循环条件是返回一个布尔值的表达式

(2)do…while循环是先执行,再判断

4.6 循环中断

1)基本说明

Scala内置控制结构特地去掉了break和continue,是为了更好的适应函数式编程,推荐使用函数式的风格解决break和continue的功能,而不是一个关键字。Scala中使用breakable控制结构来实现break和continue功能。

2)案例实操

需求1:采用异常的方式退出循环

//  1.正常结束循环
try {for (elem <- 1 to 10) {println(elem)if (elem == 5) throw new RuntimeException}
} catch {case e =>
}
println("正常结束循环")
}

需求2:采用Scala自带的函数,退出循环

//  2.采用scala自带的函数,退出循环
Breaks.breakable(for (e <- 1 to 5) {println(e)if (e == 5) Breaks.break()}
)
println("正常结束循环")

需求3:对break进行省略

import scala.util.control.Breaks._breakable {for (elem <- 1 to 10) {println(elem)if (elem == 5) break}
}
println("正常结束循环")

需求4:循环遍历10以内的所有数据,奇数打印,偶数跳过(continue)

//循环遍历10以内的所有数据,奇数打印,偶数跳过(continue)for (elem <- 1 to 10) {if (elem % 2 == 1){println(elem)}else{println("continue")}
}

4.7 多重循环

1)基本说明

(1)将一个循环放在另一个循环体内,就形成了嵌套循环。其中,for,while,do…while均可以作为外层循环和内层循环。【建议一般使用两层,最多不要超过3层】

(2)设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次。

2)案例实操

需求:打印出九九乘法表

for (i <- 1 to 10) {for (j <- 1 to i) {print(j + "*" + i + "=" + (i * j) + "\t")}
println()
}

五、函数式编程

1)面向对象编程

解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。

对象:用户

行为:登录、连接JDBC、读取数据库

属性:用户名、密码

Scala语言是一个完全面向对象编程语言。万物皆对象

对象的本质:对数据和行为的一个封装

2)函数式编程

解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。

例如:请求->用户名、密码->连接JDBC->读取数据库

Scala语言是一个完全函数式编程语言。万物皆函数。

函数的本质:函数可以当做一个值进行传递

3)在Scala中函数式编程和面向对象编程完美融合在一起了。

5.1 函数基础

5.1.1 函数基本语法

1)基本语法

def sum ( x : Int , y : Int ) : Int = {x + y
}

2)案例实操

需求:定义一个函数,实现将传入的名称打印出来。

object TestFunction {def main(args: Array[String]): Unit = {
​ // (1)函数定义def f(arg: String): Unit = {
​            println(arg)}
​ // (2)函数调用
​ // 函数名(参数)f(“hello world”)}
}

5.1.2 函数和方法的区别

1)核心概念

(1)为完成某一功能的程序语句的集合,称为函数。

(2)类中的函数称之方法。

2)案例实操

(1)Scala语言可以在任何的语法结构中声明任何的语法

(2)函数没有重载和重写的概念;方法可以进行重载和重写

(3)Scala中函数可以嵌套定义

5.1.3 函数定义

1)函数定义

(1)函数1:无参,无返回值

(2)函数2:无参,有返回值

(3)函数3:有参,无返回值

(4)函数4:有参,有返回值

(5)函数5:多参,无返回值

(6)函数6:多参,有返回值

2)案例实操

object TestFunctionDeclare {def main(args: Array[String]): Unit = {
//1.无参,无返回值def t1():Unit={println("1.无参,无返回值")}t1()//2.无参,有返回值def t2():String ={return "2.无参,有返回值"}println(t2())//3.有参,无返回值def t3(s:String):Unit={println(s)}t3("t3")//4.有参,有返回值def t4(s:String):String={return s+"4.有参,有返回值"}println(t4("hello"))//5.多参,无返回值def t5(name:String,age:Int):Unit={println(s"$name,$age")}t5("monika",22)}
}

5.1.4 函数参数

1)案例实操

(1)可变参数

(2)如果参数列表中存在多个参数,那么可变参数一般放置在最后

(3)参数默认值,一般将有默认值的参数放置在参数列表的后面

(4)带名参数

def main(args: Array[String]): Unit = {
//1.可变参数def test(s: String*): Unit = {println(s)}//有输入参数:输出Arraytest("Hello", "Scala")//无输入参数:输出List()test()//2.如果参数列表中存在多个参数,那么可变参数一般放置在最后def test2(name: String, s: String): Unit = {println(name + "," + s)}test2("jinlian", "dalang")//3.参数默认值def test3(name: String, age: Int = 30): Unit = {println(s"$name,$age")}//如果参数传递了值,那么会覆盖默认值test3("jinlian", 20)//如果参数有默认值,在调用的时候,可以省略这个参数test3("dalang")//一般情况下,将有默认值的参数放置在参数列表的后面def test4(sex: String = "男", name: String): Unit = {println(s"name,sex")}//Scala函数中参数传递是,从左到右//test4("wusong") //报错test4("女", "jinlian")//4.带名参数test4(name = "ximenqing")}

5.1.5 函数至简原则(重点)

函数至简原则:能省则省

1)至简原则细节

(1)return可以省略,Scala会使用函数体的最后一行代码作为返回值

(2)如果函数体只有一行代码,可以省略花括号

(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)

(4)如果有return,则不能省略返回值类型,必须指定

(5)如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用

(6)Scala如果期望是无返回值类型,可以省略等号

(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加

(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略

(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略

2)案例实操

def main(args: Array[String]): Unit = {def f(s: String): String = {return s + "f"}//1.return可以省略,scala会使用函数体的最后一行代码作为返回值def f1(s: String): String = {s + "f1"}//2.如果函数体只有一行代码,可以省略花括号def f2(s: String): String = s + "f2"//3.返回值类型如果能够推断出来,那么可以省略def f3(s: String) = s + "f3"//4.如果有return,则不能省略返回值类型,必须指定def f4(): String = {return "f4"}//5.如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用def f5(): Unit = {return "f5"}//6.如果期望是无返回值类型,那么可以省略等号def f6() {"f6"}//7.如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加def f7() = "f7"println(f7())println(f7)//8.如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略def f8 = "f8"println(f8)//9.如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略def f9 = (str: String) = {println("f9")}// 匿名函数,lambda表达式(name: String) => { println(name) }def f10(f: String => Unit) = {f("")}f10(f9)println(f10((str: String) => {println("f10")}))println("-------------------")def f11(fun: (String) => Unit): Unit = {println("调用别的函数,做为参数传入")fun("f11")}f11(f9)f11((x: String) => println(x))f11((x) => {println(x)})f11(x => {println(x)})f11(x => println(x))f11(println(_))f11(println)
}

5.2 函数高级

5.2.1 高阶函数

在Scala中,函数是一等公民。怎么体现的呢?

对于一个函数我们可以:定义函数、调用函数

object TestFunction {def main(args: Array[String]): Unit = {// 调用函数foo()}// 定义函数def foo():Unit = {println("foo...")}
}

但是其实函数还有更高阶的用法

1)函数可以作为值进行传递

def f(n: Int):Int = {println("f调用")n + 1
}
val result: Int = f(123)
println(result)    // 输出124// 1)函数可以作为值进行传递
val f1 = f _
val f2: Int =>Int = f
println(f1)          // 输出引用地址
println( f1(12) )    // 输出13

2)函数可以作为参数进行传递

// 2)函数可以作为参数进行传递
def func(op: (Int,Int) => Int,a: Int,b: Int): Int = {op(a,b)
}def add(a:Int,b:Int):Int={a+b
}println(func(add,1,2))
println(func( (a,b) => a+b, 1 , 2 ) )
println(_+_ , 1,2)

3)函数可以作为返回值进行传递

def f5(): Int=>Unit = {def f6(a: Int): Unit = {println("f6"调用 + a)}f6    //将函数直接返回
}

5.2.2 匿名函数

1)说明

没有名字的函数就是匿名函数。

( x : Int ) => { 函数体 }

x:表示输入参数类型;Int:表示输入参数类型;函数体:表示具体代码逻辑

2)案例实操

​ 需求1:传递的函数有一个参数

传递匿名函数至简原则:

(1)参数的类型可以省略,会根据形参进行自动的推导

(2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号。

(3)匿名函数如果只有一行,则大括号也可以省略

(4)如果参数只出现一次,则参数省略且后面参数可以用_代替

// 函数原型f( (name: String) => { println(name) } ) // (1)参数的类型可以省略,会根据形参进行自动的推导f( (name) => { println(name) })// (2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号。f( name => { println(name) })// (3)匿名函数如果只有一行,则大括号也可以省略f( (name) => println(name) )// (4)如果参数只出现一次,则参数省略且后面参数可以用_代替f( println(_) )
// (5)如果可以推导出,当前传入的println是一个函数体,而不是条用语句,可以直接省略下划线f( println )

实际案例:

定义 一个“二元运算”函数,只操作1和2两个数,但是具体运算通过参数传入

def dualFunc( fun: (Int, Int) => Int):Int = {fun(1,2)
}
val add = (a: Int, b: Int) => a + b
val minus = (a: Int, b: Int) => a - b// 函数调用
println(dualFunc(add));
println(dualFunc(minus));// 匿名函数简化
println(dualFunc( (a: Int, b: Int) => a + b ) );
println(dualFunc( (a: Int, b: Int) => a - b ) );
// 继续简化
println(dualFunc( (a, b) => _ + _ ) );
println(dualFunc(  _ + _  );

六、面向对象

Scala的面向对象思想和Java的面向对象思想和概念是一致的。

Scala中语法和Java不同,补充了更多的功能。

6.1 Scala包

1)基本语法

package 包名

2)Scala包的三大作用(和Java一样)

(1)区分相同名字的类

(2)当类很多时,可以很好的管理类

(3)控制访问范围

6.1.1 包的命名

1)命名规则

只能包含数字、字母、下划线、小圆点.,但不能用数字开头,也不要使用关键字。

2)案例实操

demo.class.exec1 //错误,因为 class 关键字
demo.12a //错误,数字开头

3)命名规范

一般是小写字母+小圆点

com.公司名.项目名.业务模块名

6.1.2 包说明(包语句)

1)说明

Scala有两种包的管理风格,一种方式和Java的包管理风格相同,每个源文件一个包(包名和源文件所在路径不要求必须一致),包名用“.”进行分隔以表示包的层级关系,如com.atguigu.scala。另一种风格,通过嵌套的风格表示层级关系,如下:

package com{package baidu{package scala{}}
}

第二种风格有以下特点:

(1)一个源文件中可以声明多个package

(2)子包中的类可以直接访问父包中的内容,而无需导包

6.1.3 包对象

在Scala中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有class和object的共享变量,可以被直接访问。

1)定义

package object com{val shareValue="share"def shareMethod()={}
}

2) 说明

若使用Java的包管理风格,则包对象一般定义在其对应包下的package.scala文件中,包对象名与包名保持一致。

6.1.4 导包说明

1)和Java一样,可以在顶部使用import导入,在这个文件中的所有类都可以使用。

2)局部导入:什么时候使用,什么时候导入。在其作用范围内都可以使用

3)通配符导入:import java.util._

4)给类起名:import java.util.{ArrayList=>JL}

5)屏蔽类:import java.util.{ArrayList =>,}

6)导入相同包的多个类:import java.util.{HashSet, ArrayList}

7)导入包的绝对路径:new root.java.util.HashMap

6.2 类和对象

类:可以看成一个模板

对象:表示具体的事物

6.2.1 定义类

0)回顾:Java中的类

如果类是public的,则必须和文件名一致。

一般,一个.java有一个public类

注意:Scala中没有public,一个.scala中可以写多个类。

1)基本语法

[修饰符] class类名 {类体
}

说明

(1)Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)

(2)一个Scala源文件可以包含多个类

2)案例实操

//    (1)Scala语法中,类并不声明public,所有这些类都具有共有可见性(即默认就是public)
class Person {// 定义属性private var name: String = "alice"var age: Int = _var sex: String = _
}//    (2) 一个Scala源文件可以包含多个类
class Teacher {}def main(args: Array[String]): Unit = {// 创建一个对象val stu = new Student;stu.name        // error,不能访问stu.agestu.sex
}

6.3 封装

封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。Java封装操作如下,

(1)将属性进行私有化

(2)提供一个公共的set方法,用于对属性赋值

(3)提供一个公共的get方法,用于获取属性的值

Scala中的public属性,底层实际为private,并通过get方法(obj.field())和set方法(obj.field_=(value))对其进行操作。所以Scala并不推荐将属性设为private,再为其设置public的get和set方法的做法。但由于很多Java框架都利用反射调用getXXX和setXXX方法,有时候为了和这些框架兼容,也会为Scala的属性设置getXXX和setXXX方法(通过@BeanProperty注解实现)。

6.3.1 访问权限

1)说明

在Java中,访问权限分为:public,private,protected和默认。在Scala中,你可以通过类似的修饰符达到同样的效果。但是使用上有区别。

(1)Scala 中属性和方法的默认访问权限为public,但Scala中无public关键字。

(2)private为私有权限,只在类的内部和伴生对象中可用。

(3)protected为受保护权限,Scala中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问。

(4)private[包名]增加包访问权限,包名下的其他类也可以使用

6.3.3 创建对象

1)基本语法

val | var 对象名 [:类型] = new类型()

2)案例实操

(1)val修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。

(2)var修饰对象,可以修改对象的引用和修改对象的属性值

(3)自动推导变量类型不能多态,所以多态需要显示声明

class Person05 {var name: String = "taotao"
}object Person05 {def main(args: Array[String]): Unit = {
//    val 修饰对象,不能改变对象的引用(即内存地址),可以改变对象属性的值。val person = new Person05()person.name = "bingbing"}
}

6.3.4 构造器

和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。

Scala类的构造器包括:主构造器和辅助构造器

1)基本语法

class 类名(形参列表){ //主构造器
// 类体
def this(形参列表){ // 辅助构造器

}
def this(形参列表){ // 辅助构造器可以有多个。。。

}
}

说明:

(1)辅助构造器,函数的名称this,可以有多个,编译器通过参数的个数及类型来区分。

(2)辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。

(3)构造器调用其他另外的构造器,要求被调用构造器必须提前声明

2)案例实操

// 定义一个类
class Student{// 定义属性var name: String = _var age: Int = _println("1. 主构造方法被调用")// 声明辅助构造方法def this(name: String){this()    // 直接调用主构造器println("2. 辅助构造方法一被调用")this.name = name}def this(name: String, age:Int){this(name)println("3. 辅助构造方法二被调用")this.age = age}
}

6.3.5 构造器参数

1)说明

Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰

(1)未用任何修饰符修饰,这个参数就是一个局部变量

(2)var修饰参数,作为类的成员属性使用,可以修改

(3)val修饰参数,作为类只读属性使用,不能修改

2)案例实操

// 定义类
// 无参构造器
class Student{// 单独定义属性var name : String = _var age : Int = _
}// 上面等价于:
class Student(var name: String, var age: Int)val stu = new Student("bob",20)

6.4 继承

1)基本语法

class 子类名 extends 父类名 { 类体 }

(1)子类继承父类的属性和方法

(2)scala是单继承

2)案例实操

(1)子类继承父类的属性和方法

(2)继承的调用顺序:父类构造器->子类构造器

6.5 抽象属性和抽象方法

6.5.1 抽象属性和抽象方法

1)基本语法

​ (1)定义抽象类:abstract class Person{} //通过abstract关键字标记抽象类

​ (2)定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性

​ (3)定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法

abstract class Person09 {val name: String  //抽象属性def hello(): Unit   //抽象方法
}class Teacher extends Person09{val name:String = "teacher"def hello():Unit = {println("hello teacher")}
}

2)继承&重写

(1)如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类

(2)重写非抽象方法需要用override修饰,重写抽象方法则可以不加override。

(3)子类中调用父类的方法使用super关键字

(4)子类对抽象属性进行实现,父类抽象属性可以用var修饰;

子类对非抽象属性重写,父类非抽象属性只支持val类型,而不支持var。

因为var修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写

(5)Scala中属性和方法都是动态绑定,而Java中只有方法为动态绑定。

6.5.2 匿名子类

1)说明

Java一样,可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类。

2)案例实操

// 定义抽象类
abstract class Persion{var name: Stringdef eat(): Unit
}def main(args:Array[String]): Unit = {val person:Person = new Persion{override var name: String = "candy"override def eat(): Unit = println("eat...")}println(person.name)person.eat()
}

6.6 单例对象(伴生对象)

Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。

6.6.1 单例对象语法

1)基本语法

object Person{val country: String = "China"
}

2)说明

(1)单例对象采用object关键字声明

(2)单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。

(3)单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。

6.7 特质(Trait)

Scala语言中,采用特质trait(特征)来代替接口的概念,也就是说,多个类具有相同的特质(特征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明。

Scala中的trait中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质。这种感觉类似于Java中的抽象类。

Scala引入trait特征,第一可以替代Java的接口,第二个也是对单继承机制的一种补充。

6.7.1 特质声明

1)基本语法

trait 特质名 {trait主体
}

6.7.2 特质基本语法

一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with关键字连接。

1)基本语法

没有父类:class 类名 extends 特质1 with 特质2 with 特质3 ...
有父类:class 类名 extends 父类 with 特质2 with 特质3 ...

2)说明

(1)类和特质的关系:使用继承的关系。

(2)当一个类去继承特质时,第一个连接词是extends,后面是with。

(3)如果一个类在同时继承特质和父类时,应当把父类写在extends后。

package org.exampleobject Person {def main(agrs: Array[String]): Unit = {val student:Student = new Studentstudent.age = 20student.say()student.studystudent.datingstudent.playprintln(student.age)}
}class Person {val name: String = "person"var age: Int = 18def say(): Unit = {println("hello from " + name )}
}// 定义一个特质
trait Young {// 声明抽象和非抽象属性var age: Intval name: String = "young"// 声明抽象和非抽象方法def play(): Unit = {println("young people is playing")}def dating(): Unit
}class Student extends Person with Young{// 重写冲突的属性override val name: String = "student"// 实现抽象方法def dating(): Unit = println(s"student $name is dating")def study(): Unit = println(s"student $name is studing")// 重写父类方法override def say(): Unit = {super.say()println(s"hello from: student $name")}
}

6.7.3 特质叠加

由于一个类可以混入(mixin)多个trait,且trait中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。冲突分为以下两种:

第一种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法。

第二种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait继承自相同的trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala采用了特质叠加(从右到左)的策略。

所谓的特质叠加,就是将混入的多个trait中的冲突方法叠加起来,案例如下,

object Test14_Trait {def main(args: Array[String]): Unit = {val myBall = new MyBall()println(myBall.describe()) //yellow-foot-ball}
}//定义一个父特征
trait Ball {def describe(): String = "ball"
}//定义子特征
trait Color extends Ball {var color: String = "yellow"override def describe(): String = color + "-" + super.describe()
}trait Category extends Ball {var category: String = "foot"override def describe(): String = category + "-" + super.describe()
}//定义子类,继承两种特征
class MyBall extends Category with Color {override def describe(): String = "my ball is a" + super.describe()
}

结果如下:

6.7.4 特质叠加执行顺序(从右到左按顺序叠加)

当一个类混入多个特质的时候,scala会对所有的特质及其父特质按照一定的顺序进行排序,而此案例中的super.describe()调用的实际上是排好序后的下一个特质中的describe()方法。,排序规则如下:

6.7.5特质和抽象类的区别

1)优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。

2)如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行(有无参构造)。

相关内容

热门资讯

JAVASE(3.18) 目录 ​编辑 1.抽象类和抽象方法 2.接口 3.比较自定义类型 学习不要眼高手低,...
教师节优美祝福语短信 教师节优美祝福语短信55条  因为有了您,世界才会如此美丽,因为有了您,我的生命才会如此多彩!医生治...
去除Spire.Doc导出字样... //去除Spire.Doc导出字样信息try (FileInputStream in = n...
给老师的春节贺卡祝福语 给老师的春节贺卡祝福语170句  在我们平凡的日常里,要用到祝福语的情况还是蛮多的,祝福语可以起到增...
父亲节暖心祝福语 父亲节暖心祝福语  在日复一日的学习、工作或生活中,大家都用到过祝福语吧,祝福语有助于促进交流,拉近...
温馨教师节祝福语 2020年温馨教师节祝福语集锦45条  您辛劳了,教师节到了,您也该歇一歇了,坐着接接电话看看短信吧...
《RabbitMQ高阶知识》—... 《RabbitMQ高阶知识》— 消息可靠性 文章目录《RabbitMQ高阶知识》— 消息可靠性&#x...
Kubernetes(5):P... 我们一般将pod对象从创建至终的这段时间范围称为pod的生命周期,它主要包含下面的过程: pod创建...
学校领导新年元旦祝福语 学校领导新年元旦祝福语校师生员工们:  新年的钟声Ji荡着神州大地,岁月的航船开启着新的征程,我们即...
hugginface相关数据集... swaption2009/20k-en-zh-translation-pinyin-hsk 翻译 S...
孙子满月酒贺词 孙子满月酒贺词  宝宝降生,我前来贺喜,愿新生的小宝贝给你们带来数不尽的`快乐,祝小宝贝身体健康,茁...
温馨端午节祝福语句 常用温馨端午节祝福语句70句  端午快乐,幸福甜蜜!下文是小编特意为各位读者准备的温馨端午节祝福语句...
SQL常用语法语句 文章目录1. 什么是sql语句?1.1 DDL(数据定义语言)用法2. 什么是数据库对...
暖心重阳节祝福语短信 2020年暖心重阳节祝福语短信大汇总77句  九九重阳节日到,我的短信首先到,登高望远先敬老,赏菊插...
经典七夕祝福语 经典七夕祝福语99句  枯草连天太苍茫,小路白杨一行行。牵手偶因手太凉,曾经一起看夕阳。夕阳挂在柳梢...
感恩老师贺卡祝福语 感恩老师贺卡祝福语(精选270句)  无论是身处学校还是步入社会,大家都写过祝福语,肯定对各类祝福语...
妇女节祝福语QQ 2020年妇女节祝福语QQ大汇总25句  送你杯茶,以芬芳的祝福为叶,温柔的叮嘱做花,用沸腾的热情为...
DINO-DETR 实验与分析 前言 自DETR提出之后,不计其数的DETR改进模型不断被提出,尽管如此...
[图神经网络]图卷积神经网络-... 一、消息传递         由于图具有“变换不变性”(即图的空间结构改变不会影响图的性状)...
开业花篮贺词 开业花篮贺词  在日常学习、工作抑或是生活中,大家对贺词都不陌生吧,贺词要求感情真挚,切合身份,用语...