1. 变量和数据类型
1.1 变量和常量
-
语法
- 定义变量:
var 变量名:数据类型 = 值
【相当于Java 没有 用final关键字修饰】 - 定义常量:
val 常量名:数据类型 = 值
【相当于Java 用 final关键字修饰】
- 定义变量:
-
注意点
// 1. 常量值无法修改 val NAME:String = "张三" NAME = "李四” // 报错 // 2. Scala是强引用,因此不可为变量赋不同数据类型的值 var age:int = 18 age = "张三" // 不可将字符串赋值给整型变量 // 3. 声明 常量、变量时,数据类型可以省略,但是初始值不可省略。这样可以自动推断数据类型 // 自动推断 // 值为整型:int // 值为小数:double // 值为字符串:String val NAME = "张三" // 会自动推断变量类型 // 4. 关于对象: // (1) var修饰的对象可以修改指向,而val修饰的对象不可以修改修改指向 // (2) 对象的属性是否可以修改,取决于属性是用var还是val修饰 val PERSON = Person() PERSON = Person() // 错误
- 建议:能用常量,就不要用变量
- 标识符的命名规范
1.2 字符串
-
字符串拼接
-
通过
+
号连接println("hello" + "world")
-
重复字符串拼接
println("linhailinhai" * 200)
-
-
字符串输出
- 通过
%
传值:和c语言一样var name = "张三" var age = 18 printf("name: %s age: %d\n", name, age) // print()不换行,println()换行
s
打头,然后通过$
获取变量值var name = "张三" var age = 18 printf(s"name: ${name} age: ${age}\n")
- 通过
-
三引号字符串:方便写SQL
// s打头,每个行用 | ,最后.stripMargin val sql = s""" | select * | from table | where name = ${name} | and age > ${num} """ // stripMargin 的作用就是忽略空格、|等符号,也就是说只剩下SQL语句
1.3 数据类型
Scala中没有基本数据类型,只有引用数据类型:分为基本引用数据类型 和 非基本引用数据类型两类
- 包含Java的9种基本引用数据类型 (Byte, Short, Char, Integer❌ Int✅, Long, Float, Double, Boolean, String)
- Unit 相当于 Java中的 void
- 常见父类 / 子类:
Any
类,是所有类的父类,等价于Java的ObjectAnyVal
类是 9种基本引用数据类型 + Unit 的父类。AnyRef
类是所有非基本引用数据类型的父类。Nothing
是所有引用数据类型的子类。应用场景:一个函数没有明确返回值时指定
细节:
- 引用数据类型的默认值:和Java一样
- 9种基本引用数据类型:0、false
- 非基本引用数据类型:null
- 给变量赋值时,可以不指定数据类型,会自动分配
- 字符串默认是
String
。比如:val name = "zhangsan"
- 整数默认是
Int
。比如:val age = 15
- 小数默认是
Double
。比如:val money = 150.0
- 字符默认是
Char
。比如:val c = 'x'
- 布尔默认是
Boolean
。比如:val is_find = false
- Nothing vs Unit vs Null
- Unit 用于方法无返回值的情况
- Nothing 用于非方法的代码块无返回值的情况
- Null 是非基本引用对象没有赋值的默认值
- 集合类型的泛型很容易和Java中的数组弄混:
- Java中的数组:
int[] a = {0, 1, 2};
- Scala中的泛型:
Array[Int] a = Array(0, 1, 2) // 使用伴生对象创建数组
// 访问`Array`: val arr = new Array(10, 20, 30) println(arr(0)) // 输出第一个元素10
1.4 代码块
在Scala中:
- 所有代码都是代码块
- 最后一行代码的结果就是返回值
object Test {
def main(args: Array[String]): Unit = {
// 所有的代码都是代码块
// 表示运行一段代码 同时将最后一行的结果作为返回值
val i: Int = {
println("我是代码块")
10 + 10
}
// 代码块为1行的时候 大括号可以省略
val i1: Int = 10 + 10
// 如果代码块没有计算结果 返回类型是unit
val unit: Unit = {
println("hello")
println("我是代码块")
}
// 当代码块没办法完成计算的时候 返回值类型为nothing
// val value: Nothing = {
// println("hello")
// throw new RuntimeException
// }
}
}
1.5 强制转换
调用方法进行强制转换:
// Java :
int num = (int)2.5
// Scala :
var num : Int = 2.7.toInt
1.6 运算符
和Java一样:
1.7 == 与 equals
- Scala中==与equals相同,都是比较内容是否相等
- 如果要比较地址是否相同,用
.eq()
方法def main(args: Array[String]): Unit = { val s1 = "abc" val s2 = new String("abc") println(s1 == s2) println(s1.eq(s2)) } 输出结果: true false
2. 控制语句
2.1 分支语句
在Scala中,有if、if-else、if-else if-else,三元运算符,没有switch。用法和Java一样。
2.2 循环语句
(1)for循环
-
基本功能
// 1. 左闭右闭 [] for(i <- 1 to 10) { } // 2. 左闭右开 [) for(i <- 1 until 10) { } // 3. 循环步长 for(i <- 1 to 10 by 2) { // 步长为2,故结果为:1 3 5 7 9 } // 4. for-each for(i <- 集合对象){ }
-
循环守卫
for(i <- 1 to 10 if i != 5) { // 排除5 }
-
一个for循环内定义多个变量
for(i <- 1 to 4; j <- 1 to 5) { }
(2)while/do-while循环
可以,前者是现在判断后执行;后者是先执行后帕顿。但是不推荐使用,推荐后面使用函数式编程的递归方法
(3)循环中断
Scala内置控制结构特地去掉了break和continue,是为了更好的适应 函数式编程,推荐使用函数式的风格解决break和continue的功能,而不是一个关键字。
// 使用Breaks.breakable()方法来实现中断
// 使用Breaks.break()方法代替java中的break关键字
Breaks.breakable(
for(i <- 1 to 10) {
if(i == 3)
Breaks.break() -- 写就是中断,不写就是continue
}
)
(4)yield关键字结合for循环使用
yield 的作用是把每次迭代生成的值封装到一个集合中,如果把yield去掉那返回给C的值就为Unit。
2.3 switch
switch
用模式匹配 match
代替:
object TestMatchCase {
def main(args: Array[String]): Unit = {
var a: Int = 10
var b: Int = 20
var operator: Char = 'd'
var result = operator match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case _ => "illegal"
}
println(result)
}
}
注意:在调用算子时,可以在里面直接写模式匹配。比如:
(1) 匹配类型
object Test02_MatchValue {
def main(args: Array[String]): Unit = {
// 匹配类型
def func2(x:Any):String ={
x match {
case i:Int => "整数"
case c:Char => "字符"
case s:String => "字符串"
case _ => "其他"
}
}
println(func2(1515))
println(func2('\t'))
println(func2("1515"))
}
}
(2) 匹配元组
object TestMatchTuple {
def main(args: Array[String]): Unit = {
//对一个元组集合进行遍历
for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) {
val result = tuple match {
case (0, _) => "0 ..." //是第一个元素是0的元组
case (y, 0) => "" + y + "0" // 匹配后一个元素是0的对偶元组
case (a, b) => "" + a + " " + b
case _ => "something else" //默认
}
println(result)
}
}
}
(3) 匹配对象及样例类
object Test05_MatchObject {
def main(args: Array[String]): Unit = {
val zhangsan = new Person05("zhangsan", 18)
zhangsan match {
case Person05("zhangsan",18) => println("找到张三啦")
case _ => println("你不是zhangsan")
}
}
}
class Person05 (val name:String,var age:Int){
}
object Person05{
// 创建对象的方法
def apply(name: String, age: Int): Person05 = new Person05(name, age)
// 解析对象的方法
def unapply(arg: Person05): Option[(String, Int)] = {
// 如果解析的参数为null
if (arg == null ) None else Some((arg.name,arg.age))
}
}
3. 方法与函数
3.1 方法
在Scala中万物皆方法。
- 所有的运算符本质上是方法。比如查看
Int
类
可以看到类中定义了很多以运算符命名的方法
因此,以下两种方法是等价的val a = 1 + 2 val a = 1.+(2)
- 反过来,可以定义这样的方法,并调用
-
例子1
object Cal { def say(x:Int):Int = x } object Main { def main(args: Array[String]): Unit = { // 1. 用 类.方法名(参数列表) 形式调用方法 var result1 = Cal.say(3) // 2. 用 类 方法 参数列表 形式调用方法 var result2 = Cal say (3) var result3 = Cal say 3 // 如果参数列表只有一个值,括号是可以省略的 } }
-
例子2
object Main { def say(x:Int):Int = x def main(args: Array[String]): Unit = { // 1. 用 类.方法名(参数列表) 形式调用方法 var result1 = this.say(3) // 2. 用 类 方法 参数列表 形式调用方法 var result2 = this say (3) // 如果参数列表只有一个值,括号是可以省略的 var result3 = this say 3 } }
-
3.2 函数
3. 函数式编程
- 函数式编程思想:① y = f(x),重要的是映射关系。② 当做数学题,故推荐使用val,因为不变值在分布式上计算后不会产生歧义
- 定义函数有两种形式:
def
形式【一般定义方法时,使用这种形态】lambda表达式
形式 【一般定义在方法内定义函数时,使用这种形态】
3.1 函数的第一种形态
(1) 定义函数
注意:一般定义方法时,使用这种形态。(方法是特殊的函数)。
(2) 函数声明
object TestFunctionDeclare {
// 函数1:无参,无返回值
def test(): Unit = {
println("无参,无返回值")
}
// 函数2:无参,有返回值
def test2(): String = {
return "无参,有返回值"
}
// 函数3:有参,无返回值
def test3(s: String): Unit = {
println(s)
}
// 函数4:有参,有返回值
def test4(s: String): String = {
return s + "有参,有返回值"
}
// 函数5:多参,无返回值
def test5(name: String, age: Int): Unit = {
println(s"$name, $age")
}
}
(3) 函数参数
object Test03_FunArgs {
def main(args: Array[String]): Unit = {
// (1)可变参数:在类型后面加*号
def sayHi(names:String*):Unit = {
println(s"hi $names")
}
sayHi()
sayHi("linhai")
sayHi("linhai","jinlian")
// (2)可变参数必须在参数列表的最后
def sayHi1(sex: String,names:String*):Unit = {
println(s"hi $names")
}
// (3)参数默认值
def sayHi2(name:String = "linhai"):Unit = {
println(s"hi ${name}")
}
sayHi2("linhai")
sayHi2()
// (4)默认值参数在使用的时候 可以不在最后
def sayHi3( name:String = "linhai" , age:Int):Unit = {
println(s"hi ${name}")
}
// (5)带名参数:指调用方法时,指定传参顺序
sayHi3(age = 10, name = "niu")
}
}
(4) 至简原则
注意:方法最后一行的return可以省略,但是除此之外都不能省略。比如
object Test04_FuncSimply {
def main(args: Array[String]): Unit = {
//(1)return可以省略,Scala会使用方法体的最后一行代码作为返回值
def func1(x: Int, y: Int): Int = {
x + y
}
// (2)如果方法体只有一行代码,可以省略花括号
def func2(x: Int, y: Int): Int = x + y
//(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
// 此时,函数就变为了数学表达式的形式:f(x, y) = x + y
def func3(x: Int, y: Int) = x + y
//(4)如果有return,则不能省略返回值类型,必须指定
def func4(x: Int, y: Int): Int = {
if (x < 20) {
return x + y
}
2 * x + 3 * y
}
//(5)如果方法明确声明unit,那么即使函数体中使用return关键字也不起作用
def func5(x: Int, y: Int): Unit = return x + y
//(6)Scala如果期望是无返回值类型,可以省略等号
def func6(x: Int, y: Int) {
println(x + y)
}
// (7)如果方法无参,但是声明了参数列表,那么调用时,小括号,可加可不加
def func7(): Unit = {
println("hello")
}
// (8)如果方法没有参数列表,那么小括号可以省略,调用时小括号必须省略
def func8 {
println("hello")
}
// (9)如果不关心函数名,只关心映射逻辑,就会变为lambda表达式
(x:Int, y:Int) => {println(x + y)}
val func = (x:Int, y:Int) => {println(x + y)} // 如果要设置函数名可以这样。此时,func就是函数名
}
}
3.2 函数的第二种形态
3.3 高阶函数
-
高阶函数:参数或返回值为函数的函数(或方法)称为高阶函数
-
函数作为参数传递:
object TestFunction { def main(args: Array[String]): Unit = { //制作一个计算器 val calculator = (a: Int, b: Int, operater: (Int, Int) => Int) => { //高阶函数————函数作为参数 operater(a, b) } //函数————求和 val plus = (x: Int, y: Int) => { x + y } //方法————求积 val multiply = (x: Int, y: Int) => { x * y } //函数作为参数 println(calculator(2, 3, plus)) println(calculator(2, 3, multiply)) } }
-
函数作为返回值传递
object TestFunction { def main(args: Array[String]): Unit = { val func1 = (x: Int) => { val func2 = (y: Int) => x + y func2 // 或者直接返回:(y: Int) => x + y } } }
一看到
=>
,就要想到这是表示函数。
3.4 函数柯里化&闭包
-
闭包:内层函数用到了外层函数变量,如果直接调用内层函数会取不到外层函数的这个变量值。此时,内层函数(万物皆对象,函数也是对象)的堆中的对象会保留一份引用到外层函数的值。
闭包参考链接 -
函数柯里化:将一个接收多个参数的函数转化成一个一个接受参数的函数过程,可以简单的理解为一种特殊的参数列表声明方式。函数柯里化
object TestFunction { val sum = (x: Int, y: Int, z: Int) => x + y + z // 函数柯里化的底层逻辑:本质是将函数作为返回值 val sum1 = (x: Int) => { y: Int => { // 匿名函数 z: Int => { // 匿名函数 x + y + z } } } // 函数柯里化的另一种简单表达 val sum2 = (x: Int) => (y: Int) => (z: Int) => x + y + z // 方法也有函数柯里化 def sum3(x: Int)(y: Int)(z: Int) = x + y + z def main(args: Array[String]): Unit = { sum(1, 2, 3) sum1(1)(2)(3) // sum1(1)调用完后,返回一个函数; sum1(1)(2)是调用返回的函数; ....... sum2(1)(2)(3) sum3(1)(2)(3) } }
3.5 递归 & 尾递归
-
递归与Java中的递归一样:前面知道scala的方法返回值是可以省略的,默认分配返回值类型,但是 如果方法是递归方法,则必须指定方法的返回值类型,否则会报错。
object Test{ def main(args: Array[String]): Unit = { // 实现阶乘 def fact(n : Int) : Int = { // 必须指名方法的返回值类型 // 跳出递归 if(n == 1) return 1 // 递归逻辑 n * fact(n - 1) } // 调用阶乘方法 println(fact(5)) } }
-
尾递归:递归是将每次调用函数/方法会压入到栈中,是累计使用资源,容易造成栈溢出;而尾递归是覆盖使用资源,不会造成栈溢出。所以,尾递归资源利用率更加高。尾递归参考链接
一般支持函数式编程语言都支持尾递归;但是Java不支持尾递归。
4. 补充
4.1 访问元祖元素
变量名._数字
比如:x._1 表示访问x的第一个元素
4.2 =>的含义
https://blog.csdn.net/qq_43546676/article/details/130992479