Scala简介
Scala安装验证
Scala IDE
Scala基础知识和语法
Scala控制结构
Scala函数
Scala数组&集合
Scala是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。
Scala运行在Java虚拟机上,并兼容现有的Java程序。
Scala源代码被编译成Java字节码,所以它可以运行于JVM之上,并可以调用现有的Java类库。
函数编程范式更适合用于Map/Reduce和大数据模型,它摒弃了数据与状态的计算模型,着眼于函数本身,而非执行的过程的数据和状态的处理。函数范式逻辑清晰、简单,非常适合用于处理基于不变数据的批量处理工作,这些工作基本都是通过map和reduce操作转换数据后,生成新的数据副本,然后再进行处理。
像Spark,Flink,kafka等都是采用Scala开发的,所以学习好大数据,掌握scala是必要的。
官网:http://scala-lang.org/
下载路径
https://www.scala-lang.org/download/2.11.8.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a9Sdusq1-1574685378473)(assets/1572489079814.png)]
安装
要求
不能安装在带有中文或者空格的目录下面,不然会报错,scala命令找不到。
安装
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6M5FzqQR-1574685378476)(assets/1572489236844.png)]
jdk必须安装
添加环境变量
配置SCALA_HOME
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c80Voa4A-1574685378476)(assets/1572489342550.png)]
添加进path
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EwDRPZ8I-1574685378477)(assets/1572489387728.png)]
验证
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QGNKatEg-1574685378477)(assets/1572489488133.png)]
出现上述效果,即scala安装成功。
入门案例----Hello World.
object HelloWorld {
def main(args:Array[String]): Unit = {
println("Hello World~")
}
}
/*
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World~");
}
}
*/
编译执行
E:\work\1906-bd\2019-10-31-[scala]\代码>scalac HelloWorld.scala
E:\work\1906-bd\2019-10-31-[scala]\代码>scala HelloWorld
Hello World~
现象:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-318qU3Ek-1574685378478)(assets/1572490150863.png)]
.java文件经过javac编译之后只会产生一个.class字节码文件,但是.scala文件经过scalac编译之后会产生两个.class文件。
HelloWorld.class
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ltb26vAG-1574685378478)(assets/1572490315744.png)]
HelloWorld$.class
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R2fU5ZpL-1574685378479)(assets/1572490338172.png)]
通过反编译软件,我们可以看出来,HelloWorld.scala,在执行的时候需要调用哪个.class,需要调用不带 的 . c l a s s 字 节 码 文 件 , 带 的.class字节码文件,带 的.class字节码文件,带的.class是scala编译之后的文件,不带$的.class是jvm和scala文件运行的纽带。
可拓展
面向对象
函数式编程
兼容JAVA
类库调用
互操作
语法简洁
代码行短
类型推断
抽象控制
静态类型化
可检验
安全重构
支持并发控制
强计算能力
自定义其他控制结构
1、都是基于JVM虚拟机运行的
Scala编译之后的文件也是.class,都要转换为字节码,然后运行在JVM虚拟机之上。
2、Scala和Java相互调用
在Scala中可以直接调用Java的代码,同时在Java中也可以直接调用Scala的代码
3、Java 8 VS Scala
1)Java 8(lambda)没有出来之前,Java只是面向对象的一门语言,但是Java 8出来以后,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之趋势。
1、Scala解释器读到一个表达式,对它进行求值,将它打印出来,接着再继续读下一个表达式。这个过程被称做读取read–求值eval–打印print–循环loop,即:REPL。
从技术上讲,scala程序并不是一个解释器。实际发生的是,你输入的内容被快速地编译成字节码,然后这段字节码交由Java虚拟机执行。正因为如此,大多数scala程序员更倾向于将它称做“REPL”
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iQTyxtNI-1574685378480)(assets/1572492506923.png)]
scala的变量定义分为两种方式,分别需要使用关键字val和var。
val
val a = 3
var
var a = 3
val和var都可以用来定义变量,唯一的区别就在于,val定义的变量是不可变,而var定义的变量是可变的。不可变的量确实类似常量(被final修饰的变量),但不叫常量,只能叫不可变量的量。
在今后的开发过程中,能使用val就使用val,var尽量不用,因为scala编程的特点,是在创建变量的时候可以省略数据类型,val有助于进行类型的推断。var在操作过程中不利于变量的类型。
scala> val c = 4
c: Int = 4
scala> c = 5
:12: error: reassignment to val
c = 5
一个完整的变量的定义如下:
val a:Int = 3
其中val为变量修饰符,a为变量名,Int为a的数据类型,其中需要将变量名和数据类型使用:连接,=右侧为变量的值。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SvdZGbz0-1574685378480)(assets/1572493461288.png)]
说明:scala拥有和java一样的数据类型,和java的数据类型的内存布局完全一致,精度也完全一致。需要说明一点的是Any和AnyRef,其中Any是对象的超类,AnyRef是类的超类,实例/对象是类的具体化。
在scala调用一些方法或者函数的时候,如果方法或者函数是空参的,可以省略掉().
在scala中一行表示的结尾不像java需要使用";",scala可以省略
scala中的if表达式,基本和java中的if表达式一模一样,唯一的区别就是scala中的if、if-else、if-elseif-else有返回值!
scala> val ret = if(x < 4) 1 else -1
ret: Int = -1
scala> val ret = if(x < 4) 1 else "afff"
ret: Any = afff
记住:scala中任意的表达式,都有返回值。
如果else丢失了
if(x>0) 1
那么有可能if语句没有输出值,但是在Scala中,每个表达式都有值,这个问题的解决方案是引入一个Unit类,写作(),不带else语句的if语句等同于if(x>0) 1 else ()
表达式的最后一句话,最为表达式的返回值返回。
var f = 3
var n = 10
var m = 1
val d = if(n < 18){f = f + n ; m = m +n ; f+m}
==>d = 24
/*
while --->
1 + .... + 10
java:
int sum = 0;
int n = 1;
while(n <= 10) {
sum += n;
n++;
}
sop();
*/
object _1WhileDemo {
def main(args:Array[String]): Unit = {
var sum = 0
var n = 1
while(n <= 10) {
sum += n
n += 1
}
println("sum= " + sum)
}
}
注意:error: value ++/-- is not a member of Int
scala中不能直接对一个变量执行自增/减,++/–是scala集合的一个函数。
object _2DoWhileDemo {
def main(args:Array[String]): Unit = {
var sum = 0
var n = 1
do {
sum += n
n += 1
}while(n <= 10)
println("sum= " + sum)
}
}
登录用户名密码的游戏:三次机会,从控制台输入输入用户名密码,如果成功登录,返回登录成功,失败,则反馈错误信息!
object _3DoWhileTest {
def main(args:Array[String]): Unit = {
val dbUserName = "pgone"
val dbPassword = "lxl"
var count = 3
do {
var inputUser = readLine("请输入您的姓名:")
var inputPwd = readLine("请输入您的密码:")
if(dbUserName == inputUser && dbPassword == inputPwd) {
//println("欢迎光临:" + inputUser)
println(s"欢迎光临:${inputUser}")
count = -1
} else {
println(s"用户名密码有误,还有${count}次机会,请重输入:")
count -= 1
}
} while(count >= 0)
}
}
在java中终止循环有关键字,continue,break,return,但是在scala中没有前两者continue和break。该如何终止循环呢?
有三种方式来结束循环:
使用循环结束的条件来进行控制,比如上例中的count >= 0
还可以使用breakable函数体来进行控制
import scala.util.control.Breaks._
object _4BreakableDemo {
def main(args:Array[String]): Unit = {
var sum = 0
var n = 1
breakable {
do {
sum += n
n += 1
if(n > 6)
break
}while(n <= 10)
}
println("sum= " + sum)
}
}
在java中有2中写法,普通的for循环for(变量;条件;自增或自减){循环体} or for(类型 变量 : 集合) {}。
scala中的for循环该如何定义呢?
scala中并没有像java中普通for循环的语法结构,更像是高级for循环。
for (变量 <- 集合) {
}
将上述的while循环使用for循环改写
object _5ForDemo {
def main(args:Array[String]): Unit = {
var sum = 0
for(n <- 1 to 10 reverse) {
println(n)
}
//println("sum= " + sum)
}
}
说明:添加reverse主要就是向进行循环的反转
object _6ForDemo {
def main(args:Array[String]): Unit = {
for(y <- 1 until 5) {
for(x <- 1 to 5) {
print("*")
}
println
}
println("-----------^~^----------")
for(y <- 1 until 5; x <- 1 to 5) {
print("*")
if(x == 5)
println
}
/*
*
**
***
****
*/
println("-----------^~^----------")
for(x <- 1 to 5; y <- 1 to 5) {
if(y <= x)
print("*")
if(x == y)
println
}
println("-----------^_^----------")
for(x <- 1 to 5; y <- 1 to 5 if y <= x) {
print("*")
if(x == y)
println
}
}
}
练习题:9*9乘法口诀表。
for(x <- 1 to 9; y <- 1 to 9 if y <= x) {
print(y+"*"+x+"="+(x*y)+"\t")
if(x == y)
println
}
scala的异常体系,在很大程度上沿用java的异常体系的,只不过在书写结构上略有差异。
val i = 1 / 0
println("i= " + i)
上述代码,在执行过程中出现异常:
java.lang.ArithmeticException: / by zero
在java中学习了两种异常的处理方式:throws和try-catch。throws和java中的一模一样,将异常转交给调用者进行处理。try-catch则意味着自己要进行处理,但是编写的方式和java不一样。
import scala.io._
object _7ExceptionDemo {
def main(args:Array[String]): Unit = {
try {
lazy val line = Source.fromFile("E:/data/hello1.txt").mkString
println(line)
val ii = Integer.valueOf("abc")
val i = 1 / 0
println("i= " + i)
} catch {// 模式匹配
case e: ArithmeticException => {
println("ArithmeticException==>" + e.getMessage)
}
case e: NumberFormatException => {
println("NumberFormatException===>" + e.getMessage)
}
case e:RuntimeException => {
println("RuntimeException===>" + e.getMessage)
}
case _ => {
println("通用的处理~")
}
}
}
}
说明:上面的案例中,更进一步的学习了一个关键字lazy(懒加载)
,被lazy所修饰的变量,只有当第一次被使用的时候才会进行初始化,没有使用之前只是记录了存在,检查语法结构是否正确。可以节省一定的资源空间。
Scala除了方法外还支持函数。方法对对象进行操作,函数不是。要定义函数,你需要给出函数的名称、参数和函数体,就像这样:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fc6tsTQO-1574685378480)(assets/1572508083096.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lfWXf4gI-1574685378481)(C:\Users\YGH\AppData\Roaming\Typora\typora-user-images\image-20191125145017459.png)]
1、你必须给出所有参数的类型。不过,只要函数不是递归的,你就不需要指定返回类型。Scala编译器可以通过=符号右侧的表达式的类型推断出返回类型。
2、“=”并不只是用来分割函数签名和函数体的,它的另一个作用是告诉编译器是否对函数的返回值进行类型推断!如果省去=,则认为函数是没有返回值的!
3、在scala中一个函数的最后一句话就是该函数的返回值,不需要使用return,当然加上也行。
object _8FunctionDemo {
def main(args:Array[String]): Unit = {
val ret = add(3, 4)
println("ret: " + ret)
}
def add(first:Int, second:Int) = {
first + second
}
}
说明:函数可以不使用return语句,但是如果使用了return,那返回值类型,必须要加上,否则会报错:
_8FunctionDemo.scala:9: error: method add has return statement; needs result type
return first + second
^
one error found
def add(first:Int, second:Int):Int = {
return first + second
}
单行函数
所谓单行函数,指的是函数体只有一行的函数。
def method = 5 or def method = println(“abcdefg”)
无参函数
函数的参数列表为空。
object _9FunctionDemo {
def main(args:Array[String]): Unit = {
//show()
show
}
def show {
println("Today is a well day")
}
}
注意:定义空参函数的时候,如果函数加了(),在调用的时候,()可以省略;但是如果在定义的时候没有加函数(),在调用的时候,也只能省略,不能加()。
递归函数
在特定条件下,函数调用本身。
阶乘求解:5!
object _10FunctionDemo {
def main(args:Array[String]): Unit = {
val ret = factorial(5)
println("5!= " + ret)
}
/*
5! = 5 * 4 * 3 * 2 * 1
5! = 5 * 4!
4! = 4 * 3!
3! = 3 * 2!
2! = 2 * 1!
1! = 1
*/
def factorial(n:Int):Int = {
if(n == 1)
1
else
n * factorial(n - 1)
}
}
object _11FunctionDemo {
def main(args:Array[String]): Unit = {
address("常军磊", "河南", 13919191119L)
address("old李", telphone = 13838383338L)
address("田月梅", telphone = 1888888888L, province = "湖北")
address(telphone = 1888888888L, name = "田月梅", province = "湖北")
}
def address(name:String, province:String = "bj", telphone:Long) {
println(s"name:${name},\tprovince:${province},\ttelphone:${telphone}")
}
}
scala的函数参数列表,在定义的时候,可以使用有默认值的参数,在做函数调用的时候可以使用带名的参数,所以有时我们是无法通过参数的参数来推断出具体的参数列表顺序。(我们无法通过调用方法时的参数推断具体方法的参数列表的顺序)
object _12FunctionDemo {
def main(args:Array[String]): Unit = {
var ret = add(1, 2)
println("ret = " + ret)
val arr = Array(3, 4, 5)
ret = add(arr: _*)
println("ret = " + ret)
}
/*
def add(a:Int, b:Int) = {
a + b
}
def add(a:Int, b:Int, c:Int) = {
a + b + c
}
*/
//数组类型可变参数
def add(arr:Array[Int]):Int = {
var sum = 0
for(a <- arr) {
sum += a
}
sum
arr.sum
}
//可变参数
def add(arr: Int*):Int = {
var sum = 0
for(a <- arr) {
sum += a
}
sum
}
}
说明:
scala中的可变参数和java有一点不同,上例中第三个函数和第四个函数,在java中实际上是同一个函数,而在scala中是两个不同的函数。
在对第四个函数add(arr:Int*)就不能向java一样,直接传递一个数组,否则报错,类型不匹配。要想给一个可变参数传递数组,就需要将数组中的元素提取出来,再传递,操作方式如下:
val arr = Array(3, 4, 5)
ret = add(arr: _*)
scala的集合分为两类,一类是可变的集合(集合可以执行增删改查操作)另一种是不可变集合(集合元素在初始化的时候确定,后续只能进行查,有的可以进行修改,有的不可以),二者可能名称一样,但是在不同的包下面,对应的包为:scala…collection.mutable和scala.collection.immutable.
scala默认使用的集合,或者默认导入的包是immutable
Array可以理解为java中的数组
1、数组的定义
object Demo1 {
def main(args: Array[String]): Unit = {
//创建数组
//int [] a = new int [5];
val arr = new Array[Int](3)
for (a <- arr){
println(a)
}
println("*********************")
//叫做数组类Array的半生对象的创建方式(没有使用new )
val arr1 = Array(1,2,3,4)// int [] a = {1,2,3,4}
for (a<-arr1){
println(a)
}
}
}
2、获取数组中的元素
println("*****************")
//获取数组中的元素(通过数组的下标)
println("下标为2的元素是"+arr1(2))
//scala中的中括号被泛型占用Array[String] 所以只能使用()
arr1(3)= -12
println(arr1(3))
$$
$$
3、元素的遍历
println("----------数组遍历------------")
//普通的for
for (a<-arr1){
println(a)
}
//for each
arr1.foreach(a=> println(a))
4、数组的长度
//数组的长度
println("----------数组长度------------")
println(arr1.length)
ArrayBuffer就可以理解为java中的ArrayList
1、可变数组的定义
println("----------数组的定义----------")
val ab = new ArrayBuffer[Int](3)//普通创建方式,创建一个初始化长度为3的Int类型的ArrayList
val ab1 =ArrayBuffer(1,2,3,4,5)//伴生方式
2、元素的操作
println("----------数组的操作----------")
//添加元素
//append 追加元素---(尾部追加元素)append
ab.append(4,2,3)
println("ab:"+ab)
//ab.insert(index,元素*)在指定的位置插入元素参数1是index的位置
ab.insert(1,-2,-1)
println("ab:"+ab)
//修改元素
ab(2)=0
println("ab"+ab)
//删除元素
//方式一
ab.remove(3)//删除下标对应的元素
println("ab"+ab)
ab.remove(1,2)//参数1下标 参数2 删除的个数
println("ab"+ab)
//方式二
ab.drop(2) //从首部开始删除N个元素,并返回一个新集合,原集合不变
ab.dropRight(2)///从尾部开始删除N个元素,并返回一个新集合,原集合不变
println(ab)
3、数组的遍历
遍历和Array方式一样
4、数组的长度
println("----------数组的长度----------")
println( "ab的长度:"+ab.length)
println( "ab的长度:"+ab.size)
1、包含
ab.contains(3)
2、数组基本算术运算
println(ab.sum)//求和
println(ab.max)//最大值
println(ab.min)//最小值
3、Array和ArrayBuffer之间的互相转换
Array----->ArrayBuffer
array.toBuffer
ArrayBuffer---->Array
array,toArray
4、数组内容的显示
//mkString的方法 将数组元素使用分隔符串成一个字符串
println(ab.mkString(","))//分隔符为 ,
println(ab.mkString("[",",","]")) //分隔符为, 其实借宿符号位【,】
Map是一个对偶,映射的k-v键值对的集合,在一个map中可以包含若干组k-v映射关系,前提条件是K不能重复。同样map也有可变和不可变之分
import scala.collection.immutable.Map
1、定义
因为Map是一个类似java中的接口,无法直接创建对象,所以需要使用它的伴生对象创建
val map = Map[K,V]()
2、初始化
val map = Map[String,Integer](
("pgb"->27),
"jdr"-> 18,
"jdr"-> 19,
"lhj" ->38
)
3、crud
/*
* 通过get(k)获得的结果为Option(可选的)
* 可以将option理解为java中的一个抽象类或者接口
* option有两个仅有的子类:
* Some --->代表有值
* None --->没有值
* 获取Some对应的值,可以通过some.get获取,对None调用get会报错
* NoSuchElementException
* 所以为了避免这个错误,我们可以调用option的另一个方法规避:getOrElse
*/
var gAge = map.get("pgp")
println(gAge.get)
gAge = map.get("zxn")
println(gAge.getOrElse(-1))
println("----获取的另外一种方式-----")
println(map("pgp"))
//判断key是否包含
if(map.contains("pga")) {
println(map("pga"))
}
1、创建并初始化
val map =mutable.map[String,Integer](
"zgj" -> 14
)
ring,Integer](
(“pgb”->27),
“jdr”-> 18,
“jdr”-> 19,
“lhj” ->38
)
3、crud
```scala
/*
* 通过get(k)获得的结果为Option(可选的)
* 可以将option理解为java中的一个抽象类或者接口
* option有两个仅有的子类:
* Some --->代表有值
* None --->没有值
* 获取Some对应的值,可以通过some.get获取,对None调用get会报错
* NoSuchElementException
* 所以为了避免这个错误,我们可以调用option的另一个方法规避:getOrElse
*/
var gAge = map.get("pgp")
println(gAge.get)
gAge = map.get("zxn")
println(gAge.getOrElse(-1))
println("----获取的另外一种方式-----")
println(map("pgp"))
//判断key是否包含
if(map.contains("pga")) {
println(map("pga"))
}
1、创建并初始化
val map =mutable.map[String,Integer](
"zgj" -> 14
)