Scala学习总结(一)

浏览: 2927

Scala是Spark的原生语言,读懂Spark源码的先前条件就是要学会Scala,因此scala学习是重要的。最近一周学习量大,主要是观看中华石杉老师的视频和快学scala这本书来学习的,自测有了基础水平,可以再进阶了。

1.基础语法

声明变量:

声明val变量,其值不能被改变。声明var变量,其值可以被改变。

无论声明val值,还是声明var变量,都可以手动指定其类型,如果不指定,scala会自动根据值,进行类型的推断。

多个值或变量可以放在一起声明。

数据类型:

Scala的基本数据类型有:Byte、Char、Short、Int、Long、Float、Double、Boolean。这些类型都是类。Scala用底层的java.lang.String类来表示字符串,通过StringOps类给字符串追加了上百种操作。

基本操作符:

scala的算术操作符与java的算术操作符也没有什么区别。scala中没有提供++、--操作符,我们只能使用+=1或者-=1,如counter += 1,表示将counter递增。

函数的调用方法:

如果调用函数时,不需要传递参数,则scala允许调用函数时省略括号的。

apply函数:

Scala中的apply函数是非常特殊的一种函数,在Scala的object中,可以声明apply函数。而使用“类名()”的形式,其实就是“类名.apply()”的一种缩写。通常使用这种方式来构造类的对象,而不是使用“new 类名()”的方式。

2.控制结构和函数

条件表达式if/else:

在Scala中,if表达式是有值的,就是if或者else中最后一行语句返回的值。

由于if表达式是有值的,而if和else子句的值类型可能不同,此时Scala会自动进行推断,取两个类型的公共父类型。

如果if后面没有跟else,则默认else的值是Unit,也用()表示,类似于java中的void或者null。

语句终结符:

默认情况下,scala不需要语句终结符,默认将每一行作为一个语句。

如果一行多个语句,则用分号隔开。

通常来说,对于多行语句,还是会使用花括号的方式

块表达式:

块表达式,指的就是{}中的值,其中可以包含多条语句,最后一个语句的值就是块表达式的返回值。

输入和输出:

print和println:print打印时不会加换行符,而println打印时会加一个换行符。

printf:printf可以用于进行格式化

readLine: readLine允许我们从控制台读取用户输入的数据,类似于java中的System.in和Scanner的作用。

循环:

Scala有while do循环,基本语义与Java相同。

Scala没有for循环,只能使用while替代for循环,或者使用简易版的for语句。

scala没有提供类似于java的break语句。但是可以使用boolean类型变量、return或者Breaks的break函数来替代使用。

// 导入以下包
import scala.util.control._
// 创建 Breaks 对象
val loop = new Breaks;
// 在 breakable 中循环
loop.breakable{
// 循环
for(...){
....
// 循环中断
loop.break;
}
}

函数:

Scala要求必须给出所有参数的类型,但是不一定给出函数返回值的类型,只要右侧的函数体中不包含递归的语句,Scala就可以自己根据右侧的表达式推断出返回类型。

如果函数体中有多行代码,则可以使用代码块的方式包裹多行代码,代码块中最后一行的返回值就是整个函数的返回值。

如果在函数体内递归调用函数自身,则必须手动给出函数的返回类型。

如果函数没有返回值,或返回值类型是Unit的函数称为过程

在Scala中,有时我们调用某些函数时,不希望给出参数的具体值,而希望使用参数自身默认的值,此时就定义在定义函数时使用默认参数

在调用函数时,也可以不按照函数定义的参数顺序来传递参数,而是使用带名参数的方式来传递。

def welcome(jobName:String,name:String = "Stranger")={
println("Welcome, "+ name + ". Your job is "+ jobName)
}

Clipboard Image.png

可变长度参数

在Scala中,有时我们需要将函数定义为参数个数可变的形式,则此时可以使用变长参数定义函数。

def cumprod(nums: Int*) = {
var res = 1
for (num <- nums) res *= num
res
}

Clipboard Image.png

lazy值:

在Scala中,提供了lazy值的特性。如果将一个变量声明为lazy,则只有在第一次使用该变量时,变量对应的表达式才会发生计算。

这种特性对于特别耗时的计算操作特别有用,比如打开文件进行IO,进行网络IO等。

Clipboard Image.png

3.数组操作

定长数组 Array

数组初始化后,长度就固定下来了,而且元素全部根据其类型初始化。

Clipboard Image.png

可以直接使用Array()创建数组,元素类型自动推断,此时不需要new。且访问元素是用(),而不是[]。

