Scala快速入门(零基础到入门)
基础语法
- 区分大小写 - Scala是区分大小写的,比如标识符
Hello
和hello
在Scala中表示不同的含义(对象)。 - 类名称 - 对于所有类名,第一个字母应为大写。如果使用多个单词来形成类的名称,则每个内部单词的第一个字母应该是大写。
示例 -class MyFirstScalaClass
,class Employee
*类等。 - 方法名称 - 所有方法名称应以小写字母开头。如果使用多个单词形成方法的名称,则每个内部单词的第一个字母应为大写。
示例 -def myMethodName()
,def getName()
等。 - 程序文件名 - 程序文件的名称应与对象名称完全匹配。保存文件时,您应该使用对象名称保存它(记住Scala是区分大小写的),并将
.scala
附加到文件名称的末尾。 (如果文件名和对象名不匹配,则程序将不会编译)。
示例 - 假设HelloWorld
是对象名称。 那么文件应该保存为HelloWorld.scala
。 - def main(args:Array [String]) - Scala程序从
main()
方法开始,这是每个Scala程序的强制性部分。
变量
-
var 用来声明变量,为可变变量。
var myVar : String = "Foo"
-
val 用来声明不可变变量,不能对其进行修改。
val myVal : String = "Foo"
变量的类型在变量名称和等号之前指定。可以通过其数据类型来定义任何类型的Scala变量。如下:
var or val VariableName : DataType = [Initial Value]
eg:val myVal:String = “datatype” //定义一个String类型的不可变变量,值为datatype
不分配初始值,则语法为:
```scala
var myVar :Int;
val myVal : String;
控制结构
-
for循环,格式如下:
for(变量<-表达式) 语句块 其中变量<-表达式被称为生成器 for(i <- 1 to 5) println(i) 输出:1 2 3 4 5
①可以设置步长,i <- 1 to 5 by 2,设置的步长为2,及只取1 3 5。
②还可以设置过滤条件,称为守卫的表达式,比如只输出1到5之间的所有偶数,可以使用以下语句进行过滤:
for(i <- 1 to 5 if i%2==0) println(i)
③for推导式,可以将每次执行时创造的值的集合返回,集合的类型由生成器中的集合类型确定。通过for循环遍历一个或多个集合,对集合中的元素进行推导,从而计算得到新的集合,用于后续的其他处理。
for(变量<-表达式) yield {语句块}
数据结构
-
Scala有一套丰富的容器(Collection)库,包括列表(List)、数组(Array)、集合(Set)、映射(Map)。
三个包来组织容器类,
scala.collection
、scala.collection.mutable
、scala.collection.immutable
。mutable是可变的集合、immutable是不可变的集合。-
列表(List)
scala的List一旦被定义,其值就不能改变,因此声明List是必须初始化,属于immutable包中。
//可以使用操作符::,在已有列表前端增加元素。 val oherList = "hcx"::strList
执行完上述语句,将产生一个新的List用于存放新的List,原来的strList保持不变。空列表为Nil。
-
集合(Set)
包括可变集和不可变集,分别在两个包中,缺省情况下创建的是immutable包中的不可变集合,如果要声明一个可变集,则需要提前引入
scala.collection.mutable.Set
。 -
映射(Map)
和集合(Set)相似。如果想使用可变映射,需要导入相应的包。不可变映射不能更新、增加映射中元素,如果要更新元素,则需要定义可变的映射,可以使用+=操作来添加新的元素。报错如下图所示。
val university = Map("x" -> "value1","y" -> "value2","z" -> "value3") //取值 university("x") //循环遍历映射 for((k,v)<-映射) 语句块
[外链图片转存失败(img-YIig0iwD-1569465571641)(photo/1568025463950.png)]
-
-
迭代器(Iterator)
- 迭代器包含两个基本操作,next和hasNext。next可以返回迭代器的下一个元素,hasNext用于检测是否还有下一个元素。
- grouped和sliding两个方法返回迭代器。grouped方法返回元素的增量分块,sliding方法返回一个元素的滑块分块。
-
数组(Array)
定长数组Array
val intValueArr = new Array[Int](3) intValueArr(0) = 12 intValueArr(1) = 24 intValueArr(2) = 48 val myStrArr = new Array[String](3) myStrArr(0) = "value1" myStrArr(1) = "value2" myStrArr(2) = "value3" //二维数组和三维数组 val myMatrix = Array.ofDim[Int](3,4) 类型实际是Array[Array[Int]] //访问元素 myMatrix(0)(1)
变长数组需要导入包,ArrayBuffer
import scala.collection.mutable.ArrayBuffer val aMutableArr = ArrayBuffer(10,20,30) aMutableArr += 40 aMutableArr.insert(2,60,40) aMutableArr -= 40 var temp = aMutableArr.remove(2)
-
元祖(Tuple)是不同类型的值的聚集。元组和列表不同,列表中各个元素必须是相同类型,而元祖可以包含不同类型的元素。
使用
_
来访问元祖中元素。val tuple = ("value1",10,521.1) println(tuple._1) println(tuple._2)
[外链图片转存失败(img-TNh6WQFD-1569465571643)(photo/1568164025539.png)]
类
class Xxx{ //定义类的字段和方法 }
- 当类没有参数传递时,可以通过new Xxx 来生成对象,当需要传递对象时,需要在 new Xxx()括号中输入需要传递的参数。另外,新建对象后,调用其中的方法时,如果是无参方法,可以省力方法名后面的圆括号。
- scala中没有提供getter和setter方法,当成员设置为私有属性时,可以通过定义方法来对这个私有成员进行访问和修改。
-
辅助构造器
scala构造器包含一个主构造器和若干个辅助构造器。
辅助构造器的名称为this,每个辅助构造器都必须调用一个此前已经定义的辅助构造器或者主构造器。
class Counter{ private var value = 0 private var name = "" private var mode = 1 def this(name:String){ //第一个辅助构造器 this() //调用主构造器 this.name = name } def this(name : String, mode : Int){ //第二个辅助构造器 this(name) //调用前一个辅助构造器 this.mdoe = mode } def increment(step: Int):Unit = {value += step} def current():Int = {value} def info():Unit = {printf("Name:%s and mode is %d\n",name,mode)} } object MyCounter{ def main(args:Array[String]){ val myCounter1 = new Counter val myCounter2 = new Counter("Runner") val myCounter3 = new Counter("Timer",2) myCounter1.info myCounter1.increment(1) printf("Current Value is : %d\n",myCounter1.current) } }
-
单例对象和伴生对象
使用object关键字实现单例对象,具备和Java静态方法同样的功能。
object Person{ private var lastId = 0 def newPersonId() = { lastId += 1 lastId } }
在Java中,经常需要用到同时包含实例方法和静态方法的类,在Scala中可以通过伴生对象来实现。
当单例对象与某个类具有相同的名称时,它被称为这个类的伴生对象。
注意:类和它的伴生对象必须存在于同一个文件中,而且可以相互访问私有成员(字段和方法)
-
apply方法和update方法
在scala中,apply方法和update方法都会遵循相关的约定被调用,约定如下:
- 用括号传递给变量(对象)一个或多个参数时,scala会把它转换成对apply方法的调用
- 当对带有括号并包括一到若干参数的对象就那些赋值时,编译器将调用对象的update方法,在调用时,是把括号里的参数和等号右边的对象一起作为update方法的输入参数来执行调用。
class TestApply{ } class ApplyTest{ def apply() = println("apply mehod in class is called!") def greetingOfClass : Unit = { println("Greeting!") } } object ApplyTest{ def apply() = { println("apply method in object is called") new ApplyTest() } } object TestApply{ def main(args:Array[String]){ val a = ApplyTest() //调用伴生对象中的apply方法 a.greetingOfClass a() //这里调用伴生类中的apply方法 } }
执行结果:
apply method in object is called Greeting! apply mehod in class is called!
update方法类似,比如:
val mystrArr = new Array[String](3) mystrArr(0) = "BigData" mystrArr(1) = "Hadoop" mystrArr(2) = "Spark"
上述采用圆括号的形式,mystrArr(0)是因为存在update方法的机制。
继承
scala中的继承与Java有着显著的不同:
- 重写一个非抽象方法必须使用override修饰符。
- 只有主构造器可以调用超类的主构造器。
- 在子类中重写超类的抽象方法时,不需要使用override关键字。
- 可以重写超类中的字段。
下面构造一个抽象类:
定义注意:
- 定义一个抽象类,需要使用关键字abstract
- 定义一个抽象类的抽象方法,不需要关键字abstract,只要把方法体空着
- 抽象类中定义的字段,只要没有给出初始化值,就表示一个抽象字段,但是,抽象字段必须要声明类型。
abstract class Car{ //抽象类,不能直接被实例化
val carBrand:String //字段没有初始化值,就是一个抽象字段
def info() //抽象方法,不需要使用abstract关键字
def greeting() {println("my Car!")}
}
扩展类继承Car类
class BMWCar extends Car{
override val carBrand = "BMW"
def info() {printf("this is a %s car.",carBrand)}
overrride def greeting() {println("my BMW car!")} //重写超类非抽象方法,必须使用override关键字
}
特质(trait)
scala中的特质相当于Java中的接口,特质定义使用关键字trait,定义好特质后,就可以使用extends或with关键字把特质混入类中。
trait CarId{
var id : Int
def currentId:Int
}
trait CarGreeting{
def greeting(msg : String){println(msg)}
}
class BMWCarId extends CarId with CarGreeting{
override var id = 10000
def currentId() :Int = {id +1;id}
}
object MyCar{
def main(args:Array[String]){
val myCarId1 = new BMWCarId()
myCarId1.greeting("my first Car")
printf("My first CarId is %d\n",myCarId1.currentId)
}
}
模式匹配
最常用于match语句中。
val colorNum = 1
val colorStr = colorNum match{
case 1 => "red"
case 2 => "green"
case _ => "not Allowed"
}
println(colorStr)
//使用变量
val colorNum = 4
val colorStr = colorNum match{
case 1 => "red"
case 2 => "green"
case unexpected => unexpected + "is not Allowed"
}
println(colorStr)
case类是一种特殊的类,经过优化以被用于模式匹配。
case class Car(brand : String,price : Int)
val Car1 = new Car("BYD",89000)
val Car2 = new Car("BMW",120000)
val Car3 = new Car("benz",150000)
for(car <- List(Car1,Car2,Car3)){
car match{
case Car("BYD",89000) => println("Car1")
case Car("BMW",120000) => println("Car2")
case Car("benz",150000) => println("Car3")
}
}
函数定义
[外链图片转存失败(img-Vii1BCfr-1569465571643)(photo/1568172061848.png)]
-
匿名函数(Lamda表达式)
(num : Int) => num * 2 (参数) => 表达式 // 如果参数只有一个,参数的圆括号可以省略 val myNumFunc: Int => Int = (num:Int) => num * 2 // scala具有类型推断机制,可以将表达式改为下列 val myNumFunc = (num:Int) => num * 2
-
闭包(反映了一个从开放到封闭的过程)
var more = 1 val addMore = (x:Int) => x + more addMore(10) //输出结果为11
每个闭包都会访问闭包活跃的元素。
-
占位符语法
val numList = List(-3,-1,0,1,3,9) numList.filter(x => x>0) numList.filter(_ > 0) //可以使用_来作为一个或多个参数的占位符
集合操作
-
遍历操作
映射(Map)、列表(List)的遍历,可以使用for循环进行遍历,也可以使用foreach进行遍历。
-
map和flatMap操作
- map操作是针对集合的典型变换操作,它将某个函数应用到集合中的每个元素,并产生一个结果集合。
val books = List("Hadoop","Hive","hdfs") book.map(s => s.toUpperCase) List(HADOOP,HIVE,HDFS)
2. flatMap会传入一个函数,该函数对每个输入都会返回一个集合(而不是一个元素),然后,flatMap把生成的多个集合拍扁称为一个集合。
val books = List("Hadoop","Hive","hdfs") book flatMap(s => s.toList) List[Char] = List(H,a,o,o,p,H,i,v,e,h,d,f,s)
上面的flatMap执行时,会把books中的每个元素都调用toList,生成List[Char],最终,多个Char的集合被拍扁成一个集合。
-
filter操作
遍历一个集合并从中获取满足指定条件的元素组成一个新的集合,scala中可以通过filter操作来实现。
val university = Map("key1" -> "value1","key2" -> "value2","hcx" -> "hcx3") val universityOfHcx = university filter {kv => kv._2 contains "hcx"} val universityOfH = university filter {kv => kv._2 startsWith "h"}
-
reduce操作
使用reduce这种二元操作对集合中的元素进行归纳,reduce包含reduceLeft和reduceRight两种操作,前者从集合的头部开始操作,后者从集合的尾部开始操作。
val list = List(1,2,3,4,5) list.reduceLeft(_ + _) 15 list.reduceRight(_ + _) 15
-
fold操作
fold折叠操作和reduce操作类似,fold操作需要从一个初始的值开始,并以该值作为上下文,处理集合中的每个元素。
val list = List(1,2,3,4,5) list.fold(10)(_ * _) 1200
WordCount实例
import java.io.File import scala.io.Source object WordCount{ def main(args: Array[String]) : Unit = { val dirfile = new File("D:\\wordcount") val files = dirfile.listFiles for(file <- files) println(file) val listFiles = files.toList val wordsMap = scala.collection.mutable.Map[String,Int]() listFiles.foreach(file => Source.fromFile(file).getLines().foreach(line => line.split(" ").foreach( word =>{ if(wordsMap.contains(word){ wordsMap(word) += 1 }else{ wordsMap += (word -> 1) } ) }))) println(wordsMap) for((k,v) <- wordsMap) println(k + ":" + v) } }