1.类、对象、字段、方法
类是对象的蓝图,一旦定义了对象,就可以使用关键字new从蓝图里创建对象
类定义里,可以放置字段和方法,统称为成员,:member
val、或者是var 都是指向对象的变量
方法用def 定义,包含了可执行代码,
字段保留了对象的状态或数据,而,方法则是用这些数据对对象做运算工作
当我们实例化类的时候,执行期环境会设定一些内存来保留对象状态的镜像——也就是说,变量的内容。举
例来说,如果你定义了ChecksumAccumulator类并给它一个叫做sum的var字段:
class ChecksumAccumulator {
var sum = 0
}
并实例化两次:
val acc = new ChecksumAccumulator
val csa = new ChecksumAccumulator
对象在内存里的镜像看上去大概是这样的:
由于在类ChecksumAccumulator里面定义的字段sum是var,而不是val,你之后可以重
新赋值给它不同的Int值,如:
acc.sum = 3
现在,图像看上去会变成:
这张图里第一件要注意的事情是这里有两个sum变量,一个在acc指向的对象里,另一个
在csa指向的对象里。字段的另一种说法是实例变量:instance variable,因为每一个实
例都有自己的变量集。总体来说,对象实例的变量组成了对象的内存镜像。你不仅可以因
为看到两个sum变量来体会关于这个的演示,同样可以通过改变其中一个时,另一个不变
来发现这点。
本例中另外一件需要注意的事情是,尽管acc是val,你仍可以改变acc指向的对象。你
对acc(或csa)不能做的事情是由于它们是val,而不是var,你不可以把它们再次赋值
为不同的对象。例如,下面的尝试将会失败:
// 编译不过,因为acc是val
acc = new ChecksumAccumulator
于是你可以总结出来,acc将永远指向初始化时指向的同一个ChecksumAccumulator对象,
但是包含于对象中的字段可以随时改动
要想保证对象具有鲁棒性的重要方法之一就是保证对象的状态--实例变量的值---在对象的整个生命周期中持续有效。
第一步:
通过把字段变为私有:private 去阻止外界直接对它的访问。因为私有字段只能被定义在同一个类里的方法访问,所有能更新字段的代码将被锁定在类里。
Pblic是scala的缺省访问级别。
class CsA{
private var sum = 0
def add(b:Byte):Unit = {
sum += b
}
def checksum() : Int = {
return ~(sum & 0xFF) + 1
}
}
scala鼓励制造很小的方法,把较大的方法分解成许多
class ChecksumAccumulator {
private var sum = 0
def add(b: Byte): Unit = sum += b
def checksum(): Int = ~(sum & 0xFF) + 1
}
add函数的结果类型是Unit,执行它的目的就是他的副作用,而他的副作用就是sum被重新赋值了,表达这个方法的另一种方式就是去掉返回结果类型和等号,把方法体放在大括号里面
class ChecksumAccumulator {
private var sum = 0
def add(b: Byte) { sum += b }
def checksum(): Int = ~(sum & 0xFF) + 1
}
去掉方法体前面的等号时,他的结果类型将注定是Unit。不论方法体里面包含了什么
2.分号推断
一行之中需要写多个语句时需要使用分号,一条语句分成多行时需要遵从下面的分号推断规则:
分割语句的精确规则非常有效却出人意料的简单。那就是,除非以下情况的一种成立,
否则行尾被认为是一个分号:
1.疑问行由一个不能合法作为语句结尾的字结束,如句点或中缀操作符。
2.下一行开始于不能作为语句开始的字。
3.行结束于括号(...)或方框[...]内部,因为这些符号不可能容纳多个语句。
3.Singleton对象( 单例对象)
scala比Java更面向对象的一个方面是scala没有静态成员。替代品是,scala有单例对象:singleton.object
除了使用object关键字替代了class关键字以为,单例对象的定义看上去就像是类定义。
import scala.collection.mutable.Map
class CsA{
private var sum = 0
def add(b:Byte) {sum += b}
def checksum() : Int = ~(sum & 0xFF) + 1
}
object CsA {
private val cache = Map[String,Int]()
def calculate(s:String):Int = {
if(cache.contains(s))
cache(s)
else{
val acc = new CsA
for(c <- s)
acc.add(c.toByte)
val cs = acc.checksum()
cache += (s -> cs)
cs
}
}
}
这个单例对象叫做CsA,与栗子中的类同名。当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion opbject
我们必须在同一个源文件中定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class.
类和他的伴生对象可以互相访问其私有成员。
单例对象扩展了超类并可以混入特质。由于每个单例对象都是超类的实例并混入了特质,你可以通过这些类型调用他的方法。用这些类型的变量指代他
单例对象和类间的一个差别是,单例对象不带参数,而类可以。每个单例对象都被作为一个由静态变量指向的虚构类的一个实例来实现。因此他们与
Java静态类有着相同的初始化语法。特别指出的是,单例对象会在第一次被访问的时候初始化
不与伴生类共享名称的单例对象被称为孤立对象:standalone object
4.scala程序
要执行scala程序,一定要提供一个main方法(仅带一个参数,Array[String],且结构类型为Unit)的孤立单例对象名。任何拥有合适签名的
main方法的单例对象都可以用来作为程序的入口点
import CsA.calculate
object Summer{
def main(args:Array[String]){
for (arg <- args)
println(arg + ":" + calculate(arg))
}
}
5.Application 特质
scala提供了一个特质,scala.Application,
使用这个特质的方法是,首先在你的单例对象后面写上“extends Application”.然后代之以main方法,然后你就看以
把想要放在main方法里面的代码直接放在单例对象的大括号之间,就这个简单<pre name="code" class="html">import ChecksumAccumulator.calculate
object FallWinterSpringSummer extends Application {
for (season <- List("fall", "winter", "spring"))
println(season +": "+ calculate(season))
}