Clipboard Image.png

变长数组ArrayBuffer

预先导入包

import scala.collection.mutable.ArrayBuffer

使用ArrayBuffer()的方式可以创建一个空的ArrayBuffer

val b = ArrayBuffer[Int]()

使用+=操作符,可以添加一个元素,或者多个元素

使用++=操作符,可以添加其他集合中的所有元素

使用trimEnd()函数,可以从尾部截断指定个数的元素

Clipboard Image.png

定长数组和变长数组可互相转换。

Clipboard Image.png

遍历数组

Clipboard Image.png

常用算法:

val a = Array(1, 2, 3, 4, 5)

数组元素求和

val sum = a.sum

获取数组最大值

val max = a.max

对数组进行排序

scala.util.Sorting.quickSort(a)

获取数组中所有元素内容,并用指定元素之间的分隔符

a.mkString("<",",", ">")

4.Map和Tuple

Map

Map有可变Map和不可变Map之分

val scores = Map("apple" ->10,"banana"->20,"orange"->15 )  //定义不可变的Map
val scores = scala.collection.mutable.Map("apple" ->10,"banana"->20,"orange"->15 )  //定义可变的Map

定义Map的另一种方式

val scores = Map(("apple" ,10),("banana",20),("orange",15))

获取映射的值,防止键不存在抛出异常,可使用scores.getOrElse("bread",0)的方式。

可变Map中,可以更新某个映射的值,或者添加一个新的映射关系(+=),或移除某个键和对应的值(-=)。

不可变Map中,不能改变该映射,除了把结果保存新值或更新var变量。

元组

定义简单元组,并用._1访问元素,元组访问元素是从1开始,而不是0

Clipboard Image.png

使用元组的原因之一是把多个值绑在一起,以便它们能够被一起处理,通常可用拉链操作(zip)来完成。

Clipboard Image.png

5.类

在Scala中,类并不声明为public。

调用无参方法时,可用不带圆括号。如果定义方法时不带括号,则调用方法时也不能带括号。

getter和setter方法

定义不带private的var变量,此时scala生成的面向JVM的类时,会定义为private的name字段,并提供public的getter和setter方法。

class Fruit{
 var name = "apple"
}

而如果使用private修饰name,则生成的getter和setter也是private的

如果定义val 变量,则只会生成getter方法

如果不希望生成setter和getter方法,则将field声明为private[this]

调用getter和setter方法,分别叫做name和name_ =

Clipboard Image.png

将Scala字段标注为@BeanProperty时,会自动生成四个方法。

Clipboard Image.png

构造器

Scala中,每个类都有一个主构造器和任意多个辅助构造器。

Scala中,主constructor是与类名放在一起的,而且类中,没有定义在任何方法或者是代码块之中的代码,就是主constructor的代码。

主constructor中还可以通过使用默认参数,来给参数默认的值,参数直接写在类名后面。

Clipboard Image.png

辅助构造器的特点:

辅助构造器的名称为this。

每一个辅助构造器都必须以一个对先前已定义的其他辅助构造器或主构造器的调用开始。

一个类如果没有显式定义主构造器则自动拥有一个无参的主构造器。

6.对象

Scala没有静态方法或静态字段,用object语法结构来达到同样的目的。

object的constructor只会在其第一次被调用时执行一次,以后再次调用就不会再次执行constructor了。


如果有一个class,和一个与class同名的object,那么就称这个object是class的伴生对象,class是object的伴生类。

伴生类和伴生对象必须存放在一个.scala文件之中。伴生类和伴生对象,最大的特点就在于,互相可以访问私有特性。

object也可以继承抽象类,并覆盖抽象类中的方法。


object中非常重要的一个特殊方法,就是apply方法。

通常在伴生对象中实现apply方法,并在其中实现构造伴生类的对象的功能。

而创建伴生类的对象时,通常不会使用newClass的方式,而是使用Class()的方式,隐式地调用伴生对象得apply方法,这样会让对象创建更加简洁。


每个Scala程序都必须从一个对象的main方法开始,定义为

def main(args: Array[String]) {}

除了自己实现main方法之外,还可以继承App Trait,然后将需要在main方法中运行的代码,直接作为object的constructor代码;而且用args可以接受传入的参数

object HelloWorld extends App {
if (args.length > 0) println("hello, " + args(0))
else println("Hello World!!!")
}

Scala没有直接提供类似于Java中的Enum这样的枚举特性,如果要实现枚举,则需要用object继承Enumeration类,并且调用Value方法来初始化枚举值。

