1.下载安装scala
2.配置环境变量:
a右击我的电脑,单击“属性”,进入如图所示页面。下面开始配置环境变量,右击【我的电脑】---【属性】-----【高级系统设置】---【环境变量】,
b设置SCALA-HOME变量:如图,单击新建,在变量名一栏输入: SCALA-HOME 变 量值一栏输入: D:\ProgramFiles\scala 也就是scala的安装目录,根据个人情况有所不 同,
c 设置path变量:找到系统变量下的“path”如图,单击编辑。在“变量值”一栏的最前面添加 如下的 code: %scala_Home%\bin;%scala_Home%\jre\bin; 注意:后面的分号 ; 不要漏掉。
d 设置classpath变量:找到找到系统变量下的“classpath”如图,单击编辑,如没有,则单击“新建”,
“变量名”:ClassPath
“变量值”:.;%scala_Home%\bin;%scala_Home%\lib\dt.jar;%scala_Home%\lib\tools.jar.; 注意:“变量值”最前面的 .; 不要漏掉。最后单击确定即可。
最后保存进入cmd检验:
2.学习基本语法:
val,常量声明
var,变量声明
•类型省略(默认类型)
•声明省略(连续声明)
def,函数声明
•无参函数
type,类型声明
class,类声明
object,对象声明
val x:T valx:T=e
var x:T varx:T=e
val x=e varx=e
val x1,x2,x3 等价于 val x1;valx2;val x3
var x1,x2,x3:T=e等价于varxn:T=e
defabc(xn:T):T*=e
def adder(m:Int,n:Int)=m+n
defadder()=e
转义序列:
\b \u0008 退格
\t \u0090制表符
\n \u0004 换行
\f \u000c 换页
\r \u000d 回车
\” \u0022双引号
\’ \u0027单引号
\\ \u005c反斜杠
【重载 a<<b 为a*(b+1)】
【重载 a-=b 为a-b*2】
注释 //
建立文本文件,把后缀名改为scala,使用记事本编写
使用Eclipse等进行scala脚本建立、编写
编译
绝大部分scala脚本在运行前需先进行编译
cmd下利用scalac、fsc对scala脚本进行编译
运行
只包含简单的语句时无需编译,能直接运行,否则需先进行编译,运行编译后生成的scala文件
cmd下利用scala运行
判断(if表达式)
循环(while/do语句)
枚举(for表达式)
匹配(match表达式)
异常处理(throw/try)
输出(print/println)
输入(readLine)
枚举(for表达式)
For表达式能用任意条件进行各种形式枚举,而在枚举的过程中能实现绝大部分的循环功能且不必使用变量。
for(i <- e) E(i)
for表达式包括发生器(i <- e)同函数体E(i),
发生器是遍历e中的元素,每一次枚举名为i的新的val就被初始化
对于每一次枚举,函数体E(i)都会执行一次,而其中的参数i由枚举时进行赋值
e可以是各种集合,包括数组、列、表达式等,或是有上下界的数值范围:
1 to 4 (1=
for语句除了对枚举值进行操作并释放该值外,可以用以赋值,即把每一次枚举的值记录在集合中。
for(i <- e) yield E(i)
若E(i)由花括号括起,yield须在花括号外,即for(i <- e) yield {E(i)}
val No=
for(i <- 1 to 4)
yield i
匹配(match表达式)
match表达式类似于其他语言的switch语句,提供多个备选项中进行选择
•a match{
•case 𝑒1 => 𝐸1
•case 𝑒2 => 𝐸2
•… …
•}
若a匹配𝑒1则执行𝐸1,若a匹配𝑒2则执行𝐸2,如此类推
a可以是数组、任意类型值等, 𝑒𝑛可以是对应的值、常量、变量、甚至是类型
match表达式能用以直接赋值,如
val sign = a match{case 𝑒1 => 123;case 𝑒2 =>“123”}
匹配是从上而下的
异常处理(throw/try)
Scala处理异常一般通过throw抛出一个异常并中止
要么捕获并处理该异常,要么简单中止之
异常抛出:throw newIllegalArgumentException
异常捕获与处理:try{函数体}catch{case…;case…}
异常捕获并中止:try{函数体}finally{A.close()}
try{函数体} catch{case…;case…}finally{A。close()}
throw、try-catch-finally表达式都能生成值
如:throw newIllegalArgumentException(“Error!”)
def f():Int=try{1}finally{2}
变长数组声明与操作
importscala.collection.mutable.ArrayBuffer
val C= newArrayBuffer[T]()
声明一个空的数组缓存,此时C是一个全为空的数组,数组元素为0
C += 𝑒1 //C:ArrayBuffer(𝑒1)
在数组尾部增加一个类型为T的元素𝑒1
C +=(𝑒2, 𝑒3)//C:ArrayBuffer(𝑒1 , 𝑒2, 𝑒3)
在数组尾部增加类型为T的元素𝑒2,𝑒3
C++=Array(𝑒2, 𝑒3)//C:ArrayBuffer(𝑒1 , 𝑒2, 𝑒3, 𝑒2, 𝑒3)
在数组尾部增加数组Array(𝑒2,𝑒3)
C .trimEnd(1)//C:ArrayBuffer(𝑒1 , 𝑒2, 𝑒3, 𝑒2)
移除最后1个元素
C .insert(2, 𝑒3)//C:ArrayBuffer(𝑒1 , 𝑒2, 𝑒3,𝑒3, 𝑒2)
在第二个元素后插入𝑒3
C .insert(2,𝑒1 , 𝑒2, 𝑒3)//C:ArrayBuffer(𝑒1,𝑒2,𝑒1,𝑒2, 𝑒3,𝑒3,𝑒3, 𝑒2)
在第二个元素后插入𝑒1 , 𝑒2,𝑒3
C.remove(3)//C:ArrayBuffer(𝑒1,𝑒2,𝑒1,𝑒3,𝑒3,𝑒3, 𝑒2)
移除第三个元素后的一个元素
C.remove(3,2)//C:ArrayBuffer(𝑒1,𝑒2,𝑒1,𝑒3, 𝑒2)
移除第三个元素后的两个元素
D=C.toArray把变长数组C转换为定长数组D
E=A.toBuffer把定长数组A转换为变长数组E
zip方法能把几个集合结合起来
valone=Array(‘a’,‘b’,‘c’)
valtwo=Array(1,2,3)
val three=one.zip(two)或 valthree=one zip
内层能调用外层声明的量
{val a=b...{val b=0...}...}外层不能调用内层声明的量
内层声明与外层声明相同时,内层使用的是内层的声明,外层使用的是外层的声明
类定义
class HELLOWORLD{
private val value1 = “HELLO”
var value2 = “WORLD“
def add() { println(value1+value2)}
def plus(m:Char)=value2+m
}
类成员主要包括字段(val跟var)、方法与函数(def),但Scala禁止使用同样的名称命名字段和方法,即既声明一个value字段,又声明一个value方法是不允许的
类成员可见性有两种,private(私有)跟public(公有),private需要声明,public无需额外声明
类声明后利用new声明对象
val one= newHELLOWORLD
getter、setter
Scala对每个类中的字段都提供getter和setter方法
对于公有字段来说,其getter和setter方法同样是共有的,对于私有字段来说,则是私有的
var声明的字段带有getter和setter方法(读写)
val声明的字段自带有getter方法(只读)
对于字段value1,其getter形式为value1,并没有setter方法
对于字段value2,其getter和setter方法形式为value2和value2_=
实际使用时,在类定义外,getter和setter方法使用是一致的,形如one.value2
getter方法与setter方法的意义在于什么地方呢?
getter方法与setter方法的意义在于控制类中私有对象的数据
在类中可以通过重定义getter和setter方法获取、有限制的修改私有字段
class HELLOWORLD{
private var privatevalue1 = “HELLO”
var value2 = “WORLD“
def add() { println(value1+privatevalue2) }
def plus(m:Char)=value2+m
def value1 = privatevalue1
def value1_ = (newvalue1:String) {
if(newvalue1.length>privatevalue1.length)privatevalue1=newvalue1
} //若新的字段比原私有字段长,则更改,否则保持私有字段不变
}
主构造器
每个类都有主构造器,且与类定义交织在一起
主构造器的参数直接放置在类名之后
class HELLOWORLD(val value1:String,varvalue2:String){...}
主构造器的参数被编译成字段,并在构造对象时初始化传入
一个类若没有显式定义主构造器自动拥有一个无参主构造器
若类中有直接执行的语句(非定义的方法、函数),每次构造对象时皆会执行一次,不论什么样的构造器类型
如:class HELLOWORLD(val value1:String,varvalue2:String){
println(“HELLOWORLD IS CREATED”)
val value3=value1+value2
}
val two = new HELLOWORLD(“WELCOME”,“HOME”)
主构造器的参数一般有四种:
value:String
生成对象私有字段,对象中没有方法使用value,则没有该字段
private val/var value:String
私有字段,私有的getter/setter方法
val/var value:String
私有字段,公有的getter/setter方法
@BeanProperty val/var value:String
私有字段,共有的Scala和JavaBean的getter/setter方法
class HELLOWORD private(主构造器){ 类成员}
主构造器私有,只能通过辅助构造器构造对象
辅助构造器
Scala类能有任意多的辅助构造器
辅助构造器的名称为this,在类中定义
辅助构造器必须以一个主构造器或其他已定义的辅助构造器调用开始
class HELLOWORLD{
private var value1=“”
private var value2=“”
def this(m:String){
this() //调用主构造器
this.value1=m}
def this(m:String,n:String){
this(m) //调用已定义的辅助构造器
this.value2=n}}
单例对象
object语法定义了某个类的单个实例
对象的构造器在该对象第一次被使用时调用
object语法结构与class大致相同,除了object不能提供构造器参数
通常使用单例对象的环境:
作为存放工具函数或常量的地方
共享单个不可变实例
利用单个实例协调某个服务
伴生对象
当一个单例对象存在同名类的时候,称为伴生对象
class HELLOWORLD{...}
objectHELLOWORLD{...}
类和其伴生对象可以互相访问私有属性,但必须存在同一个源文件中
类的伴生对象可以被访问,但并不在作用域中,如;
class HELLOWORLD{...}
object HELLOWORLD{ def NOW{...} }
HELLOWORLD类必须通过HELLOWORLD.NOW调用伴生对象中的NOW方法,而不能直接用NOW来调用
apply方法
需要构造有参数需求的伴生对象时,可定义并使用apply方法
class HELLOWORLD(var m:String,n:Char){...}
object HELLOWORLD{
def apply(n:Char)=new HELLOWORLD(“”,n)
}
val hi=HELLOWORLD(‘j’)
重写
Scala中使用override保留字进行方法、字段重写
class week extends month{
overridedef firstday = {...}
}
override保留字实际使用类似与private,声明这个保留字后的定义、声明是对超类的重写,因此,其也可以写在类定义的参数中
class week(overrideval lastday:String)extendsmonth{...}
子类的重写或修改Scala会检查其超类,但是,超类的修改并不会检查其子类
重写包括字段和方法,但参数不同的方法可以不重写
class month{ def secondday(m:String)={...}}
class week extends month{ def secondday ={...}}
规则:
重写def
用val :利用val能重写超类用没有参数的方法(getter)
用def:子类的方法与超类方法重名
用var:同时重写getter、setter方法,只重写getter方法报错
重写val
用val:子类的一个私有字段与超类的字段重名,getter方法重写超类的getter方法
重写var
用var:且当超类的var是抽象的才能被重写,否则超类的var都会被继承
class month{
val one = 25 //可在子类中用val重写
var two = 15 //不可在子类中用var重写,因为不是抽象的
var three:Int
def firstday = //可在子类中用val重写
def now = //可在子类中用var重写
def now_ =
def lastday(m:Char)={} //可在子类中用def重写
}
子类中,def只能重写超类的def,val能重写超类的val或不带参数的def,var只能重写超类中抽象的var或者超类的getter/setter对
抽象
不能被实例的类叫做抽象类
抽象类的某个或某几个成员没有被完整定义,这些没有被完整定义的成员称为抽象方法或抽象字段
用abstract保留字标记抽象类
abstractyear{
val name:Array[String] //抽象的val,带有一个抽象的getter方法
var num:Int //抽象的var,带有抽象的getter/setter方法
def sign//没有方法体/函数体,是一个抽象方法
}
只要类中有任意一个抽象成员,必须使用abstract标记
重写抽象方法、抽象字段不需要使用override保留字
保护
当一个类不希望被继承、拓展时,可在类声明前加上final保留字
final class year{...}
当一个类的某些成员不希望被重写时,可以在成员声明前加上final保留字
class year{ final def sign{...} }
当超类中的某些成员需要被子类继承,又不想对子类以外成员可见时,在成员声明前加上protected保留字
protected[this],将访问权限定于当前对象,类似于private[this]
类中protected的成员对其子类可见,对其超类不可见
class year{ protecteddef sign{...} }
多重继承
Scala不支持多重继承,取而代之的是特质
一个子类只能拥有一个超类,一个超类能拥有多个子类
即:class week extends month,year是不合法的
为什么?
若一个子类继承自不同的超类,不同的超类中同名成员子类不知如何处理
多重继承产生菱形继承问题
解决多重继承可能导致的问题消耗的资源远比多重继承产生的价值高
Scala使用特质达到类似多重继承的效果
一个类可以扩展自一个或多个特质,一个特质可以被多个类扩展
特质能限制被什么样的类所扩展
使用
特质是Scala里代码复用的基础单元,封装了方法和字段的定义
特质的定义使用保留字trait,具体语法与类定义相似,除了不能拥有构造参数
traitreset{
def reset(m:Int,n:Int)=if(m>=n) 1 }
一旦特质被定义了,就可以混入到类中
class week extendsreset {...}
当要混入多个特质时,利用with保留字
class week extendsreset withB withC {...}
特质中的成员能是抽象的吗?
备注:所有JAVA接口都能当做是特质使用在Scala中
特质的成员可以是抽象的,而且,不需要使用abstract声明
同样的,重写特质的抽象方法无需给出override
但是,多个特质重写同一个特质的抽象方法需给出override
除了在类定义中混入特质以外,还可以
在特质定义中混入特质:
trait reseting extendsreset{...}
在对象构造时混入特质
val five = new month withreseting
构造
特质的构造是有顺序的,从左到右被构造
构造器按如下顺序构造:
超类
父特质
第一个特质
第二个特质(父特质不重复构造)
类
如果class A extends B1 with B2 with B3....
那么,串接B1、B2、B3...等特质,去掉重复项且右侧胜出
应用
特质的一个主要应用方面在于接口,根据类已有的方法自动为类添加方法
利用特质实现富接口:
构造一个具有少量抽象方法和大量基于抽象方法的具体方法的特质
那么,只要把特质混入类中,通过类重写抽象方法后,类便自动获得大量具体方法
trait Logger{
def log(msg:String)
def warn(msg:String) { log(“server”+msg) }
def server(msg:String) { log(“server”+msg) }}
class week extends Logger{
def log(msg:String){println(msg)}
server(“HI”)}
特质的另一个应用方面在于:为类提供可堆叠的改变(super保留字)
当为类添加多个互相调用的特质时,从最后一个开始进行处理
在类中super.foo()这样的方法调用是静态绑定的,明确是调用它的父类的foo()方法
在特质中写下了super.foo()时,它的调用是动态绑定的。调用的实现讲在每一次特质被混入到具体类的时候才被决定
因此,特质混入的次序的不同其执行效果也就不同
Scala语言入门
高阶函数
Scala作为一门“函数式编程语言”,函数是一个值,能被传递和操作
模式匹配
match表达式的高级应用,样例类
类型参数
通过类型参数构建类和函数、方法,使之适应不同类型的参数
高阶函数
头等函数
匿名函数
柯里化
柯里化是指将接受两个参数的函数变成新的接受一个参数的函数的过程
控制抽象
模式匹配
模式匹配
模式匹配是数据结构上的概念
val hi=Array("hi","hello","world")
hi match{
case Array(“hi”, _*) => println(“Ok”)
case Array("hi","world",_) => println("Wrong")
case _ => println(“None") }
数据结构包括各种集合,类,函数等
通配符“_”表示任意,“_*”则表示任意长度,
样例类
封闭类
偏函数
类型参数
泛型
类型变量界限
型变(协变与逆变)
泛型
类、特质、函数、方法可带有类型参数
def getType(a:Any)//a是Any类型
def getType[T](a:T)//a是泛型,getType是泛型函数
class Pair[T,S](val first:T,val second:S)//泛型类
trait Pair[T] //泛型特质
当类型被指定的时候构成具体的类、函数等
val getInt = getType[Int] _
支持类型推断
val p1=new Pair[25,25.0] //生成Pair[Int,Double]类
val p2=new Pair[25.0,25]
类型参数界限
对于泛型结构,类型参数界限能限制应用在该泛型上的类型
Ordered[T]:Scala标准库中提供的一个特质,用以表示可比较的类型
abstract class Stack[ T <: Ordered[T]]
class EmptyStack[T <: Ordered[T]] extends Stack[T]
class NonEmptyStack[ T <: Ordered[T]](elem:T,rest:Stack[T])extends Stack[T]
可以通过自行构建的类混入Ordered特质使之符合参数界限(能比较)
类型参数上界实则通过限制类型必须拥有某些特质
T <: Compatable[T]
T <: Integral[T]
类型参数界限
abstract class Stack[ T <: Ordered[T]]
class EmptyStack[T <: Ordered[T]] extends Stack[T]
class NonEmptyStack[ T <: Ordered[T]](elem:T,rest:Stack[T])extends Stack[T]
val m=EmptyStack[Int]
Int不是Ordered[Int]的子类,但RichInt是
视界限定
T
注解
注解的是在编译和运行之外,对程序进行的操作
自动生成文档
在生成文档时自动排版
代码的错误检查与忽略
注解的实质是一个标签,插入到代码中标志元编程工具对该段代码使用
Java的注解并不影响编译器的运行,但Scala的注解会,如@BeanProperty
可以自行编写新的元编程工具
注解语法
Scala可以为任意的结构代码甚至是参数添加注解
@deprecated def Sum() = { ... } //为方法添加注解
@deprecated class IntStack { ... } //为类添加注解
def check(@NotNull password:String) //为参数添加注解
Scala支持多注解,且注解次序没有影响
@BeanProperty @Id var username = _
部分注解需要引入参数,Scala支持任意类型作为注解参数
@Test( timeout = 100 ) def testA() = { ... }
注解语法
给主构造器添加注解时,需要将注解放置在构造器之前,并加上一对圆括号
class Credentials @Inject() (var username: String, var password:String)
为表达式添加注解,在表达式后加上冒号,然后是注解
(myMap.get(key): @unchecked) match { ... }
为类型参数添加注解
class MyContainer[@specialized T]
针对实际类型的注解应放置在类型名称之后
String @cps[Unit]
注解举例
Scala拥有若干个标准注解
废弃@deprecated
标示某段代码(方法或类)不提倡使用,调用该段代码将会收到废弃警告
易变字段@volatile
标示相关变量将会被多个线程使用,使得多线程访问具有预见性
自动的get/set方法对@scala.reflect.BeanProperty
添加该注解将会在编译时自动产生对应的get/set方法,注意不能在同一时间编译的代
码中调用注解字段的get/set方法
不检查@unchecked
生成跳转表@switch
(m : @switch) match {
case 0 => ...
case 1 => ...
case 2 => ... }
基本类型特殊化@specialized
def sum[@specialized(Int , Double) T] = { ... }
并发
什么是并发?
将一个计算任务,分成几个小的部份,让它们同时被计算,之后再汇整计算结果
常见的并发结构
多线程,分布式计算,消息传递,资源共享(内存共享)
常用的并发模型
参与者模式
Petri网
通信顺序进程
Actor
参与者模式Actor model
当一个参与者接收到一则讯息,它可以做出一些决策、建立更多的参与者、传送更多
的讯息、决定要如何回答接下来的讯息
Scala使用Actor作为其并发编程模型
一种基于消息传递而非资源共享的并发模型,能尽可能避免死锁和共享状态
actor可以理解为虚拟线程,能实现线程的复用,从而产生数百上千的actor并有效控制
系统开销
Scala在2.10.0版本之后在自带Akka类库作为其Actor推荐实现
Akka是使用Scala编写的实现Actor模型的一个类库
Akka包括了大量的工具去完善、辅助并发开发
使用actor需要引入Scala的Actor类库
import scala.actors.Actor
import akka.actor.Actor
继承Actor以使用
class HI extends Actor
object HELLO extends Actor
利用actor方法声明使用
val hi = actor { receive { ... } }
val hello = Array.fill()( actor { react { case _ => } } )
消息传递是Actor的核心
使用!向actor发送信息
actor使用receive接收信息
msg代表actor当前接收到的信息
对于信息,actor往往使用模式匹配来进行处理,而对于消息的发送者而言,其并不关
心actor接收到信息后如何处理,更不期待actor对该信息有返回,当消息发送后,发送
者便处理下一步
receive方法的参数有各种case语句组成,receive方法获取到消息后依次传递给其参数
actor通过react实现线程的复用
receive从线程池获取一个线程并一直使用,react从线程池获取一个线程,使用完释放
def act( ) { react { ... } }
actor(Scala.Actor)的主体为act方法,act方法不能被外部显式调用
actor的act方法在start后启动,act方法本身并不会自动获取线程
在act方法里只能获取一次线程
在以下情况下actor终止执行act方法
• act方法返回
• act方法由于异常被禁止
• actor调用exit方法
Actor使用原则
避免共享
不调用方法
足够简单
异步调用
使用react
容错
更深入学习Scala
XML处理
与JAVA的互操作
Scaladoc
并发模型
深入学习Akka类库的特性
学习阅读Scala的外语参考书,如
《Actors in Scala》
《Scala Cookbooks》