[TOC]html
Scala是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各类特性。java
Scala运行在Java虚拟机上,并兼容现有的Java程序。linux
Scala源代码被编译成Java字节码,因此它能够运行于JVM之上,并能够调用现有的Java类库。程序员
函数编程范式更适合用于Map/Reduce和大数据模型,它摒弃了数据与状态的计算模型,着眼于函数自己,而非执行的过程的数据和状态的处理。函数范式逻辑清晰、简单,很是适合用于处理基于不变数据的批量处理工做,这些工做基本都是经过map和reduce操做转换数据后,生成新的数据副本,而后再进行处理。es6
像Spark,Flink等都是采用Scala开发的,因此学习好大数据,掌握scala是必要的。算法
官网:http://scala-lang.org/ 数据库
一、安装JDK 二、JAVA_HOME, PATH 三、Maven 四、SCALA SDK 下载地址:http://scala-lang.org/download/all.html 这里选择的版本为Scala 2.10.5,分为windows和linux版本 五、配置SCALA_HOME 在windows环境变量中添加SCALA_HOME 六、验证 scala -version Scala code runner version 2.10.5 -- Copyright 2002-2013, LAMP/EPFL
object HelloWorld { def main(args:Array[String]):Unit = { println("Hello World!") } }
保存为HelloWorld.scala
,而后再执行下面两步便可:apache
scalac HelloWorld.scala scala HelloWorld
1.可拓展 面向对象 函数式编程 2.兼容JAVA 类库调用 互操做 3.语法简洁 代码行短 类型推断 抽象控制 4.静态类型化 可检验 安全重构 5.支持并发控制 强计算能力 自定义其余控制结构
一、都是基于JVM虚拟机运行的编程
Scala编译以后的文件也是.class,都要转换为字节码,而后运行在JVM虚拟机之上。windows
二、Scala和Java相互调用
在Scala中能够直接调用Java的代码,同时在Java中也能够直接调用Scala的代码
三、Java8 VS Scala
1)Java8(lambda)没有出来以前,Java只是面向对象的一门语言,可是Java8出来之后,Java就是一个面向对象和面向函数的混合语言了。
2)首先咱们要对Scala进行精肯定位,从某种程度上讲,Scala并非一个纯粹的面向函数的编程语言,有人认为Scala是一个带有闭包的静态面向对象语言),更准确地说,Scala是面向函数与面向对象的混合。
3)Scala设计的初衷是面向函数FP,而Java起家是面向对象OO,如今二者都是OO和FP的混合语言,是否能够这么认为:Scala= FP + OO,而Java =OO + FP?
因为面向对象OO和面向函数FP两种范式是相似横坐标和纵坐标的二者不一样坐标方向的思考方式,相似数据库和对象之间的不匹配阻抗关系,二者若是结合得很差恐怕就不会产生1+1>2的效果。
面向对象是最接近人类思惟的方式,而面向函数是最接近计算机的思惟方式。若是你想让计算机为人的业务建模服务,那么以OO为主;若是你但愿让计算机能本身经过算法从大数据中自动建模,那么以FP为主。因此,Java可能还会在企业工程类软件中占主要市场,而Scala则会在科学计算大数据分析等领域抢占Java市场,好比Scala的Spark大有替代Java的Hadoop之趋势。
一、Scala解释器读到一个表达式,对它进行求值,将它打印出来,接着再继续读下一个表达式。这个过程被称作读取--求值--打印--循环,即:REPL。
从技术上讲,scala程序并非一个解释器。实际发生的是,你输入的内容被快速地编译成字节码,而后这段字节码交由Java虚拟机执行。正由于如此,大多数scala程序员更倾向于将它称作“REPL”
二、scala
scala>"Hello" res1: String = Hello scala> 1+2 res5: Int = 3 scala>"Hello".filter(line=>(line!='l')) res2: String = Heo
你应该注意到了在咱们输入解释器的每一个语句后,它会输出一行信息,相似res0:
java.lang.String
= Hello。输出的第一部分是REPL给表达式起的变量名。在这几个例子里,REPL为每一个表达式定义了一个新变量(res0到res3)。输出的第二部分(:后面的部分)是表达式的静态类型。第一个例子的类型是java.lang.String,最后一个例子的类型则是scala.util.matching.Regex。输出的最后一部分是表达式求值后的结果的字符串化显示。通常是对结果调用toString方法获得的输出,JVM给全部的类都定义了toString方法。
一、Scala中没有static的类,可是他有一种相似的伴生对象
二、字段:
字段/变量的定义Scala中使用 var/val 变量/不变量名称 : 类型的方式进行定义,例如:
var index1 : Int = 1 val index2 : Int = 1
其中var与val的区别在于,var是变量,之后的值还能够改变,val的值只能在声明的时候赋值,可是val不是常量,只能说是不变量或只读变量。
三、你们确定会以为这种var/val名称 : 类型的声明方式太过于繁琐了,有另一种方式
因此你在声明字段的时候,可使用编译器自动推断类型,即不用写: 类型
,例如:
var index1 = 1 (类型推断) val index2 = 1
这个例子演示了被称为类型推断(type inference)的能力,它能让scala自动理解你省略了的类型。这里,你用int字面量初始化index1,所以scala推断index2的类型是Int。对于能够由Scala解释器(或编译器)自动推断类型的状况,就没有必要非得写出类型标注不可。
四、a.方法(b)
这里的方法是一个带有2个参数的方法(一个显示的和一个隐式的)
1.to(10), 1 to 10, 1 until 10 scala> 1.to(10) res19: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> 1 to 10 res20: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> 1 until 10 res21: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
五、其实根据函数式编程思想中,var变量是个很差的存在,Scala中推荐你们尽量的采用val的不变量,主要缘由是
scala拥有和java同样的数据类型,和java的数据类型的内存布局彻底一致,精度也彻底一致。
下面表格中是scala支持的数据类型:
上表中列出的数据类型都是对象,也就是说scala没有java中的原生类型。也就是说scala没有基本数据类型与包装类型的概念。
Scala里,你能够舍弃方法调用的空括号。例外就是若是方法带有反作用就加上括号,如println(),不过若是方法没有反作用就能够去掉括号,如String上调用的toLowerCase:
scala> "Hello".toLowerCase res22: String = hello scala> "Hello".toLowerCase() res23: String = hello
Scala里,你能够舍弃方法调用的空括号。例外就是若是方法带有反作用就加上括号,如println(),不过若是方法没有反作用就能够去掉括号,如String上调用的toLowerCase:
你能够经过中缀操做符,加号(+),减号(-),乘号(*),除号(/)和余数(%),在任何数类型上调用数学方法, scala中的基础运算与java一致
scala> 1.2 + 2.3 res6: Double = 3.5 scala> 3 - 1 res7: Int = 2 scala> 'b' - 'a' res8: Int = 1 scala> 2L * 3L res9: Long = 6 scala> 11 / 4 res10: Int = 2 scala> 11 % 4 res11: Int = 3 scala> 11.0f / 4.0f res12: Float = 2.75 scala> 11.0 % 4.0 res13: Double = 3.0
你能够用关系方法:大于(>),小于(<),大于等于(>=)和小于等于(<=)比较数类型,像等号操做符那样,产生一个Boolean结果。另外,你可使用一元操做符!(unary_!方法)改变Boolean值
scala> 1 > 2 res16: Boolean = false scala> 1 < 2 res17: Boolean = true scala> 1.0 <= 1.0 res18: Boolean = true scala> 3.5f >= 3.6f res19: Boolean = false scala> 'a' >= 'A' res20: Boolean = true scala> val thisIsBoring = !true thisIsBoring: Boolean = false scala> !thisIsBoring res21: Boolean = true
scala> ("he" + "llo") == "hello" res33: Boolean = true === 比较2个不一样的对象 scala> 1 == 1.0 res34: Boolean = true
其比较的是值,而不是Java概念中的地址值。另外第二个例子,1 == 1.0,会为true,是由于1会作类型的提高变为1.0
一、Scala的if/else语法结构和Java或者C++同样,不过,在Scala中if/else表达式有值,这个值就是跟在if或else以后的表达式的值
val x = 3 if(x > 0) 1 else -1
上述表达式的值是1或-1,具体是哪个取决于x的值,同时你也能够将if/else表达式的值复制给变量
val s = if(x>0) 1 else -1 // 这与以下语句的效果是同样的 if(x >0) s = 1 else s =-1
不过,第一种写法更好,由于它能够用来初始化一个val,而在第二种写法当中,s必须是var
二、val result = if(personAge > 18) "Adult" else 0
其中一个分支是java.lang.string,另一个类型是Int, 因此他们的公共超类是Any
三、若是else丢失了
if(x>0) 1
那么有可能if语句没有输出值,可是在Scala中,每一个表达式都有值,这个问题的解决方案是引入一个Unit类,写做(),不带else语句的if语句等同于if(x>0) 1else ()
一、在Java和C++中,每一个语句都已分号结束。可是在Scala中—与JavaScript和其它语言相似—行尾的位置不须要分号。一样,在}、else以及相似的位置也不须要写分号。
不过,若是你想在单行中写下多个语句,就须要将他们以分号隔开
If(n>0){r = r *n ; n-=1}
咱们须要用分号将r = r *n 和n -=1隔开,因为有},在第二个语句以后并不须要写分号。
二、分行显示
If(n>0){ r = r *n n-=1 }
scala> val n = 9 n: Int = 9 scala> var f = 0 f: Int = 0 scala> var m = 5 m: Int = 5 scala> val d = if(n < 18){f = f + n ; m = m + n ; f + m} d: AnyVal = 23
若是要打印一个值,咱们用print或println函数。后者在打印完内容后会追加一个换行符。举例来讲,
print("Answer:") println(42)
与下面的代码输出的内容相同:
println("Answer:" + 42)
另外,还有一个带有C风格格式化字符串的printf函数:system.in
printf("Hello,%s! You are %d years old.\n", "Fred", 42)
你能够用readLine函数从控制台读取一行输入。若是要读取数字、Boolean或者是字符,能够用readInt、readDouble、readByte、readShort、readLong、readFloat、readBoolean或者readChar。与其余方法不一样,readLine带一个参数做为提示字符串:
scala> val name = readLine("Please input your name:") Please input your name:name: String = xpleaf
一个简单的输入输出案例以下:
val name = readLine("What's Your name: ") print("Your age: ") val age = readInt() if(age > 18) { printf("Hello, %s! Next year, your will be %d.\n", name, age + 1) }else{ printf("Hello, %s! You are still a children your will be %d.\n", name, age + 1 +" Come up!!!") } }
Scala拥有与Java和C++相同的while和do循环。例如:
object _04LoopDemo { def main(args: Array[String]):Unit = { var sum = 0 var i = 1 while(i < 11) { sum += i i += 1 } } }
Scala 也有do-while循环,它和while循环相似,只是检查条件是否知足在循环体执行以后检查。例如:
object _04LoopDemo { def main(args: Array[String]):Unit = { var sum = 0 var i = 1 do { sum += i i += 1 } while(i <= 10) println("1+...+10=" + sum) } }
登陆用户名密码的游戏:三次机会,从控制台输入输入用户名密码,若是成功登陆,返回登陆成功,失败,则反馈错误信息!以下:
object _05LoopTest { def main(args: Array[String]):Unit = { val dbUser = "zhangyl" val dbPassword = "uplooking" var count = 3 while(count > 0) { val name = readLine("亲,请输入用户名:") val pwd = readLine("请输入密码:") if(name == dbUser && pwd == dbPassword) { println("登录成功,正在为您跳转到主页呐," + name + "^_^") // count = 0 return } else { count -= 1 println("连用户名和密码都记不住,你一天到底在弄啥嘞!您还有<" + count + ">次机会") } } } }
Scala没有与for(初始化变量;检查变量是否知足某条件;更新变量)
循环直接对应的结构。若是你须要这样的循环,有两个选择:一是使用while循环,二是使用以下for语句:
for (i <- 表达式)
让变量i
遍历< -
右边的表达式的全部值。至于这个遍历具体如何执行,则取决于表达式的类型。对于Scala集合好比Range而言,这个循环会让i依次取得区间中的每一个值。
遍历字符串或数组时,你一般须要使用从0到n-1的区间。这个时候你能够用until方法而不是to方法。util方法返回一个并不包含上限的区间。
val s = "Hello" var sum = 0 for (i <- 0 until s.length) { // i的最后一个取值是s.length - 1 sum += s(i) // 注意此时为对应的ASCII值相加 }
在本例中,事实上咱们并不须要使用下标。你能够直接遍历对应的字符序列:
var sum = 0 for (ch <- "Hello") sum += ch
说明:Scala并无提供break或continue语句来退出循环。那么若是须要break时咱们该怎么作呢?有以下几个选项:
object _06LoopBreakTest { def main(args: Array[String]):Unit = { import scala.util.control.Breaks._ var n = 15 breakable { for(c <- "Spark Scala Storm") { if(n == 10) { println() break } else { print(c) } n -= 1 } } } }
一、除了for循环的基本形态以外,Scala也提供了其它丰富的高级特性。好比能够在for循环括号里同时包含多组变量 <- 表达式
结构,组之间用分号分隔
for (i <- 1 to 3;j <- 1 to 3) print ((10 * i +j) + " ")
for循环的这种结构相似Java中的嵌套循环结构。
例如要实现一个九九乘法表,使用基本的for循环形态时,代码以下:
object _07LoopForTest { def main(args: Array[String]):Unit = { for(i <- 1 to 9) { for(j <- 1 to i){ var ret = i * j print(s"$i*$j=$ret\t") } // println() System.out.println() } } }
而使用高级for循环时,以下:
object _07LoopForTest { def main(args: Array[String]):Unit = { // for(i <- 1 to 9; j <- 1 to 9 if j <= i) { for(i <- 1 to 9; j <- 1 to i) { var ret = i * j print(s"$i*$j=$ret\t") if(i == j) { println } } } }
二、if循环守卫
能够为嵌套循环经过if表达式添加条件:
for (i <- 1 to 3; j <- 1 to 3 if i != j) print ((10 * i + j) + " ")
if表达式是否添加括号,结果无变化:
for (i <- 1 to 3; j <- 1 to 3 if (i != j)) print ((10 * i + j) + " ")
注意:注意在if以前并无分号。
三、For推导式
Scala中的yield不像Ruby里的yield,Ruby里的yield象是个占位符。Scala中的yield的主要做用是记住每次迭代中的有关值,并逐一存入到一个数组中。用法以下:
for {子句} yield {变量或表达式}
1)若是for循环的循环体以yield开始,则该循环会构造出一个集合,每次迭代生成集中的一个值:
scala> for(i <- 1 to 10) yield println(i % 3) 1 2 0 1 2 0 1 2 0 1 res47: scala.collection.immutable.IndexedSeq[Unit] = Vector((), (), (), (), (), (), (), (), (), ()) scala> val ret1 = for(i <- 1 to 10) yield(i) ret1: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> for(i <- ret1) println(i) 1 2 3 4 5 6 7 8 9 10
2) for 循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合
Scala中for循环是有返回值的。若是被循环的是Map,返回的就是Map,被循环的是List,返回的就是List,以此类推。
yield关键字的简短总结:
1.针对每一次for循环的迭代, yield会产生一个值,被循环记录下来(内部实现上,像是一个缓冲区) 2.当循环结束后, 会返回全部yield的值组成的集合 3.返回集合的类型与被遍历的集合类型是一致的
Scala的异常处理和其它语言好比Java相似,一个方法能够经过抛出异常的方法而不返回值的方式终止相关代码的运行。调用函数能够捕获这个异常做出相应的处理或者直接退出,在这种状况下,异常会传递给调用函数的调用者,依次向上传递,直到有方法处理这个异常。
object _01ExceptionDemo { def main(args:Array[String]):Unit = { import scala.io.Source import java.io.FileNotFoundException try { val line = Source.fromFile("./wordcount.txt").mkString val ret = 1 / 0 println(line) } catch { case fNFE:FileNotFoundException => { println("FileNotFoundException:文件找不到了,传的路径有误。。。") } case e:Exception => { println("Exception: " + e.getMessage) } case _ => println("default处理方式") } finally { println("this is 必需要执行的语句") } } }
Scala除了方法外还支持函数。方法对对象进行操做,函数不是。要定义函数,你须要给出函数的名称、参数和函数体,就像这样:
下面是须要注意的问题:
def myFirstFunction(name:String, age:Int) : String ={ println("name=> " + name +"\t age=> " + age) "Welcome " + name + " to our world, your age is => " + age }
在本例中咱们并不须要用到return。咱们也能够像Java或C++那样使用return,来当即从某个函数中退出,不过在Scala中这种作法并不常见。
提示:虽然在带名函数中使用return并无什么不对(除了浪费7次按键动做外),咱们最好适应没有return的日子。很快,你就会使用大量匿名函数,这些函数中return并不返回值给调用者。它跳出到包含它的带名函数中。咱们能够把return当作是函数版的break语句,仅在须要时使用。
def myFirstFunctionWithoutFeedBackValues: Unit ={ println("This is our first function without feedback values") }
def printMsg(name:String) = println("Hello, " + name +", welcome happy day!!!")
上面所对应函数的案例以下:
/** 注意: 1.scala中若是要给一个函数作返回值,能够不用return语句, 使用也是能够的,可是scala语言强烈建议你们不要用 由于scala崇尚的是简约而不简单 通常也就是将这个return当作break来使用 2.在scala中在同一条语句中,变量名不能和引用函数名重复 3.使用递归时,须要指定返回值类型 */ object _02FunctionDemo { def main(args:Array[String]):Unit = { // val ret = myFirstFunc("xpleaf", 23) // println(ret) // show("xpleaf", 23) // val ret = singleLineFunc("xpleaf") // println(ret) val ret = factorial(5) println(ret) } def testFunc(num:Int) = { if(num == 1) { 1 } 10 } // 使用递归来求解5的阶乘 def factorial(num:Int):Int = { if(num == 1) { 1 } else { num * factorial(num - 1) } // 若是num * factorial(num - 1)不写在else里面,则须要return,不然会有异常, // testFunc中则不会这样,因此猜想递归函数才会有此问题 } // 单行函数 def singleLineFunc(name:String) = "hello, " + name // 有"="号,有返回值,自动判断返回值类型 // def singleLineFunc(name:String) {"hello, " + name} // 没有“=”号,没有返回值 // def singleLineFunc(name:String):Unit = "hello, " + name // 有"="号,但指定返回值为空,至关于没有返回值 // def singleLineFunc(name:String) = println("hello, " + name) // 没有返回值,println并非一个表达式 // 没有返回值的函数 def show(name:String, age:Int):Unit = { println(s"My name is $name, and I'm $age years old.") } // 有返回值的函数 def myFirstFunc(name:String, age:Int):String = { println(s"My name is $name, and I'm $age years old.") // return "Welcome you " + name + ", make yourself..." "Welcome you " + name + ", make yourself..." } }
一、咱们在调用某些函数时并不显式地给出全部参数值,对于这些函数咱们可使用默认参数。
def sayDefaultFunc(name: String, address: String = "Beijing", tellphone: String ="139****") ={ println(name +"address=> " + address +"\t tellphone=> " + tellphone) }
二、不指定具体参数时:给出默认值
sayDefaultFunc("Garry")
三、若是相对参数的数量,你给出的值不够,默认参数会从后往前逐个应用进来。
sayDefaultFunc("Garry","Shanhai")
四、给出所有的参数值
sayDefaultFunc("Garry","Shanhai","13709872335")
五、带名参数可让函数更加可读。它们对于那些有不少默认参数的函数来讲也颇有用。
sayDefaultFunc(address ="上海", tellphone="12109876543",name="Tom")
六、你能够混用未命名参数和带名参数,只要那些未命名的参数是排在前面的便可:
sayDefaultFunc("Tom",tellphone="12109876543",address= "上海")
前面咱们介绍的函数的参数是固定的,本篇介绍Scala函数支持的可变参数列表,命名参数和参数缺省值定义。
重复参数
Scala在定义函数时容许指定最后一个参数能够重复(变长参数),从而容许函数调用者使用变长参数列表来调用该函数,Scala中使用“*”来指明该参数为重复参数。例如
Scala:
def echo(args: String*) = { for (arg <- args) println(arg) }
Java:
public staticvoid echo(String ...args){ for(String str: args){ System.out.println(str) } }
一、在函数内部,变长参数的类型,实际为一数组,好比上例的String* 类型实际为 Array[String]。
然而,现在你试图直接传入一个数组类型的参数给这个参数,编译器会报错:
val arr= Array("Spark","Scala","AKKA") Error message as bellows: error: type mismatch;
二、为了不这种状况,你能够经过在变量后面添加_*来解决,这个符号告诉Scala编译器在传递参数时逐个传入数组的每一个元素,而不是数组总体
val arr= Array("Spark","Scala","AKKA") echo(arr:_*)
一个例子以下:
object _04FunctionDemo { def main(args:Array[String]):Unit = { show("Spark", "Strom", "Hadoop") var arr = Array("Spark", "Strom", "Hadoop") show(arr:_*) } def show(strs:String*) { for(str <- strs) { println(str) } } }
一、Scala对于不返回值的函数有特殊的表示法。若是函数体包含在花括号当中但没有前面的=号,那么返回类型就是Unit。这样的函数被称作过程(procedure)。过程不返回值,咱们调用它仅仅是为了它的反作用。
案例:
如:咱们须要打印一些图案,那么能够定义一个过程:
def draw(str:String) { println("-------") println("|"+" "+"|") println("|"+ str +"|") println("|"+" "+"|") println("-------") }
二、咱们也能够显示指定的函数的返回值:Unit
一、当val被声明为lazy时,它的初始化将被推迟,直到咱们首次对它取值。例如,
lazy val lines= scala.io.Source.fromFile("D:/test/scala/wordcount.txt").mkString
二、若是程序从不访问lines ,那么文件也不会被打开。但故意拼错文件名。在初始化语句被执行的时候并不会报错。不过,一旦你访问words,就将会获得一个错误提示:文件未找到。
三、懒值对于开销较大的初始化语句而言十分有用。它们还能够应对其余初始化问题,好比循环依赖。更重要的是,它们是开发懒数据结构的基础。(spark 底层严重依赖这些lazy)
四、加载(使用它的时候才会被加载)
println(lines)
一个例子以下:
object _05LazyDemo { def main(args:Array[String]):Unit = { import scala.io.Source import java.io.FileNotFoundException try { lazy val line = Source.fromFile("./wordcountq.txt").mkString // wordcountq.txt这个文件并不存在 // println(line) } catch { case fNFE:FileNotFoundException => { println("FileNotFoundException:文件找不到了,传的路径有误。。。") } case e:Exception => { println("Exception: " + e.getMessage) } } finally { println("this is 必需要执行的语句") } } }