object Season extends Enumeration {
val SPRING, SUMMER, AUTUMN, WINTER = Value
}

7.继承

Scala中,让子类继承父类,与Java一样,也是使用extends关键字

子类可以从父类继承父类的field和method;然后子类可以在自己内部放入父类所没有,子类特有的field和method;使用继承可以有效复用代码。

子类可以重写父类的field和method;但是如果父类用final修饰,field和method用final修饰,则该类是无法被继承的,field和method是不能被重写的。

scala中,如果子类要重写一个父类中的非抽象方法,则必须使用override关键字

override关键字可以帮助我们尽早发现代码里的错误

此外,在子类重写父类方法之后,如果在子类中要调用父类的被重写的方法,那就使用super关键字,显示地指定要调用的父类的方法。

class Person3{
  private var name = "happy"
  def getName = name
}
class Student3 extends Person3{
  private var score = "A"
  def getScore = score
  override def getName = "Hi,I'm "+ super.getName
}

isInstanceOf判断对象是否是指定类的对象,asInstanceOf将对象转换为指定类型。

如果要求精确地判断对象就是指定类的对象,就使用getClass和classOf。

对象.getClass可以精确获取对象的类,classOf[类]可以精确获取类,然后使用==操作符即可判断。

 val p:Person = new Student
if(p.isInstanceOf[Student]) p.asInstanceOf[Student]
println(p.isInstanceOf[Student]) //判断p是Student及其父类的一个对象
println(p.isInstanceOf[Person]) //判断p是Person及其子类的一个对象
println(p.getClass == classOf[Student]) //判断p是Student的对象
println(p.getClass == classOf[Person]) //判断p是Person的对象

模式匹配的方式来进行类型的判断,这种方式比较简洁明了,而且代码的可维护性和可扩展性也非常高。

使用模式匹配,功能性上来说,与isInstanceOf一样,也是判断是否是该类以及该类的子类的对象。

p match{
case per:Person => println("it's Person's object") //如果p是person类型
case _ => println("unknown type") //如果p不是Person类型
}

8.接口

将Trait作为接口来使用,可在trait中定义具体方法、具体字段、抽象字段。

类可以使用extends关键字继承trait。无论继承类还是trait,统一都是extends。

类继承trait后,必须实现其中的抽象方法,实现时不需要使用override关键字。

scala不支持对类进行多继承,但是支持多重继承trait,使用with关键字。

trait HelloTrait{
def sayHello(name:String)
}
trait MakeFriendsTrait{
def makeFriends(p:Person4)
}
class Person4(val name:String) extends HelloTrait with MakeFriendsTrait{
def sayHello(othername:String) = println("Hello,"+othername+"I'm "+name)
def makeFriends(p:Person4)=println("Hello,"+p.name+",I'm "+name+",I want to make friends with you.")
}

trait调用链

Scala中支持让类继承多个trait后,依次调用多个trait中的同一个方法,只要让多个trait的同一个方法中,在最后都执行super.方法即可

类中调用多个trait中都有的这个方法时,首先会从最右边的trait的方法开始执行,然后依次往左执行,形成一个调用链条。

这种特性非常强大,其实就相当于设计模式中的责任链模式的一种具体实现依赖。

trait Handler{
def handle(data:String){}
}
trait DataValidHandler extends Handler{
override def handle(data:String): Unit ={
println("check data:"+data)
super.handle(data)
}
}
trait SignatureValidHandler extends Handler{
override def handle(data:String): Unit ={
println("check signature:"+data)
super.handle(data)
}
}
class Person9(val name:String) extends SignatureValidHandler with DataValidHandler{
def sayHello= {
println("Hello," + name)
handle(name)
}
}

Clipboard Image.png

trait的构造机制

trait也是有构造代码的,也就是trait中的,不包含在任何方法中的代码

继承了trait的类的构造机制如下:

 1、父类的构造函数执行;

 2、trait的构造代码执行,多个trait从左到右依次执行;

 3、构造trait时会先构造父trait,如果多个trait继承同一个父trait,则父trait只会构造一次;

 4、所有trait构造完毕之后,子类的构造函数执行

class Person11{
println("Person's constructor!")
}
trait Logger1{println("Logger's constructor!")}
trait MyLogger1 extends Logger1{println("MyLogger's constructor!")}
trait TimeLogger extends Logger1{println("TimeLogger's constructor!")}
class Student11 extends Person11 with MyLogger1 with TimeLogger{
println("Student's constructor!")
}

Clipboard Image.png

推荐 2
本文由 _缘君_ 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册