第二章 Scala基本语法 v1.0
第一部分 数据类型
一、变量与常量
1.不可变变量(val)-推荐使用
使用val定义基本数据类型时,相当于加上了final修饰,值(引用)不可变,可以使用val重新定义
基本语法:val value[:手动指定类型] = "初值" //声明变量时,可不指定类型,编译器会自动推断
2.懒加载不可变变量(lazy val)
只有val修饰的变量才能被lazy修饰,使用lazy定义变量后,只有在调用该变量时才会实例化这个变量的值
基本语法:lazy val value[:手动指定类型] = "初值"
3.可变变量(var)
var修饰的变量,内容和引用都可变,一般只有当真正需要改变状态时,才使用var
基本语法:var value[:手动指定类型] = "初值"
4.字符串定义
- 使用双引号
val/var str = "str"- 使用三引号
val/var str = """str
str"""- 使用插值表达式
val str = "str"
val/var str = s"str=${str}" //使用${}作为占位符二、数据类型
1.基本数据类型

2.类型转换
- 自动类型转换
示例:val x: Long = 987654321 //将987654321赋值给Long类型的x,会进行自动类型转换
- 强制类型转换
示例:int a = (int) 100.00 //将100.00强制类型转换为int赋值给a
- 类型判定
示例:i1.isInstanceOf[Int] //判定i1是否为int类型(或其子类)
- 类型转换
示例:i2.asInstanceOf[类型] //将对象i2转换为指定类型
第二部分 控制语句
一、选择结构
1.基本语法
if (条件表达式1) {
//执行代码块1
}
else if (条件表达式2) {
//执行代码块2
}
……
else {
//执行代码块n
}2.注意要点
- Scala中,条件表达式是有返回值的
- Scala中没有三元表达式,使用
if(条件表达式) <真结果> else <假结果>代替三元表达式
二、循环结构
1.for循环-基本语法
for(i <- 表达式[; j <- 表达式; ... ]){ //for语句中可以有多个生成器
//循环体
}
//在for表达式中可以添加if判定语句,该if判断称之为守卫
for(i <- 表达式; if 过滤条件]){
//循环体
}i <- 表达式 称为生成器,可从集合中取出每个元素,或创建迭代器,常用的两种方式为:to和until
区别:i <- i to j //包括i和j,i <- i until j //包括i,不包括j
2.for循环-yield循环体
在Scala中,yield的主要作用是记住每次迭代中的有关值,并逐一存入到一个数组中
val retVal = for {子句} yield {变量或表达式}
val a = for(i <- 1 to 10) yield i*10 //示例:生成10、20、...、100的集合3.do-while循环
基本语法:while(条件语句){表达式}
4.while循环
基本语法:do{表达式}while(条件语句)
5.跳出循环语句(breakable块)
import scala.util.control.Breaks._ //相当于Java导入包时的*
breakable{ //使用break块
for(i <- 1 to 10){
if(i == 7){
break() //breakable将整个for循环包含,相当于break
}
}
}import scala.util.control.Breaks._ //相当于Java导入包时的*
//使用break块
for(i <- 1 to 10){
breakable{
if(i == 7){
break() //breakable将for循环的循环体包含,相当于continue
}
}
}三、块表达式
Scala中使用{}表示一个块表达式,快表达式有返回值,返回值为最后一个表达式的值
val a = {
println("1+1")
1 + 1
}
println("a:" + a) //结果为2第三部分 方法与函数
一、方法
1.方法的定义
def 方法名称[(参数名:参数类型, ...):返回值类型] = { //返回值类型可以省略(递归方法除外)
方法体
return value
}
def 方法名称[(参数名:参数类型, ...):返回值类型] = 方法返回值 //基本格式(简写)
def add=System.getProperty("user.name") //无参方法
def add()=System.getProperty("user.name") //空参方法,可作为最终表达式出现,实际是方法调用
def getMax(a:Int, b:Int):Int = { //得到两个数的最大值
return if(a>=b) a else b
}
def getMax(a:Int, b:Int) = if(a>=b) a else b //得到两个数的最大值(简写)2.惰性方法
当记录方法返回值的变量被声明为lazy时,方法的执行将被推迟到首次使用该值时,方法才会执行
示例:lazy val sum = getSum(1, 2) //当变量sum被使用时才执行方法
使用场景:
- 打开数据库连接
- 提升某些特定模块的启动时间
- 确保对象中的某些字段能优先初始化
3.方法参数
- 默认参数:
def 方法名称[(参数名:参数类型 = 默认值, ...):返回值类型] = {} - 变长参数:
def 方法名称[(参数名:参数类型*):返回值类型] = {}
4.方法调用
- 后缀调用法:
对象名.方法名(参数) - 中缀调用法:
对象名 方法名 参数(在Scala中,所有操作符都是方法) - 花括号调用法:
对象名.方法名{参数} - 无括号调用法:
方法名(方法没有参数时可以省略方法名后的括号)
二、函数
可以看做是带有参数的表达式,其本质是一个完整的对象(变量),无需指定返回值类型
1.函数的定义
val 函数变量名 = (参数名:参数类型, ...) => {
函数体
}
var mul = (x: Int, y: Int) => x*y //多个参数的函数
val getTheAnswer = () => 42 //空参函数2.方法与函数的区别
- 方法是隶属于类或者对象的,在运行时,它是加载到JVM的方法区中
- 可以将函数对象赋值给一个变量, 在运行时,它是加载到JVM的堆内存中
- 函数是一个对象,继承自FunctionN, 函数对象有apply, curried, toString, tupled这些方法;方法则没有
3.方法转换为函数
有事需要将方法转换为函数,可以使用"_"将方法转换为函数
基本格式:val 变量名 = 方法名 _
4.使用情形
方法:需要执行一些与对象状态相关的操作(例如修改对象属性、访问对象私有成员等)
函数:需要进行一些纯粹的计算操作(例如数学运算、字符串处理等)
第四部分 面向对象编程
一、实体类(class)
1.定义实体类
class Obj //当类为空,没有任何成员,可以省略{}
class Obj(主构造器){ //构造器详见后面特殊方法部分
//构造器代码块
}2.创建对象
val obj = new Obj(...) //创建一个Obj类型对象
val obj = new Obj //当构造器的参数为空,可以省略()3.成员变量初始化
- 普通写法:
val name:String = "" - 类型推断:
val name = "" - 使用下划线初始化成员变量:
var name:String = _ //初始化为默认值,val类型变量不可用
4.访问修饰符
在Scala中,没有public修饰符,没有被标为private/protected的成员都是公共的
5.继承
class/object 子类([override val 变量名,] ...) extends 父类(...) {
//成员方法
override val 变量名 //可以通过override重写父类中val修饰的成员变量(var可以直接修改故不可override)
override def 方法名 = {} //重写非抽象方法必须使用override修饰符
}二、单例对象(object)
单例对象表示全局仅有一个对象,也叫孤立对象,类似于Java中的静态对象
1.定义单例对象
object 单例对象名{ } //定义一个单例对象2.成员方法
单例对象中定义的方法类似于Java中的静态方法
三、伴生对象
1.伴生对象的作用
在Scala中,通过伴生对象来实现类中又有实例成员又有静态成员的类似效果
一个class和object具有同样的名字,则称这个object为伴生对象,class为伴生类
注意要点:
- 伴生对象必须要和伴生类名字一样
- 伴生对象和伴生类应当处于同一个Scala源文件中
- 伴生对象和伴生类和可以相互访问private属性
2.定义
class Obj{ //伴生类
//...
}
object Obj{ //伴生对象
//...
}四、类中特殊方法
1.main方法
由于在Scala中没有静态方法的概念,所以在Scala中,main方法必须放在一个单例对象中
def main(args: Array[String]): Unit = {
//主方法体
}2.构造器
/** 主构造器参数列表直接定义在类名后,添加var/val表示直接通过主构造器定义成员变量 */
class 类名(var/val 参数名:类型 = 默认值, ...){ //构造器参数可以指定默认值
//构造代码块(class中除了字段定义和方法定义的代码都是构造代码)
}class 类名(var/val 参数名:类型 = 默认值) {
def this(var/val 参数名:类型 = 默认值) {
this(...) //辅助构造器第一行代码必须调用主构造器或者其他构造器
//构造器代码
}
}3.apply方法
在Scala中,可以通过伴生对象实现apply方法来实现在创建对象时免new的操作
object 伴生对象名 {
def apply(参数名:参数类型, ...) = new 类名(...)
}
val 对象名 = 伴生对象名(参数1, 参数2, ...)4.getClass方法与classOf方法
可以用于判定对象是否为指定类型对象(而非指定类型对象或其子类)
示例:p.getClass == classOf([MyClass]) //判断p是否为MyClass类型对象
五、其他类
1.抽象类
abstract class 抽象类名 {
//抽象字段:没有初始化值的变量
val/var 抽象字段名:类型
//抽象方法:没有方法体的方法
def 方法名(参数:数据类型, ...):返回类型
}2.匿名内部类
作用:当对象方法只调用一次的时候使用;或作为方法的参数进行传递时使用
new 类名(构造器参数列表) { //若构造器参数列表为空,()可以省略不写
//重写类中所有抽象内容
}第五部分 数组与集合类
一、数组
1.创建定长数组(Array)
//使用new关键字创建Array
var arr=new Array[数据类型](数组长度)
//直接使用Array创建并初始化一个数组
var arr=Array(value1, value2, ...)//访问定长数组
arr(1) //访问数组中第一个元素
//操作定长数组
arr.toBuffer //将数组转换成数组缓冲2.创建变长数组(ArrayBuffer)
//创建变长数组
val arr = ArrayBuffer[数据类型]()//访问变长数组
arr(1) //访问数组中第一个元素
//操作变长数组
arr += 1 //向变长数组arr中添加元素1
arr += (1,2,3,4) //向变长数组arr中添加多个元素1,2,3,4
arr ++= Array(5,6,7) //向变长数组arr中添加任何集合
arr.append(1) //追加元素
arr.insert(2,20) //在下标2前插入元素
arr.trimEnd(2) //移除最后2个元素
arr.trimStart(1) //移除最开始的一个元素3.定长数组与变长数组的转换
定长数组转为变长数组:arr.toBuffer
变长数组转为定长数组:arr.toArray
二、元组(Tuple)
固定元素的组合,可以装着多个不同类型的值,是不同类型的值的聚集
1.特点
- 最多支持22个元素组合,分别对应类型Tuple1~Tuple22,相应也称为一元组、二元组、三元组、...
- 元组可以容纳不同类型的元素
- 元组不可变
2.创建元组
val t = (value1, ...) //定义一个元组
val t = new Tuple<n>(...) //使用new标识符创建元组
//例
val tuple3 = new Tuple3(1, 3.14, "Fred")2.访问元组元素
val t1 = t._1 //取元组第1个元素(注意:使用此种方式取元素,下标从1开始)
val t2 = t.productElement(0) //取元组第1个元素(下标从0开始)3.遍历元组
元组本身不是集合成员,所以元组不能作为for生成器的表达式,可以通过迭代器实现
//方式1:迭代器遍历
for (elem <- t.productIterator) {
print(elem)
}
//方式2:foreach遍历
t.productIterator.foreach(i => println(i)) //foreach是高阶函数
t.productIterator.foreach(print(_))三、映射(Map)
1.创建映射
//一般方式构建
val m = Map("key1"->value1,"key2"->value2,"key3"->value3)
//使用元组构建
val m = Map(("key1", value1), ("key2", value2), ("key3", value3))
//构建可变Map
val m = scala.collection.mutable.Map(("key1", value1), ("key2", value2), ("key3", value3))2.访问映射的值
//方式1
val v = m("key") //key不存在,则抛出异常[java.util.NoSuchElementException]
val v = if(m.contains("key")) m("key") else 0
//方式2
val v = m.get("key") //存在则返回Option对象Some,不存在返回None
val v = m.get("key").get //Some再调用get取出值
//方式3:使用getOrElse()取值
val v = m.getOrElse("key",0) //key不存在返回默认值3.修改可变映射的信息
//修改value值(key存在)|添加键值对(key不存在)
m("newKey") = newValue
//添加键值对
m += ("newKey", newValue)
m += ("newKey"->newValue)
m += ("newKey1"->newValue1, "newKey2"->newValue2)
m ++= Map("newKey1"->newValue1, "newKey2"->newValue2)
//移除键值对
m - "oldKey"
m - ("oldKey1", "oldKey2")
m -- List("oldKey1", "oldKey2")4.遍历映射
val res = m.keySet //得到Map中所有键值对的迭代器(set集合)
val keys = m.keys //得到Map中所有key的迭代器
val values = m.values //得到Map中所有value的迭代器
//直接使用(k,v)表示二元组中的键和值
for ((k,v) <- m)
println(k+":"+v)四、列表(List)
1.创建列表与访问列表元素
val l = List(value1, value2, ...)
//访问列表中元素
l(idx)2.相关操作
//添加元素到集合开头生成新集合
val l1 = value :: l
val l1 = value +: l
//添加元素到集合结尾生成新集合
val l2 = l :: value
val l2 = l :+ value
//添加一个集合生成新集合
val l3 = l1 ++: l2 //l1在前
val l3 = l1 ::: l2 //l2在前3.遍历列表
//foreach遍历
l.foreach{x => print(x+" ")}
//map函数生成新列表
var nl = l.map{ x => x+1 }4.可变列表(ListBuffer)
val l = scala.collection.mutable.ListBuffer[数据类型](value1, value2, ...)五、集合(Set)
1.创建可变集合
//创建不可变集合
val s = Set(value1, value2, ...)
//创建有序集合
val ts = scala.collection.immutable.TreeSet(alue1, value2, ...)2.创建不可变集合
val s = new scala.collection.mutable.Hashset[数据类型]()第六部分 Java调用Scala
一、流程与步骤
1.获取Scala单例对象(object修饰)
使用Java中的类名$语法获取Scala对象,示例如下:
import com.example.MyScalaObject$; //导入Scala类
MyScalaObject$ myScalaObject = MyScalaObject$.MODULE$; //获取Scala对象
Object result = myScalaObject.myMethod(...);2.获取Scala示例对象(class修饰)
class类在Scala中是可以被实例化的,因此在Java中也使用正常的实例化方法来创建这个类的对象
import com.example.MyScalaClass; //导入Scala类
MyScalaClass myScalaClass = new MyScalaClass(...); //生成类对象
Object result = myScalaClass.myMethod(...); //调用实例对象方法